解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
11.37 KiB
X
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
#if NET5_0_OR_GREATER
using System.Buffers.Binary;
#endif

namespace NewLife.Security;

/// <summary>高性能低碰撞Murmur128哈希算法</summary>
/// <remarks>
/// Redis等大量使用,比MD5要好
/// </remarks>
public class Murmur128 : HashAlgorithm
{
    #region 属性
    const UInt64 C1 = 0x87c37b91114253d5;
    const UInt64 C2 = 0x4cf5ad432745937f;

    private readonly UInt32 _Seed;
    /// <summary>种子</summary>
    public UInt32 Seed => _Seed;

    /// <summary>哈希大小</summary>
    public override Int32 HashSize => 128;

    private Int32 _Length;
    private UInt64 _H1;
    private UInt64 _H2;

    // 跨块尾部缓冲:分块流式哈希时缓存不足 16 字节的数据,凑满后按完整块处理
    private Byte[]? _tail;
    private Int32 _tailLen;
    #endregion

    #region 构造
    /// <summary>实例化</summary>
    /// <param name="seed"></param>
    public Murmur128(UInt32 seed = 0)
    {
        _Seed = seed;
        // 必须设置基类 HashSizeValue(位)。基类 TryComputeHash/TryHashFinal 依赖该字段做一致性校验,
        // 仅重写 HashSize 属性不会更新该字段,导致 TryComputeHash 抛"The algorithm's implementation is incorrect"
        HashSizeValue = 128;
        Reset();
    }
    #endregion

    #region 方法
    private void Reset()
    {
        // 初始化哈希值到种子
        _H1 = _H2 = Seed;

        // 重置长度为0
        _Length = 0;

        // 清空跨块尾部缓冲
        _tailLen = 0;
    }

    /// <summary>初始化</summary>
    public override void Initialize() => Reset();

