Editor/HumanoidRetargeter/Embedded/ZstdSharp/Unsafe/FseDecompress.cs
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using static HumanoidRetargeterZstd.UnsafeHelper;
using System.Runtime.CompilerServices;

namespace HumanoidRetargeterZstd.Unsafe
{
    public static unsafe partial class Methods
    {
        private static nuint FSE_buildDTable_internal(uint* dt, short* normalizedCounter, uint maxSymbolValue, uint tableLog, void* workSpace, nuint wkspSize)
        {
            /* because *dt is unsigned, 32-bits aligned on 32-bits */
            void* tdPtr = dt + 1;
            FSE_decode_t* tableDecode = (FSE_decode_t*)tdPtr;
            ushort* symbolNext = (ushort*)workSpace;
            byte* spread = (byte*)(symbolNext + maxSymbolValue + 1);
            uint maxSV1 = maxSymbolValue + 1;
            uint tableSize = (uint)(1 << (int)tableLog);
            uint highThreshold = tableSize - 1;
            if (sizeof(short) * (maxSymbolValue + 1) + (1UL << (int)tableLog) + 8 > wkspSize)
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooLarge));
            if (maxSymbolValue > 255)
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooLarge));
            if (tableLog > 14 - 2)
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge));
            {
                FSE_DTableHeader DTableH;
                DTableH.tableLog = (ushort)tableLog;
                DTableH.fastMode = 1;
                {
                    short largeLimit = (short)(1 << (int)(tableLog - 1));
                    uint s;
                    for (s = 0; s < maxSV1; s++)
                    {
                        if (normalizedCounter[s] == -1)
                        {
                            tableDecode[highThreshold--].symbol = (byte)s;
                            symbolNext[s] = 1;
                        }
                        else
                        {
                            if (normalizedCounter[s] >= largeLimit)
                                DTableH.fastMode = 0;
                            symbolNext[s] = (ushort)normalizedCounter[s];
                        }
                    }
                }

                memcpy(dt, &DTableH, (uint)sizeof(FSE_DTableHeader));
            }

            if (highThreshold == tableSize - 1)
            {
                nuint tableMask = tableSize - 1;
                nuint step = (tableSize >> 1) + (tableSize >> 3) + 3;
                {
                    const ulong add = 0x0101010101010101UL;
                    nuint pos = 0;
                    ulong sv = 0;
                    uint s;
                    for (s = 0; s < maxSV1; ++s, sv += add)
                    {
                        int i;
                        int n = normalizedCounter[s];
                        MEM_write64(spread + pos, sv);
                        for (i = 8; i < n; i += 8)
                        {
                            MEM_write64(spread + pos + i, sv);
                        }

                        pos += (nuint)n;
                    }
                }

                {
                    nuint position = 0;
                    nuint s;
                    const nuint unroll = 2;
                    assert(tableSize % unroll == 0);
                    for (s = 0; s < tableSize; s += unroll)
                    {
                        nuint u;
                        for (u = 0; u < unroll; ++u)
                        {
                            nuint uPosition = position + u * step & tableMask;
                            tableDecode[uPosition].symbol = spread[s + u];
                        }

                        position = position + unroll * step & tableMask;
                    }

                    assert(position == 0);
                }
            }
            else
            {
                uint tableMask = tableSize - 1;
                uint step = (tableSize >> 1) + (tableSize >> 3) + 3;
                uint s, position = 0;
                for (s = 0; s < maxSV1; s++)
                {
                    int i;
                    for (i = 0; i < normalizedCounter[s]; i++)
                    {
                        tableDecode[position].symbol = (byte)s;
                        position = position + step & tableMask;
                        while (position > highThreshold)
                            position = position + step & tableMask;
                    }
                }

                if (position != 0)
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC));
            }

            {
                uint u;
                for (u = 0; u < tableSize; u++)
                {
                    byte symbol = tableDecode[u].symbol;
                    uint nextState = symbolNext[symbol]++;
                    tableDecode[u].nbBits = (byte)(tableLog - ZSTD_highbit32(nextState));
                    tableDecode[u].newState = (ushort)((nextState << tableDecode[u].nbBits) - tableSize);
                }
            }

            return 0;
        }

        /*-*******************************************************
         *  Decompression (Byte symbols)
         *********************************************************/
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static nuint FSE_decompress_usingDTable_generic(void* dst, nuint maxDstSize, void* cSrc, nuint cSrcSize, uint* dt, uint fast)
        {
            byte* ostart = (byte*)dst;
            byte* op = ostart;
            byte* omax = op + maxDstSize;
            byte* olimit = omax - 3;
            BIT_DStream_t bitD;
            System.Runtime.CompilerServices.Unsafe.SkipInit(out bitD);
            FSE_DState_t state1;
            System.Runtime.CompilerServices.Unsafe.SkipInit(out state1);
            FSE_DState_t state2;
            System.Runtime.CompilerServices.Unsafe.SkipInit(out state2);
            {
                /* Init */
                nuint _var_err__ = BIT_initDStream(ref bitD, cSrc, cSrcSize);
                if (ERR_isError(_var_err__))
                    return _var_err__;
            }

            FSE_initDState(ref state1, ref bitD, dt);
            FSE_initDState(ref state2, ref bitD, dt);

            var bitD_bitContainer = bitD.bitContainer;
            var bitD_bitsConsumed = bitD.bitsConsumed;
            var bitD_ptr = bitD.ptr;
            var bitD_start = bitD.start;
            var bitD_limitPtr = bitD.limitPtr;

            if (BIT_reloadDStream(ref bitD_bitContainer, ref bitD_bitsConsumed, ref bitD_ptr, bitD_start, bitD_limitPtr) == BIT_DStream_status.BIT_DStream_overflow)
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
            }

            for (; BIT_reloadDStream(ref bitD_bitContainer, ref bitD_bitsConsumed, ref bitD_ptr, bitD_start, bitD_limitPtr) == BIT_DStream_status.BIT_DStream_unfinished && op < olimit; op += 4)
            {
                op[0] = fast != 0 ? FSE_decodeSymbolFast(ref state1, bitD_bitContainer, ref bitD_bitsConsumed) : FSE_decodeSymbol(ref state1, bitD_bitContainer, ref bitD_bitsConsumed);
                if ((14 - 2) * 2 + 7 > sizeof(nuint) * 8)
                    BIT_reloadDStream(ref bitD_bitContainer, ref bitD_bitsConsumed, ref bitD_ptr, bitD_start, bitD_limitPtr);
                op[1] = fast != 0 ? FSE_decodeSymbolFast(ref state2, bitD_bitContainer, ref bitD_bitsConsumed) : FSE_decodeSymbol(ref state2, bitD_bitContainer, ref bitD_bitsConsumed);
                if ((14 - 2) * 4 + 7 > sizeof(nuint) * 8)
                {
                    if (BIT_reloadDStream(ref bitD_bitContainer, ref bitD_bitsConsumed, ref bitD_ptr, bitD_start, bitD_limitPtr) > BIT_DStream_status.BIT_DStream_unfinished)
                    {
                        op += 2;
                        break;
                    }
                }

                op[2] = fast != 0 ? FSE_decodeSymbolFast(ref state1, bitD_bitContainer, ref bitD_bitsConsumed) : FSE_decodeSymbol(ref state1, bitD_bitContainer, ref bitD_bitsConsumed);
                if ((14 - 2) * 2 + 7 > sizeof(nuint) * 8)
                    BIT_reloadDStream(ref bitD_bitContainer, ref bitD_bitsConsumed, ref bitD_ptr, bitD_start, bitD_limitPtr);
                op[3] = fast != 0 ? FSE_decodeSymbolFast(ref state2, bitD_bitContainer, ref bitD_bitsConsumed) : FSE_decodeSymbol(ref state2, bitD_bitContainer, ref bitD_bitsConsumed);
            }

            while (true)
            {
                if (op > omax - 2)
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                *op++ = fast != 0 ? FSE_decodeSymbolFast(ref state1, bitD_bitContainer, ref bitD_bitsConsumed) : FSE_decodeSymbol(ref state1, bitD_bitContainer, ref bitD_bitsConsumed);
                if (BIT_reloadDStream(ref bitD_bitContainer, ref bitD_bitsConsumed, ref bitD_ptr, bitD_start, bitD_limitPtr) == BIT_DStream_status.BIT_DStream_overflow)
                {
                    *op++ = fast != 0 ? FSE_decodeSymbolFast(ref state2, bitD_bitContainer, ref bitD_bitsConsumed) : FSE_decodeSymbol(ref state2, bitD_bitContainer, ref bitD_bitsConsumed);
                    break;
                }

                if (op > omax - 2)
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                *op++ = fast != 0 ? FSE_decodeSymbolFast(ref state2, bitD_bitContainer, ref bitD_bitsConsumed) : FSE_decodeSymbol(ref state2, bitD_bitContainer, ref bitD_bitsConsumed);
                if (BIT_reloadDStream(ref bitD_bitContainer, ref bitD_bitsConsumed, ref bitD_ptr, bitD_start, bitD_limitPtr) == BIT_DStream_status.BIT_DStream_overflow)
                {
                    *op++ = fast != 0 ? FSE_decodeSymbolFast(ref state1, bitD_bitContainer, ref bitD_bitsConsumed) : FSE_decodeSymbol(ref state1, bitD_bitContainer, ref bitD_bitsConsumed);
                    break;
                }
            }

            assert(op >= ostart);
            return (nuint)(op - ostart);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static nuint FSE_decompress_wksp_body(void* dst, nuint dstCapacity, void* cSrc, nuint cSrcSize, uint maxLog, void* workSpace, nuint wkspSize, int bmi2)
        {
            byte* istart = (byte*)cSrc;
            byte* ip = istart;
            uint tableLog;
            uint maxSymbolValue = 255;
            FSE_DecompressWksp* wksp = (FSE_DecompressWksp*)workSpace;
            nuint dtablePos = (nuint)(sizeof(FSE_DecompressWksp) / sizeof(uint));
            uint* dtable = (uint*)workSpace + dtablePos;
            if (wkspSize < (nuint)sizeof(FSE_DecompressWksp))
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC));
            {
                nuint NCountLength = FSE_readNCount_bmi2(wksp->ncount, &maxSymbolValue, &tableLog, istart, cSrcSize, bmi2);
                if (ERR_isError(NCountLength))
                    return NCountLength;
                if (tableLog > maxLog)
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge));
                assert(NCountLength <= cSrcSize);
                ip += NCountLength;
                cSrcSize -= NCountLength;
            }

            if (((ulong)(1 + (1 << (int)tableLog) + 1) + (sizeof(short) * (maxSymbolValue + 1) + (1UL << (int)tableLog) + 8 + sizeof(uint) - 1) / sizeof(uint) + (255 + 1) / 2 + 1) * sizeof(uint) > wkspSize)
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge));
            assert((nuint)(sizeof(FSE_DecompressWksp) + (1 + (1 << (int)tableLog)) * sizeof(uint)) <= wkspSize);
            workSpace = (byte*)workSpace + sizeof(FSE_DecompressWksp) + (1 + (1 << (int)tableLog)) * sizeof(uint);
            wkspSize -= (nuint)(sizeof(FSE_DecompressWksp) + (1 + (1 << (int)tableLog)) * sizeof(uint));
            {
                nuint _var_err__ = FSE_buildDTable_internal(dtable, wksp->ncount, maxSymbolValue, tableLog, workSpace, wkspSize);
                if (ERR_isError(_var_err__))
                    return _var_err__;
            }

            {
                void* ptr = dtable;
                FSE_DTableHeader* DTableH = (FSE_DTableHeader*)ptr;
                uint fastMode = DTableH->fastMode;
                if (fastMode != 0)
                    return FSE_decompress_usingDTable_generic(dst, dstCapacity, ip, cSrcSize, dtable, 1);
                return FSE_decompress_usingDTable_generic(dst, dstCapacity, ip, cSrcSize, dtable, 0);
            }
        }

        /* Avoids the FORCE_INLINE of the _body() function. */
        private static nuint FSE_decompress_wksp_body_default(void* dst, nuint dstCapacity, void* cSrc, nuint cSrcSize, uint maxLog, void* workSpace, nuint wkspSize)
        {
            return FSE_decompress_wksp_body(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize, 0);
        }

        private static nuint FSE_decompress_wksp_bmi2(void* dst, nuint dstCapacity, void* cSrc, nuint cSrcSize, uint maxLog, void* workSpace, nuint wkspSize, int bmi2)
        {
            return FSE_decompress_wksp_body_default(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize);
        }
    }
}