Editor/HumanoidRetargeter/Embedded/ZstdSharp/Unsafe/Xxhash.cs
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using static HumanoidRetargeterZstd.UnsafeHelper;
using System;
using System.Buffers.Binary;
using System.Numerics;

namespace HumanoidRetargeterZstd.Unsafe
{
    public static unsafe partial class Methods
    {

        /*!
         * @internal
         * @brief Modify this function to use a different routine than memcpy().
         */
private static void XXH_memcpy(void* dest, void* src, nuint size)
        {
            memcpy(dest, src, (uint)size);
        }
private static uint XXH_readLE32(void* ptr)
        {
            return BitConverter.IsLittleEndian ? MEM_read32(ptr) : BinaryPrimitives.ReverseEndianness(MEM_read32(ptr));
        }

        private static uint XXH_readLE32_align(void* ptr, XXH_alignment align)
        {
            if (align == XXH_alignment.XXH_unaligned)
            {
                return XXH_readLE32(ptr);
            }
            else
            {
                return BitConverter.IsLittleEndian ? *(uint*)ptr : BinaryPrimitives.ReverseEndianness(*(uint*)ptr);
            }
        }
private static ulong XXH_readLE64(void* ptr)
        {
            return BitConverter.IsLittleEndian ? MEM_read64(ptr) : BinaryPrimitives.ReverseEndianness(MEM_read64(ptr));
        }

        private static ulong XXH_readLE64_align(void* ptr, XXH_alignment align)
        {
            if (align == XXH_alignment.XXH_unaligned)
                return XXH_readLE64(ptr);
            else
                return BitConverter.IsLittleEndian ? *(ulong*)ptr : BinaryPrimitives.ReverseEndianness(*(ulong*)ptr);
        }

        /*! @copydoc XXH32_round */
private static ulong XXH64_round(ulong acc, ulong input)
        {
            acc += input * 0xC2B2AE3D27D4EB4FUL;
            acc = BitOperations.RotateLeft(acc, 31);
            acc *= 0x9E3779B185EBCA87UL;
            return acc;
        }
private static ulong XXH64_mergeRound(ulong acc, ulong val)
        {
            val = XXH64_round(0, val);
            acc ^= val;
            acc = acc * 0x9E3779B185EBCA87UL + 0x85EBCA77C2B2AE63UL;
            return acc;
        }

        /*! @copydoc XXH32_avalanche */
        private static ulong XXH64_avalanche(ulong hash)
        {
            hash ^= hash >> 33;
            hash *= 0xC2B2AE3D27D4EB4FUL;
            hash ^= hash >> 29;
            hash *= 0x165667B19E3779F9UL;
            hash ^= hash >> 32;
            return hash;
        }

        /*!
         * @internal
         * @brief Processes the last 0-31 bytes of @p ptr.
         *
         * There may be up to 31 bytes remaining to consume from the input.
         * This final stage will digest them to ensure that all input bytes are present
         * in the final mix.
         *
         * @param hash The hash to finalize.
         * @param ptr The pointer to the remaining input.
         * @param len The remaining length, modulo 32.
         * @param align Whether @p ptr is aligned.
         * @return The finalized hash
         * @see XXH32_finalize().
         */
        private static ulong XXH64_finalize(ulong hash, byte* ptr, nuint len, XXH_alignment align)
        {
            len &= 31;
            while (len >= 8)
            {
                ulong k1 = XXH64_round(0, XXH_readLE64_align(ptr, align));
                ptr += 8;
                hash ^= k1;
                hash = BitOperations.RotateLeft(hash, 27) * 0x9E3779B185EBCA87UL + 0x85EBCA77C2B2AE63UL;
                len -= 8;
            }

            if (len >= 4)
            {
                hash ^= XXH_readLE32_align(ptr, align) * 0x9E3779B185EBCA87UL;
                ptr += 4;
                hash = BitOperations.RotateLeft(hash, 23) * 0xC2B2AE3D27D4EB4FUL + 0x165667B19E3779F9UL;
                len -= 4;
            }

            while (len > 0)
            {
                hash ^= *ptr++ * 0x27D4EB2F165667C5UL;
                hash = BitOperations.RotateLeft(hash, 11) * 0x9E3779B185EBCA87UL;
                --len;
            }

            return XXH64_avalanche(hash);
        }

