Rasagar/Library/PackageCache/com.unity.package-validation-suite/Lib/PvpXray/xxHash.cs
2024-08-26 23:07:20 +03:00

275 lines
10 KiB
C#

// This file was copied and modified from
// https://github.com/xoofx/smash/blob/v0.3.0/src/Smash/xxHash.cs
// Copyright (c) 2017, Alexandre Mutel
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification
// , are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/*
* xxHash - Fast Hash algorithm
* Copyright (C) 2012-2016, Yann Collet
*
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You can contact the author at :
* - xxHash homepage: http://www.xxhash.com
* - xxHash source repository : https://github.com/Cyan4973/xxHash
*/
using System;
using System.Runtime.CompilerServices;
// ReSharper disable InconsistentNaming
using U64 = System.UInt64;
using U32 = System.UInt32;
namespace PvpXray
{
/// <summary>
/// The xxHash method. Use <see cref="Create64"/>.
/// </summary>
static class xxHash
{
[MethodImpl((MethodImplOptions)0x100)] // Aggressive inlining for all .NETs
public static Hash64 Create64(ulong seed)
{
return new Hash64(seed);
}
/// <summary>
/// xxHash for computing a 64bit hash.
/// </summary>
public struct Hash64
{
const U64 PRIME64_1 = 11400714785074694791UL;
const U64 PRIME64_2 = 14029467366897019727UL;
const U64 PRIME64_3 = 1609587929392839161UL;
const U64 PRIME64_4 = 9650029242287828579UL;
// BUG: Prime #5 is unused. It is SUPPOSED to be used for inputs that are not a multiple
// of 32 bytes. Which explains why this xxHash implementation is broken for such inputs.
const U64 PRIME64_5 = 2870177450012600261UL;
U64 _v1;
U64 _v2;
U64 _v3;
U64 _v4;
ulong _writeCount;
ulong _length;
[MethodImpl((MethodImplOptions)0x100)] // Aggressive inlining for all .NETs
internal Hash64(ulong seed)
{
// NOTE: Duplicated with Reset method, keep in sync!
_v1 = seed + PRIME64_1 + PRIME64_2;
_v2 = seed + PRIME64_2;
_v3 = seed + 0;
_v4 = seed - PRIME64_1;
_writeCount = 0;
_length = 0;
}
[MethodImpl((MethodImplOptions)0x100)] // Aggressive inlining for all .NETs
void Write(ulong input)
{
WriteInternal(input);
_length += 8;
}
[MethodImpl((MethodImplOptions)0x100)] // Aggressive inlining for all .NETs
void Write(byte input)
{
WriteInternal(input * PRIME64_1);
_length += 1;
}
unsafe void Write(IntPtr buffer, ulong length)
{
if (buffer == IntPtr.Zero) throw new ArgumentNullException(nameof(buffer));
var pBuffer = (byte*)(void*)buffer;
const ulong FetchSize = sizeof(ulong) * 4;
while (length >= FetchSize)
{
var pulong = (ulong*)pBuffer;
Write(pulong[0]);
Write(pulong[1]);
Write(pulong[2]);
Write(pulong[3]);
pBuffer += FetchSize;
length -= FetchSize;
}
while (length >= 8)
{
Write(*(ulong*)pBuffer);
pBuffer += 8;
length -= 8;
}
while (length > 0)
{
Write(*(byte*)pBuffer);
pBuffer += 1;
length -= 1;
}
}
public unsafe void Write(byte[] buffer, int offset, int count)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset), "offset cannot be < 0");
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), "count cannot be < 0");
if (buffer.Length - offset < count)
throw new ArgumentException("Invalid offset + count > length");
if (count == 0)
{
return;
}
fixed (void* pByte = &buffer[offset])
{
Write(new IntPtr(pByte), (ulong)count);
}
}
[MethodImpl((MethodImplOptions)0x100)] // Aggressive inlining for all .NETs
public ulong Compute()
{
// This part is slightly different from xxHash to use a generic algorithm
// depending on the number of writes
// Might not give the exact same result as we are feeding all states v1,v2,v3,v4
// while xxHash calculates the hash differently for input buffer < 64/32/8/4 bytes
var h64 = XXH_rotl64(_v1, 1);
if (_writeCount >= 2)
{
h64 += XXH_rotl64(_v2, 7);
if (_writeCount >= 3)
{
h64 += XXH_rotl64(_v3, 12);
if (_writeCount >= 4)
{
h64 += XXH_rotl64(_v4, 18);
}
}
}
h64 = XXH64_mergeRound(h64, _v1);
if (_writeCount >= 2)
{
h64 = XXH64_mergeRound(h64, _v2);
if (_writeCount >= 3)
{
h64 = XXH64_mergeRound(h64, _v3);
if (_writeCount >= 4)
{
h64 = XXH64_mergeRound(h64, _v4);
}
}
}
h64 += _length;
h64 ^= h64 >> 33;
h64 *= PRIME64_2;
h64 ^= h64 >> 29;
h64 *= PRIME64_3;
h64 ^= h64 >> 32;
return h64;
}
[MethodImpl((MethodImplOptions)0x100)] // Aggressive inlining for all .NETs
void WriteInternal(ulong input)
{
switch (_writeCount & 3)
{
case 0:
_v1 = XXH64_round(_v1, input);
break;
case 1:
_v2 = XXH64_round(_v2, input);
break;
case 2:
_v3 = XXH64_round(_v3, input);
break;
case 3:
_v4 = XXH64_round(_v4, input);
break;
}
_writeCount++;
}
[MethodImpl((MethodImplOptions)0x100)] // Aggressive inlining for all .NETs
static U64 XXH_rotl64(U64 x, int r)
{
return (x << r) | (x >> (64 - r));
}
[MethodImpl((MethodImplOptions)0x100)] // Aggressive inlining for all .NETs
static U64 XXH64_round(U64 acc, U64 input)
{
acc += input * PRIME64_2;
acc = XXH_rotl64(acc, 31);
acc *= PRIME64_1;
return acc;
}
[MethodImpl((MethodImplOptions)0x100)] // Aggressive inlining for all .NETs
static U64 XXH64_mergeRound(U64 acc, U64 val)
{
val = XXH64_round(0, val);
acc ^= val;
acc = acc * PRIME64_1 + PRIME64_4;
return acc;
}
}
}
}