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

namespace HumanoidRetargeterZstd.Unsafe
{
    public static unsafe partial class Methods
    {
        /*_*******************************************************
         *  Memory operations
         **********************************************************/
        private static void ZSTD_copy4(void* dst, void* src)
        {
            memcpy(dst, src, 4);
        }

        /*-*************************************************************
         *   Block decoding
         ***************************************************************/
        private static nuint ZSTD_blockSizeMax(ZSTD_DCtx_s* dctx)
        {
            nuint blockSizeMax = dctx->isFrameDecompression != 0 ? dctx->fParams.blockSizeMax : 1 << 17;
            assert(blockSizeMax <= 1 << 17);
            return blockSizeMax;
        }

        /*! ZSTD_getcBlockSize() :
         *  Provides the size of compressed block from block header `src` */
        private static nuint ZSTD_getcBlockSize(void* src, nuint srcSize, blockProperties_t* bpPtr)
        {
            if (srcSize < ZSTD_blockHeaderSize)
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
            }

            {
                uint cBlockHeader = MEM_readLE24(src);
                uint cSize = cBlockHeader >> 3;
                bpPtr->lastBlock = cBlockHeader & 1;
                bpPtr->blockType = (blockType_e)(cBlockHeader >> 1 & 3);
                bpPtr->origSize = cSize;
                if (bpPtr->blockType == blockType_e.bt_rle)
                    return 1;
                if (bpPtr->blockType == blockType_e.bt_reserved)
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                return cSize;
            }
        }

        /* Allocate buffer for literals, either overlapping current dst, or split between dst and litExtraBuffer, or stored entirely within litExtraBuffer */
        private static void ZSTD_allocateLiteralsBuffer(ZSTD_DCtx_s* dctx, void* dst, nuint dstCapacity, nuint litSize, streaming_operation streaming, nuint expectedWriteSize, uint splitImmediately)
        {
            nuint blockSizeMax = ZSTD_blockSizeMax(dctx);
            assert(litSize <= blockSizeMax);
            assert(dctx->isFrameDecompression != 0 || streaming == streaming_operation.not_streaming);
            assert(expectedWriteSize <= blockSizeMax);
            if (streaming == streaming_operation.not_streaming && dstCapacity > blockSizeMax + 32 + litSize + 32)
            {
                dctx->litBuffer = (byte*)dst + blockSizeMax + 32;
                dctx->litBufferEnd = dctx->litBuffer + litSize;
                dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_in_dst;
            }
            else if (litSize <= 1 << 16)
            {
                dctx->litBuffer = dctx->litExtraBuffer;
                dctx->litBufferEnd = dctx->litBuffer + litSize;
                dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst;
            }
            else
            {
                assert(blockSizeMax > 1 << 16);
                if (splitImmediately != 0)
                {
                    dctx->litBuffer = (byte*)dst + expectedWriteSize - litSize + (1 << 16) - 32;
                    dctx->litBufferEnd = dctx->litBuffer + litSize - (1 << 16);
                }
                else
                {
                    dctx->litBuffer = (byte*)dst + expectedWriteSize - litSize;
                    dctx->litBufferEnd = (byte*)dst + expectedWriteSize;
                }

                dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_split;
                assert(dctx->litBufferEnd <= (byte*)dst + expectedWriteSize);
            }
        }

        /*! ZSTD_decodeLiteralsBlock() :
         * Where it is possible to do so without being stomped by the output during decompression, the literals block will be stored
         * in the dstBuffer.  If there is room to do so, it will be stored in full in the excess dst space after where the current
         * block will be output.  Otherwise it will be stored at the end of the current dst blockspace, with a small portion being
         * stored in dctx->litExtraBuffer to help keep it "ahead" of the current output write.
         *
         * @return : nb of bytes read from src (< srcSize )
         *  note : symbol not declared but exposed for fullbench */
        private static nuint ZSTD_decodeLiteralsBlock(ZSTD_DCtx_s* dctx, void* src, nuint srcSize, void* dst, nuint dstCapacity, streaming_operation streaming)
        {
            if (srcSize < 1 + 1)
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
            }