        /*!
         * @internal
         * @brief The implementation for @ref XXH64().
         *
         * @param input , len , seed Directly passed from @ref XXH64().
         * @param align Whether @p input is aligned.
         * @return The calculated hash.
         */
        private static ulong XXH64_endian_align(byte* input, nuint len, ulong seed, XXH_alignment align)
        {
            ulong h64;
            if (len >= 32)
            {
                byte* bEnd = input + len;
                byte* limit = bEnd - 31;
                ulong v1 = seed + 0x9E3779B185EBCA87UL + 0xC2B2AE3D27D4EB4FUL;
                ulong v2 = seed + 0xC2B2AE3D27D4EB4FUL;
                ulong v3 = seed + 0;
                ulong v4 = seed - 0x9E3779B185EBCA87UL;
                do
                {
                    v1 = XXH64_round(v1, XXH_readLE64_align(input, align));
                    input += 8;
                    v2 = XXH64_round(v2, XXH_readLE64_align(input, align));
                    input += 8;
                    v3 = XXH64_round(v3, XXH_readLE64_align(input, align));
                    input += 8;
                    v4 = XXH64_round(v4, XXH_readLE64_align(input, align));
                    input += 8;
                }
                while (input < limit);
                h64 = BitOperations.RotateLeft(v1, 1) + BitOperations.RotateLeft(v2, 7) + BitOperations.RotateLeft(v3, 12) + BitOperations.RotateLeft(v4, 18);
                h64 = XXH64_mergeRound(h64, v1);
                h64 = XXH64_mergeRound(h64, v2);
                h64 = XXH64_mergeRound(h64, v3);
                h64 = XXH64_mergeRound(h64, v4);
            }
            else
            {
                h64 = seed + 0x27D4EB2F165667C5UL;
            }

            h64 += len;
            return XXH64_finalize(h64, input, len, align);
        }

        /*! @ingroup XXH64_family */
        private static ulong ZSTD_XXH64(void* input, nuint len, ulong seed)
        {
            return XXH64_endian_align((byte*)input, len, seed, XXH_alignment.XXH_unaligned);
        }

        /*! @ingroup XXH64_family */
        private static XXH_errorcode ZSTD_XXH64_reset(XXH64_state_s* statePtr, ulong seed)
        {
            *statePtr = new XXH64_state_s();
            statePtr->v[0] = seed + 0x9E3779B185EBCA87UL + 0xC2B2AE3D27D4EB4FUL;
            statePtr->v[1] = seed + 0xC2B2AE3D27D4EB4FUL;
            statePtr->v[2] = seed + 0;
            statePtr->v[3] = seed - 0x9E3779B185EBCA87UL;
            return XXH_errorcode.XXH_OK;
        }

        /*! @ingroup XXH64_family */
        private static XXH_errorcode ZSTD_XXH64_update(XXH64_state_s* state, void* input, nuint len)
        {
            if (input == null)
            {
                return XXH_errorcode.XXH_OK;
            }

            {
                byte* p = (byte*)input;
                byte* bEnd = p + len;
                state->total_len += len;
                if (state->memsize + len < 32)
                {
                    XXH_memcpy((byte*)state->mem64 + state->memsize, input, len);
                    state->memsize += (uint)len;
                    return XXH_errorcode.XXH_OK;
                }

                if (state->memsize != 0)
                {
                    XXH_memcpy((byte*)state->mem64 + state->memsize, input, 32 - state->memsize);
                    state->v[0] = XXH64_round(state->v[0], XXH_readLE64(state->mem64 + 0));
                    state->v[1] = XXH64_round(state->v[1], XXH_readLE64(state->mem64 + 1));
                    state->v[2] = XXH64_round(state->v[2], XXH_readLE64(state->mem64 + 2));
                    state->v[3] = XXH64_round(state->v[3], XXH_readLE64(state->mem64 + 3));
                    p += 32 - state->memsize;
                    state->memsize = 0;
                }

                if (p + 32 <= bEnd)
                {
                    byte* limit = bEnd - 32;
                    do
                    {
                        state->v[0] = XXH64_round(state->v[0], XXH_readLE64(p));
                        p += 8;
                        state->v[1] = XXH64_round(state->v[1], XXH_readLE64(p));
                        p += 8;
                        state->v[2] = XXH64_round(state->v[2], XXH_readLE64(p));
                        p += 8;
                        state->v[3] = XXH64_round(state->v[3], XXH_readLE64(p));
                        p += 8;
                    }
                    while (p <= limit);
                }

                if (p < bEnd)
                {
                    XXH_memcpy(state->mem64, p, (nuint)(bEnd - p));
                    state->memsize = (uint)(bEnd - p);
                }
            }

            return XXH_errorcode.XXH_OK;
        }

        /*! @ingroup XXH64_family */
        private static ulong ZSTD_XXH64_digest(XXH64_state_s* state)
        {
            ulong h64;
            if (state->total_len >= 32)
            {
                h64 = BitOperations.RotateLeft(state->v[0], 1) + BitOperations.RotateLeft(state->v[1], 7) + BitOperations.RotateLeft(state->v[2], 12) + BitOperations.RotateLeft(state->v[3], 18);
                h64 = XXH64_mergeRound(h64, state->v[0]);
                h64 = XXH64_mergeRound(h64, state->v[1]);
                h64 = XXH64_mergeRound(h64, state->v[2]);
                h64 = XXH64_mergeRound(h64, state->v[3]);
            }
            else
            {
                h64 = state->v[2] + 0x27D4EB2F165667C5UL;
            }

            h64 += state->total_len;
            return XXH64_finalize(h64, (byte*)state->mem64, (nuint)state->total_len, XXH_alignment.XXH_aligned);
        }
    }
}