Editor/HumanoidRetargeter/Embedded/ZstdSharp/Unsafe/ZstdDecompress.cs
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using static HumanoidRetargeterZstd.UnsafeHelper;
namespace HumanoidRetargeterZstd.Unsafe
{
public static unsafe partial class Methods
{
/* Hash function to determine starting position of dict insertion within the table
* Returns an index between [0, hashSet->ddictPtrTableSize]
*/
private static nuint ZSTD_DDictHashSet_getIndex(ZSTD_DDictHashSet* hashSet, uint dictID)
{
ulong hash = ZSTD_XXH64(&dictID, sizeof(uint), 0);
return (nuint)(hash & hashSet->ddictPtrTableSize - 1);
}
/* Fetches a DDict with the given dictID
* Returns the ZSTD_DDict* with the requested dictID. If it doesn't exist, then returns NULL.
*/
private static ZSTD_DDict_s* ZSTD_DDictHashSet_getDDict(ZSTD_DDictHashSet* hashSet, uint dictID)
{
nuint idx = ZSTD_DDictHashSet_getIndex(hashSet, dictID);
nuint idxRangeMask = hashSet->ddictPtrTableSize - 1;
for (; ; )
{
nuint currDictID = ZSTD_getDictID_fromDDict(hashSet->ddictPtrTable[idx]);
if (currDictID == dictID || currDictID == 0)
{
break;
}
else
{
idx &= idxRangeMask;
idx++;
}
}
return hashSet->ddictPtrTable[idx];
}
/* Frees the table of ZSTD_DDict* within a hashset, then frees the hashset itself.
* Note: The ZSTD_DDict* within the table are NOT freed.
*/
private static void ZSTD_freeDDictHashSet(ZSTD_DDictHashSet* hashSet, ZSTD_customMem customMem)
{
if (hashSet != null && hashSet->ddictPtrTable != null)
{
ZSTD_customFree(hashSet->ddictPtrTable, customMem);
}
if (hashSet != null)
{
ZSTD_customFree(hashSet, customMem);
}
}
private static nuint ZSTD_startingInputLength(ZSTD_format_e format)
{
nuint startingInputLength = (nuint)(format == ZSTD_format_e.ZSTD_f_zstd1 ? 5 : 1);
assert(format == ZSTD_format_e.ZSTD_f_zstd1 || format == ZSTD_format_e.ZSTD_f_zstd1_magicless);
return startingInputLength;
}
private static void ZSTD_DCtx_resetParameters(ZSTD_DCtx_s* dctx)
{
assert(dctx->streamStage == ZSTD_dStreamStage.zdss_init);
dctx->format = ZSTD_format_e.ZSTD_f_zstd1;
dctx->maxWindowSize = ((uint)1 << 27) + 1;
dctx->outBufferMode = ZSTD_bufferMode_e.ZSTD_bm_buffered;
dctx->forceIgnoreChecksum = ZSTD_forceIgnoreChecksum_e.ZSTD_d_validateChecksum;
dctx->refMultipleDDicts = ZSTD_refMultipleDDicts_e.ZSTD_rmd_refSingleDDict;
dctx->disableHufAsm = 0;
dctx->maxBlockSizeParam = 0;
}
private static void ZSTD_initDCtx_internal(ZSTD_DCtx_s* dctx)
{
dctx->staticSize = 0;
dctx->ddict = null;
dctx->ddictLocal = null;
dctx->dictEnd = null;
dctx->ddictIsCold = 0;
dctx->dictUses = ZSTD_dictUses_e.ZSTD_dont_use;
dctx->inBuff = null;
dctx->inBuffSize = 0;
dctx->outBuffSize = 0;
dctx->streamStage = ZSTD_dStreamStage.zdss_init;
dctx->noForwardProgress = 0;
dctx->oversizedDuration = 0;
dctx->isFrameDecompression = 1;
dctx->ddictSet = null;
ZSTD_DCtx_resetParameters(dctx);
}
private static ZSTD_DCtx_s* ZSTD_createDCtx_internal(ZSTD_customMem customMem)
{
if (((customMem.customAlloc == null ? 1 : 0) ^ (customMem.customFree == null ? 1 : 0)) != 0)
return null;
{
ZSTD_DCtx_s* dctx = (ZSTD_DCtx_s*)ZSTD_customMalloc((nuint)sizeof(ZSTD_DCtx_s), customMem);
if (dctx == null)
return null;
dctx->customMem = customMem;
ZSTD_initDCtx_internal(dctx);
return dctx;
}
}
public static ZSTD_DCtx_s* ZSTD_createDCtx()
{
return ZSTD_createDCtx_internal(ZSTD_defaultCMem);
}
private static void ZSTD_clearDict(ZSTD_DCtx_s* dctx)
{
ZSTD_freeDDict(dctx->ddictLocal);
dctx->ddictLocal = null;
dctx->ddict = null;
dctx->dictUses = ZSTD_dictUses_e.ZSTD_dont_use;
}
public static nuint ZSTD_freeDCtx(ZSTD_DCtx_s* dctx)
{
if (dctx == null)
return 0;
if (dctx->staticSize != 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation));
}
{
ZSTD_customMem cMem = dctx->customMem;
ZSTD_clearDict(dctx);
ZSTD_customFree(dctx->inBuff, cMem);
dctx->inBuff = null;
if (dctx->ddictSet != null)
{
ZSTD_freeDDictHashSet(dctx->ddictSet, cMem);
dctx->ddictSet = null;
}
ZSTD_customFree(dctx, cMem);
return 0;
}
}
/* Given a dctx with a digested frame params, re-selects the correct ZSTD_DDict based on
* the requested dict ID from the frame. If there exists a reference to the correct ZSTD_DDict, then
* accordingly sets the ddict to be used to decompress the frame.
*
* If no DDict is found, then no action is taken, and the ZSTD_DCtx::ddict remains as-is.
*
* ZSTD_d_refMultipleDDicts must be enabled for this function to be called.
*/
private static void ZSTD_DCtx_selectFrameDDict(ZSTD_DCtx_s* dctx)
{
assert(dctx->refMultipleDDicts != default && dctx->ddictSet != null);
if (dctx->ddict != null)
{
ZSTD_DDict_s* frameDDict = ZSTD_DDictHashSet_getDDict(dctx->ddictSet, dctx->fParams.dictID);
if (frameDDict != null)
{
ZSTD_clearDict(dctx);
dctx->dictID = dctx->fParams.dictID;
dctx->ddict = frameDDict;
dctx->dictUses = ZSTD_dictUses_e.ZSTD_use_indefinitely;
}
}
}
/** ZSTD_frameHeaderSize_internal() :
* srcSize must be large enough to reach header size fields.
* note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless.
* @return : size of the Frame Header
* or an error code, which can be tested with ZSTD_isError() */
private static nuint ZSTD_frameHeaderSize_internal(void* src, nuint srcSize, ZSTD_format_e format)
{
nuint minInputSize = ZSTD_startingInputLength(format);
if (srcSize < minInputSize)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
}
{
byte fhd = ((byte*)src)[minInputSize - 1];
uint dictID = (uint)(fhd & 3);
uint singleSegment = (uint)(fhd >> 5 & 1);
uint fcsId = (uint)(fhd >> 6);
return minInputSize + (nuint)(singleSegment == 0 ? 1 : 0) + ZSTD_did_fieldSize[dictID] + ZSTD_fcs_fieldSize[fcsId] + (nuint)(singleSegment != 0 && fcsId == 0 ? 1 : 0);
}
}
/** ZSTD_getFrameHeader_advanced() :
* decode Frame Header, or require larger `srcSize`.
* note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless
* @return : 0, `zfhPtr` is correctly filled,
* >0, `srcSize` is too small, value is wanted `srcSize` amount,
** or an error code, which can be tested using ZSTD_isError() */
public static nuint ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, void* src, nuint srcSize, ZSTD_format_e format)
{
byte* ip = (byte*)src;
nuint minInputSize = ZSTD_startingInputLength(format);
if (srcSize > 0)
{
if (src == null)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC));
}
}
if (srcSize < minInputSize)
{
if (srcSize > 0 && format != ZSTD_format_e.ZSTD_f_zstd1_magicless)
{
/* when receiving less than @minInputSize bytes,
* control these bytes at least correspond to a supported magic number
* in order to error out early if they don't.
**/
nuint toCopy = 4 < srcSize ? 4 : srcSize;
byte* hbuf = stackalloc byte[4];
MEM_writeLE32(hbuf, 0xFD2FB528);
assert(src != null);
memcpy(hbuf, src, (uint)toCopy);
if (MEM_readLE32(hbuf) != 0xFD2FB528)
{
MEM_writeLE32(hbuf, 0x184D2A50);
memcpy(hbuf, src, (uint)toCopy);
if ((MEM_readLE32(hbuf) & 0xFFFFFFF0) != 0x184D2A50)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_prefix_unknown));
}
}
}
return minInputSize;
}
*zfhPtr = new ZSTD_frameHeader();
if (format != ZSTD_format_e.ZSTD_f_zstd1_magicless && MEM_readLE32(src) != 0xFD2FB528)
{
if ((MEM_readLE32(src) & 0xFFFFFFF0) == 0x184D2A50)
{
if (srcSize < 8)
return 8;
*zfhPtr = new ZSTD_frameHeader
{
frameType = ZSTD_frameType_e.ZSTD_skippableFrame,
dictID = MEM_readLE32(src) - 0x184D2A50,
headerSize = 8,
frameContentSize = MEM_readLE32((sbyte*)src + 4)
};
return 0;
}
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_prefix_unknown));
}
{
nuint fhsize = ZSTD_frameHeaderSize_internal(src, srcSize, format);
if (srcSize < fhsize)
return fhsize;
zfhPtr->headerSize = (uint)fhsize;
}
{
byte fhdByte = ip[minInputSize - 1];
nuint pos = minInputSize;
uint dictIDSizeCode = (uint)(fhdByte & 3);
uint checksumFlag = (uint)(fhdByte >> 2 & 1);
uint singleSegment = (uint)(fhdByte >> 5 & 1);
uint fcsID = (uint)(fhdByte >> 6);
ulong windowSize = 0;
uint dictID = 0;
ulong frameContentSize = unchecked(0UL - 1);
if ((fhdByte & 0x08) != 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_unsupported));
}
if (singleSegment == 0)
{
byte wlByte = ip[pos++];
uint windowLog = (uint)((wlByte >> 3) + 10);
if (windowLog > (uint)(sizeof(nuint) == 4 ? 30 : 31))
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_windowTooLarge));
}
windowSize = 1UL << (int)windowLog;
windowSize += (windowSize >> 3) * (ulong)(wlByte & 7);
}
switch (dictIDSizeCode)
{
default:
assert(0 != 0);
goto case 0;
case 0:
break;
case 1:
dictID = ip[pos];
pos++;
break;
case 2:
dictID = MEM_readLE16(ip + pos);
pos += 2;
break;
case 3:
dictID = MEM_readLE32(ip + pos);
pos += 4;
break;
}
switch (fcsID)
{
default:
assert(0 != 0);
goto case 0;
case 0:
if (singleSegment != 0)
frameContentSize = ip[pos];
break;
case 1:
frameContentSize = (ulong)(MEM_readLE16(ip + pos) + 256);
break;
case 2:
frameContentSize = MEM_readLE32(ip + pos);
break;
case 3:
frameContentSize = MEM_readLE64(ip + pos);
break;
}
if (singleSegment != 0)
windowSize = frameContentSize;
zfhPtr->frameType = ZSTD_frameType_e.ZSTD_frame;
zfhPtr->frameContentSize = frameContentSize;
zfhPtr->windowSize = windowSize;
zfhPtr->blockSizeMax = (uint)(windowSize < 1 << 17 ? windowSize : 1 << 17);
zfhPtr->dictID = dictID;
zfhPtr->checksumFlag = checksumFlag;
}
return 0;
}
private static nuint readSkippableFrameSize(void* src, nuint srcSize)
{
const nuint skippableHeaderSize = 8;
uint sizeU32;
if (srcSize < 8)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
}
sizeU32 = MEM_readLE32((byte*)src + 4);
if (sizeU32 + 8 < sizeU32)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_unsupported));
}
{
nuint skippableSize = skippableHeaderSize + sizeU32;
if (skippableSize > srcSize)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
}
return skippableSize;
}
}
/** ZSTD_decodeFrameHeader() :
* `headerSize` must be the size provided by ZSTD_frameHeaderSize().
* If multiple DDict references are enabled, also will choose the correct DDict to use.
* @return : 0 if success, or an error code, which can be tested using ZSTD_isError() */
private static nuint ZSTD_decodeFrameHeader(ZSTD_DCtx_s* dctx, void* src, nuint headerSize)
{
nuint result = ZSTD_getFrameHeader_advanced(&dctx->fParams, src, headerSize, dctx->format);
if (ERR_isError(result))
return result;
if (result > 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
}
if (dctx->refMultipleDDicts == ZSTD_refMultipleDDicts_e.ZSTD_rmd_refMultipleDDicts && dctx->ddictSet != null)
{
ZSTD_DCtx_selectFrameDDict(dctx);
}
if (dctx->fParams.dictID != 0 && dctx->dictID != dctx->fParams.dictID)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_wrong));
}
dctx->validateChecksum = (uint)(dctx->fParams.checksumFlag != 0 && dctx->forceIgnoreChecksum == default ? 1 : 0);
if (dctx->validateChecksum != 0)
ZSTD_XXH64_reset(&dctx->xxhState, 0);
dctx->processedCSize += headerSize;
return 0;
}
private static ZSTD_frameSizeInfo ZSTD_errorFrameSizeInfo(nuint ret)
{
ZSTD_frameSizeInfo frameSizeInfo;
System.Runtime.CompilerServices.Unsafe.SkipInit(out frameSizeInfo);
frameSizeInfo.compressedSize = ret;
frameSizeInfo.decompressedBound = unchecked(0UL - 2);
return frameSizeInfo;
}
private static ZSTD_frameSizeInfo ZSTD_findFrameSizeInfo(void* src, nuint srcSize, ZSTD_format_e format)
{
ZSTD_frameSizeInfo frameSizeInfo;
frameSizeInfo = new ZSTD_frameSizeInfo();
if (format == ZSTD_format_e.ZSTD_f_zstd1 && srcSize >= 8 && (MEM_readLE32(src) & 0xFFFFFFF0) == 0x184D2A50)
{
frameSizeInfo.compressedSize = readSkippableFrameSize(src, srcSize);
assert(ERR_isError(frameSizeInfo.compressedSize) || frameSizeInfo.compressedSize <= srcSize);
return frameSizeInfo;
}
else
{
byte* ip = (byte*)src;
byte* ipstart = ip;
nuint remainingSize = srcSize;
nuint nbBlocks = 0;
ZSTD_frameHeader zfh;
{
nuint ret = ZSTD_getFrameHeader_advanced(&zfh, src, srcSize, format);
if (ERR_isError(ret))
return ZSTD_errorFrameSizeInfo(ret);
if (ret > 0)
return ZSTD_errorFrameSizeInfo(unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)));
}
ip += zfh.headerSize;
remainingSize -= zfh.headerSize;
while (true)
{
blockProperties_t blockProperties;
nuint cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties);
if (ERR_isError(cBlockSize))
return ZSTD_errorFrameSizeInfo(cBlockSize);
if (ZSTD_blockHeaderSize + cBlockSize > remainingSize)
return ZSTD_errorFrameSizeInfo(unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)));
ip += ZSTD_blockHeaderSize + cBlockSize;
remainingSize -= ZSTD_blockHeaderSize + cBlockSize;
nbBlocks++;
if (blockProperties.lastBlock != 0)
break;
}
if (zfh.checksumFlag != 0)
{
if (remainingSize < 4)
return ZSTD_errorFrameSizeInfo(unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)));
ip += 4;
}
frameSizeInfo.nbBlocks = nbBlocks;
frameSizeInfo.compressedSize = (nuint)(ip - ipstart);
frameSizeInfo.decompressedBound = zfh.frameContentSize != unchecked(0UL - 1) ? zfh.frameContentSize : (ulong)nbBlocks * zfh.blockSizeMax;
return frameSizeInfo;
}
}
private static nuint ZSTD_findFrameCompressedSize_advanced(void* src, nuint srcSize, ZSTD_format_e format)
{
ZSTD_frameSizeInfo frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize, format);
return frameSizeInfo.compressedSize;
}
/** ZSTD_decompressBound() :
* compatible with legacy mode
* `src` must point to the start of a ZSTD frame or a skippable frame
* `srcSize` must be at least as large as the frame contained
* @return : the maximum decompressed size of the compressed source
*/
public static ulong ZSTD_decompressBound(void* src, nuint srcSize)
{
ulong bound = 0;
while (srcSize > 0)
{
ZSTD_frameSizeInfo frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize, ZSTD_format_e.ZSTD_f_zstd1);
nuint compressedSize = frameSizeInfo.compressedSize;
ulong decompressedBound = frameSizeInfo.decompressedBound;
if (ERR_isError(compressedSize) || decompressedBound == unchecked(0UL - 2))
return unchecked(0UL - 2);
assert(srcSize >= compressedSize);
src = (byte*)src + compressedSize;
srcSize -= compressedSize;
bound += decompressedBound;
}
return bound;
}
private static nuint ZSTD_copyRawBlock(void* dst, nuint dstCapacity, void* src, nuint srcSize)
{
if (srcSize > dstCapacity)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
}
if (dst == null)
{
if (srcSize == 0)
return 0;
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstBuffer_null));
}
memmove(dst, src, srcSize);
return srcSize;
}
private static nuint ZSTD_setRleBlock(void* dst, nuint dstCapacity, byte b, nuint regenSize)
{
if (regenSize > dstCapacity)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
}
if (dst == null)
{
if (regenSize == 0)
return 0;
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstBuffer_null));
}
memset(dst, b, (uint)regenSize);
return regenSize;
}
private static void ZSTD_DCtx_trace_end(ZSTD_DCtx_s* dctx, ulong uncompressedSize, ulong compressedSize, int streaming)
{
}
/*! ZSTD_decompressFrame() :
* @dctx must be properly initialized
* will update *srcPtr and *srcSizePtr,
* to make *srcPtr progress by one frame. */
private static nuint ZSTD_decompressFrame(ZSTD_DCtx_s* dctx, void* dst, nuint dstCapacity, void** srcPtr, nuint* srcSizePtr)
{
byte* istart = (byte*)*srcPtr;
byte* ip = istart;
byte* ostart = (byte*)dst;
byte* oend = dstCapacity != 0 ? ostart + dstCapacity : ostart;
byte* op = ostart;
nuint remainingSrcSize = *srcSizePtr;
if (remainingSrcSize < (nuint)(dctx->format == ZSTD_format_e.ZSTD_f_zstd1 ? 6 : 2) + ZSTD_blockHeaderSize)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
}
{
nuint frameHeaderSize = ZSTD_frameHeaderSize_internal(ip, (nuint)(dctx->format == ZSTD_format_e.ZSTD_f_zstd1 ? 5 : 1), dctx->format);
if (ERR_isError(frameHeaderSize))
return frameHeaderSize;
if (remainingSrcSize < frameHeaderSize + ZSTD_blockHeaderSize)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
}
{
nuint err_code = ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize);
if (ERR_isError(err_code))
{
return err_code;
}
}
ip += frameHeaderSize;
remainingSrcSize -= frameHeaderSize;
}
if (dctx->maxBlockSizeParam != 0)
dctx->fParams.blockSizeMax = dctx->fParams.blockSizeMax < (uint)dctx->maxBlockSizeParam ? dctx->fParams.blockSizeMax : (uint)dctx->maxBlockSizeParam;
while (true)
{
byte* oBlockEnd = oend;
nuint decodedSize;
blockProperties_t blockProperties;
nuint cBlockSize = ZSTD_getcBlockSize(ip, remainingSrcSize, &blockProperties);
if (ERR_isError(cBlockSize))
return cBlockSize;
ip += ZSTD_blockHeaderSize;
remainingSrcSize -= ZSTD_blockHeaderSize;
if (cBlockSize > remainingSrcSize)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
}
if (ip >= op && ip < oBlockEnd)
{
oBlockEnd = op + (ip - op);
}
switch (blockProperties.blockType)
{
case blockType_e.bt_compressed:
assert(dctx->isFrameDecompression == 1);
decodedSize = ZSTD_decompressBlock_internal(dctx, op, (nuint)(oBlockEnd - op), ip, cBlockSize, streaming_operation.not_streaming);
break;
case blockType_e.bt_raw:
decodedSize = ZSTD_copyRawBlock(op, (nuint)(oend - op), ip, cBlockSize);
break;
case blockType_e.bt_rle:
decodedSize = ZSTD_setRleBlock(op, (nuint)(oBlockEnd - op), *ip, blockProperties.origSize);
break;
case blockType_e.bt_reserved:
default:
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
}
{
nuint err_code = decodedSize;
if (ERR_isError(err_code))
{
return err_code;
}
}
if (dctx->validateChecksum != 0)
{
ZSTD_XXH64_update(&dctx->xxhState, op, decodedSize);
}
if (decodedSize != 0)
{
op += decodedSize;
}
assert(ip != null);
ip += cBlockSize;
remainingSrcSize -= cBlockSize;
if (blockProperties.lastBlock != 0)
break;
}
if (dctx->fParams.frameContentSize != unchecked(0UL - 1))
{
if ((ulong)(op - ostart) != dctx->fParams.frameContentSize)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
}
}
if (dctx->fParams.checksumFlag != 0)
{
if (remainingSrcSize < 4)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_checksum_wrong));
}
if (dctx->forceIgnoreChecksum == default)
{
uint checkCalc = (uint)ZSTD_XXH64_digest(&dctx->xxhState);
uint checkRead;
checkRead = MEM_readLE32(ip);
if (checkRead != checkCalc)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_checksum_wrong));
}
}
ip += 4;
remainingSrcSize -= 4;
}
ZSTD_DCtx_trace_end(dctx, (ulong)(op - ostart), (ulong)(ip - istart), 0);
*srcPtr = ip;
*srcSizePtr = remainingSrcSize;
return (nuint)(op - ostart);
}
private static nuint ZSTD_decompressMultiFrame(ZSTD_DCtx_s* dctx, void* dst, nuint dstCapacity, void* src, nuint srcSize, void* dict, nuint dictSize, ZSTD_DDict_s* ddict)
{
void* dststart = dst;
int moreThan1Frame = 0;
assert(dict == null || ddict == null);
if (ddict != null)
{
dict = ZSTD_DDict_dictContent(ddict);
dictSize = ZSTD_DDict_dictSize(ddict);
}
while (srcSize >= ZSTD_startingInputLength(dctx->format))
{
if (dctx->format == ZSTD_format_e.ZSTD_f_zstd1 && srcSize >= 4)
{
uint magicNumber = MEM_readLE32(src);
if ((magicNumber & 0xFFFFFFF0) == 0x184D2A50)
{
/* skippable frame detected : skip it */
nuint skippableSize = readSkippableFrameSize(src, srcSize);
{
nuint err_code = skippableSize;
if (ERR_isError(err_code))
{
return err_code;
}
}
assert(skippableSize <= srcSize);
src = (byte*)src + skippableSize;
srcSize -= skippableSize;
continue;
}
}
if (ddict != null)
{
/* we were called from ZSTD_decompress_usingDDict */
nuint err_code = ZSTD_decompressBegin_usingDDict(dctx, ddict);
if (ERR_isError(err_code))
{
return err_code;
}
}
else
{
/* this will initialize correctly with no dict if dict == NULL, so
* use this in all cases but ddict */
nuint err_code = ZSTD_decompressBegin_usingDict(dctx, dict, dictSize);
if (ERR_isError(err_code))
{
return err_code;
}
}
ZSTD_checkContinuity(dctx, dst, dstCapacity);
{
nuint res = ZSTD_decompressFrame(dctx, dst, dstCapacity, &src, &srcSize);
if (ZSTD_getErrorCode(res) == ZSTD_ErrorCode.ZSTD_error_prefix_unknown && moreThan1Frame == 1)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
}
if (ERR_isError(res))
return res;
assert(res <= dstCapacity);
if (res != 0)
dst = (byte*)dst + res;
dstCapacity -= res;
}
moreThan1Frame = 1;
}
if (srcSize != 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
}
return (nuint)((byte*)dst - (byte*)dststart);
}
private static ZSTD_DDict_s* ZSTD_getDDict(ZSTD_DCtx_s* dctx)
{
switch (dctx->dictUses)
{
default:
assert(0 != 0);
goto case ZSTD_dictUses_e.ZSTD_dont_use;
case ZSTD_dictUses_e.ZSTD_dont_use:
ZSTD_clearDict(dctx);
return null;
case ZSTD_dictUses_e.ZSTD_use_indefinitely:
return dctx->ddict;
case ZSTD_dictUses_e.ZSTD_use_once:
dctx->dictUses = ZSTD_dictUses_e.ZSTD_dont_use;
return dctx->ddict;
}
}
/*! ZSTD_decompressDCtx() :
* Same as ZSTD_decompress(),
* requires an allocated ZSTD_DCtx.
* Compatible with sticky parameters (see below).
*/
public static nuint ZSTD_decompressDCtx(ZSTD_DCtx_s* dctx, void* dst, nuint dstCapacity, void* src, nuint srcSize)
{
return ZSTD_decompress_usingDDict(dctx, dst, dstCapacity, src, srcSize, ZSTD_getDDict(dctx));
}
/*-**************************************
* Advanced Streaming Decompression API
* Bufferless and synchronous
****************************************/
public static nuint ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx_s* dctx)
{
return dctx->expected;
}
/**
* Similar to ZSTD_nextSrcSizeToDecompress(), but when a block input can be streamed, we
* allow taking a partial block as the input. Currently only raw uncompressed blocks can
* be streamed.
*
* For blocks that can be streamed, this allows us to reduce the latency until we produce
* output, and avoid copying the input.
*
* @param inputSize - The total amount of input that the caller currently has.
*/
private static nuint ZSTD_nextSrcSizeToDecompressWithInputSize(ZSTD_DCtx_s* dctx, nuint inputSize)
{
if (!(dctx->stage == ZSTD_dStage.ZSTDds_decompressBlock || dctx->stage == ZSTD_dStage.ZSTDds_decompressLastBlock))
return dctx->expected;
if (dctx->bType != blockType_e.bt_raw)
return dctx->expected;
return inputSize <= 1 ? 1 : inputSize <= dctx->expected ? inputSize : dctx->expected;
}
public static ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx_s* dctx)
{
switch (dctx->stage)
{
default:
assert(0 != 0);
goto case ZSTD_dStage.ZSTDds_getFrameHeaderSize;
case ZSTD_dStage.ZSTDds_getFrameHeaderSize:
case ZSTD_dStage.ZSTDds_decodeFrameHeader:
return ZSTD_nextInputType_e.ZSTDnit_frameHeader;
case ZSTD_dStage.ZSTDds_decodeBlockHeader:
return ZSTD_nextInputType_e.ZSTDnit_blockHeader;
case ZSTD_dStage.ZSTDds_decompressBlock:
return ZSTD_nextInputType_e.ZSTDnit_block;
case ZSTD_dStage.ZSTDds_decompressLastBlock:
return ZSTD_nextInputType_e.ZSTDnit_lastBlock;
case ZSTD_dStage.ZSTDds_checkChecksum:
return ZSTD_nextInputType_e.ZSTDnit_checksum;
case ZSTD_dStage.ZSTDds_decodeSkippableHeader:
case ZSTD_dStage.ZSTDds_skipFrame:
return ZSTD_nextInputType_e.ZSTDnit_skippableFrame;
}
}
private static int ZSTD_isSkipFrame(ZSTD_DCtx_s* dctx)
{
return dctx->stage == ZSTD_dStage.ZSTDds_skipFrame ? 1 : 0;
}
/** ZSTD_decompressContinue() :
* srcSize : must be the exact nb of bytes expected (see ZSTD_nextSrcSizeToDecompress())
* @return : nb of bytes generated into `dst` (necessarily <= `dstCapacity)
* or an error code, which can be tested using ZSTD_isError() */
public static nuint ZSTD_decompressContinue(ZSTD_DCtx_s* dctx, void* dst, nuint dstCapacity, void* src, nuint srcSize)
{
if (srcSize != ZSTD_nextSrcSizeToDecompressWithInputSize(dctx, srcSize))
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
}
ZSTD_checkContinuity(dctx, dst, dstCapacity);
dctx->processedCSize += srcSize;
switch (dctx->stage)
{
case ZSTD_dStage.ZSTDds_getFrameHeaderSize:
assert(src != null);
if (dctx->format == ZSTD_format_e.ZSTD_f_zstd1)
{
assert(srcSize >= 4);
if ((MEM_readLE32(src) & 0xFFFFFFF0) == 0x184D2A50)
{
memcpy(dctx->headerBuffer, src, (uint)srcSize);
dctx->expected = 8 - srcSize;
dctx->stage = ZSTD_dStage.ZSTDds_decodeSkippableHeader;
return 0;
}
}
dctx->headerSize = ZSTD_frameHeaderSize_internal(src, srcSize, dctx->format);
if (ERR_isError(dctx->headerSize))
return dctx->headerSize;
memcpy(dctx->headerBuffer, src, (uint)srcSize);
dctx->expected = dctx->headerSize - srcSize;
dctx->stage = ZSTD_dStage.ZSTDds_decodeFrameHeader;
return 0;
case ZSTD_dStage.ZSTDds_decodeFrameHeader:
assert(src != null);
memcpy(dctx->headerBuffer + (dctx->headerSize - srcSize), src, (uint)srcSize);
{
nuint err_code = ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize);
if (ERR_isError(err_code))
{
return err_code;
}
}
dctx->expected = ZSTD_blockHeaderSize;
dctx->stage = ZSTD_dStage.ZSTDds_decodeBlockHeader;
return 0;
case ZSTD_dStage.ZSTDds_decodeBlockHeader:
{
blockProperties_t bp;
nuint cBlockSize = ZSTD_getcBlockSize(src, ZSTD_blockHeaderSize, &bp);
if (ERR_isError(cBlockSize))
return cBlockSize;
if (cBlockSize > dctx->fParams.blockSizeMax)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
}
dctx->expected = cBlockSize;
dctx->bType = bp.blockType;
dctx->rleSize = bp.origSize;
if (cBlockSize != 0)
{
dctx->stage = bp.lastBlock != 0 ? ZSTD_dStage.ZSTDds_decompressLastBlock : ZSTD_dStage.ZSTDds_decompressBlock;
return 0;
}
if (bp.lastBlock != 0)
{
if (dctx->fParams.checksumFlag != 0)
{
dctx->expected = 4;
dctx->stage = ZSTD_dStage.ZSTDds_checkChecksum;
}
else
{
dctx->expected = 0;
dctx->stage = ZSTD_dStage.ZSTDds_getFrameHeaderSize;
}
}
else
{
dctx->expected = ZSTD_blockHeaderSize;
dctx->stage = ZSTD_dStage.ZSTDds_decodeBlockHeader;
}
return 0;
}
case ZSTD_dStage.ZSTDds_decompressLastBlock:
case ZSTD_dStage.ZSTDds_decompressBlock:
{
nuint rSize;
switch (dctx->bType)
{
case blockType_e.bt_compressed:
assert(dctx->isFrameDecompression == 1);
rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, streaming_operation.is_streaming);
dctx->expected = 0;
break;
case blockType_e.bt_raw:
assert(srcSize <= dctx->expected);
rSize = ZSTD_copyRawBlock(dst, dstCapacity, src, srcSize);
{
nuint err_code = rSize;
if (ERR_isError(err_code))
{
return err_code;
}
}
assert(rSize == srcSize);
dctx->expected -= rSize;
break;
case blockType_e.bt_rle:
rSize = ZSTD_setRleBlock(dst, dstCapacity, *(byte*)src, dctx->rleSize);
dctx->expected = 0;
break;
case blockType_e.bt_reserved:
default:
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
}
{
nuint err_code = rSize;
if (ERR_isError(err_code))
{
return err_code;
}
}
if (rSize > dctx->fParams.blockSizeMax)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
}
dctx->decodedSize += rSize;
if (dctx->validateChecksum != 0)
ZSTD_XXH64_update(&dctx->xxhState, dst, rSize);
dctx->previousDstEnd = (sbyte*)dst + rSize;
if (dctx->expected > 0)
{
return rSize;
}
if (dctx->stage == ZSTD_dStage.ZSTDds_decompressLastBlock)
{
if (dctx->fParams.frameContentSize != unchecked(0UL - 1) && dctx->decodedSize != dctx->fParams.frameContentSize)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
}
if (dctx->fParams.checksumFlag != 0)
{
dctx->expected = 4;
dctx->stage = ZSTD_dStage.ZSTDds_checkChecksum;
}
else
{
ZSTD_DCtx_trace_end(dctx, dctx->decodedSize, dctx->processedCSize, 1);
dctx->expected = 0;
dctx->stage = ZSTD_dStage.ZSTDds_getFrameHeaderSize;
}
}
else
{
dctx->stage = ZSTD_dStage.ZSTDds_decodeBlockHeader;
dctx->expected = ZSTD_blockHeaderSize;
}
return rSize;
}
case ZSTD_dStage.ZSTDds_checkChecksum:
assert(srcSize == 4);
{
if (dctx->validateChecksum != 0)
{
uint h32 = (uint)ZSTD_XXH64_digest(&dctx->xxhState);
uint check32 = MEM_readLE32(src);
if (check32 != h32)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_checksum_wrong));
}
}
ZSTD_DCtx_trace_end(dctx, dctx->decodedSize, dctx->processedCSize, 1);
dctx->expected = 0;
dctx->stage = ZSTD_dStage.ZSTDds_getFrameHeaderSize;
return 0;
}
case ZSTD_dStage.ZSTDds_decodeSkippableHeader:
assert(src != null);
assert(srcSize <= 8);
assert(dctx->format != ZSTD_format_e.ZSTD_f_zstd1_magicless);
memcpy(dctx->headerBuffer + (8 - srcSize), src, (uint)srcSize);
dctx->expected = MEM_readLE32(dctx->headerBuffer + 4);
dctx->stage = ZSTD_dStage.ZSTDds_skipFrame;
return 0;
case ZSTD_dStage.ZSTDds_skipFrame:
dctx->expected = 0;
dctx->stage = ZSTD_dStage.ZSTDds_getFrameHeaderSize;
return 0;
default:
assert(0 != 0);
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC));
}
}
private static nuint ZSTD_refDictContent(ZSTD_DCtx_s* dctx, void* dict, nuint dictSize)
{
dctx->dictEnd = dctx->previousDstEnd;
dctx->virtualStart = (sbyte*)dict - ((sbyte*)dctx->previousDstEnd - (sbyte*)dctx->prefixStart);
dctx->prefixStart = dict;
dctx->previousDstEnd = (sbyte*)dict + dictSize;
return 0;
}
/*! ZSTD_loadDEntropy() :
* dict : must point at beginning of a valid zstd dictionary.
* @return : size of entropy tables read */
private static nuint ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy, void* dict, nuint dictSize)
{
byte* dictPtr = (byte*)dict;
byte* dictEnd = dictPtr + dictSize;
if (dictSize <= 8)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
assert(MEM_readLE32(dict) == 0xEC30A437);
dictPtr += 8;
{
/* use fse tables as temporary workspace; implies fse tables are grouped together */
void* workspace = &entropy->LLTable;
nuint workspaceSize = (nuint)(sizeof(ZSTD_seqSymbol) * 513 + sizeof(ZSTD_seqSymbol) * 257 + sizeof(ZSTD_seqSymbol) * 513);
nuint hSize = HUF_readDTableX2_wksp(entropy->hufTable, dictPtr, (nuint)(dictEnd - dictPtr), workspace, workspaceSize, 0);
if (ERR_isError(hSize))
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
dictPtr += hSize;
}
{
short* offcodeNCount = stackalloc short[32];
uint offcodeMaxValue = 31, offcodeLog;
nuint offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, (nuint)(dictEnd - dictPtr));
if (ERR_isError(offcodeHeaderSize))
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
if (offcodeMaxValue > 31)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
if (offcodeLog > 8)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
ZSTD_buildFSETable(&entropy->OFTable.e0, offcodeNCount, offcodeMaxValue, OF_base, OF_bits, offcodeLog, entropy->workspace, sizeof(uint) * 157, 0);
dictPtr += offcodeHeaderSize;
}
{
short* matchlengthNCount = stackalloc short[53];
uint matchlengthMaxValue = 52, matchlengthLog;
nuint matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, (nuint)(dictEnd - dictPtr));
if (ERR_isError(matchlengthHeaderSize))
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
if (matchlengthMaxValue > 52)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
if (matchlengthLog > 9)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
ZSTD_buildFSETable(&entropy->MLTable.e0, matchlengthNCount, matchlengthMaxValue, ML_base, ML_bits, matchlengthLog, entropy->workspace, sizeof(uint) * 157, 0);
dictPtr += matchlengthHeaderSize;
}
{
short* litlengthNCount = stackalloc short[36];
uint litlengthMaxValue = 35, litlengthLog;
nuint litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, (nuint)(dictEnd - dictPtr));
if (ERR_isError(litlengthHeaderSize))
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
if (litlengthMaxValue > 35)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
if (litlengthLog > 9)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
ZSTD_buildFSETable(&entropy->LLTable.e0, litlengthNCount, litlengthMaxValue, LL_base, LL_bits, litlengthLog, entropy->workspace, sizeof(uint) * 157, 0);
dictPtr += litlengthHeaderSize;
}
if (dictPtr + 12 > dictEnd)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
{
int i;
nuint dictContentSize = (nuint)(dictEnd - (dictPtr + 12));
for (i = 0; i < 3; i++)
{
uint rep = MEM_readLE32(dictPtr);
dictPtr += 4;
if (rep == 0 || rep > dictContentSize)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
entropy->rep[i] = rep;
}
}
return (nuint)(dictPtr - (byte*)dict);
}
private static nuint ZSTD_decompress_insertDictionary(ZSTD_DCtx_s* dctx, void* dict, nuint dictSize)
{
if (dictSize < 8)
return ZSTD_refDictContent(dctx, dict, dictSize);
{
uint magic = MEM_readLE32(dict);
if (magic != 0xEC30A437)
{
return ZSTD_refDictContent(dctx, dict, dictSize);
}
}
dctx->dictID = MEM_readLE32((sbyte*)dict + 4);
{
nuint eSize = ZSTD_loadDEntropy(&dctx->entropy, dict, dictSize);
if (ERR_isError(eSize))
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
dict = (sbyte*)dict + eSize;
dictSize -= eSize;
}
dctx->litEntropy = dctx->fseEntropy = 1;
return ZSTD_refDictContent(dctx, dict, dictSize);
}
public static nuint ZSTD_decompressBegin(ZSTD_DCtx_s* dctx)
{
assert(dctx != null);
dctx->expected = ZSTD_startingInputLength(dctx->format);
dctx->stage = ZSTD_dStage.ZSTDds_getFrameHeaderSize;
dctx->processedCSize = 0;
dctx->decodedSize = 0;
dctx->previousDstEnd = null;
dctx->prefixStart = null;
dctx->virtualStart = null;
dctx->dictEnd = null;
dctx->entropy.hufTable[0] = 12 * 0x1000001;
dctx->litEntropy = dctx->fseEntropy = 0;
dctx->dictID = 0;
dctx->bType = blockType_e.bt_reserved;
dctx->isFrameDecompression = 1;
memcpy(dctx->entropy.rep, repStartValue, sizeof(uint) * 3);
dctx->LLTptr = &dctx->entropy.LLTable.e0;
dctx->MLTptr = &dctx->entropy.MLTable.e0;
dctx->OFTptr = &dctx->entropy.OFTable.e0;
dctx->HUFptr = dctx->entropy.hufTable;
return 0;
}
public static nuint ZSTD_decompressBegin_usingDict(ZSTD_DCtx_s* dctx, void* dict, nuint dictSize)
{
{
nuint err_code = ZSTD_decompressBegin(dctx);
if (ERR_isError(err_code))
{
return err_code;
}
}
if (dict != null && dictSize != 0)
if (ERR_isError(ZSTD_decompress_insertDictionary(dctx, dict, dictSize)))
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted));
}
return 0;
}
/* ====== ZSTD_DDict ====== */
public static nuint ZSTD_decompressBegin_usingDDict(ZSTD_DCtx_s* dctx, ZSTD_DDict_s* ddict)
{
assert(dctx != null);
if (ddict != null)
{
sbyte* dictStart = (sbyte*)ZSTD_DDict_dictContent(ddict);
nuint dictSize = ZSTD_DDict_dictSize(ddict);
void* dictEnd = dictStart + dictSize;
dctx->ddictIsCold = dctx->dictEnd != dictEnd ? 1 : 0;
}
{
nuint err_code = ZSTD_decompressBegin(dctx);
if (ERR_isError(err_code))
{
return err_code;
}
}
if (ddict != null)
{
ZSTD_copyDDictParameters(dctx, ddict);
}
return 0;
}
/*! ZSTD_decompress_usingDDict() :
* Decompression using a pre-digested Dictionary
* Use dictionary without significant overhead. */
public static nuint ZSTD_decompress_usingDDict(ZSTD_DCtx_s* dctx, void* dst, nuint dstCapacity, void* src, nuint srcSize, ZSTD_DDict_s* ddict)
{
return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, null, 0, ddict);
}
/*! ZSTD_DCtx_loadDictionary_advanced() :
* Same as ZSTD_DCtx_loadDictionary(),
* but gives direct control over
* how to load the dictionary (by copy ? by reference ?)
* and how to interpret it (automatic ? force raw mode ? full mode only ?). */
public static nuint ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx_s* dctx, void* dict, nuint dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType)
{
if (dctx->streamStage != ZSTD_dStreamStage.zdss_init)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong));
}
ZSTD_clearDict(dctx);
if (dict != null && dictSize != 0)
{
dctx->ddictLocal = ZSTD_createDDict_advanced(dict, dictSize, dictLoadMethod, dictContentType, dctx->customMem);
if (dctx->ddictLocal == null)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation));
}
dctx->ddict = dctx->ddictLocal;
dctx->dictUses = ZSTD_dictUses_e.ZSTD_use_indefinitely;
}
return 0;
}
/*! ZSTD_DCtx_loadDictionary() : Requires v1.4.0+
* Create an internal DDict from dict buffer, to be used to decompress all future frames.
* The dictionary remains valid for all future frames, until explicitly invalidated, or
* a new dictionary is loaded.
* @result : 0, or an error code (which can be tested with ZSTD_isError()).
* Special : Adding a NULL (or 0-size) dictionary invalidates any previous dictionary,
* meaning "return to no-dictionary mode".
* Note 1 : Loading a dictionary involves building tables,
* which has a non-negligible impact on CPU usage and latency.
* It's recommended to "load once, use many times", to amortize the cost
* Note 2 :`dict` content will be copied internally, so `dict` can be released after loading.
* Use ZSTD_DCtx_loadDictionary_byReference() to reference dictionary content instead.
* Note 3 : Use ZSTD_DCtx_loadDictionary_advanced() to take control of
* how dictionary content is loaded and interpreted.
*/
public static nuint ZSTD_DCtx_loadDictionary(ZSTD_DCtx_s* dctx, void* dict, nuint dictSize)
{
return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dictLoadMethod_e.ZSTD_dlm_byCopy, ZSTD_dictContentType_e.ZSTD_dct_auto);
}
/*! ZSTD_dParam_getBounds() :
* All parameters must belong to an interval with lower and upper bounds,
* otherwise they will either trigger an error or be automatically clamped.
* @return : a structure, ZSTD_bounds, which contains
* - an error status field, which must be tested using ZSTD_isError()
* - both lower and upper bounds, inclusive
*/
public static ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam)
{
ZSTD_bounds bounds = new ZSTD_bounds
{
error = 0,
lowerBound = 0,
upperBound = 0
};
switch (dParam)
{
case ZSTD_dParameter.ZSTD_d_windowLogMax:
bounds.lowerBound = 10;
bounds.upperBound = sizeof(nuint) == 4 ? 30 : 31;
return bounds;
case ZSTD_dParameter.ZSTD_d_experimentalParam1:
bounds.lowerBound = (int)ZSTD_format_e.ZSTD_f_zstd1;
bounds.upperBound = (int)ZSTD_format_e.ZSTD_f_zstd1_magicless;
return bounds;
case ZSTD_dParameter.ZSTD_d_experimentalParam2:
bounds.lowerBound = (int)ZSTD_bufferMode_e.ZSTD_bm_buffered;
bounds.upperBound = (int)ZSTD_bufferMode_e.ZSTD_bm_stable;
return bounds;
case ZSTD_dParameter.ZSTD_d_experimentalParam3:
bounds.lowerBound = (int)ZSTD_forceIgnoreChecksum_e.ZSTD_d_validateChecksum;
bounds.upperBound = (int)ZSTD_forceIgnoreChecksum_e.ZSTD_d_ignoreChecksum;
return bounds;
case ZSTD_dParameter.ZSTD_d_experimentalParam4:
bounds.lowerBound = (int)ZSTD_refMultipleDDicts_e.ZSTD_rmd_refSingleDDict;
bounds.upperBound = (int)ZSTD_refMultipleDDicts_e.ZSTD_rmd_refMultipleDDicts;
return bounds;
case ZSTD_dParameter.ZSTD_d_experimentalParam5:
bounds.lowerBound = 0;
bounds.upperBound = 1;
return bounds;
case ZSTD_dParameter.ZSTD_d_experimentalParam6:
bounds.lowerBound = 1 << 10;
bounds.upperBound = 1 << 17;
return bounds;
default:
break;
}
bounds.error = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported));
return bounds;
}
/* ZSTD_dParam_withinBounds:
* @return 1 if value is within dParam bounds,
* 0 otherwise */
private static int ZSTD_dParam_withinBounds(ZSTD_dParameter dParam, int value)
{
ZSTD_bounds bounds = ZSTD_dParam_getBounds(dParam);
if (ERR_isError(bounds.error))
return 0;
if (value < bounds.lowerBound)
return 0;
if (value > bounds.upperBound)
return 0;
return 1;
}
/*! ZSTD_DCtx_getParameter() :
* Get the requested decompression parameter value, selected by enum ZSTD_dParameter,
* and store it into int* value.
* @return : 0, or an error code (which can be tested with ZSTD_isError()).
*/
public static nuint ZSTD_DCtx_getParameter(ZSTD_DCtx_s* dctx, ZSTD_dParameter param, int* value)
{
switch (param)
{
case ZSTD_dParameter.ZSTD_d_windowLogMax:
*value = (int)ZSTD_highbit32((uint)dctx->maxWindowSize);
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam1:
*value = (int)dctx->format;
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam2:
*value = (int)dctx->outBufferMode;
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam3:
*value = (int)dctx->forceIgnoreChecksum;
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam4:
*value = (int)dctx->refMultipleDDicts;
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam5:
*value = dctx->disableHufAsm;
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam6:
*value = dctx->maxBlockSizeParam;
return 0;
default:
break;
}
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported));
}
/*! ZSTD_DCtx_setParameter() :
* Set one compression parameter, selected by enum ZSTD_dParameter.
* All parameters have valid bounds. Bounds can be queried using ZSTD_dParam_getBounds().
* Providing a value beyond bound will either clamp it, or trigger an error (depending on parameter).
* Setting a parameter is only possible during frame initialization (before starting decompression).
* @return : 0, or an error code (which can be tested using ZSTD_isError()).
*/
public static nuint ZSTD_DCtx_setParameter(ZSTD_DCtx_s* dctx, ZSTD_dParameter dParam, int value)
{
if (dctx->streamStage != ZSTD_dStreamStage.zdss_init)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong));
}
switch (dParam)
{
case ZSTD_dParameter.ZSTD_d_windowLogMax:
if (value == 0)
value = 27;
{
if (ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_windowLogMax, value) == 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound));
}
}
dctx->maxWindowSize = (nuint)1 << value;
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam1:
{
if (ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam1, value) == 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound));
}
}
dctx->format = (ZSTD_format_e)value;
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam2:
{
if (ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam2, value) == 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound));
}
}
dctx->outBufferMode = (ZSTD_bufferMode_e)value;
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam3:
{
if (ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam3, value) == 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound));
}
}
dctx->forceIgnoreChecksum = (ZSTD_forceIgnoreChecksum_e)value;
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam4:
{
if (ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam4, value) == 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound));
}
}
if (dctx->staticSize != 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported));
}
dctx->refMultipleDDicts = (ZSTD_refMultipleDDicts_e)value;
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam5:
{
if (ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam5, value) == 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound));
}
}
dctx->disableHufAsm = value != 0 ? 1 : 0;
return 0;
case ZSTD_dParameter.ZSTD_d_experimentalParam6:
if (value != 0)
{
if (ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam6, value) == 0)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound));
}
}
dctx->maxBlockSizeParam = value;
return 0;
default:
break;
}
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported));
}
private static nuint ZSTD_decodingBufferSize_internal(ulong windowSize, ulong frameContentSize, nuint blockSizeMax)
{
nuint blockSize = (nuint)(windowSize < 1 << 17 ? windowSize : 1 << 17) < blockSizeMax ? (nuint)(windowSize < 1 << 17 ? windowSize : 1 << 17) : blockSizeMax;
/* We need blockSize + WILDCOPY_OVERLENGTH worth of buffer so that if a block
* ends at windowSize + WILDCOPY_OVERLENGTH + 1 bytes, we can start writing
* the block at the beginning of the output buffer, and maintain a full window.
*
* We need another blockSize worth of buffer so that we can store split
* literals at the end of the block without overwriting the extDict window.
*/
ulong neededRBSize = windowSize + blockSize * 2 + 32 * 2;
ulong neededSize = frameContentSize < neededRBSize ? frameContentSize : neededRBSize;
nuint minRBSize = (nuint)neededSize;
if (minRBSize != neededSize)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_windowTooLarge));
}
return minRBSize;
}
/* ***** Decompression ***** */
private static int ZSTD_DCtx_isOverflow(ZSTD_DCtx_s* zds, nuint neededInBuffSize, nuint neededOutBuffSize)
{
return zds->inBuffSize + zds->outBuffSize >= (neededInBuffSize + neededOutBuffSize) * 3 ? 1 : 0;
}
private static void ZSTD_DCtx_updateOversizedDuration(ZSTD_DCtx_s* zds, nuint neededInBuffSize, nuint neededOutBuffSize)
{
if (ZSTD_DCtx_isOverflow(zds, neededInBuffSize, neededOutBuffSize) != 0)
zds->oversizedDuration++;
else
zds->oversizedDuration = 0;
}
private static int ZSTD_DCtx_isOversizedTooLong(ZSTD_DCtx_s* zds)
{
return zds->oversizedDuration >= 128 ? 1 : 0;
}
/* Checks that the output buffer hasn't changed if ZSTD_obm_stable is used. */
private static nuint ZSTD_checkOutBuffer(ZSTD_DCtx_s* zds, ZSTD_outBuffer_s* output)
{
ZSTD_outBuffer_s expect = zds->expectedOutBuffer;
if (zds->outBufferMode != ZSTD_bufferMode_e.ZSTD_bm_stable)
return 0;
if (zds->streamStage == ZSTD_dStreamStage.zdss_init)
return 0;
if (expect.dst == output->dst && expect.pos == output->pos && expect.size == output->size)
return 0;
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstBuffer_wrong));
}
/* Calls ZSTD_decompressContinue() with the right parameters for ZSTD_decompressStream()
* and updates the stage and the output buffer state. This call is extracted so it can be
* used both when reading directly from the ZSTD_inBuffer, and in buffered input mode.
* NOTE: You must break after calling this function since the streamStage is modified.
*/
private static nuint ZSTD_decompressContinueStream(ZSTD_DCtx_s* zds, sbyte** op, sbyte* oend, void* src, nuint srcSize)
{
int isSkipFrame = ZSTD_isSkipFrame(zds);
if (zds->outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered)
{
nuint dstSize = isSkipFrame != 0 ? 0 : zds->outBuffSize - zds->outStart;
nuint decodedSize = ZSTD_decompressContinue(zds, zds->outBuff + zds->outStart, dstSize, src, srcSize);
{
nuint err_code = decodedSize;
if (ERR_isError(err_code))
{
return err_code;
}
}
if (decodedSize == 0 && isSkipFrame == 0)
{
zds->streamStage = ZSTD_dStreamStage.zdss_read;
}
else
{
zds->outEnd = zds->outStart + decodedSize;
zds->streamStage = ZSTD_dStreamStage.zdss_flush;
}
}
else
{
/* Write directly into the output buffer */
nuint dstSize = isSkipFrame != 0 ? 0 : (nuint)(oend - *op);
nuint decodedSize = ZSTD_decompressContinue(zds, *op, dstSize, src, srcSize);
{
nuint err_code = decodedSize;
if (ERR_isError(err_code))
{
return err_code;
}
}
*op += decodedSize;
zds->streamStage = ZSTD_dStreamStage.zdss_read;
assert(*op <= oend);
assert(zds->outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable);
}
return 0;
}
/*! ZSTD_decompressStream() :
* Streaming decompression function.
* Call repetitively to consume full input updating it as necessary.
* Function will update both input and output `pos` fields exposing current state via these fields:
* - `input.pos < input.size`, some input remaining and caller should provide remaining input
* on the next call.
* - `output.pos < output.size`, decoder flushed internal output buffer.
* - `output.pos == output.size`, unflushed data potentially present in the internal buffers,
* check ZSTD_decompressStream() @return value,
* if > 0, invoke it again to flush remaining data to output.
* Note : with no additional input, amount of data flushed <= ZSTD_BLOCKSIZE_MAX.
*
* @return : 0 when a frame is completely decoded and fully flushed,
* or an error code, which can be tested using ZSTD_isError(),
* or any other value > 0, which means there is some decoding or flushing to do to complete current frame.
*
* Note: when an operation returns with an error code, the @zds state may be left in undefined state.
* It's UB to invoke `ZSTD_decompressStream()` on such a state.
* In order to re-use such a state, it must be first reset,
* which can be done explicitly (`ZSTD_DCtx_reset()`),
* or is implied for operations starting some new decompression job (`ZSTD_initDStream`, `ZSTD_decompressDCtx()`, `ZSTD_decompress_usingDict()`)
*/
public static nuint ZSTD_decompressStream(ZSTD_DCtx_s* zds, ZSTD_outBuffer_s* output, ZSTD_inBuffer_s* input)
{
sbyte* src = (sbyte*)input->src;
sbyte* istart = input->pos != 0 ? src + input->pos : src;
sbyte* iend = input->size != 0 ? src + input->size : src;
sbyte* ip = istart;
sbyte* dst = (sbyte*)output->dst;
sbyte* ostart = output->pos != 0 ? dst + output->pos : dst;
sbyte* oend = output->size != 0 ? dst + output->size : dst;
sbyte* op = ostart;
uint someMoreWork = 1;
assert(zds != null);
if (input->pos > input->size)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong));
}
if (output->pos > output->size)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
}
{
nuint err_code = ZSTD_checkOutBuffer(zds, output);
if (ERR_isError(err_code))
{
return err_code;
}
}
while (someMoreWork != 0)
{
switch (zds->streamStage)
{
case ZSTD_dStreamStage.zdss_init:
zds->streamStage = ZSTD_dStreamStage.zdss_loadHeader;
zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0;
zds->hostageByte = 0;
zds->expectedOutBuffer = *output;
goto case ZSTD_dStreamStage.zdss_loadHeader;
case ZSTD_dStreamStage.zdss_loadHeader:
{
nuint hSize = ZSTD_getFrameHeader_advanced(&zds->fParams, zds->headerBuffer, zds->lhSize, zds->format);
if (zds->refMultipleDDicts != default && zds->ddictSet != null)
{
ZSTD_DCtx_selectFrameDDict(zds);
}
if (ERR_isError(hSize))
{
return hSize;
}
if (hSize != 0)
{
/* if hSize!=0, hSize > zds->lhSize */
nuint toLoad = hSize - zds->lhSize;
nuint remainingInput = (nuint)(iend - ip);
assert(iend >= ip);
if (toLoad > remainingInput)
{
if (remainingInput > 0)
{
memcpy(zds->headerBuffer + zds->lhSize, ip, (uint)remainingInput);
zds->lhSize += remainingInput;
}
input->pos = input->size;
{
/* check first few bytes */
nuint err_code = ZSTD_getFrameHeader_advanced(&zds->fParams, zds->headerBuffer, zds->lhSize, zds->format);
if (ERR_isError(err_code))
{
return err_code;
}
}
return ((nuint)(zds->format == ZSTD_format_e.ZSTD_f_zstd1 ? 6 : 2) > hSize ? (nuint)(zds->format == ZSTD_format_e.ZSTD_f_zstd1 ? 6 : 2) : hSize) - zds->lhSize + ZSTD_blockHeaderSize;
}
assert(ip != null);
memcpy(zds->headerBuffer + zds->lhSize, ip, (uint)toLoad);
zds->lhSize = hSize;
ip += toLoad;
break;
}
}
if (zds->fParams.frameContentSize != unchecked(0UL - 1) && zds->fParams.frameType != ZSTD_frameType_e.ZSTD_skippableFrame && (nuint)(oend - op) >= zds->fParams.frameContentSize)
{
nuint cSize = ZSTD_findFrameCompressedSize_advanced(istart, (nuint)(iend - istart), zds->format);
if (cSize <= (nuint)(iend - istart))
{
/* shortcut : using single-pass mode */
nuint decompressedSize = ZSTD_decompress_usingDDict(zds, op, (nuint)(oend - op), istart, cSize, ZSTD_getDDict(zds));
if (ERR_isError(decompressedSize))
return decompressedSize;
assert(istart != null);
ip = istart + cSize;
op = op != null ? op + decompressedSize : op;
zds->expected = 0;
zds->streamStage = ZSTD_dStreamStage.zdss_init;
someMoreWork = 0;
break;
}
}
if (zds->outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable && zds->fParams.frameType != ZSTD_frameType_e.ZSTD_skippableFrame && zds->fParams.frameContentSize != unchecked(0UL - 1) && (nuint)(oend - op) < zds->fParams.frameContentSize)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall));
}
{
nuint err_code = ZSTD_decompressBegin_usingDDict(zds, ZSTD_getDDict(zds));
if (ERR_isError(err_code))
{
return err_code;
}
}
if (zds->format == ZSTD_format_e.ZSTD_f_zstd1 && (MEM_readLE32(zds->headerBuffer) & 0xFFFFFFF0) == 0x184D2A50)
{
zds->expected = MEM_readLE32(zds->headerBuffer + 4);
zds->stage = ZSTD_dStage.ZSTDds_skipFrame;
}
else
{
{
nuint err_code = ZSTD_decodeFrameHeader(zds, zds->headerBuffer, zds->lhSize);
if (ERR_isError(err_code))
{
return err_code;
}
}
zds->expected = ZSTD_blockHeaderSize;
zds->stage = ZSTD_dStage.ZSTDds_decodeBlockHeader;
}
zds->fParams.windowSize = zds->fParams.windowSize > 1U << 10 ? zds->fParams.windowSize : 1U << 10;
if (zds->fParams.windowSize > zds->maxWindowSize)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_windowTooLarge));
}
if (zds->maxBlockSizeParam != 0)
zds->fParams.blockSizeMax = zds->fParams.blockSizeMax < (uint)zds->maxBlockSizeParam ? zds->fParams.blockSizeMax : (uint)zds->maxBlockSizeParam;
{
/* frame checksum */
nuint neededInBuffSize = zds->fParams.blockSizeMax > 4 ? zds->fParams.blockSizeMax : 4;
nuint neededOutBuffSize = zds->outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered ? ZSTD_decodingBufferSize_internal(zds->fParams.windowSize, zds->fParams.frameContentSize, zds->fParams.blockSizeMax) : 0;
ZSTD_DCtx_updateOversizedDuration(zds, neededInBuffSize, neededOutBuffSize);
{
int tooSmall = zds->inBuffSize < neededInBuffSize || zds->outBuffSize < neededOutBuffSize ? 1 : 0;
int tooLarge = ZSTD_DCtx_isOversizedTooLong(zds);
if (tooSmall != 0 || tooLarge != 0)
{
nuint bufferSize = neededInBuffSize + neededOutBuffSize;
if (zds->staticSize != 0)
{
assert(zds->staticSize >= (nuint)sizeof(ZSTD_DCtx_s));
if (bufferSize > zds->staticSize - (nuint)sizeof(ZSTD_DCtx_s))
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation));
}
}
else
{
ZSTD_customFree(zds->inBuff, zds->customMem);
zds->inBuffSize = 0;
zds->outBuffSize = 0;
zds->inBuff = (sbyte*)ZSTD_customMalloc(bufferSize, zds->customMem);
if (zds->inBuff == null)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation));
}
}
zds->inBuffSize = neededInBuffSize;
zds->outBuff = zds->inBuff + zds->inBuffSize;
zds->outBuffSize = neededOutBuffSize;
}
}
}
zds->streamStage = ZSTD_dStreamStage.zdss_read;
goto case ZSTD_dStreamStage.zdss_read;
case ZSTD_dStreamStage.zdss_read:
{
nuint neededInSize = ZSTD_nextSrcSizeToDecompressWithInputSize(zds, (nuint)(iend - ip));
if (neededInSize == 0)
{
zds->streamStage = ZSTD_dStreamStage.zdss_init;
someMoreWork = 0;
break;
}
if ((nuint)(iend - ip) >= neededInSize)
{
{
nuint err_code = ZSTD_decompressContinueStream(zds, &op, oend, ip, neededInSize);
if (ERR_isError(err_code))
{
return err_code;
}
}
assert(ip != null);
ip += neededInSize;
break;
}
}
if (ip == iend)
{
someMoreWork = 0;
break;
}
zds->streamStage = ZSTD_dStreamStage.zdss_load;
goto case ZSTD_dStreamStage.zdss_load;
case ZSTD_dStreamStage.zdss_load:
{
nuint neededInSize = ZSTD_nextSrcSizeToDecompress(zds);
nuint toLoad = neededInSize - zds->inPos;
int isSkipFrame = ZSTD_isSkipFrame(zds);
nuint loadedSize;
assert(neededInSize == ZSTD_nextSrcSizeToDecompressWithInputSize(zds, (nuint)(iend - ip)));
if (isSkipFrame != 0)
{
loadedSize = toLoad < (nuint)(iend - ip) ? toLoad : (nuint)(iend - ip);
}
else
{
if (toLoad > zds->inBuffSize - zds->inPos)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected));
}
loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, (nuint)(iend - ip));
}
if (loadedSize != 0)
{
ip += loadedSize;
zds->inPos += loadedSize;
}
if (loadedSize < toLoad)
{
someMoreWork = 0;
break;
}
zds->inPos = 0;
{
nuint err_code = ZSTD_decompressContinueStream(zds, &op, oend, zds->inBuff, neededInSize);
if (ERR_isError(err_code))
{
return err_code;
}
}
break;
}
case ZSTD_dStreamStage.zdss_flush:
{
nuint toFlushSize = zds->outEnd - zds->outStart;
nuint flushedSize = ZSTD_limitCopy(op, (nuint)(oend - op), zds->outBuff + zds->outStart, toFlushSize);
op = op != null ? op + flushedSize : op;
zds->outStart += flushedSize;
if (flushedSize == toFlushSize)
{
zds->streamStage = ZSTD_dStreamStage.zdss_read;
if (zds->outBuffSize < zds->fParams.frameContentSize && zds->outStart + zds->fParams.blockSizeMax > zds->outBuffSize)
{
zds->outStart = zds->outEnd = 0;
}
break;
}
}
someMoreWork = 0;
break;
default:
assert(0 != 0);
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC));
}
}
input->pos = (nuint)(ip - (sbyte*)input->src);
output->pos = (nuint)(op - (sbyte*)output->dst);
zds->expectedOutBuffer = *output;
if (ip == istart && op == ostart)
{
zds->noForwardProgress++;
if (zds->noForwardProgress >= 16)
{
if (op == oend)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_noForwardProgress_destFull));
}
if (ip == iend)
{
return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_noForwardProgress_inputEmpty));
}
assert(0 != 0);
}
}
else
{
zds->noForwardProgress = 0;
}
{
nuint nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zds);
if (nextSrcSizeHint == 0)
{
if (zds->outEnd == zds->outStart)
{
if (zds->hostageByte != 0)
{
if (input->pos >= input->size)
{
zds->streamStage = ZSTD_dStreamStage.zdss_read;
return 1;
}
input->pos++;
}
return 0;
}
if (zds->hostageByte == 0)
{
input->pos--;
zds->hostageByte = 1;
}
return 1;
}
nextSrcSizeHint += ZSTD_blockHeaderSize * (nuint)(ZSTD_nextInputType(zds) == ZSTD_nextInputType_e.ZSTDnit_block ? 1 : 0);
assert(zds->inPos <= nextSrcSizeHint);
nextSrcSizeHint -= zds->inPos;
return nextSrcSizeHint;
}
}
}
}