    /// <summary>哈希核心</summary>
    /// <param name="array"></param>
    /// <param name="ibStart"></param>
    /// <param name="cbSize"></param>
    protected override void HashCore(Byte[] array, Int32 ibStart, Int32 cbSize)
    {
#if NET5_0_OR_GREATER
        // NET5+ 委托 Span 路径,复用 Span 版 Body/Tail,避免双份实现
        HashCore(array.AsSpan(ibStart, cbSize));
#else
        Core(array, ibStart, cbSize);
#endif
    }

#if !NET5_0_OR_GREATER
    // 数组版哈希核心(低版本 TFM)。跨块缓存不足 16 字节的数据,凑满后按完整块处理
    private void Core(Byte[] data, Int32 offset, Int32 count)
    {
        // 增加长度
        _Length += count;

        // 拼上之前缓存的尾部,凑满 16 字节块
        if (_tailLen > 0)
        {
            var need = 16 - _tailLen;
            var take = Math.Min(need, count);
            Buffer.BlockCopy(data, offset, _tail!, _tailLen, take);
            _tailLen += take;
            offset += take;
            count -= take;
            if (_tailLen == 16)
            {
                Body(_tail!, 0, 16);
                _tailLen = 0;
            }
        }

        // 处理完整 16 字节块
        var blocks = count / 16;
        if (blocks > 0)
        {
            Body(data, offset, blocks * 16);
            offset += blocks * 16;
            count -= blocks * 16;
        }

        // 剩余不足 16 字节,缓存
        if (count > 0)
        {
            _tail ??= new Byte[16];
            Buffer.BlockCopy(data, offset, _tail, 0, count);
            _tailLen = count;
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private void Body(Byte[] data, Int32 start, Int32 length)
    {
        var remainder = length & 15;
        var alignedLength = start + (length - remainder);
        for (var i = start; i < alignedLength; i += 16)
        {
            _H1 ^= RotateLeft(data.ToUInt64(i) * C1, 31) * C2;
            _H1 = (RotateLeft(_H1, 27) + _H2) * 5 + 0x52dce729;

            _H2 ^= RotateLeft(data.ToUInt64(i + 8) * C2, 33) * C1;
            _H2 = (RotateLeft(_H2, 31) + _H1) * 5 + 0x38495ab5;
        }

        if (remainder > 0)
            Tail(data, alignedLength, remainder);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private void Tail(Byte[] tail, Int32 start, Int32 remaining)
    {
        // create our keys and initialize to 0
        UInt64 k1 = 0, k2 = 0;

        // determine how many bytes we have left to work with based on length
        switch (remaining)
        {
            case 15: k2 ^= (UInt64)tail[start + 14] << 48; goto case 14;
            case 14: k2 ^= (UInt64)tail[start + 13] << 40; goto case 13;
            case 13: k2 ^= (UInt64)tail[start + 12] << 32; goto case 12;
            case 12: k2 ^= (UInt64)tail[start + 11] << 24; goto case 11;
            case 11: k2 ^= (UInt64)tail[start + 10] << 16; goto case 10;
            case 10: k2 ^= (UInt64)tail[start + 9] << 8; goto case 9;
            case 9: k2 ^= (UInt64)tail[start + 8] << 0; goto case 8;
            case 8: k1 ^= (UInt64)tail[start + 7] << 56; goto case 7;
            case 7: k1 ^= (UInt64)tail[start + 6] << 48; goto case 6;
            case 6: k1 ^= (UInt64)tail[start + 5] << 40; goto case 5;
            case 5: k1 ^= (UInt64)tail[start + 4] << 32; goto case 4;
            case 4: k1 ^= (UInt64)tail[start + 3] << 24; goto case 3;
            case 3: k1 ^= (UInt64)tail[start + 2] << 16; goto case 2;
            case 2: k1 ^= (UInt64)tail[start + 1] << 8; goto case 1;
            case 1: k1 ^= (UInt64)tail[start] << 0; break;
        }

        _H2 ^= RotateLeft(k2 * C2, 33) * C1;
        _H1 ^= RotateLeft(k1 * C1, 31) * C2;
    }
#endif

    /// <summary>哈希结束</summary>
    /// <returns></returns>
    protected override Byte[] HashFinal()
    {
        // 处理跨块缓存的尾部
        if (_tailLen > 0)
        {
#if NET5_0_OR_GREATER
            Tail(_tail.AsSpan(0, _tailLen), 0, _tailLen);
#else
            Tail(_tail!, 0, _tailLen);
#endif
            _tailLen = 0;
        }

        var result = new Byte[16];
        FinalizeHash(result);

        return result;
    }

    /// <summary>计算最终哈希值写入目标缓冲区(小端序)。零分配</summary>
    /// <param name="destination">目标缓冲区,长度至少 16</param>
    private void FinalizeHash(Span<Byte> destination)
    {
        var len = (UInt64)_Length;
        _H1 ^= len; _H2 ^= len;

        _H1 += _H2;
        _H2 += _H1;

        _H1 = FMix(_H1);
        _H2 = FMix(_H2);

        _H1 += _H2;
        _H2 += _H1;

        // 直接写入字节,避免 BitConverter.GetBytes 分配
        for (var i = 0; i < 8; i++)
        {
            destination[i] = (Byte)(_H1 >> (8 * i));
            destination[8 + i] = (Byte)(_H2 >> (8 * i));
        }
    }

#if NET5_0_OR_GREATER
    /// <summary>Span 哈希核心。避免 ArrayPool 拷贝,支撑零分配 TryComputeHash</summary>
    /// <param name="source">源数据</param>
    protected override void HashCore(ReadOnlySpan<Byte> source)
    {
        Core(source);
    }

    // Span 版哈希核心。跨块缓存不足 16 字节的数据,凑满后按完整块处理
    private void Core(ReadOnlySpan<Byte> data)
    {
        // 增加长度
        _Length += data.Length;

        // 拼上之前缓存的尾部,凑满 16 字节块
        if (_tailLen > 0)
        {
            var need = 16 - _tailLen;
            var take = Math.Min(need, data.Length);
            data[..take].CopyTo(_tail.AsSpan(_tailLen));
            _tailLen += take;
            data = data[take..];
            if (_tailLen == 16)
            {
                Body(_tail);
                _tailLen = 0;
            }
        }

        // 处理完整 16 字节块
        var blocks = data.Length / 16;
        if (blocks > 0)
        {
            Body(data[..(blocks * 16)]);
            data = data[(blocks * 16)..];
        }

        // 剩余不足 16 字节,缓存
        if (data.Length > 0)
        {
            _tail ??= new Byte[16];
            data.CopyTo(_tail);
            _tailLen = data.Length;
        }
    }

    /// <summary>Span 哈希结束。零分配写入目标缓冲区</summary>
    /// <param name="destination">目标缓冲区,长度至少 16</param>
    /// <param name="bytesWritten">写入字节数</param>
    /// <returns>成功返回 true</returns>
    protected override Boolean TryHashFinal(Span<Byte> destination, out Int32 bytesWritten)
    {
        if (destination.Length < 16)
        {
            bytesWritten = 0;
            return false;
        }

        // 处理跨块缓存的尾部
        if (_tailLen > 0)
        {
            Tail(_tail.AsSpan(0, _tailLen), 0, _tailLen);
            _tailLen = 0;
        }

        FinalizeHash(destination);
        bytesWritten = 16;

        return true;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private void Body(ReadOnlySpan<Byte> data)
    {
        var remainder = data.Length & 15;
        var alignedLength = data.Length - remainder;
        for (var i = 0; i < alignedLength; i += 16)
        {
            _H1 ^= RotateLeft(BinaryPrimitives.ReadUInt64LittleEndian(data.Slice(i)) * C1, 31) * C2;
            _H1 = (RotateLeft(_H1, 27) + _H2) * 5 + 0x52dce729;

            _H2 ^= RotateLeft(BinaryPrimitives.ReadUInt64LittleEndian(data.Slice(i + 8)) * C2, 33) * C1;
            _H2 = (RotateLeft(_H2, 31) + _H1) * 5 + 0x38495ab5;
        }

        if (remainder > 0)
            Tail(data, alignedLength, remainder);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private void Tail(ReadOnlySpan<Byte> tail, Int32 start, Int32 remaining)
    {
        // create our keys and initialize to 0
        UInt64 k1 = 0, k2 = 0;

        // determine how many bytes we have left to work with based on length
        switch (remaining)
        {
            case 15: k2 ^= (UInt64)tail[start + 14] << 48; goto case 14;
            case 14: k2 ^= (UInt64)tail[start + 13] << 40; goto case 13;
            case 13: k2 ^= (UInt64)tail[start + 12] << 32; goto case 12;
            case 12: k2 ^= (UInt64)tail[start + 11] << 24; goto case 11;
            case 11: k2 ^= (UInt64)tail[start + 10] << 16; goto case 10;
            case 10: k2 ^= (UInt64)tail[start + 9] << 8; goto case 9;
            case 9: k2 ^= (UInt64)tail[start + 8] << 0; goto case 8;
            case 8: k1 ^= (UInt64)tail[start + 7] << 56; goto case 7;
            case 7: k1 ^= (UInt64)tail[start + 6] << 48; goto case 6;
            case 6: k1 ^= (UInt64)tail[start + 5] << 40; goto case 5;
            case 5: k1 ^= (UInt64)tail[start + 4] << 32; goto case 4;
            case 4: k1 ^= (UInt64)tail[start + 3] << 24; goto case 3;
            case 3: k1 ^= (UInt64)tail[start + 2] << 16; goto case 2;
            case 2: k1 ^= (UInt64)tail[start + 1] << 8; goto case 1;
            case 1: k1 ^= (UInt64)tail[start] << 0; break;
        }

        _H2 ^= RotateLeft(k2 * C2, 33) * C1;
        _H1 ^= RotateLeft(k1 * C1, 31) * C2;
    }
#endif
    #endregion

    #region 辅助
    //[MethodImpl(MethodImplOptions.AggressiveInlining)]
    //private static UInt32 RotateLeft(UInt32 x, Byte r) => (x << r) | (x >> (32 - r));

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static UInt64 RotateLeft(UInt64 x, Byte r) => (x << r) | (x >> (64 - r));

    //[MethodImpl(MethodImplOptions.AggressiveInlining)]
    //private static UInt32 FMix(UInt32 h)
    //{
    //    h = (h ^ (h >> 16)) * 0x85ebca6b;
    //    h = (h ^ (h >> 13)) * 0xc2b2ae35;
    //    return h ^ (h >> 16);
    //}

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static UInt64 FMix(UInt64 h)
    {
        h = (h ^ (h >> 33)) * 0xff51afd7ed558ccd;
        h = (h ^ (h >> 33)) * 0xc4ceb9fe1a85ec53;

        return (h ^ (h >> 33));
    }
    #endregion
}