            {
                byte* istart = (byte*)src;
                SymbolEncodingType_e litEncType = (SymbolEncodingType_e)(istart[0] & 3);
                nuint blockSizeMax = ZSTD_blockSizeMax(dctx);
                switch (litEncType)
                {
                    case SymbolEncodingType_e.set_repeat:
                        if (dctx->litEntropy == 0)
                        {
                            return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
                        }

                        goto case SymbolEncodingType_e.set_compressed;
                    case SymbolEncodingType_e.set_compressed:
                        if (srcSize < 5)
                        {
                            return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                        }

                        {
                            nuint lhSize, litSize, litCSize;
                            uint singleStream = 0;
                            uint lhlCode = (uint)(istart[0] >> 2 & 3);
                            uint lhc = MEM_readLE32(istart);
                            nuint hufSuccess;
                            nuint expectedWriteSize = blockSizeMax < dstCapacity ? blockSizeMax : dstCapacity;
                            int flags = 0 | (ZSTD_DCtx_get_bmi2(dctx) != 0 ? (int)HUF_flags_e.HUF_flags_bmi2 : 0) | (dctx->disableHufAsm != 0 ? (int)HUF_flags_e.HUF_flags_disableAsm : 0);
                            switch (lhlCode)
                            {
                                case 0:
                                case 1:
                                default:
                                    singleStream = lhlCode == 0 ? 1U : 0U;
                                    lhSize = 3;
                                    litSize = lhc >> 4 & 0x3FF;
                                    litCSize = lhc >> 14 & 0x3FF;
                                    break;
                                case 2:
                                    lhSize = 4;
                                    litSize = lhc >> 4 & 0x3FFF;
                                    litCSize = lhc >> 18;
                                    break;
                                case 3:
                                    lhSize = 5;
                                    litSize = lhc >> 4 & 0x3FFFF;
                                    litCSize = (lhc >> 22) + ((nuint)istart[4] << 10);
                                    break;
                            }

                            if (litSize > 0 && dst == null)
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                            }

                            if (litSize > blockSizeMax)
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                            }

                            if (singleStream == 0)
                                if (litSize < 6)
                                {
                                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_literals_headerWrong));
                                }

                            if (litCSize + lhSize > srcSize)
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                            }

                            if (expectedWriteSize < litSize)
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                            }

                            ZSTD_allocateLiteralsBuffer(dctx, dst, dstCapacity, litSize, streaming, expectedWriteSize, 0);
                            if (dctx->ddictIsCold != 0 && litSize > 768)
                            {
                                sbyte* _ptr = (sbyte*)dctx->HUFptr;
                                const nuint _size = sizeof(uint) * 4097;
                                nuint _pos;
                                for (_pos = 0; _pos < _size; _pos += 64)
                                {
                                    if (System.Runtime.Intrinsics.X86.Sse.IsSupported)
                                    {
                                        System.Runtime.Intrinsics.X86.Sse.Prefetch1(_ptr + _pos);
                                    }
                                }
                            }

                            if (litEncType == SymbolEncodingType_e.set_repeat)
                            {
                                if (singleStream != 0)
                                {
                                    hufSuccess = HUF_decompress1X_usingDTable(dctx->litBuffer, litSize, istart + lhSize, litCSize, dctx->HUFptr, flags);
                                }
                                else
                                {
                                    assert(litSize >= 6);
                                    hufSuccess = HUF_decompress4X_usingDTable(dctx->litBuffer, litSize, istart + lhSize, litCSize, dctx->HUFptr, flags);
                                }
                            }
                            else
                            {
                                if (singleStream != 0)
                                {
                                    hufSuccess = HUF_decompress1X1_DCtx_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart + lhSize, litCSize, dctx->workspace, sizeof(uint) * 640, flags);
                                }
                                else
                                {
                                    hufSuccess = HUF_decompress4X_hufOnly_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart + lhSize, litCSize, dctx->workspace, sizeof(uint) * 640, flags);
                                }
                            }

                            if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split)
                            {
                                assert(litSize > 1 << 16);
                                memcpy(dctx->litExtraBuffer, dctx->litBufferEnd - (1 << 16), 1 << 16);
                                memmove(dctx->litBuffer + (1 << 16) - 32, dctx->litBuffer, litSize - (1 << 16));
                                dctx->litBuffer += (1 << 16) - 32;
                                dctx->litBufferEnd -= 32;
                                assert(dctx->litBufferEnd <= (byte*)dst + blockSizeMax);
                            }

                            if (ERR_isError(hufSuccess))
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                            }

                            dctx->litPtr = dctx->litBuffer;
                            dctx->litSize = litSize;
                            dctx->litEntropy = 1;
                            if (litEncType == SymbolEncodingType_e.set_compressed)
                                dctx->HUFptr = dctx->entropy.hufTable;
                            return litCSize + lhSize;
                        }

                    case SymbolEncodingType_e.set_basic:
                        {
                            nuint litSize, lhSize;
                            uint lhlCode = (uint)(istart[0] >> 2 & 3);
                            nuint expectedWriteSize = blockSizeMax < dstCapacity ? blockSizeMax : dstCapacity;
                            switch (lhlCode)
                            {
                                case 0:
                                case 2:
                                default:
                                    lhSize = 1;
                                    litSize = (nuint)(istart[0] >> 3);
                                    break;
                                case 1:
                                    lhSize = 2;
                                    litSize = (nuint)(MEM_readLE16(istart) >> 4);
                                    break;
                                case 3:
                                    lhSize = 3;
                                    if (srcSize < 3)
                                    {
                                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                                    }

                                    litSize = MEM_readLE24(istart) >> 4;
                                    break;
                            }

                            if (litSize > 0 && dst == null)
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                            }

                            if (litSize > blockSizeMax)
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                            }

                            if (expectedWriteSize < litSize)
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                            }

                            ZSTD_allocateLiteralsBuffer(dctx, dst, dstCapacity, litSize, streaming, expectedWriteSize, 1);
                            if (lhSize + litSize + 32 > srcSize)
                            {
                                if (litSize + lhSize > srcSize)
                                {
                                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                                }

                                if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split)
                                {
                                    memcpy(dctx->litBuffer, istart + lhSize, (uint)(litSize - (1 << 16)));
                                    memcpy(dctx->litExtraBuffer, istart + lhSize + litSize - (1 << 16), 1 << 16);
                                }
                                else
                                {
                                    memcpy(dctx->litBuffer, istart + lhSize, (uint)litSize);
                                }

                                dctx->litPtr = dctx->litBuffer;
                                dctx->litSize = litSize;
                                return lhSize + litSize;
                            }

                            dctx->litPtr = istart + lhSize;
                            dctx->litSize = litSize;
                            dctx->litBufferEnd = dctx->litPtr + litSize;
                            dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst;
                            return lhSize + litSize;
                        }

                    case SymbolEncodingType_e.set_rle:
                        {
                            uint lhlCode = (uint)(istart[0] >> 2 & 3);
                            nuint litSize, lhSize;
                            nuint expectedWriteSize = blockSizeMax < dstCapacity ? blockSizeMax : dstCapacity;
                            switch (lhlCode)
                            {
                                case 0:
                                case 2:
                                default:
                                    lhSize = 1;
                                    litSize = (nuint)(istart[0] >> 3);
                                    break;
                                case 1:
                                    lhSize = 2;
                                    if (srcSize < 3)
                                    {
                                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                                    }

                                    litSize = (nuint)(MEM_readLE16(istart) >> 4);
                                    break;
                                case 3:
                                    lhSize = 3;
                                    if (srcSize < 4)
                                    {
                                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                                    }

                                    litSize = MEM_readLE24(istart) >> 4;
                                    break;
                            }

                            if (litSize > 0 && dst == null)
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                            }

                            if (litSize > blockSizeMax)
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                            }

                            if (expectedWriteSize < litSize)
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                            }

                            ZSTD_allocateLiteralsBuffer(dctx, dst, dstCapacity, litSize, streaming, expectedWriteSize, 1);
                            if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split)
                            {
                                memset(dctx->litBuffer, istart[lhSize], (uint)(litSize - (1 << 16)));
                                memset(dctx->litExtraBuffer, istart[lhSize], 1 << 16);
                            }
                            else
                            {
                                memset(dctx->litBuffer, istart[lhSize], (uint)litSize);
                            }

                            dctx->litPtr = dctx->litBuffer;
                            dctx->litSize = litSize;
                            return lhSize + 1;
                        }

                    default:
                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }
            }
        }

        private static readonly ZSTD_seqSymbol* LL_defaultDTable = GetArrayPointer(new ZSTD_seqSymbol[65] { new ZSTD_seqSymbol(nextState: 1, nbAdditionalBits: 1, nbBits: 1, baseValue: 6), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 4, baseValue: 0), new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 0, nbBits: 4, baseValue: 0), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 1), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 3), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 4), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 6), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 7), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 9), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 10), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 12), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 14), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 5, baseValue: 16), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 5, baseValue: 20), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 5, baseValue: 22), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 2, nbBits: 5, baseValue: 28), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 3, nbBits: 5, baseValue: 32), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 4, nbBits: 5, baseValue: 48), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 6, nbBits: 5, baseValue: 64), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 7, nbBits: 5, baseValue: 128), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 8, nbBits: 6, baseValue: 256), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 10, nbBits: 6, baseValue: 1024), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 12, nbBits: 6, baseValue: 4096), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 4, baseValue: 0), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 4, baseValue: 1), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 2), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 4), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 5), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 7), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 8), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 10), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 11), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 13), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 1, nbBits: 5, baseValue: 16), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 5, baseValue: 18), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 1, nbBits: 5, baseValue: 22), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 2, nbBits: 5, baseValue: 24), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 3, nbBits: 5, baseValue: 32), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 3, nbBits: 5, baseValue: 40), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 6, nbBits: 4, baseValue: 64), new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 6, nbBits: 4, baseValue: 64), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 7, nbBits: 5, baseValue: 128), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 9, nbBits: 6, baseValue: 512), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 11, nbBits: 6, baseValue: 2048), new ZSTD_seqSymbol(nextState: 48, nbAdditionalBits: 0, nbBits: 4, baseValue: 0), new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 0, nbBits: 4, baseValue: 1), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 2), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 3), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 5), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 6), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 8), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 9), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 11), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 12), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 15), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 1, nbBits: 5, baseValue: 18), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 1, nbBits: 5, baseValue: 20), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 2, nbBits: 5, baseValue: 24), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 2, nbBits: 5, baseValue: 28), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 3, nbBits: 5, baseValue: 40), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 4, nbBits: 5, baseValue: 48), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 16, nbBits: 6, baseValue: 65536), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 15, nbBits: 6, baseValue: 32768), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 14, nbBits: 6, baseValue: 16384), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 13, nbBits: 6, baseValue: 8192) });
        private static readonly ZSTD_seqSymbol* OF_defaultDTable = GetArrayPointer(new ZSTD_seqSymbol[33] { new ZSTD_seqSymbol(nextState: 1, nbAdditionalBits: 1, nbBits: 1, baseValue: 5), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 0), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 6, nbBits: 4, baseValue: 61), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 9, nbBits: 5, baseValue: 509), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 15, nbBits: 5, baseValue: 32765), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 21, nbBits: 5, baseValue: 2097149), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 3, nbBits: 5, baseValue: 5), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 7, nbBits: 4, baseValue: 125), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 12, nbBits: 5, baseValue: 4093), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 18, nbBits: 5, baseValue: 262141), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 23, nbBits: 5, baseValue: 8388605), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 5, nbBits: 5, baseValue: 29), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 8, nbBits: 4, baseValue: 253), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 14, nbBits: 5, baseValue: 16381), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 20, nbBits: 5, baseValue: 1048573), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 2, nbBits: 5, baseValue: 1), new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 7, nbBits: 4, baseValue: 125), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 11, nbBits: 5, baseValue: 2045), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 17, nbBits: 5, baseValue: 131069), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 22, nbBits: 5, baseValue: 4194301), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 4, nbBits: 5, baseValue: 13), new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 8, nbBits: 4, baseValue: 253), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 13, nbBits: 5, baseValue: 8189), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 19, nbBits: 5, baseValue: 524285), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 5, baseValue: 1), new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 6, nbBits: 4, baseValue: 61), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 10, nbBits: 5, baseValue: 1021), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 16, nbBits: 5, baseValue: 65533), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 28, nbBits: 5, baseValue: 268435453), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 27, nbBits: 5, baseValue: 134217725), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 26, nbBits: 5, baseValue: 67108861), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 25, nbBits: 5, baseValue: 33554429), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 24, nbBits: 5, baseValue: 16777213) });
        private static readonly ZSTD_seqSymbol* ML_defaultDTable = GetArrayPointer(new ZSTD_seqSymbol[65] { new ZSTD_seqSymbol(nextState: 1, nbAdditionalBits: 1, nbBits: 1, baseValue: 6), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 3), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 4, baseValue: 4), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 5), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 6), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 8), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 9), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 11), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 13), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 16), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 19), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 22), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 25), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 28), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 31), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 34), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 6, baseValue: 37), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 6, baseValue: 41), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 2, nbBits: 6, baseValue: 47), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 3, nbBits: 6, baseValue: 59), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 4, nbBits: 6, baseValue: 83), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 7, nbBits: 6, baseValue: 131), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 9, nbBits: 6, baseValue: 515), new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 0, nbBits: 4, baseValue: 4), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 4, baseValue: 5), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 6), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 7), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 9), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 10), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 12), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 15), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 18), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 21), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 24), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 27), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 30), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 33), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 6, baseValue: 35), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 6, baseValue: 39), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 2, nbBits: 6, baseValue: 43), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 3, nbBits: 6, baseValue: 51), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 4, nbBits: 6, baseValue: 67), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 5, nbBits: 6, baseValue: 99), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 8, nbBits: 6, baseValue: 259), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 4, baseValue: 4), new ZSTD_seqSymbol(nextState: 48, nbAdditionalBits: 0, nbBits: 4, baseValue: 4), new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 0, nbBits: 4, baseValue: 5), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 7), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 8), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 10), new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 11), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 14), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 17), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 20), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 23), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 26), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 29), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 32), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 16, nbBits: 6, baseValue: 65539), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 15, nbBits: 6, baseValue: 32771), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 14, nbBits: 6, baseValue: 16387), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 13, nbBits: 6, baseValue: 8195), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 12, nbBits: 6, baseValue: 4099), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 11, nbBits: 6, baseValue: 2051), new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 10, nbBits: 6, baseValue: 1027) });
        private static void ZSTD_buildSeqTable_rle(ZSTD_seqSymbol* dt, uint baseValue, byte nbAddBits)
        {
            void* ptr = dt;
            ZSTD_seqSymbol_header* DTableH = (ZSTD_seqSymbol_header*)ptr;
            ZSTD_seqSymbol* cell = dt + 1;
            DTableH->tableLog = 0;
            DTableH->fastMode = 0;
            cell->nbBits = 0;
            cell->nextState = 0;
            assert(nbAddBits < 255);
            cell->nbAdditionalBits = nbAddBits;
            cell->baseValue = baseValue;
        }

        /* ZSTD_buildFSETable() :
         * generate FSE decoding table for one symbol (ll, ml or off)
         * cannot fail if input is valid =>
         * all inputs are presumed validated at this stage */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static void ZSTD_buildFSETable_body(ZSTD_seqSymbol* dt, short* normalizedCounter, uint maxSymbolValue, uint* baseValue, byte* nbAdditionalBits, uint tableLog, void* wksp, nuint wkspSize)
        {
            ZSTD_seqSymbol* tableDecode = dt + 1;
            uint maxSV1 = maxSymbolValue + 1;
            uint tableSize = (uint)(1 << (int)tableLog);
            ushort* symbolNext = (ushort*)wksp;
            byte* spread = (byte*)(symbolNext + 52 + 1);
            uint highThreshold = tableSize - 1;
            assert(maxSymbolValue <= 52);
            assert(tableLog <= 9);
            assert(wkspSize >= sizeof(short) * (52 + 1) + (1U << 9) + sizeof(ulong));
            {
                ZSTD_seqSymbol_header DTableH;
                DTableH.tableLog = 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--].baseValue = s;
                            symbolNext[s] = 1;
                        }
                        else
                        {
                            if (normalizedCounter[s] >= largeLimit)
                                DTableH.fastMode = 0;
                            assert(normalizedCounter[s] >= 0);
                            symbolNext[s] = (ushort)normalizedCounter[s];
                        }
                    }
                }

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

            assert(tableSize <= 512);
            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);
                        }

                        assert(n >= 0);
                        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].baseValue = 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;
                    int n = normalizedCounter[s];
                    for (i = 0; i < n; i++)
                    {
                        tableDecode[position].baseValue = s;
                        position = position + step & tableMask;
                        while (position > highThreshold)
                            position = position + step & tableMask;
                    }
                }

                assert(position == 0);
            }

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

        /* Avoids the FORCE_INLINE of the _body() function. */
        private static void ZSTD_buildFSETable_body_default(ZSTD_seqSymbol* dt, short* normalizedCounter, uint maxSymbolValue, uint* baseValue, byte* nbAdditionalBits, uint tableLog, void* wksp, nuint wkspSize)
        {
            ZSTD_buildFSETable_body(dt, normalizedCounter, maxSymbolValue, baseValue, nbAdditionalBits, tableLog, wksp, wkspSize);
        }

        /* ZSTD_buildFSETable() :
         * generate FSE decoding table for one symbol (ll, ml or off)
         * this function must be called with valid parameters only
         * (dt is large enough, normalizedCounter distribution total is a power of 2, max is within range, etc.)
         * in which case it cannot fail.
         * The workspace must be 4-byte aligned and at least ZSTD_BUILD_FSE_TABLE_WKSP_SIZE bytes, which is
         * defined in zstd_decompress_internal.h.
         * Internal use only.
         */
        private static void ZSTD_buildFSETable(ZSTD_seqSymbol* dt, short* normalizedCounter, uint maxSymbolValue, uint* baseValue, byte* nbAdditionalBits, uint tableLog, void* wksp, nuint wkspSize, int bmi2)
        {
            ZSTD_buildFSETable_body_default(dt, normalizedCounter, maxSymbolValue, baseValue, nbAdditionalBits, tableLog, wksp, wkspSize);
        }

        /*! ZSTD_buildSeqTable() :
         * @return : nb bytes read from src,
         *           or an error code if it fails */
        private static nuint ZSTD_buildSeqTable(ZSTD_seqSymbol* DTableSpace, ZSTD_seqSymbol** DTablePtr, SymbolEncodingType_e type, uint max, uint maxLog, void* src, nuint srcSize, uint* baseValue, byte* nbAdditionalBits, ZSTD_seqSymbol* defaultTable, uint flagRepeatTable, int ddictIsCold, int nbSeq, uint* wksp, nuint wkspSize, int bmi2)
        {
            switch (type)
            {
                case SymbolEncodingType_e.set_rle:
                    if (srcSize == 0)
                    {
                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
                    }

                    if (*(byte*)src > max)
                    {
                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                    }

                    {
                        uint symbol = *(byte*)src;
                        uint baseline = baseValue[symbol];
                        byte nbBits = nbAdditionalBits[symbol];
                        ZSTD_buildSeqTable_rle(DTableSpace, baseline, nbBits);
                    }

                    *DTablePtr = DTableSpace;
                    return 1;
                case SymbolEncodingType_e.set_basic:
                    *DTablePtr = defaultTable;
                    return 0;
                case SymbolEncodingType_e.set_repeat:
                    if (flagRepeatTable == 0)
                    {
                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                    }

                    if (ddictIsCold != 0 && nbSeq > 24)
                    {
                        void* pStart = *DTablePtr;
                        nuint pSize = (nuint)(sizeof(ZSTD_seqSymbol) * (1 + (1 << (int)maxLog)));
                        {
                            sbyte* _ptr = (sbyte*)pStart;
                            nuint _size = pSize;
                            nuint _pos;
                            for (_pos = 0; _pos < _size; _pos += 64)
                            {
                                if (System.Runtime.Intrinsics.X86.Sse.IsSupported)
                                {
                                    System.Runtime.Intrinsics.X86.Sse.Prefetch1(_ptr + _pos);
                                }
                            }
                        }
                    }

                    return 0;
                case SymbolEncodingType_e.set_compressed:
                    {
                        uint tableLog;
                        short* norm = stackalloc short[53];
                        nuint headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize);
                        if (ERR_isError(headerSize))
                        {
                            return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                        }

                        if (tableLog > maxLog)
                        {
                            return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                        }

                        ZSTD_buildFSETable(DTableSpace, norm, max, baseValue, nbAdditionalBits, tableLog, wksp, wkspSize, bmi2);
                        *DTablePtr = DTableSpace;
                        return headerSize;
                    }

                default:
                    assert(0 != 0);
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC));
            }
        }

        /*! ZSTD_decodeSeqHeaders() :
         *  decode sequence header from src */
        /*  Used by: zstd_decompress_block, fullbench */
        private static nuint ZSTD_decodeSeqHeaders(ZSTD_DCtx_s* dctx, int* nbSeqPtr, void* src, nuint srcSize)
        {
            byte* istart = (byte*)src;
            byte* iend = istart + srcSize;
            byte* ip = istart;
            int nbSeq;
            if (srcSize < 1)
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
            }

            nbSeq = *ip++;
            if (nbSeq > 0x7F)
            {
                if (nbSeq == 0xFF)
                {
                    if (ip + 2 > iend)
                    {
                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
                    }

                    nbSeq = MEM_readLE16(ip) + 0x7F00;
                    ip += 2;
                }
                else
                {
                    if (ip >= iend)
                    {
                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
                    }

                    nbSeq = (nbSeq - 0x80 << 8) + *ip++;
                }
            }

            *nbSeqPtr = nbSeq;
            if (nbSeq == 0)
            {
                if (ip != iend)
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                return (nuint)(ip - istart);
            }

            if (ip + 1 > iend)
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
            }

            if ((*ip & 3) != 0)
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
            }

            {
                SymbolEncodingType_e LLtype = (SymbolEncodingType_e)(*ip >> 6);
                SymbolEncodingType_e OFtype = (SymbolEncodingType_e)(*ip >> 4 & 3);
                SymbolEncodingType_e MLtype = (SymbolEncodingType_e)(*ip >> 2 & 3);
                ip++;
                {
                    nuint llhSize = ZSTD_buildSeqTable(&dctx->entropy.LLTable.e0, &dctx->LLTptr, LLtype, 35, 9, ip, (nuint)(iend - ip), LL_base, LL_bits, LL_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq, dctx->workspace, sizeof(uint) * 640, ZSTD_DCtx_get_bmi2(dctx));
                    if (ERR_isError(llhSize))
                    {
                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                    }

                    ip += llhSize;
                }

                {
                    nuint ofhSize = ZSTD_buildSeqTable(&dctx->entropy.OFTable.e0, &dctx->OFTptr, OFtype, 31, 8, ip, (nuint)(iend - ip), OF_base, OF_bits, OF_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq, dctx->workspace, sizeof(uint) * 640, ZSTD_DCtx_get_bmi2(dctx));
                    if (ERR_isError(ofhSize))
                    {
                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                    }

                    ip += ofhSize;
                }

                {
                    nuint mlhSize = ZSTD_buildSeqTable(&dctx->entropy.MLTable.e0, &dctx->MLTptr, MLtype, 52, 9, ip, (nuint)(iend - ip), ML_base, ML_bits, ML_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq, dctx->workspace, sizeof(uint) * 640, ZSTD_DCtx_get_bmi2(dctx));
                    if (ERR_isError(mlhSize))
                    {
                        return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                    }

                    ip += mlhSize;
                }
            }

            return (nuint)(ip - istart);
        }

        private static ReadOnlySpan<uint> Span_dec32table => new uint[8]
        {
            0,
            1,
            2,
            1,
            4,
            4,
            4,
            4
        };
        private static uint* dec32table => (uint*)System.Runtime.CompilerServices.Unsafe.AsPointer(ref MemoryMarshal.GetReference(Span_dec32table));
        private static ReadOnlySpan<int> Span_dec64table => new int[8]
        {
            8,
            8,
            8,
            7,
            8,
            9,
            10,
            11
        };
        private static int* dec64table => (int*)System.Runtime.CompilerServices.Unsafe.AsPointer(ref MemoryMarshal.GetReference(Span_dec64table));
        /*! ZSTD_overlapCopy8() :
         *  Copies 8 bytes from ip to op and updates op and ip where ip <= op.
         *  If the offset is < 8 then the offset is spread to at least 8 bytes.
         *
         *  Precondition: *ip <= *op
         *  Postcondition: *op - *op >= 8
         */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static void ZSTD_overlapCopy8(byte** op, byte** ip, nuint offset)
        {
            assert(*ip <= *op);
            if (offset < 8)
            {
                int sub2 = dec64table[offset];
                (*op)[0] = (*ip)[0];
                (*op)[1] = (*ip)[1];
                (*op)[2] = (*ip)[2];
                (*op)[3] = (*ip)[3];
                *ip += dec32table[offset];
                ZSTD_copy4(*op + 4, *ip);
                *ip -= sub2;
            }
            else
            {
                ZSTD_copy8(*op, *ip);
            }

            *ip += 8;
            *op += 8;
            assert(*op - *ip >= 8);
        }

        /*! ZSTD_safecopy() :
         *  Specialized version of memcpy() that is allowed to READ up to WILDCOPY_OVERLENGTH past the input buffer
         *  and write up to 16 bytes past oend_w (op >= oend_w is allowed).
         *  This function is only called in the uncommon case where the sequence is near the end of the block. It
         *  should be fast for a single long sequence, but can be slow for several short sequences.
         *
         *  @param ovtype controls the overlap detection
         *         - ZSTD_no_overlap: The source and destination are guaranteed to be at least WILDCOPY_VECLEN bytes apart.
         *         - ZSTD_overlap_src_before_dst: The src and dst may overlap and may be any distance apart.
         *           The src buffer must be before the dst buffer.
         */
        private static void ZSTD_safecopy(byte* op, byte* oend_w, byte* ip, nint length, ZSTD_overlap_e ovtype)
        {
            nint diff = (nint)(op - ip);
            byte* oend = op + length;
            assert(ovtype == ZSTD_overlap_e.ZSTD_no_overlap && (diff <= -8 || diff >= 8 || op >= oend_w) || ovtype == ZSTD_overlap_e.ZSTD_overlap_src_before_dst && diff >= 0);
            if (length < 8)
            {
                while (op < oend)
                    *op++ = *ip++;
                return;
            }

            if (ovtype == ZSTD_overlap_e.ZSTD_overlap_src_before_dst)
            {
                assert(length >= 8);
                ZSTD_overlapCopy8(&op, &ip, (nuint)diff);
                length -= 8;
                assert(op - ip >= 8);
                assert(op <= oend);
            }

            if (oend <= oend_w)
            {
                ZSTD_wildcopy(op, ip, length, ovtype);
                return;
            }

            if (op <= oend_w)
            {
                assert(oend > oend_w);
                ZSTD_wildcopy(op, ip, (nint)(oend_w - op), ovtype);
                ip += oend_w - op;
                op += oend_w - op;
            }

            while (op < oend)
                *op++ = *ip++;
        }

        /* ZSTD_safecopyDstBeforeSrc():
         * This version allows overlap with dst before src, or handles the non-overlap case with dst after src
         * Kept separate from more common ZSTD_safecopy case to avoid performance impact to the safecopy common case */
        private static void ZSTD_safecopyDstBeforeSrc(byte* op, byte* ip, nint length)
        {
            nint diff = (nint)(op - ip);
            byte* oend = op + length;
            if (length < 8 || diff > -8)
            {
                while (op < oend)
                    *op++ = *ip++;
                return;
            }

            if (op <= oend - 32 && diff < -16)
            {
                ZSTD_wildcopy(op, ip, (nint)(oend - 32 - op), ZSTD_overlap_e.ZSTD_no_overlap);
                ip += oend - 32 - op;
                op += oend - 32 - op;
            }

            while (op < oend)
                *op++ = *ip++;
        }

        /* ZSTD_execSequenceEnd():
         * This version handles cases that are near the end of the output buffer. It requires
         * more careful checks to make sure there is no overflow. By separating out these hard
         * and unlikely cases, we can speed up the common cases.
         *
         * NOTE: This function needs to be fast for a single long sequence, but doesn't need
         * to be optimized for many small sequences, since those fall into ZSTD_execSequence().
         */
        private static nuint ZSTD_execSequenceEnd(byte* op, byte* oend, seq_t sequence, byte** litPtr, byte* litLimit, byte* prefixStart, byte* virtualStart, byte* dictEnd)
        {
            byte* oLitEnd = op + sequence.litLength;
            nuint sequenceLength = sequence.litLength + sequence.matchLength;
            byte* iLitEnd = *litPtr + sequence.litLength;
            byte* match = oLitEnd - sequence.offset;
            byte* oend_w = oend - 32;
            if (sequenceLength > (nuint)(oend - op))
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
            }

            if (sequence.litLength > (nuint)(litLimit - *litPtr))
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
            }

            assert(op < op + sequenceLength);
            assert(oLitEnd < op + sequenceLength);
            ZSTD_safecopy(op, oend_w, *litPtr, (nint)sequence.litLength, ZSTD_overlap_e.ZSTD_no_overlap);
            op = oLitEnd;
            *litPtr = iLitEnd;
            if (sequence.offset > (nuint)(oLitEnd - prefixStart))
            {
                if (sequence.offset > (nuint)(oLitEnd - virtualStart))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                match = dictEnd - (prefixStart - match);
                if (match + sequence.matchLength <= dictEnd)
                {
                    memmove(oLitEnd, match, sequence.matchLength);
                    return sequenceLength;
                }

                {
                    nuint length1 = (nuint)(dictEnd - match);
                    memmove(oLitEnd, match, length1);
                    op = oLitEnd + length1;
                    sequence.matchLength -= length1;
                    match = prefixStart;
                }
            }

            ZSTD_safecopy(op, oend_w, match, (nint)sequence.matchLength, ZSTD_overlap_e.ZSTD_overlap_src_before_dst);
            return sequenceLength;
        }

        /* ZSTD_execSequenceEndSplitLitBuffer():
         * This version is intended to be used during instances where the litBuffer is still split.  It is kept separate to avoid performance impact for the good case.
         */
        private static nuint ZSTD_execSequenceEndSplitLitBuffer(byte* op, byte* oend, byte* oend_w, seq_t sequence, byte** litPtr, byte* litLimit, byte* prefixStart, byte* virtualStart, byte* dictEnd)
        {
            byte* oLitEnd = op + sequence.litLength;
            nuint sequenceLength = sequence.litLength + sequence.matchLength;
            byte* iLitEnd = *litPtr + sequence.litLength;
            byte* match = oLitEnd - sequence.offset;
            if (sequenceLength > (nuint)(oend - op))
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
            }

            if (sequence.litLength > (nuint)(litLimit - *litPtr))
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
            }

            assert(op < op + sequenceLength);
            assert(oLitEnd < op + sequenceLength);
            if (op > *litPtr && op < *litPtr + sequence.litLength)
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
            }

            ZSTD_safecopyDstBeforeSrc(op, *litPtr, (nint)sequence.litLength);
            op = oLitEnd;
            *litPtr = iLitEnd;
            if (sequence.offset > (nuint)(oLitEnd - prefixStart))
            {
                if (sequence.offset > (nuint)(oLitEnd - virtualStart))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                match = dictEnd - (prefixStart - match);
                if (match + sequence.matchLength <= dictEnd)
                {
                    memmove(oLitEnd, match, sequence.matchLength);
                    return sequenceLength;
                }

                {
                    nuint length1 = (nuint)(dictEnd - match);
                    memmove(oLitEnd, match, length1);
                    op = oLitEnd + length1;
                    sequence.matchLength -= length1;
                    match = prefixStart;
                }
            }

            ZSTD_safecopy(op, oend_w, match, (nint)sequence.matchLength, ZSTD_overlap_e.ZSTD_overlap_src_before_dst);
            return sequenceLength;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static nuint ZSTD_execSequence(byte* op, byte* oend, seq_t sequence, byte** litPtr, byte* litLimit, byte* prefixStart, byte* virtualStart, byte* dictEnd)
        {
            var sequence_litLength = sequence.litLength;
            var sequence_matchLength = sequence.matchLength;
            var sequence_offset = sequence.offset;
            byte* oLitEnd = op + sequence_litLength;
            nuint sequenceLength = sequence_litLength + sequence_matchLength;
            /* risk : address space overflow (32-bits) */
            byte* oMatchEnd = op + sequenceLength;
            /* risk : address space underflow on oend=NULL */
            byte* oend_w = oend - 32;
            byte* iLitEnd = *litPtr + sequence_litLength;
            byte* match = oLitEnd - sequence_offset;
            assert(op != null);
            assert(oend_w < oend);
            if (iLitEnd > litLimit || oMatchEnd > oend_w || MEM_32bits && (nuint)(oend - op) < sequenceLength + 32)
                return ZSTD_execSequenceEnd(op, oend, new seq_t { litLength = sequence_litLength, matchLength = sequence_matchLength, offset = sequence_offset }, litPtr, litLimit, prefixStart, virtualStart, dictEnd);
            assert(op <= oLitEnd);
            assert(oLitEnd < oMatchEnd);
            assert(oMatchEnd <= oend);
            assert(iLitEnd <= litLimit);
            assert(oLitEnd <= oend_w);
            assert(oMatchEnd <= oend_w);
            assert(32 >= 16);
            ZSTD_copy16(op, *litPtr);
            if (sequence_litLength > 16)
            {
                ZSTD_wildcopy(op + 16, *litPtr + 16, (nint)(sequence_litLength - 16), ZSTD_overlap_e.ZSTD_no_overlap);
            }

            op = oLitEnd;
            *litPtr = iLitEnd;
            if (sequence_offset > (nuint)(oLitEnd - prefixStart))
            {
                if (sequence_offset > (nuint)(oLitEnd - virtualStart))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                match = dictEnd + (match - prefixStart);
                if (match + sequence_matchLength <= dictEnd)
                {
                    memmove(oLitEnd, match, sequence_matchLength);
                    return sequenceLength;
                }

                {
                    nuint length1 = (nuint)(dictEnd - match);
                    memmove(oLitEnd, match, length1);
                    op = oLitEnd + length1;
                    sequence_matchLength -= length1;
                    match = prefixStart;
                }
            }

            assert(op <= oMatchEnd);
            assert(oMatchEnd <= oend_w);
            assert(match >= prefixStart);
            assert(sequence_matchLength >= 1);
            if (sequence_offset >= 16)
            {
                ZSTD_wildcopy(op, match, (nint)sequence_matchLength, ZSTD_overlap_e.ZSTD_no_overlap);
                return sequenceLength;
            }

            assert(sequence_offset < 16);
            ZSTD_overlapCopy8(ref op, ref match, sequence_offset);
            if (sequence_matchLength > 8)
            {
                assert(op < oMatchEnd);
                ZSTD_wildcopy(op, match, (nint)sequence_matchLength - 8, ZSTD_overlap_e.ZSTD_overlap_src_before_dst);
            }

            return sequenceLength;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static nuint ZSTD_execSequenceSplitLitBuffer(byte* op, byte* oend, byte* oend_w, seq_t sequence, byte** litPtr, byte* litLimit, byte* prefixStart, byte* virtualStart, byte* dictEnd)
        {
            byte* oLitEnd = op + sequence.litLength;
            nuint sequenceLength = sequence.litLength + sequence.matchLength;
            /* risk : address space overflow (32-bits) */
            byte* oMatchEnd = op + sequenceLength;
            byte* iLitEnd = *litPtr + sequence.litLength;
            byte* match = oLitEnd - sequence.offset;
            assert(op != null);
            assert(oend_w < oend);
            if (iLitEnd > litLimit || oMatchEnd > oend_w || MEM_32bits && (nuint)(oend - op) < sequenceLength + 32)
                return ZSTD_execSequenceEndSplitLitBuffer(op, oend, oend_w, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd);
            assert(op <= oLitEnd);
            assert(oLitEnd < oMatchEnd);
            assert(oMatchEnd <= oend);
            assert(iLitEnd <= litLimit);
            assert(oLitEnd <= oend_w);
            assert(oMatchEnd <= oend_w);
            assert(32 >= 16);
            ZSTD_copy16(op, *litPtr);
            if (sequence.litLength > 16)
            {
                ZSTD_wildcopy(op + 16, *litPtr + 16, (nint)(sequence.litLength - 16), ZSTD_overlap_e.ZSTD_no_overlap);
            }

            op = oLitEnd;
            *litPtr = iLitEnd;
            if (sequence.offset > (nuint)(oLitEnd - prefixStart))
            {
                if (sequence.offset > (nuint)(oLitEnd - virtualStart))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                match = dictEnd + (match - prefixStart);
                if (match + sequence.matchLength <= dictEnd)
                {
                    memmove(oLitEnd, match, sequence.matchLength);
                    return sequenceLength;
                }

                {
                    nuint length1 = (nuint)(dictEnd - match);
                    memmove(oLitEnd, match, length1);
                    op = oLitEnd + length1;
                    sequence.matchLength -= length1;
                    match = prefixStart;
                }
            }

            assert(op <= oMatchEnd);
            assert(oMatchEnd <= oend_w);
            assert(match >= prefixStart);
            assert(sequence.matchLength >= 1);
            if (sequence.offset >= 16)
            {
                ZSTD_wildcopy(op, match, (nint)sequence.matchLength, ZSTD_overlap_e.ZSTD_no_overlap);
                return sequenceLength;
            }

            assert(sequence.offset < 16);
            ZSTD_overlapCopy8(&op, &match, sequence.offset);
            if (sequence.matchLength > 8)
            {
                assert(op < oMatchEnd);
                ZSTD_wildcopy(op, match, (nint)sequence.matchLength - 8, ZSTD_overlap_e.ZSTD_overlap_src_before_dst);
            }

            return sequenceLength;
        }

        private static void ZSTD_initFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, ZSTD_seqSymbol* dt)
        {
            void* ptr = dt;
            ZSTD_seqSymbol_header* DTableH = (ZSTD_seqSymbol_header*)ptr;
            DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog);
            BIT_reloadDStream(bitD);
            DStatePtr->table = dt + 1;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static void ZSTD_updateFseStateWithDInfo(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, ushort nextState, uint nbBits)
        {
            nuint lowBits = BIT_readBits(bitD, nbBits);
            DStatePtr->state = nextState + lowBits;
        }

        /**
         * ZSTD_decodeSequence():
         * @p longOffsets : tells the decoder to reload more bit while decoding large offsets
         *                  only used in 32-bit mode
         * @return : Sequence (litL + matchL + offset)
         */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static seq_t ZSTD_decodeSequence(seqState_t* seqState, ZSTD_longOffset_e longOffsets, int isLastSeq)
        {
            seq_t seq;
            ZSTD_seqSymbol* llDInfo = seqState->stateLL.table + seqState->stateLL.state;
            ZSTD_seqSymbol* mlDInfo = seqState->stateML.table + seqState->stateML.state;
            ZSTD_seqSymbol* ofDInfo = seqState->stateOffb.table + seqState->stateOffb.state;
            seq.matchLength = mlDInfo->baseValue;
            seq.litLength = llDInfo->baseValue;
            {
                uint ofBase = ofDInfo->baseValue;
                byte llBits = llDInfo->nbAdditionalBits;
                byte mlBits = mlDInfo->nbAdditionalBits;
                byte ofBits = ofDInfo->nbAdditionalBits;
                byte totalBits = (byte)(llBits + mlBits + ofBits);
                ushort llNext = llDInfo->nextState;
                ushort mlNext = mlDInfo->nextState;
                ushort ofNext = ofDInfo->nextState;
                uint llnbBits = llDInfo->nbBits;
                uint mlnbBits = mlDInfo->nbBits;
                uint ofnbBits = ofDInfo->nbBits;
                assert(llBits <= 16);
                assert(mlBits <= 16);
                assert(ofBits <= 31);
                {
                    nuint offset;
                    if (ofBits > 1)
                    {
                        if (MEM_32bits && longOffsets != default && ofBits >= 25)
                        {
                            /* Always read extra bits, this keeps the logic simple,
                             * avoids branches, and avoids accidentally reading 0 bits.
                             */
                            const uint extraBits = 30 - 25;
                            offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << (int)extraBits);
                            BIT_reloadDStream(&seqState->DStream);
                            offset += BIT_readBitsFast(&seqState->DStream, extraBits);
                        }
                        else
                        {
                            offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits);
                            if (MEM_32bits)
                                BIT_reloadDStream(&seqState->DStream);
                        }

                        seqState->prevOffset.e2 = seqState->prevOffset.e1;
                        seqState->prevOffset.e1 = seqState->prevOffset.e0;
                        seqState->prevOffset.e0 = offset;
                    }
                    else
                    {
                        uint ll0 = llDInfo->baseValue == 0 ? 1U : 0U;
                        if (ofBits == 0)
                        {
                            offset = (&seqState->prevOffset.e0)[ll0];
                            seqState->prevOffset.e1 = (&seqState->prevOffset.e0)[ll0 == 0 ? 1 : 0];
                            seqState->prevOffset.e0 = offset;
                        }
                        else
                        {
                            offset = ofBase + ll0 + BIT_readBitsFast(&seqState->DStream, 1);
                            {
                                nuint temp = offset == 3 ? seqState->prevOffset.e0 - 1 : (&seqState->prevOffset.e0)[offset];
                                temp -= temp == 0 ? 1U : 0U;
                                if (offset != 1)
                                    seqState->prevOffset.e2 = seqState->prevOffset.e1;
                                seqState->prevOffset.e1 = seqState->prevOffset.e0;
                                seqState->prevOffset.e0 = offset = temp;
                            }
                        }
                    }

                    seq.offset = offset;
                }

                if (mlBits > 0)
                    seq.matchLength += BIT_readBitsFast(&seqState->DStream, mlBits);
                if (MEM_32bits && mlBits + llBits >= 25 - (30 - 25))
                    BIT_reloadDStream(&seqState->DStream);
                if (MEM_64bits && totalBits >= 57 - (9 + 9 + 8))
                    BIT_reloadDStream(&seqState->DStream);
                if (llBits > 0)
                    seq.litLength += BIT_readBitsFast(&seqState->DStream, llBits);
                if (MEM_32bits)
                    BIT_reloadDStream(&seqState->DStream);
                if (isLastSeq == 0)
                {
                    ZSTD_updateFseStateWithDInfo(&seqState->stateLL, &seqState->DStream, llNext, llnbBits);
                    ZSTD_updateFseStateWithDInfo(&seqState->stateML, &seqState->DStream, mlNext, mlnbBits);
                    if (MEM_32bits)
                        BIT_reloadDStream(&seqState->DStream);
                    ZSTD_updateFseStateWithDInfo(&seqState->stateOffb, &seqState->DStream, ofNext, ofnbBits);
                    BIT_reloadDStream(&seqState->DStream);
                }
            }

            return seq;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static nuint ZSTD_decompressSequences_bodySplitLitBuffer(ZSTD_DCtx_s* dctx, void* dst, nuint maxDstSize, void* seqStart, nuint seqSize, int nbSeq, ZSTD_longOffset_e isLongOffset)
        {
            byte* ip = (byte*)seqStart;
            byte* iend = ip + seqSize;
            byte* ostart = (byte*)dst;
            byte* oend = ZSTD_maybeNullPtrAdd(ostart, (nint)maxDstSize);
            byte* op = ostart;
            byte* litPtr = dctx->litPtr;
            byte* litBufferEnd = dctx->litBufferEnd;
            byte* prefixStart = (byte*)dctx->prefixStart;
            byte* vBase = (byte*)dctx->virtualStart;
            byte* dictEnd = (byte*)dctx->dictEnd;
            if (nbSeq != 0)
            {
                seqState_t seqState;
                dctx->fseEntropy = 1;
                {
                    uint i;
                    for (i = 0; i < 3; i++)
                        (&seqState.prevOffset.e0)[i] = dctx->entropy.rep[i];
                }

                if (ERR_isError(BIT_initDStream(&seqState.DStream, ip, (nuint)(iend - ip))))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr);
                ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
                ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
                assert(dst != null);
                {
                    /* some static analyzer believe that @sequence is not initialized (it necessarily is, since for(;;) loop as at least one iteration) */
                    seq_t sequence = new seq_t
                    {
                        litLength = 0,
                        matchLength = 0,
                        offset = 0
                    };
                    for (; nbSeq != 0; nbSeq--)
                    {
                        sequence = ZSTD_decodeSequence(&seqState, isLongOffset, nbSeq == 1 ? 1 : 0);
                        if (litPtr + sequence.litLength > dctx->litBufferEnd)
                            break;
                        {
                            nuint oneSeqSize = ZSTD_execSequenceSplitLitBuffer(op, oend, litPtr + sequence.litLength - 32, sequence, &litPtr, litBufferEnd, prefixStart, vBase, dictEnd);
                            if (ERR_isError(oneSeqSize))
                                return oneSeqSize;
                            op += oneSeqSize;
                        }
                    }

                    if (nbSeq > 0)
                    {
                        nuint leftoverLit = (nuint)(dctx->litBufferEnd - litPtr);
                        if (leftoverLit != 0)
                        {
                            if (leftoverLit > (nuint)(oend - op))
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                            }

                            ZSTD_safecopyDstBeforeSrc(op, litPtr, (nint)leftoverLit);
                            sequence.litLength -= leftoverLit;
                            op += leftoverLit;
                        }

                        litPtr = dctx->litExtraBuffer;
                        litBufferEnd = dctx->litExtraBuffer + (1 << 16);
                        dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst;
                        {
                            nuint oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litBufferEnd, prefixStart, vBase, dictEnd);
                            if (ERR_isError(oneSeqSize))
                                return oneSeqSize;
                            op += oneSeqSize;
                        }

                        nbSeq--;
                    }
                }

                if (nbSeq > 0)
                {
                    for (; nbSeq != 0; nbSeq--)
                    {
                        seq_t sequence = ZSTD_decodeSequence(&seqState, isLongOffset, nbSeq == 1 ? 1 : 0);
                        nuint oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litBufferEnd, prefixStart, vBase, dictEnd);
                        if (ERR_isError(oneSeqSize))
                            return oneSeqSize;
                        op += oneSeqSize;
                    }
                }

                if (nbSeq != 0)
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                if (BIT_endOfDStream(&seqState.DStream) == 0)
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                {
                    uint i;
                    for (i = 0; i < 3; i++)
                        dctx->entropy.rep[i] = (uint)(&seqState.prevOffset.e0)[i];
                }
            }

            if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split)
            {
                /* split hasn't been reached yet, first get dst then copy litExtraBuffer */
                nuint lastLLSize = (nuint)(litBufferEnd - litPtr);
                if (lastLLSize > (nuint)(oend - op))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                }

                if (op != null)
                {
                    memmove(op, litPtr, lastLLSize);
                    op += lastLLSize;
                }

                litPtr = dctx->litExtraBuffer;
                litBufferEnd = dctx->litExtraBuffer + (1 << 16);
                dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst;
            }

            {
                nuint lastLLSize = (nuint)(litBufferEnd - litPtr);
                if (lastLLSize > (nuint)(oend - op))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                }

                if (op != null)
                {
                    memcpy(op, litPtr, (uint)lastLLSize);
                    op += lastLLSize;
                }
            }

            return (nuint)(op - ostart);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static nuint ZSTD_decompressSequences_body(ZSTD_DCtx_s* dctx, void* dst, nuint maxDstSize, void* seqStart, nuint seqSize, int nbSeq, ZSTD_longOffset_e isLongOffset)
        {
            // HACK, force nbSeq to stack (better register usage)
            System.Threading.Volatile.Read(ref nbSeq);
            byte* ip = (byte*)seqStart;
            byte* iend = ip + seqSize;
            byte* ostart = (byte*)dst;
            byte* oend = dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_not_in_dst ? ZSTD_maybeNullPtrAdd(ostart, (nint)maxDstSize) : dctx->litBuffer;
            byte* op = ostart;
            byte* litPtr = dctx->litPtr;
            byte* litEnd = litPtr + dctx->litSize;
            byte* prefixStart = (byte*)dctx->prefixStart;
            byte* vBase = (byte*)dctx->virtualStart;
            byte* dictEnd = (byte*)dctx->dictEnd;
            if (nbSeq != 0)
            {
                seqState_t seqState;
                System.Runtime.CompilerServices.Unsafe.SkipInit(out seqState);
                dctx->fseEntropy = 1;
                {
                    uint i;
                    for (i = 0; i < 3; i++)
                        System.Runtime.CompilerServices.Unsafe.Add(ref seqState.prevOffset.e0, (int)i) = dctx->entropy.rep[i];
                }

                if (ERR_isError(BIT_initDStream(ref seqState.DStream, ip, (nuint)(iend - ip))))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                ZSTD_initFseState(ref seqState.stateLL, ref seqState.DStream, dctx->LLTptr);
                ZSTD_initFseState(ref seqState.stateOffb, ref seqState.DStream, dctx->OFTptr);
                ZSTD_initFseState(ref seqState.stateML, ref seqState.DStream, dctx->MLTptr);
                assert(dst != null);
                nuint seqState_DStream_bitContainer = seqState.DStream.bitContainer;
                uint seqState_DStream_bitsConsumed = seqState.DStream.bitsConsumed;
                sbyte* seqState_DStream_ptr = seqState.DStream.ptr;
                sbyte* seqState_DStream_start = seqState.DStream.start;
                sbyte* seqState_DStream_limitPtr = seqState.DStream.limitPtr;
                for (; nbSeq != 0; nbSeq--)
                {
                    nuint sequence_litLength;
                    nuint sequence_matchLength;
                    nuint sequence_offset;
                    ZSTD_seqSymbol* llDInfo = seqState.stateLL.table + seqState.stateLL.state;
                    ZSTD_seqSymbol* mlDInfo = seqState.stateML.table + seqState.stateML.state;
                    ZSTD_seqSymbol* ofDInfo = seqState.stateOffb.table + seqState.stateOffb.state;
                    sequence_matchLength = mlDInfo->baseValue;
                    sequence_litLength = llDInfo->baseValue;
                    {
                        uint ofBase = ofDInfo->baseValue;
                        byte llBits = llDInfo->nbAdditionalBits;
                        byte mlBits = mlDInfo->nbAdditionalBits;
                        byte ofBits = ofDInfo->nbAdditionalBits;
                        byte totalBits = (byte)(llBits + mlBits + ofBits);
                        ushort llNext = llDInfo->nextState;
                        ushort mlNext = mlDInfo->nextState;
                        ushort ofNext = ofDInfo->nextState;
                        uint llnbBits = llDInfo->nbBits;
                        uint mlnbBits = mlDInfo->nbBits;
                        uint ofnbBits = ofDInfo->nbBits;
                        assert(llBits <= 16);
                        assert(mlBits <= 16);
                        assert(ofBits <= 31);
                        {
                            nuint offset;
                            if (ofBits > 1)
                            {
                                if (MEM_32bits && isLongOffset != default && ofBits >= 25)
                                {
                                    /* Always read extra bits, this keeps the logic simple,
                                     * avoids branches, and avoids accidentally reading 0 bits.
                                     */
                                    const uint extraBits = 30 - 25;
                                    offset = ofBase + (BIT_readBitsFast(seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, ofBits - extraBits) << (int)extraBits);
                                    BIT_reloadDStream(ref seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, ref seqState_DStream_ptr, seqState_DStream_start, seqState_DStream_limitPtr);
                                    offset += BIT_readBitsFast(seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, extraBits);
                                }
                                else
                                {
                                    offset = ofBase + BIT_readBitsFast(seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, ofBits);
                                    if (MEM_32bits)
                                        BIT_reloadDStream(ref seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, ref seqState_DStream_ptr, seqState_DStream_start, seqState_DStream_limitPtr);
                                }

                                seqState.prevOffset.e2 = seqState.prevOffset.e1;
                                seqState.prevOffset.e1 = seqState.prevOffset.e0;
                                seqState.prevOffset.e0 = offset;
                            }
                            else
                            {
                                uint ll0 = llDInfo->baseValue == 0 ? 1U : 0U;
                                if (ofBits == 0)
                                {
                                    offset = System.Runtime.CompilerServices.Unsafe.Add(ref seqState.prevOffset.e0, (int)ll0);
                                    seqState.prevOffset.e1 = System.Runtime.CompilerServices.Unsafe.Add(ref seqState.prevOffset.e0, ll0 == 0 ? 1 : 0);
                                    seqState.prevOffset.e0 = offset;
                                }
                                else
                                {
                                    offset = ofBase + ll0 + BIT_readBitsFast(seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, 1);
                                    {
                                        nuint temp = offset == 3 ? seqState.prevOffset.e0 - 1 : System.Runtime.CompilerServices.Unsafe.Add(ref seqState.prevOffset.e0, (int)offset);
                                        temp -= temp == 0 ? 1U : 0U;
                                        if (offset != 1)
                                            seqState.prevOffset.e2 = seqState.prevOffset.e1;
                                        seqState.prevOffset.e1 = seqState.prevOffset.e0;
                                        seqState.prevOffset.e0 = offset = temp;
                                    }
                                }
                            }

                            sequence_offset = offset;
                        }

                        if (mlBits > 0)
                            sequence_matchLength += BIT_readBitsFast(seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, mlBits);
                        if (MEM_32bits && mlBits + llBits >= 25 - (30 - 25))
                            BIT_reloadDStream(ref seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, ref seqState_DStream_ptr, seqState_DStream_start, seqState_DStream_limitPtr);
                        if (MEM_64bits && totalBits >= 57 - (9 + 9 + 8))
                            BIT_reloadDStream(ref seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, ref seqState_DStream_ptr, seqState_DStream_start, seqState_DStream_limitPtr);
                        if (llBits > 0)
                            sequence_litLength += BIT_readBitsFast(seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, llBits);
                        if (MEM_32bits)
                            BIT_reloadDStream(ref seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, ref seqState_DStream_ptr, seqState_DStream_start, seqState_DStream_limitPtr);
                        if ((nbSeq == 1 ? 1 : 0) == 0)
                        {
                            ZSTD_updateFseStateWithDInfo(ref seqState.stateLL, seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, llNext, llnbBits);
                            ZSTD_updateFseStateWithDInfo(ref seqState.stateML, seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, mlNext, mlnbBits);
                            if (MEM_32bits)
                                BIT_reloadDStream(ref seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, ref seqState_DStream_ptr, seqState_DStream_start, seqState_DStream_limitPtr);
                            ZSTD_updateFseStateWithDInfo(ref seqState.stateOffb, seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, ofNext, ofnbBits);
                            BIT_reloadDStream(ref seqState_DStream_bitContainer, ref seqState_DStream_bitsConsumed, ref seqState_DStream_ptr, seqState_DStream_start, seqState_DStream_limitPtr);
                        }
                    }

                    nuint oneSeqSize;
                    {
                        byte* oLitEnd = op + sequence_litLength;
                        oneSeqSize = sequence_litLength + sequence_matchLength;
                        /* risk : address space overflow (32-bits) */
                        byte* oMatchEnd = op + oneSeqSize;
                        /* risk : address space underflow on oend=NULL */
                        byte* oend_w = oend - 32;
                        byte* iLitEnd = litPtr + sequence_litLength;
                        byte* match = oLitEnd - sequence_offset;
                        assert(op != null);
                        assert(oend_w < oend);
                        if (iLitEnd > litEnd || oMatchEnd > oend_w || MEM_32bits && (nuint)(oend - op) < oneSeqSize + 32)
                        {
                            oneSeqSize = ZSTD_execSequenceEnd(op, oend, new seq_t { litLength = sequence_litLength, matchLength = sequence_matchLength, offset = sequence_offset }, &litPtr, litEnd, prefixStart, vBase, dictEnd);
                            goto returnOneSeqSize;
                        }

                        assert(op <= oLitEnd);
                        assert(oLitEnd < oMatchEnd);
                        assert(oMatchEnd <= oend);
                        assert(iLitEnd <= litEnd);
                        assert(oLitEnd <= oend_w);
                        assert(oMatchEnd <= oend_w);
                        assert(32 >= 16);
                        ZSTD_copy16(op, litPtr);
                        if (sequence_litLength > 16)
                        {
                            ZSTD_wildcopy(op + 16, litPtr + 16, (nint)(sequence_litLength - 16), ZSTD_overlap_e.ZSTD_no_overlap);
                        }

                        byte* opInner = oLitEnd;
                        litPtr = iLitEnd;
                        if (sequence_offset > (nuint)(oLitEnd - prefixStart))
                        {
                            if (sequence_offset > (nuint)(oLitEnd - vBase))
                            {
                                oneSeqSize = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                                goto returnOneSeqSize;
                            }

                            match = dictEnd + (match - prefixStart);
                            if (match + sequence_matchLength <= dictEnd)
                            {
                                memmove(oLitEnd, match, sequence_matchLength);
                                goto returnOneSeqSize;
                            }

                            {
                                nuint length1 = (nuint)(dictEnd - match);
                                memmove(oLitEnd, match, length1);
                                opInner = oLitEnd + length1;
                                sequence_matchLength -= length1;
                                match = prefixStart;
                            }
                        }

                        assert(opInner <= oMatchEnd);
                        assert(oMatchEnd <= oend_w);
                        assert(match >= prefixStart);
                        assert(sequence_matchLength >= 1);
                        if (sequence_offset >= 16)
                        {
                            ZSTD_wildcopy(opInner, match, (nint)sequence_matchLength, ZSTD_overlap_e.ZSTD_no_overlap);
                            goto returnOneSeqSize;
                        }

                        assert(sequence_offset < 16);
                        ZSTD_overlapCopy8(ref opInner, ref match, sequence_offset);
                        if (sequence_matchLength > 8)
                        {
                            assert(opInner < oMatchEnd);
                            ZSTD_wildcopy(opInner, match, (nint)sequence_matchLength - 8, ZSTD_overlap_e.ZSTD_overlap_src_before_dst);
                        }

                    returnOneSeqSize:
                        ;
                    }

                    if (ERR_isError(oneSeqSize))
                        return oneSeqSize;
                    op += oneSeqSize;
                }

                assert(nbSeq == 0);
                if (BIT_endOfDStream(seqState_DStream_bitsConsumed, seqState_DStream_ptr, seqState_DStream_start) == 0)
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                {
                    uint i;
                    for (i = 0; i < 3; i++)
                        dctx->entropy.rep[i] = (uint)System.Runtime.CompilerServices.Unsafe.Add(ref seqState.prevOffset.e0, (int)i);
                }
            }

            {
                nuint lastLLSize = (nuint)(litEnd - litPtr);
                if (lastLLSize > (nuint)(oend - op))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                }

                if (op != null)
                {
                    memcpy(op, litPtr, (uint)lastLLSize);
                    op += lastLLSize;
                }
            }

            return (nuint)(op - ostart);
        }

        private static nuint ZSTD_decompressSequences_default(ZSTD_DCtx_s* dctx, void* dst, nuint maxDstSize, void* seqStart, nuint seqSize, int nbSeq, ZSTD_longOffset_e isLongOffset)
        {
            return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
        }

        private static nuint ZSTD_decompressSequencesSplitLitBuffer_default(ZSTD_DCtx_s* dctx, void* dst, nuint maxDstSize, void* seqStart, nuint seqSize, int nbSeq, ZSTD_longOffset_e isLongOffset)
        {
            return ZSTD_decompressSequences_bodySplitLitBuffer(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static nuint ZSTD_prefetchMatch(nuint prefetchPos, seq_t sequence, byte* prefixStart, byte* dictEnd)
        {
            prefetchPos += sequence.litLength;
            {
                byte* matchBase = sequence.offset > prefetchPos ? dictEnd : prefixStart;
                /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted.
                 * No consequence though : memory address is only used for prefetching, not for dereferencing */
                byte* match = ZSTD_wrappedPtrSub(ZSTD_wrappedPtrAdd(matchBase, (nint)prefetchPos), (nint)sequence.offset);
                if (System.Runtime.Intrinsics.X86.Sse.IsSupported)
                {
                    System.Runtime.Intrinsics.X86.Sse.Prefetch0(match);
                    System.Runtime.Intrinsics.X86.Sse.Prefetch0(match + 64);
                }
            }

            return prefetchPos + sequence.matchLength;
        }

        /* This decoding function employs prefetching
         * to reduce latency impact of cache misses.
         * It's generally employed when block contains a significant portion of long-distance matches
         * or when coupled with a "cold" dictionary */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static nuint ZSTD_decompressSequencesLong_body(ZSTD_DCtx_s* dctx, void* dst, nuint maxDstSize, void* seqStart, nuint seqSize, int nbSeq, ZSTD_longOffset_e isLongOffset)
        {
            byte* ip = (byte*)seqStart;
            byte* iend = ip + seqSize;
            byte* ostart = (byte*)dst;
            byte* oend = dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_in_dst ? dctx->litBuffer : ZSTD_maybeNullPtrAdd(ostart, (nint)maxDstSize);
            byte* op = ostart;
            byte* litPtr = dctx->litPtr;
            byte* litBufferEnd = dctx->litBufferEnd;
            byte* prefixStart = (byte*)dctx->prefixStart;
            byte* dictStart = (byte*)dctx->virtualStart;
            byte* dictEnd = (byte*)dctx->dictEnd;
            if (nbSeq != 0)
            {
                seq_t* sequences = stackalloc seq_t[8];
                int seqAdvance = nbSeq < 8 ? nbSeq : 8;
                seqState_t seqState;
                int seqNb;
                /* track position relative to prefixStart */
                nuint prefetchPos = (nuint)(op - prefixStart);
                dctx->fseEntropy = 1;
                {
                    int i;
                    for (i = 0; i < 3; i++)
                        (&seqState.prevOffset.e0)[i] = dctx->entropy.rep[i];
                }

                assert(dst != null);
                assert(iend >= ip);
                if (ERR_isError(BIT_initDStream(&seqState.DStream, ip, (nuint)(iend - ip))))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr);
                ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
                ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
                for (seqNb = 0; seqNb < seqAdvance; seqNb++)
                {
                    seq_t sequence = ZSTD_decodeSequence(&seqState, isLongOffset, seqNb == nbSeq - 1 ? 1 : 0);
                    prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd);
                    sequences[seqNb] = sequence;
                }

                for (; seqNb < nbSeq; seqNb++)
                {
                    seq_t sequence = ZSTD_decodeSequence(&seqState, isLongOffset, seqNb == nbSeq - 1 ? 1 : 0);
                    if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split && litPtr + sequences[seqNb - 8 & 8 - 1].litLength > dctx->litBufferEnd)
                    {
                        /* lit buffer is reaching split point, empty out the first buffer and transition to litExtraBuffer */
                        nuint leftoverLit = (nuint)(dctx->litBufferEnd - litPtr);
                        if (leftoverLit != 0)
                        {
                            if (leftoverLit > (nuint)(oend - op))
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                            }

                            ZSTD_safecopyDstBeforeSrc(op, litPtr, (nint)leftoverLit);
                            sequences[seqNb - 8 & 8 - 1].litLength -= leftoverLit;
                            op += leftoverLit;
                        }

                        litPtr = dctx->litExtraBuffer;
                        litBufferEnd = dctx->litExtraBuffer + (1 << 16);
                        dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst;
                        {
                            nuint oneSeqSize = ZSTD_execSequence(op, oend, sequences[seqNb - 8 & 8 - 1], &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd);
                            if (ERR_isError(oneSeqSize))
                                return oneSeqSize;
                            prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd);
                            sequences[seqNb & 8 - 1] = sequence;
                            op += oneSeqSize;
                        }
                    }
                    else
                    {
                        /* lit buffer is either wholly contained in first or second split, or not split at all*/
                        nuint oneSeqSize = dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split ? ZSTD_execSequenceSplitLitBuffer(op, oend, litPtr + sequences[seqNb - 8 & 8 - 1].litLength - 32, sequences[seqNb - 8 & 8 - 1], &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd) : ZSTD_execSequence(op, oend, sequences[seqNb - 8 & 8 - 1], &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd);
                        if (ERR_isError(oneSeqSize))
                            return oneSeqSize;
                        prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd);
                        sequences[seqNb & 8 - 1] = sequence;
                        op += oneSeqSize;
                    }
                }

                if (BIT_endOfDStream(&seqState.DStream) == 0)
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
                }

                seqNb -= seqAdvance;
                for (; seqNb < nbSeq; seqNb++)
                {
                    seq_t* sequence = &sequences[seqNb & 8 - 1];
                    if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split && litPtr + sequence->litLength > dctx->litBufferEnd)
                    {
                        nuint leftoverLit = (nuint)(dctx->litBufferEnd - litPtr);
                        if (leftoverLit != 0)
                        {
                            if (leftoverLit > (nuint)(oend - op))
                            {
                                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                            }

                            ZSTD_safecopyDstBeforeSrc(op, litPtr, (nint)leftoverLit);
                            sequence->litLength -= leftoverLit;
                            op += leftoverLit;
                        }

                        litPtr = dctx->litExtraBuffer;
                        litBufferEnd = dctx->litExtraBuffer + (1 << 16);
                        dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst;
                        {
                            nuint oneSeqSize = ZSTD_execSequence(op, oend, *sequence, &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd);
                            if (ERR_isError(oneSeqSize))
                                return oneSeqSize;
                            op += oneSeqSize;
                        }
                    }
                    else
                    {
                        nuint oneSeqSize = dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split ? ZSTD_execSequenceSplitLitBuffer(op, oend, litPtr + sequence->litLength - 32, *sequence, &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd) : ZSTD_execSequence(op, oend, *sequence, &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd);
                        if (ERR_isError(oneSeqSize))
                            return oneSeqSize;
                        op += oneSeqSize;
                    }
                }

                {
                    uint i;
                    for (i = 0; i < 3; i++)
                        dctx->entropy.rep[i] = (uint)(&seqState.prevOffset.e0)[i];
                }
            }

            if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split)
            {
                nuint lastLLSize = (nuint)(litBufferEnd - litPtr);
                if (lastLLSize > (nuint)(oend - op))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                }

                if (op != null)
                {
                    memmove(op, litPtr, lastLLSize);
                    op += lastLLSize;
                }

                litPtr = dctx->litExtraBuffer;
                litBufferEnd = dctx->litExtraBuffer + (1 << 16);
            }

            {
                nuint lastLLSize = (nuint)(litBufferEnd - litPtr);
                if (lastLLSize > (nuint)(oend - op))
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                }

                if (op != null)
                {
                    memmove(op, litPtr, lastLLSize);
                    op += lastLLSize;
                }
            }

            return (nuint)(op - ostart);
        }

        private static nuint ZSTD_decompressSequencesLong_default(ZSTD_DCtx_s* dctx, void* dst, nuint maxDstSize, void* seqStart, nuint seqSize, int nbSeq, ZSTD_longOffset_e isLongOffset)
        {
            return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
        }

        private static nuint ZSTD_decompressSequences(ZSTD_DCtx_s* dctx, void* dst, nuint maxDstSize, void* seqStart, nuint seqSize, int nbSeq, ZSTD_longOffset_e isLongOffset)
        {
            return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
        }

        private static nuint ZSTD_decompressSequencesSplitLitBuffer(ZSTD_DCtx_s* dctx, void* dst, nuint maxDstSize, void* seqStart, nuint seqSize, int nbSeq, ZSTD_longOffset_e isLongOffset)
        {
            return ZSTD_decompressSequencesSplitLitBuffer_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
        }

        /* ZSTD_decompressSequencesLong() :
         * decompression function triggered when a minimum share of offsets is considered "long",
         * aka out of cache.
         * note : "long" definition seems overloaded here, sometimes meaning "wider than bitstream register", and sometimes meaning "farther than memory cache distance".
         * This function will try to mitigate main memory latency through the use of prefetching */
        private static nuint ZSTD_decompressSequencesLong(ZSTD_DCtx_s* dctx, void* dst, nuint maxDstSize, void* seqStart, nuint seqSize, int nbSeq, ZSTD_longOffset_e isLongOffset)
        {
            return ZSTD_decompressSequencesLong_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
        }

        /**
         * @returns The total size of the history referenceable by zstd, including
         * both the prefix and the extDict. At @p op any offset larger than this
         * is invalid.
         */
        private static nuint ZSTD_totalHistorySize(byte* op, byte* virtualStart)
        {
            return (nuint)(op - virtualStart);
        }

        /* ZSTD_getOffsetInfo() :
         * condition : offTable must be valid
         * @return : "share" of long offsets (arbitrarily defined as > (1<<23))
         *           compared to maximum possible of (1<<OffFSELog),
         *           as well as the maximum number additional bits required.
         */
        private static ZSTD_OffsetInfo ZSTD_getOffsetInfo(ZSTD_seqSymbol* offTable, int nbSeq)
        {
            ZSTD_OffsetInfo info = new ZSTD_OffsetInfo
            {
                longOffsetShare = 0,
                maxNbAdditionalBits = 0
            };
            if (nbSeq != 0)
            {
                void* ptr = offTable;
                uint tableLog = ((ZSTD_seqSymbol_header*)ptr)[0].tableLog;
                ZSTD_seqSymbol* table = offTable + 1;
                uint max = (uint)(1 << (int)tableLog);
                uint u;
                assert(max <= 1 << 8);
                for (u = 0; u < max; u++)
                {
                    info.maxNbAdditionalBits = info.maxNbAdditionalBits > table[u].nbAdditionalBits ? info.maxNbAdditionalBits : table[u].nbAdditionalBits;
                    if (table[u].nbAdditionalBits > 22)
                        info.longOffsetShare += 1;
                }

                assert(tableLog <= 8);
                info.longOffsetShare <<= (int)(8 - tableLog);
            }

            return info;
        }

        /**
         * @returns The maximum offset we can decode in one read of our bitstream, without
         * reloading more bits in the middle of the offset bits read. Any offsets larger
         * than this must use the long offset decoder.
         */
        private static nuint ZSTD_maxShortOffset()
        {
            if (MEM_64bits)
            {
                return unchecked((nuint)(-1));
            }
            else
            {
                /* The maximum offBase is (1 << (STREAM_ACCUMULATOR_MIN + 1)) - 1.
                 * This offBase would require STREAM_ACCUMULATOR_MIN extra bits.
                 * Then we have to subtract ZSTD_REP_NUM to get the maximum possible offset.
                 */
                nuint maxOffbase = ((nuint)1 << (int)((uint)(MEM_32bits ? 25 : 57) + 1)) - 1;
                nuint maxOffset = maxOffbase - 3;
                assert(ZSTD_highbit32((uint)maxOffbase) == (uint)(MEM_32bits ? 25 : 57));
                return maxOffset;
            }
        }

        /* ZSTD_decompressBlock_internal() :
         * decompress block, starting at `src`,
         * into destination buffer `dst`.
         * @return : decompressed block size,
         *           or an error code (which can be tested using ZSTD_isError())
         */
        private static nuint ZSTD_decompressBlock_internal(ZSTD_DCtx_s* dctx, void* dst, nuint dstCapacity, void* src, nuint srcSize, streaming_operation streaming)
        {
            byte* ip = (byte*)src;
            if (srcSize > ZSTD_blockSizeMax(dctx))
            {
                return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
            }

            {
                nuint litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize, dst, dstCapacity, streaming);
                if (ERR_isError(litCSize))
                    return litCSize;
                ip += litCSize;
                srcSize -= litCSize;
            }

            {
                /* Compute the maximum block size, which must also work when !frame and fParams are unset.
                 * Additionally, take the min with dstCapacity to ensure that the totalHistorySize fits in a size_t.
                 */
                nuint blockSizeMax = dstCapacity < ZSTD_blockSizeMax(dctx) ? dstCapacity : ZSTD_blockSizeMax(dctx);
                nuint totalHistorySize = ZSTD_totalHistorySize(ZSTD_maybeNullPtrAdd((byte*)dst, (nint)blockSizeMax), (byte*)dctx->virtualStart);
                /* isLongOffset must be true if there are long offsets.
                 * Offsets are long if they are larger than ZSTD_maxShortOffset().
                 * We don't expect that to be the case in 64-bit mode.
                 *
                 * We check here to see if our history is large enough to allow long offsets.
                 * If it isn't, then we can't possible have (valid) long offsets. If the offset
                 * is invalid, then it is okay to read it incorrectly.
                 *
                 * If isLongOffsets is true, then we will later check our decoding table to see
                 * if it is even possible to generate long offsets.
                 */
                ZSTD_longOffset_e isLongOffset = (ZSTD_longOffset_e)(MEM_32bits && totalHistorySize > ZSTD_maxShortOffset() ? 1 : 0);
                int usePrefetchDecoder = dctx->ddictIsCold;
                int nbSeq;
                nuint seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, srcSize);
                if (ERR_isError(seqHSize))
                    return seqHSize;
                ip += seqHSize;
                srcSize -= seqHSize;
                if ((dst == null || dstCapacity == 0) && nbSeq > 0)
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                }

                if (MEM_64bits && sizeof(nuint) == sizeof(void*) && unchecked((nuint)(-1)) - (nuint)dst < 1 << 20)
                {
                    return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
                }

                if (isLongOffset != default || usePrefetchDecoder == 0 && totalHistorySize > 1U << 24 && nbSeq > 8)
                {
                    ZSTD_OffsetInfo info = ZSTD_getOffsetInfo(dctx->OFTptr, nbSeq);
                    if (isLongOffset != default && info.maxNbAdditionalBits <= (uint)(MEM_32bits ? 25 : 57))
                    {
                        isLongOffset = ZSTD_longOffset_e.ZSTD_lo_isRegularOffset;
                    }

                    if (usePrefetchDecoder == 0)
                    {
                        /* heuristic values, correspond to 2.73% and 7.81% */
                        uint minShare = (uint)(MEM_64bits ? 7 : 20);
                        usePrefetchDecoder = info.longOffsetShare >= minShare ? 1 : 0;
                    }
                }

                dctx->ddictIsCold = 0;
                if (usePrefetchDecoder != 0)
                {
                    return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset);
                }

                if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split)
                    return ZSTD_decompressSequencesSplitLitBuffer(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset);
                else
                    return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset);
            }
        }

        /*! ZSTD_checkContinuity() :
         *  check if next `dst` follows previous position, where decompression ended.
         *  If yes, do nothing (continue on current segment).
         *  If not, classify previous segment as "external dictionary", and start a new segment.
         *  This function cannot fail. */
        private static void ZSTD_checkContinuity(ZSTD_DCtx_s* dctx, void* dst, nuint dstSize)
        {
            if (dst != dctx->previousDstEnd && dstSize > 0)
            {
                dctx->dictEnd = dctx->previousDstEnd;
                dctx->virtualStart = (sbyte*)dst - ((sbyte*)dctx->previousDstEnd - (sbyte*)dctx->prefixStart);
                dctx->prefixStart = dst;
                dctx->previousDstEnd = dst;
            }
        }

        private static void ZSTD_initFseState(ref ZSTD_fseState DStatePtr, ref BIT_DStream_t bitD, ZSTD_seqSymbol* dt)
        {
            void* ptr = dt;
            ZSTD_seqSymbol_header* DTableH = (ZSTD_seqSymbol_header*)ptr;
            DStatePtr.state = BIT_readBits(bitD.bitContainer, ref bitD.bitsConsumed, DTableH->tableLog);
            BIT_reloadDStream(ref bitD.bitContainer, ref bitD.bitsConsumed, ref bitD.ptr, bitD.start, bitD.limitPtr);
            DStatePtr.table = dt + 1;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static void ZSTD_updateFseStateWithDInfo(ref ZSTD_fseState DStatePtr, nuint bitD_bitContainer, ref uint bitD_bitsConsumed, ushort nextState, uint nbBits)
        {
            nuint lowBits = BIT_readBits(bitD_bitContainer, ref bitD_bitsConsumed, nbBits);
            DStatePtr.state = nextState + lowBits;
        }

        /*! ZSTD_overlapCopy8() :
         *  Copies 8 bytes from ip to op and updates op and ip where ip <= op.
         *  If the offset is < 8 then the offset is spread to at least 8 bytes.
         *
         *  Precondition: *ip <= *op
         *  Postcondition: *op - *op >= 8
         */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static void ZSTD_overlapCopy8(ref byte* op, ref byte* ip, nuint offset)
        {
            assert(ip <= op);
            if (offset < 8)
            {
                int sub2 = dec64table[offset];
                op[0] = ip[0];
                op[1] = ip[1];
                op[2] = ip[2];
                op[3] = ip[3];
                ip += dec32table[offset];
                ZSTD_copy4(op + 4, ip);
                ip -= sub2;
            }
            else
            {
                ZSTD_copy8(op, ip);
            }

            ip += 8;
            op += 8;
            assert(op - ip >= 8);
        }
    }
}