kusano 7d535a
/*
kusano 7d535a
LZ4 auto-framing library
kusano 7d535a
Copyright (C) 2011-2015, Yann Collet.
kusano 7d535a
kusano 7d535a
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
kusano 7d535a
kusano 7d535a
Redistribution and use in source and binary forms, with or without
kusano 7d535a
modification, are permitted provided that the following conditions are
kusano 7d535a
met:
kusano 7d535a
kusano 7d535a
* Redistributions of source code must retain the above copyright
kusano 7d535a
notice, this list of conditions and the following disclaimer.
kusano 7d535a
* Redistributions in binary form must reproduce the above
kusano 7d535a
copyright notice, this list of conditions and the following disclaimer
kusano 7d535a
in the documentation and/or other materials provided with the
kusano 7d535a
distribution.
kusano 7d535a
kusano 7d535a
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
kusano 7d535a
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
kusano 7d535a
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
kusano 7d535a
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
kusano 7d535a
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
kusano 7d535a
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
kusano 7d535a
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
kusano 7d535a
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
kusano 7d535a
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
kusano 7d535a
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
kusano 7d535a
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
kusano 7d535a
kusano 7d535a
You can contact the author at :
kusano 7d535a
- LZ4 source repository : https://github.com/Cyan4973/lz4
kusano 7d535a
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
kusano 7d535a
*/
kusano 7d535a
kusano 7d535a
/* LZ4F is a stand-alone API to create LZ4-compressed Frames
kusano 7d535a
*  in full conformance with specification v1.5.0
kusano 7d535a
*  All related operations, including memory management, are handled by the library.
kusano 7d535a
* */
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Compiler Options
kusano 7d535a
**************************************/
kusano 7d535a
#ifdef _MSC_VER    /* Visual Studio */
kusano 7d535a
#  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
kusano 7d535a
#endif
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Memory routines
kusano 7d535a
**************************************/
kusano 7d535a
#include <stdlib.h>   /* malloc, calloc, free */</stdlib.h>
kusano 7d535a
#define ALLOCATOR(s)   calloc(1,s)
kusano 7d535a
#define FREEMEM        free
kusano 7d535a
#include <string.h>   /* memset, memcpy, memmove */</string.h>
kusano 7d535a
#define MEM_INIT       memset
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Includes
kusano 7d535a
**************************************/
kusano 7d535a
#include "lz4frame_static.h"
kusano 7d535a
#include "lz4.h"
kusano 7d535a
#include "lz4hc.h"
kusano 7d535a
#include "xxhash.h"
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Basic Types
kusano 7d535a
**************************************/
kusano 7d535a
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)   /* C99 */
kusano 7d535a
# include <stdint.h></stdint.h>
kusano 7d535a
typedef  uint8_t BYTE;
kusano 7d535a
typedef uint16_t U16;
kusano 7d535a
typedef uint32_t U32;
kusano 7d535a
typedef  int32_t S32;
kusano 7d535a
typedef uint64_t U64;
kusano 7d535a
#else
kusano 7d535a
typedef unsigned char       BYTE;
kusano 7d535a
typedef unsigned short      U16;
kusano 7d535a
typedef unsigned int        U32;
kusano 7d535a
typedef   signed int        S32;
kusano 7d535a
typedef unsigned long long  U64;
kusano 7d535a
#endif
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Constants
kusano 7d535a
**************************************/
kusano 7d535a
#define KB *(1<<10)
kusano 7d535a
#define MB *(1<<20)
kusano 7d535a
#define GB *(1<<30)
kusano 7d535a
kusano 7d535a
#define _1BIT  0x01
kusano 7d535a
#define _2BITS 0x03
kusano 7d535a
#define _3BITS 0x07
kusano 7d535a
#define _4BITS 0x0F
kusano 7d535a
#define _8BITS 0xFF
kusano 7d535a
kusano 7d535a
#define LZ4F_MAGIC_SKIPPABLE_START 0x184D2A50U
kusano 7d535a
#define LZ4F_MAGICNUMBER 0x184D2204U
kusano 7d535a
#define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U
kusano 7d535a
#define LZ4F_BLOCKSIZEID_DEFAULT LZ4F_max64KB
kusano 7d535a
kusano 7d535a
static const size_t minFHSize = 7;
kusano 7d535a
static const size_t maxFHSize = 15;
kusano 7d535a
static const size_t BHSize = 4;
kusano 7d535a
static const int    minHClevel = 3;
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Structures and local types
kusano 7d535a
**************************************/
kusano 7d535a
typedef struct LZ4F_cctx_s
kusano 7d535a
{
kusano 7d535a
    LZ4F_preferences_t prefs;
kusano 7d535a
    U32    version;
kusano 7d535a
    U32    cStage;
kusano 7d535a
    size_t maxBlockSize;
kusano 7d535a
    size_t maxBufferSize;
kusano 7d535a
    BYTE*  tmpBuff;
kusano 7d535a
    BYTE*  tmpIn;
kusano 7d535a
    size_t tmpInSize;
kusano 7d535a
    U64    totalInSize;
kusano 7d535a
    XXH32_state_t xxh;
kusano 7d535a
    void*  lz4CtxPtr;
kusano 7d535a
    U32    lz4CtxLevel;     /* 0: unallocated;  1: LZ4_stream_t;  3: LZ4_streamHC_t */
kusano 7d535a
} LZ4F_cctx_t;
kusano 7d535a
kusano 7d535a
typedef struct LZ4F_dctx_s
kusano 7d535a
{
kusano 7d535a
    LZ4F_frameInfo_t frameInfo;
kusano 7d535a
    U32    version;
kusano 7d535a
    U32    dStage;
kusano 7d535a
    U64    frameRemainingSize;
kusano 7d535a
    size_t maxBlockSize;
kusano 7d535a
    size_t maxBufferSize;
kusano 7d535a
    const BYTE* srcExpect;
kusano 7d535a
    BYTE*  tmpIn;
kusano 7d535a
    size_t tmpInSize;
kusano 7d535a
    size_t tmpInTarget;
kusano 7d535a
    BYTE*  tmpOutBuffer;
kusano 7d535a
    const BYTE*  dict;
kusano 7d535a
    size_t dictSize;
kusano 7d535a
    BYTE*  tmpOut;
kusano 7d535a
    size_t tmpOutSize;
kusano 7d535a
    size_t tmpOutStart;
kusano 7d535a
    XXH32_state_t xxh;
kusano 7d535a
    BYTE   header[16];
kusano 7d535a
} LZ4F_dctx_t;
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Error management
kusano 7d535a
**************************************/
kusano 7d535a
#define LZ4F_GENERATE_STRING(STRING) #STRING,
kusano 7d535a
static const char* LZ4F_errorStrings[] = { LZ4F_LIST_ERRORS(LZ4F_GENERATE_STRING) };
kusano 7d535a
kusano 7d535a
kusano 7d535a
unsigned LZ4F_isError(LZ4F_errorCode_t code)
kusano 7d535a
{
kusano 7d535a
    return (code > (LZ4F_errorCode_t)(-LZ4F_ERROR_maxCode));
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
const char* LZ4F_getErrorName(LZ4F_errorCode_t code)
kusano 7d535a
{
kusano 7d535a
    static const char* codeError = "Unspecified error code";
kusano 7d535a
    if (LZ4F_isError(code)) return LZ4F_errorStrings[-(int)(code)];
kusano 7d535a
    return codeError;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Private functions
kusano 7d535a
**************************************/
kusano 7d535a
static size_t LZ4F_getBlockSize(unsigned blockSizeID)
kusano 7d535a
{
kusano 7d535a
    static const size_t blockSizes[4] = { 64 KB, 256 KB, 1 MB, 4 MB };
kusano 7d535a
kusano 7d535a
    if (blockSizeID == 0) blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;
kusano 7d535a
    blockSizeID -= 4;
kusano 7d535a
    if (blockSizeID > 3) return (size_t)-LZ4F_ERROR_maxBlockSize_invalid;
kusano 7d535a
    return blockSizes[blockSizeID];
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* unoptimized version; solves endianess & alignment issues */
kusano 7d535a
static U32 LZ4F_readLE32 (const BYTE* srcPtr)
kusano 7d535a
{
kusano 7d535a
    U32 value32 = srcPtr[0];
kusano 7d535a
    value32 += (srcPtr[1]<<8);
kusano 7d535a
    value32 += (srcPtr[2]<<16);
kusano 7d535a
    value32 += ((U32)srcPtr[3])<<24;
kusano 7d535a
    return value32;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static void LZ4F_writeLE32 (BYTE* dstPtr, U32 value32)
kusano 7d535a
{
kusano 7d535a
    dstPtr[0] = (BYTE)value32;
kusano 7d535a
    dstPtr[1] = (BYTE)(value32 >> 8);
kusano 7d535a
    dstPtr[2] = (BYTE)(value32 >> 16);
kusano 7d535a
    dstPtr[3] = (BYTE)(value32 >> 24);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static U64 LZ4F_readLE64 (const BYTE* srcPtr)
kusano 7d535a
{
kusano 7d535a
    U64 value64 = srcPtr[0];
kusano 7d535a
    value64 += ((U64)srcPtr[1]<<8);
kusano 7d535a
    value64 += ((U64)srcPtr[2]<<16);
kusano 7d535a
    value64 += ((U64)srcPtr[3]<<24);
kusano 7d535a
    value64 += ((U64)srcPtr[4]<<32);
kusano 7d535a
    value64 += ((U64)srcPtr[5]<<40);
kusano 7d535a
    value64 += ((U64)srcPtr[6]<<48);
kusano 7d535a
    value64 += ((U64)srcPtr[7]<<56);
kusano 7d535a
    return value64;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static void LZ4F_writeLE64 (BYTE* dstPtr, U64 value64)
kusano 7d535a
{
kusano 7d535a
    dstPtr[0] = (BYTE)value64;
kusano 7d535a
    dstPtr[1] = (BYTE)(value64 >> 8);
kusano 7d535a
    dstPtr[2] = (BYTE)(value64 >> 16);
kusano 7d535a
    dstPtr[3] = (BYTE)(value64 >> 24);
kusano 7d535a
    dstPtr[4] = (BYTE)(value64 >> 32);
kusano 7d535a
    dstPtr[5] = (BYTE)(value64 >> 40);
kusano 7d535a
    dstPtr[6] = (BYTE)(value64 >> 48);
kusano 7d535a
    dstPtr[7] = (BYTE)(value64 >> 56);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
static BYTE LZ4F_headerChecksum (const void* header, size_t length)
kusano 7d535a
{
kusano 7d535a
    U32 xxh = XXH32(header, length, 0);
kusano 7d535a
    return (BYTE)(xxh >> 8);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**************************************
kusano 7d535a
*  Simple compression functions
kusano 7d535a
**************************************/
kusano 7d535a
static LZ4F_blockSizeID_t LZ4F_optimalBSID(const LZ4F_blockSizeID_t requestedBSID, const size_t srcSize)
kusano 7d535a
{
kusano 7d535a
    LZ4F_blockSizeID_t proposedBSID = LZ4F_max64KB;
kusano 7d535a
    size_t maxBlockSize = 64 KB;
kusano 7d535a
    while (requestedBSID > proposedBSID)
kusano 7d535a
    {
kusano 7d535a
        if (srcSize <= maxBlockSize)
kusano 7d535a
            return proposedBSID;
kusano 7d535a
        proposedBSID = (LZ4F_blockSizeID_t)((int)proposedBSID + 1);
kusano 7d535a
        maxBlockSize <<= 2;
kusano 7d535a
    }
kusano 7d535a
    return requestedBSID;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
kusano 7d535a
{
kusano 7d535a
    LZ4F_preferences_t prefs;
kusano 7d535a
    size_t headerSize;
kusano 7d535a
    size_t streamSize;
kusano 7d535a
kusano 7d535a
    if (preferencesPtr!=NULL) prefs = *preferencesPtr;
kusano 7d535a
    else memset(&prefs, 0, sizeof(prefs));
kusano 7d535a
kusano 7d535a
    prefs.frameInfo.blockSizeID = LZ4F_optimalBSID(prefs.frameInfo.blockSizeID, srcSize);
kusano 7d535a
    prefs.autoFlush = 1;
kusano 7d535a
kusano 7d535a
    headerSize = maxFHSize;      /* header size, including magic number and frame content size*/
kusano 7d535a
    streamSize = LZ4F_compressBound(srcSize, &prefs);
kusano 7d535a
kusano 7d535a
    return headerSize + streamSize;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* LZ4F_compressFrame()
kusano 7d535a
* Compress an entire srcBuffer into a valid LZ4 frame, as defined by specification v1.5.0, in a single step.
kusano 7d535a
* The most important rule is that dstBuffer MUST be large enough (dstMaxSize) to ensure compression completion even in worst case.
kusano 7d535a
* You can get the minimum value of dstMaxSize by using LZ4F_compressFrameBound()
kusano 7d535a
* If this condition is not respected, LZ4F_compressFrame() will fail (result is an errorCode)
kusano 7d535a
* The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will then be set to default.
kusano 7d535a
* The result of the function is the number of bytes written into dstBuffer.
kusano 7d535a
* The function outputs an error code if it fails (can be tested using LZ4F_isError())
kusano 7d535a
*/
kusano 7d535a
size_t LZ4F_compressFrame(void* dstBuffer, size_t dstMaxSize, const void* srcBuffer, size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
kusano 7d535a
{
kusano 7d535a
    LZ4F_cctx_t cctxI;
kusano 7d535a
    LZ4_stream_t lz4ctx;
kusano 7d535a
    LZ4F_preferences_t prefs;
kusano 7d535a
    LZ4F_compressOptions_t options;
kusano 7d535a
    LZ4F_errorCode_t errorCode;
kusano 7d535a
    BYTE* const dstStart = (BYTE*) dstBuffer;
kusano 7d535a
    BYTE* dstPtr = dstStart;
kusano 7d535a
    BYTE* const dstEnd = dstStart + dstMaxSize;
kusano 7d535a
kusano 7d535a
    memset(&cctxI, 0, sizeof(cctxI));   /* works because no allocation */
kusano 7d535a
    memset(&options, 0, sizeof(options));
kusano 7d535a
kusano 7d535a
    cctxI.version = LZ4F_VERSION;
kusano 7d535a
    cctxI.maxBufferSize = 5 MB;   /* mess with real buffer size to prevent allocation; works because autoflush==1 & stableSrc==1 */
kusano 7d535a
kusano 7d535a
    if (preferencesPtr!=NULL)
kusano 7d535a
        prefs = *preferencesPtr;
kusano 7d535a
    else
kusano 7d535a
        memset(&prefs, 0, sizeof(prefs));
kusano 7d535a
    if (prefs.frameInfo.contentSize != 0)
kusano 7d535a
        prefs.frameInfo.contentSize = (U64)srcSize;   /* auto-correct content size if selected (!=0) */
kusano 7d535a
kusano 7d535a
    if (prefs.compressionLevel < (int)minHClevel)
kusano 7d535a
    {
kusano 7d535a
        cctxI.lz4CtxPtr = &lz4ctx;
kusano 7d535a
        cctxI.lz4CtxLevel = 1;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    prefs.frameInfo.blockSizeID = LZ4F_optimalBSID(prefs.frameInfo.blockSizeID, srcSize);
kusano 7d535a
    prefs.autoFlush = 1;
kusano 7d535a
    if (srcSize <= LZ4F_getBlockSize(prefs.frameInfo.blockSizeID))
kusano 7d535a
        prefs.frameInfo.blockMode = LZ4F_blockIndependent;   /* no need for linked blocks */
kusano 7d535a
kusano 7d535a
    options.stableSrc = 1;
kusano 7d535a
kusano 7d535a
    if (dstMaxSize < LZ4F_compressFrameBound(srcSize, &prefs))
kusano 7d535a
        return (size_t)-LZ4F_ERROR_dstMaxSize_tooSmall;
kusano 7d535a
kusano 7d535a
    errorCode = LZ4F_compressBegin(&cctxI, dstBuffer, dstMaxSize, &prefs);  /* write header */
kusano 7d535a
    if (LZ4F_isError(errorCode)) return errorCode;
kusano 7d535a
    dstPtr += errorCode;   /* header size */
kusano 7d535a
kusano 7d535a
    errorCode = LZ4F_compressUpdate(&cctxI, dstPtr, dstEnd-dstPtr, srcBuffer, srcSize, &options);
kusano 7d535a
    if (LZ4F_isError(errorCode)) return errorCode;
kusano 7d535a
    dstPtr += errorCode;
kusano 7d535a
kusano 7d535a
    errorCode = LZ4F_compressEnd(&cctxI, dstPtr, dstEnd-dstPtr, &options);   /* flush last block, and generate suffix */
kusano 7d535a
    if (LZ4F_isError(errorCode)) return errorCode;
kusano 7d535a
    dstPtr += errorCode;
kusano 7d535a
kusano 7d535a
    if (prefs.compressionLevel >= (int)minHClevel)   /* no allocation necessary with lz4 fast */
kusano 7d535a
        FREEMEM(cctxI.lz4CtxPtr);
kusano 7d535a
kusano 7d535a
    return (dstPtr - dstStart);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/***********************************
kusano 7d535a
*  Advanced compression functions
kusano 7d535a
***********************************/
kusano 7d535a
kusano 7d535a
/* LZ4F_createCompressionContext() :
kusano 7d535a
* The first thing to do is to create a compressionContext object, which will be used in all compression operations.
kusano 7d535a
* This is achieved using LZ4F_createCompressionContext(), which takes as argument a version and an LZ4F_preferences_t structure.
kusano 7d535a
* The version provided MUST be LZ4F_VERSION. It is intended to track potential version differences between different binaries.
kusano 7d535a
* The function will provide a pointer to an allocated LZ4F_compressionContext_t object.
kusano 7d535a
* If the result LZ4F_errorCode_t is not OK_NoError, there was an error during context creation.
kusano 7d535a
* Object can release its memory using LZ4F_freeCompressionContext();
kusano 7d535a
*/
kusano 7d535a
LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_compressionContext_t* LZ4F_compressionContextPtr, unsigned version)
kusano 7d535a
{
kusano 7d535a
    LZ4F_cctx_t* cctxPtr;
kusano 7d535a
kusano 7d535a
    cctxPtr = (LZ4F_cctx_t*)ALLOCATOR(sizeof(LZ4F_cctx_t));
kusano 7d535a
    if (cctxPtr==NULL) return (LZ4F_errorCode_t)(-LZ4F_ERROR_allocation_failed);
kusano 7d535a
kusano 7d535a
    cctxPtr->version = version;
kusano 7d535a
    cctxPtr->cStage = 0;   /* Next stage : write header */
kusano 7d535a
kusano 7d535a
    *LZ4F_compressionContextPtr = (LZ4F_compressionContext_t)cctxPtr;
kusano 7d535a
kusano 7d535a
    return LZ4F_OK_NoError;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_compressionContext_t LZ4F_compressionContext)
kusano 7d535a
{
kusano 7d535a
    LZ4F_cctx_t* cctxPtr = (LZ4F_cctx_t*)LZ4F_compressionContext;
kusano 7d535a
kusano 7d535a
    if (cctxPtr != NULL)   /* null pointers can be safely provided to this function, like free() */
kusano 7d535a
    {
kusano 7d535a
       FREEMEM(cctxPtr->lz4CtxPtr);
kusano 7d535a
       FREEMEM(cctxPtr->tmpBuff);
kusano 7d535a
       FREEMEM(LZ4F_compressionContext);
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    return LZ4F_OK_NoError;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* LZ4F_compressBegin() :
kusano 7d535a
* will write the frame header into dstBuffer.
kusano 7d535a
* dstBuffer must be large enough to accommodate a header (dstMaxSize). Maximum header size is LZ4F_MAXHEADERFRAME_SIZE bytes.
kusano 7d535a
* The result of the function is the number of bytes written into dstBuffer for the header
kusano 7d535a
* or an error code (can be tested using LZ4F_isError())
kusano 7d535a
*/
kusano 7d535a
size_t LZ4F_compressBegin(LZ4F_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const LZ4F_preferences_t* preferencesPtr)
kusano 7d535a
{
kusano 7d535a
    LZ4F_preferences_t prefNull;
kusano 7d535a
    LZ4F_cctx_t* cctxPtr = (LZ4F_cctx_t*)compressionContext;
kusano 7d535a
    BYTE* const dstStart = (BYTE*)dstBuffer;
kusano 7d535a
    BYTE* dstPtr = dstStart;
kusano 7d535a
    BYTE* headerStart;
kusano 7d535a
    size_t requiredBuffSize;
kusano 7d535a
kusano 7d535a
    if (dstMaxSize < maxFHSize) return (size_t)-LZ4F_ERROR_dstMaxSize_tooSmall;
kusano 7d535a
    if (cctxPtr->cStage != 0) return (size_t)-LZ4F_ERROR_GENERIC;
kusano 7d535a
    memset(&prefNull, 0, sizeof(prefNull));
kusano 7d535a
    if (preferencesPtr == NULL) preferencesPtr = &prefNull;
kusano 7d535a
    cctxPtr->prefs = *preferencesPtr;
kusano 7d535a
kusano 7d535a
    /* ctx Management */
kusano 7d535a
    {
kusano 7d535a
        U32 tableID = (cctxPtr->prefs.compressionLevel < minHClevel) ? 1 : 2;  /* 0:nothing ; 1:LZ4 table ; 2:HC tables */
kusano 7d535a
        if (cctxPtr->lz4CtxLevel < tableID)
kusano 7d535a
        {
kusano 7d535a
            FREEMEM(cctxPtr->lz4CtxPtr);
kusano 7d535a
            if (cctxPtr->prefs.compressionLevel < minHClevel)
kusano 7d535a
                cctxPtr->lz4CtxPtr = (void*)LZ4_createStream();
kusano 7d535a
            else
kusano 7d535a
                cctxPtr->lz4CtxPtr = (void*)LZ4_createStreamHC();
kusano 7d535a
            cctxPtr->lz4CtxLevel = tableID;
kusano 7d535a
        }
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* Buffer Management */
kusano 7d535a
    if (cctxPtr->prefs.frameInfo.blockSizeID == 0) cctxPtr->prefs.frameInfo.blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;
kusano 7d535a
    cctxPtr->maxBlockSize = LZ4F_getBlockSize(cctxPtr->prefs.frameInfo.blockSizeID);
kusano 7d535a
kusano 7d535a
    requiredBuffSize = cctxPtr->maxBlockSize + ((cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) * 128 KB);
kusano 7d535a
    if (preferencesPtr->autoFlush)
kusano 7d535a
        requiredBuffSize = (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) * 64 KB;   /* just needs dict */
kusano 7d535a
kusano 7d535a
    if (cctxPtr->maxBufferSize < requiredBuffSize)
kusano 7d535a
    {
kusano 7d535a
        cctxPtr->maxBufferSize = requiredBuffSize;
kusano 7d535a
        FREEMEM(cctxPtr->tmpBuff);
kusano 7d535a
        cctxPtr->tmpBuff = (BYTE*)ALLOCATOR(requiredBuffSize);
kusano 7d535a
        if (cctxPtr->tmpBuff == NULL) return (size_t)-LZ4F_ERROR_allocation_failed;
kusano 7d535a
    }
kusano 7d535a
    cctxPtr->tmpIn = cctxPtr->tmpBuff;
kusano 7d535a
    cctxPtr->tmpInSize = 0;
kusano 7d535a
    XXH32_reset(&(cctxPtr->xxh), 0);
kusano 7d535a
    if (cctxPtr->prefs.compressionLevel < minHClevel)
kusano 7d535a
        LZ4_resetStream((LZ4_stream_t*)(cctxPtr->lz4CtxPtr));
kusano 7d535a
    else
kusano 7d535a
        LZ4_resetStreamHC((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), cctxPtr->prefs.compressionLevel);
kusano 7d535a
kusano 7d535a
    /* Magic Number */
kusano 7d535a
    LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER);
kusano 7d535a
    dstPtr += 4;
kusano 7d535a
    headerStart = dstPtr;
kusano 7d535a
kusano 7d535a
    /* FLG Byte */
kusano 7d535a
    *dstPtr++ = (BYTE)(((1 & _2BITS) << 6)    /* Version('01') */
kusano 7d535a
        + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5)    /* Block mode */
kusano 7d535a
        + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2)   /* Frame checksum */
kusano 7d535a
        + ((cctxPtr->prefs.frameInfo.contentSize > 0) << 3));   /* Frame content size */
kusano 7d535a
    /* BD Byte */
kusano 7d535a
    *dstPtr++ = (BYTE)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4);
kusano 7d535a
    /* Optional Frame content size field */
kusano 7d535a
    if (cctxPtr->prefs.frameInfo.contentSize)
kusano 7d535a
    {
kusano 7d535a
        LZ4F_writeLE64(dstPtr, cctxPtr->prefs.frameInfo.contentSize);
kusano 7d535a
        dstPtr += 8;
kusano 7d535a
        cctxPtr->totalInSize = 0;
kusano 7d535a
    }
kusano 7d535a
    /* CRC Byte */
kusano 7d535a
    *dstPtr = LZ4F_headerChecksum(headerStart, dstPtr - headerStart);
kusano 7d535a
    dstPtr++;
kusano 7d535a
kusano 7d535a
    cctxPtr->cStage = 1;   /* header written, now request input data block */
kusano 7d535a
kusano 7d535a
    return (dstPtr - dstStart);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* LZ4F_compressBound() : gives the size of Dst buffer given a srcSize to handle worst case situations.
kusano 7d535a
*                        The LZ4F_frameInfo_t structure is optional :
kusano 7d535a
*                        you can provide NULL as argument, preferences will then be set to cover worst case situations.
kusano 7d535a
* */
kusano 7d535a
size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
kusano 7d535a
{
kusano 7d535a
    LZ4F_preferences_t prefsNull;
kusano 7d535a
    memset(&prefsNull, 0, sizeof(prefsNull));
kusano 7d535a
    prefsNull.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled;   /* worst case */
kusano 7d535a
    {
kusano 7d535a
        const LZ4F_preferences_t* prefsPtr = (preferencesPtr==NULL) ? &prefsNull : preferencesPtr;
kusano 7d535a
        LZ4F_blockSizeID_t bid = prefsPtr->frameInfo.blockSizeID;
kusano 7d535a
        size_t blockSize = LZ4F_getBlockSize(bid);
kusano 7d535a
        unsigned nbBlocks = (unsigned)(srcSize / blockSize) + 1;
kusano 7d535a
        size_t lastBlockSize = prefsPtr->autoFlush ? srcSize % blockSize : blockSize;
kusano 7d535a
        size_t blockInfo = 4;   /* default, without block CRC option */
kusano 7d535a
        size_t frameEnd = 4 + (prefsPtr->frameInfo.contentChecksumFlag*4);
kusano 7d535a
kusano 7d535a
        return (blockInfo * nbBlocks) + (blockSize * (nbBlocks-1)) + lastBlockSize + frameEnd;;
kusano 7d535a
    }
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
typedef int (*compressFunc_t)(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level);
kusano 7d535a
kusano 7d535a
static size_t LZ4F_compressBlock(void* dst, const void* src, size_t srcSize, compressFunc_t compress, void* lz4ctx, int level)
kusano 7d535a
{
kusano 7d535a
    /* compress one block */
kusano 7d535a
    BYTE* cSizePtr = (BYTE*)dst;
kusano 7d535a
    U32 cSize;
kusano 7d535a
    cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+4), (int)(srcSize), (int)(srcSize-1), level);
kusano 7d535a
    LZ4F_writeLE32(cSizePtr, cSize);
kusano 7d535a
    if (cSize == 0)   /* compression failed */
kusano 7d535a
    {
kusano 7d535a
        cSize = (U32)srcSize;
kusano 7d535a
        LZ4F_writeLE32(cSizePtr, cSize + LZ4F_BLOCKUNCOMPRESSED_FLAG);
kusano 7d535a
        memcpy(cSizePtr+4, src, srcSize);
kusano 7d535a
    }
kusano 7d535a
    return cSize + 4;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
static int LZ4F_localLZ4_compress_limitedOutput_withState(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level)
kusano 7d535a
{
kusano 7d535a
    (void) level;
kusano 7d535a
    return LZ4_compress_limitedOutput_withState(ctx, src, dst, srcSize, dstSize);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static int LZ4F_localLZ4_compress_limitedOutput_continue(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level)
kusano 7d535a
{
kusano 7d535a
    (void) level;
kusano 7d535a
    return LZ4_compress_limitedOutput_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstSize);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static int LZ4F_localLZ4_compressHC_limitedOutput_continue(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level)
kusano 7d535a
{
kusano 7d535a
    (void) level;
kusano 7d535a
    return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstSize);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int level)
kusano 7d535a
{
kusano 7d535a
    if (level < minHClevel)
kusano 7d535a
    {
kusano 7d535a
        if (blockMode == LZ4F_blockIndependent) return LZ4F_localLZ4_compress_limitedOutput_withState;
kusano 7d535a
        return LZ4F_localLZ4_compress_limitedOutput_continue;
kusano 7d535a
    }
kusano 7d535a
    if (blockMode == LZ4F_blockIndependent) return LZ4_compress_HC_extStateHC;
kusano 7d535a
    return LZ4F_localLZ4_compressHC_limitedOutput_continue;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
static int LZ4F_localSaveDict(LZ4F_cctx_t* cctxPtr)
kusano 7d535a
{
kusano 7d535a
    if (cctxPtr->prefs.compressionLevel < minHClevel)
kusano 7d535a
        return LZ4_saveDict ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);
kusano 7d535a
    return LZ4_saveDictHC ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus;
kusano 7d535a
kusano 7d535a
/* LZ4F_compressUpdate()
kusano 7d535a
* LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.
kusano 7d535a
* The most important rule is that dstBuffer MUST be large enough (dstMaxSize) to ensure compression completion even in worst case.
kusano 7d535a
* If this condition is not respected, LZ4F_compress() will fail (result is an errorCode)
kusano 7d535a
* You can get the minimum value of dstMaxSize by using LZ4F_compressBound()
kusano 7d535a
* The LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
kusano 7d535a
* The result of the function is the number of bytes written into dstBuffer : it can be zero, meaning input data was just buffered.
kusano 7d535a
* The function outputs an error code if it fails (can be tested using LZ4F_isError())
kusano 7d535a
*/
kusano 7d535a
size_t LZ4F_compressUpdate(LZ4F_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const void* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* compressOptionsPtr)
kusano 7d535a
{
kusano 7d535a
    LZ4F_compressOptions_t cOptionsNull;
kusano 7d535a
    LZ4F_cctx_t* cctxPtr = (LZ4F_cctx_t*)compressionContext;
kusano 7d535a
    size_t blockSize = cctxPtr->maxBlockSize;
kusano 7d535a
    const BYTE* srcPtr = (const BYTE*)srcBuffer;
kusano 7d535a
    const BYTE* const srcEnd = srcPtr + srcSize;
kusano 7d535a
    BYTE* const dstStart = (BYTE*)dstBuffer;
kusano 7d535a
    BYTE* dstPtr = dstStart;
kusano 7d535a
    LZ4F_lastBlockStatus lastBlockCompressed = notDone;
kusano 7d535a
    compressFunc_t compress;
kusano 7d535a
kusano 7d535a
kusano 7d535a
    if (cctxPtr->cStage != 1) return (size_t)-LZ4F_ERROR_GENERIC;
kusano 7d535a
    if (dstMaxSize < LZ4F_compressBound(srcSize, &(cctxPtr->prefs))) return (size_t)-LZ4F_ERROR_dstMaxSize_tooSmall;
kusano 7d535a
    memset(&cOptionsNull, 0, sizeof(cOptionsNull));
kusano 7d535a
    if (compressOptionsPtr == NULL) compressOptionsPtr = &cOptionsNull;
kusano 7d535a
kusano 7d535a
    /* select compression function */
kusano 7d535a
    compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel);
kusano 7d535a
kusano 7d535a
    /* complete tmp buffer */
kusano 7d535a
    if (cctxPtr->tmpInSize > 0)   /* some data already within tmp buffer */
kusano 7d535a
    {
kusano 7d535a
        size_t sizeToCopy = blockSize - cctxPtr->tmpInSize;
kusano 7d535a
        if (sizeToCopy > srcSize)
kusano 7d535a
        {
kusano 7d535a
            /* add src to tmpIn buffer */
kusano 7d535a
            memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize);
kusano 7d535a
            srcPtr = srcEnd;
kusano 7d535a
            cctxPtr->tmpInSize += srcSize;
kusano 7d535a
            /* still needs some CRC */
kusano 7d535a
        }
kusano 7d535a
        else
kusano 7d535a
        {
kusano 7d535a
            /* complete tmpIn block and then compress it */
kusano 7d535a
            lastBlockCompressed = fromTmpBuffer;
kusano 7d535a
            memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy);
kusano 7d535a
            srcPtr += sizeToCopy;
kusano 7d535a
kusano 7d535a
            dstPtr += LZ4F_compressBlock(dstPtr, cctxPtr->tmpIn, blockSize, compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);
kusano 7d535a
kusano 7d535a
            if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += blockSize;
kusano 7d535a
            cctxPtr->tmpInSize = 0;
kusano 7d535a
        }
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    while ((size_t)(srcEnd - srcPtr) >= blockSize)
kusano 7d535a
    {
kusano 7d535a
        /* compress full block */
kusano 7d535a
        lastBlockCompressed = fromSrcBuffer;
kusano 7d535a
        dstPtr += LZ4F_compressBlock(dstPtr, srcPtr, blockSize, compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);
kusano 7d535a
        srcPtr += blockSize;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd))
kusano 7d535a
    {
kusano 7d535a
        /* compress remaining input < blockSize */
kusano 7d535a
        lastBlockCompressed = fromSrcBuffer;
kusano 7d535a
        dstPtr += LZ4F_compressBlock(dstPtr, srcPtr, srcEnd - srcPtr, compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);
kusano 7d535a
        srcPtr  = srcEnd;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* preserve dictionary if necessary */
kusano 7d535a
    if ((cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) && (lastBlockCompressed==fromSrcBuffer))
kusano 7d535a
    {
kusano 7d535a
        if (compressOptionsPtr->stableSrc)
kusano 7d535a
        {
kusano 7d535a
            cctxPtr->tmpIn = cctxPtr->tmpBuff;
kusano 7d535a
        }
kusano 7d535a
        else
kusano 7d535a
        {
kusano 7d535a
            int realDictSize = LZ4F_localSaveDict(cctxPtr);
kusano 7d535a
            if (realDictSize==0) return (size_t)-LZ4F_ERROR_GENERIC;
kusano 7d535a
            cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
kusano 7d535a
        }
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* keep tmpIn within limits */
kusano 7d535a
    if ((cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)   /* necessarily LZ4F_blockLinked && lastBlockCompressed==fromTmpBuffer */
kusano 7d535a
        && !(cctxPtr->prefs.autoFlush))
kusano 7d535a
    {
kusano 7d535a
        int realDictSize = LZ4F_localSaveDict(cctxPtr);
kusano 7d535a
        cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* some input data left, necessarily < blockSize */
kusano 7d535a
    if (srcPtr < srcEnd)
kusano 7d535a
    {
kusano 7d535a
        /* fill tmp buffer */
kusano 7d535a
        size_t sizeToCopy = srcEnd - srcPtr;
kusano 7d535a
        memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy);
kusano 7d535a
        cctxPtr->tmpInSize = sizeToCopy;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled)
kusano 7d535a
        XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize);
kusano 7d535a
kusano 7d535a
    cctxPtr->totalInSize += srcSize;
kusano 7d535a
    return dstPtr - dstStart;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* LZ4F_flush()
kusano 7d535a
* Should you need to create compressed data immediately, without waiting for a block to be filled,
kusano 7d535a
* you can call LZ4_flush(), which will immediately compress any remaining data stored within compressionContext.
kusano 7d535a
* The result of the function is the number of bytes written into dstBuffer
kusano 7d535a
* (it can be zero, this means there was no data left within compressionContext)
kusano 7d535a
* The function outputs an error code if it fails (can be tested using LZ4F_isError())
kusano 7d535a
* The LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
kusano 7d535a
*/
kusano 7d535a
size_t LZ4F_flush(LZ4F_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const LZ4F_compressOptions_t* compressOptionsPtr)
kusano 7d535a
{
kusano 7d535a
    LZ4F_cctx_t* cctxPtr = (LZ4F_cctx_t*)compressionContext;
kusano 7d535a
    BYTE* const dstStart = (BYTE*)dstBuffer;
kusano 7d535a
    BYTE* dstPtr = dstStart;
kusano 7d535a
    compressFunc_t compress;
kusano 7d535a
kusano 7d535a
kusano 7d535a
    if (cctxPtr->tmpInSize == 0) return 0;   /* nothing to flush */
kusano 7d535a
    if (cctxPtr->cStage != 1) return (size_t)-LZ4F_ERROR_GENERIC;
kusano 7d535a
    if (dstMaxSize < (cctxPtr->tmpInSize + 8)) return (size_t)-LZ4F_ERROR_dstMaxSize_tooSmall;   /* +8 : block header(4) + block checksum(4) */
kusano 7d535a
    (void)compressOptionsPtr;   /* not yet useful */
kusano 7d535a
kusano 7d535a
    /* select compression function */
kusano 7d535a
    compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel);
kusano 7d535a
kusano 7d535a
    /* compress tmp buffer */
kusano 7d535a
    dstPtr += LZ4F_compressBlock(dstPtr, cctxPtr->tmpIn, cctxPtr->tmpInSize, compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);
kusano 7d535a
    if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += cctxPtr->tmpInSize;
kusano 7d535a
    cctxPtr->tmpInSize = 0;
kusano 7d535a
kusano 7d535a
    /* keep tmpIn within limits */
kusano 7d535a
    if ((cctxPtr->tmpIn + cctxPtr->maxBlockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize))   /* necessarily LZ4F_blockLinked */
kusano 7d535a
    {
kusano 7d535a
        int realDictSize = LZ4F_localSaveDict(cctxPtr);
kusano 7d535a
        cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    return dstPtr - dstStart;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* LZ4F_compressEnd()
kusano 7d535a
* When you want to properly finish the compressed frame, just call LZ4F_compressEnd().
kusano 7d535a
* It will flush whatever data remained within compressionContext (like LZ4_flush())
kusano 7d535a
* but also properly finalize the frame, with an endMark and a checksum.
kusano 7d535a
* The result of the function is the number of bytes written into dstBuffer (necessarily >= 4 (endMark size))
kusano 7d535a
* The function outputs an error code if it fails (can be tested using LZ4F_isError())
kusano 7d535a
* The LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
kusano 7d535a
* compressionContext can then be used again, starting with LZ4F_compressBegin(). The preferences will remain the same.
kusano 7d535a
*/
kusano 7d535a
size_t LZ4F_compressEnd(LZ4F_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const LZ4F_compressOptions_t* compressOptionsPtr)
kusano 7d535a
{
kusano 7d535a
    LZ4F_cctx_t* cctxPtr = (LZ4F_cctx_t*)compressionContext;
kusano 7d535a
    BYTE* const dstStart = (BYTE*)dstBuffer;
kusano 7d535a
    BYTE* dstPtr = dstStart;
kusano 7d535a
    size_t errorCode;
kusano 7d535a
kusano 7d535a
    errorCode = LZ4F_flush(compressionContext, dstBuffer, dstMaxSize, compressOptionsPtr);
kusano 7d535a
    if (LZ4F_isError(errorCode)) return errorCode;
kusano 7d535a
    dstPtr += errorCode;
kusano 7d535a
kusano 7d535a
    LZ4F_writeLE32(dstPtr, 0);
kusano 7d535a
    dstPtr+=4;   /* endMark */
kusano 7d535a
kusano 7d535a
    if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled)
kusano 7d535a
    {
kusano 7d535a
        U32 xxh = XXH32_digest(&(cctxPtr->xxh));
kusano 7d535a
        LZ4F_writeLE32(dstPtr, xxh);
kusano 7d535a
        dstPtr+=4;   /* content Checksum */
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    cctxPtr->cStage = 0;   /* state is now re-usable (with identical preferences) */
kusano 7d535a
kusano 7d535a
    if (cctxPtr->prefs.frameInfo.contentSize)
kusano 7d535a
    {
kusano 7d535a
        if (cctxPtr->prefs.frameInfo.contentSize != cctxPtr->totalInSize)
kusano 7d535a
            return (size_t)-LZ4F_ERROR_frameSize_wrong;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    return dstPtr - dstStart;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/**********************************
kusano 7d535a
*  Decompression functions
kusano 7d535a
**********************************/
kusano 7d535a
kusano 7d535a
/* Resource management */
kusano 7d535a
kusano 7d535a
/* LZ4F_createDecompressionContext() :
kusano 7d535a
* The first thing to do is to create a decompressionContext object, which will be used in all decompression operations.
kusano 7d535a
* This is achieved using LZ4F_createDecompressionContext().
kusano 7d535a
* The function will provide a pointer to a fully allocated and initialized LZ4F_decompressionContext object.
kusano 7d535a
* If the result LZ4F_errorCode_t is not zero, there was an error during context creation.
kusano 7d535a
* Object can release its memory using LZ4F_freeDecompressionContext();
kusano 7d535a
*/
kusano 7d535a
LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_decompressionContext_t* LZ4F_decompressionContextPtr, unsigned versionNumber)
kusano 7d535a
{
kusano 7d535a
    LZ4F_dctx_t* dctxPtr;
kusano 7d535a
kusano 7d535a
    dctxPtr = (LZ4F_dctx_t*)ALLOCATOR(sizeof(LZ4F_dctx_t));
kusano 7d535a
    if (dctxPtr==NULL) return (LZ4F_errorCode_t)-LZ4F_ERROR_GENERIC;
kusano 7d535a
kusano 7d535a
    dctxPtr->version = versionNumber;
kusano 7d535a
    *LZ4F_decompressionContextPtr = (LZ4F_decompressionContext_t)dctxPtr;
kusano 7d535a
    return LZ4F_OK_NoError;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_decompressionContext_t LZ4F_decompressionContext)
kusano 7d535a
{
kusano 7d535a
    LZ4F_errorCode_t result = LZ4F_OK_NoError;
kusano 7d535a
    LZ4F_dctx_t* dctxPtr = (LZ4F_dctx_t*)LZ4F_decompressionContext;
kusano 7d535a
    if (dctxPtr != NULL)   /* can accept NULL input, like free() */
kusano 7d535a
    {
kusano 7d535a
      result = (LZ4F_errorCode_t)dctxPtr->dStage;
kusano 7d535a
      FREEMEM(dctxPtr->tmpIn);
kusano 7d535a
      FREEMEM(dctxPtr->tmpOutBuffer);
kusano 7d535a
      FREEMEM(dctxPtr);
kusano 7d535a
    }
kusano 7d535a
    return result;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* ******************************************************************** */
kusano 7d535a
/* ********************* Decompression ******************************** */
kusano 7d535a
/* ******************************************************************** */
kusano 7d535a
kusano 7d535a
typedef enum { dstage_getHeader=0, dstage_storeHeader,
kusano 7d535a
    dstage_getCBlockSize, dstage_storeCBlockSize,
kusano 7d535a
    dstage_copyDirect,
kusano 7d535a
    dstage_getCBlock, dstage_storeCBlock,
kusano 7d535a
    dstage_decodeCBlock, dstage_decodeCBlock_intoDst,
kusano 7d535a
    dstage_decodeCBlock_intoTmp, dstage_flushOut,
kusano 7d535a
    dstage_getSuffix, dstage_storeSuffix,
kusano 7d535a
    dstage_getSFrameSize, dstage_storeSFrameSize,
kusano 7d535a
    dstage_skipSkippable
kusano 7d535a
} dStage_t;
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* LZ4F_decodeHeader
kusano 7d535a
   return : nb Bytes read from srcVoidPtr (necessarily <= srcSize)
kusano 7d535a
            or an error code (testable with LZ4F_isError())
kusano 7d535a
   output : set internal values of dctx, such as
kusano 7d535a
            dctxPtr->frameInfo and dctxPtr->dStage.
kusano 7d535a
   input  : srcVoidPtr points at the **beginning of the frame**
kusano 7d535a
*/
kusano 7d535a
static size_t LZ4F_decodeHeader(LZ4F_dctx_t* dctxPtr, const void* srcVoidPtr, size_t srcSize)
kusano 7d535a
{
kusano 7d535a
    BYTE FLG, BD, HC;
kusano 7d535a
    unsigned version, blockMode, blockChecksumFlag, contentSizeFlag, contentChecksumFlag, blockSizeID;
kusano 7d535a
    size_t bufferNeeded;
kusano 7d535a
    size_t frameHeaderSize;
kusano 7d535a
    const BYTE* srcPtr = (const BYTE*)srcVoidPtr;
kusano 7d535a
kusano 7d535a
    /* need to decode header to get frameInfo */
kusano 7d535a
    if (srcSize < minFHSize) return (size_t)-LZ4F_ERROR_frameHeader_incomplete;   /* minimal frame header size */
kusano 7d535a
    memset(&(dctxPtr->frameInfo), 0, sizeof(dctxPtr->frameInfo));
kusano 7d535a
kusano 7d535a
    /* special case : skippable frames */
kusano 7d535a
    if ((LZ4F_readLE32(srcPtr) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START)
kusano 7d535a
    {
kusano 7d535a
        dctxPtr->frameInfo.frameType = LZ4F_skippableFrame;
kusano 7d535a
        if (srcVoidPtr == (void*)(dctxPtr->header))
kusano 7d535a
        {
kusano 7d535a
            dctxPtr->tmpInSize = srcSize;
kusano 7d535a
            dctxPtr->tmpInTarget = 8;
kusano 7d535a
            dctxPtr->dStage = dstage_storeSFrameSize;
kusano 7d535a
            return srcSize;
kusano 7d535a
        }
kusano 7d535a
        else
kusano 7d535a
        {
kusano 7d535a
            dctxPtr->dStage = dstage_getSFrameSize;
kusano 7d535a
            return 4;
kusano 7d535a
        }
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* control magic number */
kusano 7d535a
    if (LZ4F_readLE32(srcPtr) != LZ4F_MAGICNUMBER) return (size_t)-LZ4F_ERROR_frameType_unknown;
kusano 7d535a
    dctxPtr->frameInfo.frameType = LZ4F_frame;
kusano 7d535a
kusano 7d535a
    /* Flags */
kusano 7d535a
    FLG = srcPtr[4];
kusano 7d535a
    version = (FLG>>6) & _2BITS;
kusano 7d535a
    blockMode = (FLG>>5) & _1BIT;
kusano 7d535a
    blockChecksumFlag = (FLG>>4) & _1BIT;
kusano 7d535a
    contentSizeFlag = (FLG>>3) & _1BIT;
kusano 7d535a
    contentChecksumFlag = (FLG>>2) & _1BIT;
kusano 7d535a
kusano 7d535a
    /* Frame Header Size */
kusano 7d535a
    frameHeaderSize = contentSizeFlag ? maxFHSize : minFHSize;
kusano 7d535a
kusano 7d535a
    if (srcSize < frameHeaderSize)
kusano 7d535a
    {
kusano 7d535a
        /* not enough input to fully decode frame header */
kusano 7d535a
        if (srcPtr != dctxPtr->header)
kusano 7d535a
            memcpy(dctxPtr->header, srcPtr, srcSize);
kusano 7d535a
        dctxPtr->tmpInSize = srcSize;
kusano 7d535a
        dctxPtr->tmpInTarget = frameHeaderSize;
kusano 7d535a
        dctxPtr->dStage = dstage_storeHeader;
kusano 7d535a
        return srcSize;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    BD = srcPtr[5];
kusano 7d535a
    blockSizeID = (BD>>4) & _3BITS;
kusano 7d535a
kusano 7d535a
    /* validate */
kusano 7d535a
    if (version != 1) return (size_t)-LZ4F_ERROR_headerVersion_wrong;        /* Version Number, only supported value */
kusano 7d535a
    if (blockChecksumFlag != 0) return (size_t)-LZ4F_ERROR_blockChecksum_unsupported; /* Not supported for the time being */
kusano 7d535a
    if (((FLG>>0)&_2BITS) != 0) return (size_t)-LZ4F_ERROR_reservedFlag_set; /* Reserved bits */
kusano 7d535a
    if (((BD>>7)&_1BIT) != 0) return (size_t)-LZ4F_ERROR_reservedFlag_set;   /* Reserved bit */
kusano 7d535a
    if (blockSizeID < 4) return (size_t)-LZ4F_ERROR_maxBlockSize_invalid;    /* 4-7 only supported values for the time being */
kusano 7d535a
    if (((BD>>0)&_4BITS) != 0) return (size_t)-LZ4F_ERROR_reservedFlag_set;  /* Reserved bits */
kusano 7d535a
kusano 7d535a
    /* check */
kusano 7d535a
    HC = LZ4F_headerChecksum(srcPtr+4, frameHeaderSize-5);
kusano 7d535a
    if (HC != srcPtr[frameHeaderSize-1]) return (size_t)-LZ4F_ERROR_headerChecksum_invalid;   /* Bad header checksum error */
kusano 7d535a
kusano 7d535a
    /* save */
kusano 7d535a
    dctxPtr->frameInfo.blockMode = (LZ4F_blockMode_t)blockMode;
kusano 7d535a
    dctxPtr->frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)contentChecksumFlag;
kusano 7d535a
    dctxPtr->frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID;
kusano 7d535a
    dctxPtr->maxBlockSize = LZ4F_getBlockSize(blockSizeID);
kusano 7d535a
    if (contentSizeFlag)
kusano 7d535a
        dctxPtr->frameRemainingSize = dctxPtr->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6);
kusano 7d535a
kusano 7d535a
    /* init */
kusano 7d535a
    if (contentChecksumFlag) XXH32_reset(&(dctxPtr->xxh), 0);
kusano 7d535a
kusano 7d535a
    /* alloc */
kusano 7d535a
    bufferNeeded = dctxPtr->maxBlockSize + ((dctxPtr->frameInfo.blockMode==LZ4F_blockLinked) * 128 KB);
kusano 7d535a
    if (bufferNeeded > dctxPtr->maxBufferSize)   /* tmp buffers too small */
kusano 7d535a
    {
kusano 7d535a
        FREEMEM(dctxPtr->tmpIn);
kusano 7d535a
        FREEMEM(dctxPtr->tmpOutBuffer);
kusano 7d535a
        dctxPtr->maxBufferSize = bufferNeeded;
kusano 7d535a
        dctxPtr->tmpIn = (BYTE*)ALLOCATOR(dctxPtr->maxBlockSize);
kusano 7d535a
        if (dctxPtr->tmpIn == NULL) return (size_t)-LZ4F_ERROR_GENERIC;
kusano 7d535a
        dctxPtr->tmpOutBuffer= (BYTE*)ALLOCATOR(dctxPtr->maxBufferSize);
kusano 7d535a
        if (dctxPtr->tmpOutBuffer== NULL) return (size_t)-LZ4F_ERROR_GENERIC;
kusano 7d535a
    }
kusano 7d535a
    dctxPtr->tmpInSize = 0;
kusano 7d535a
    dctxPtr->tmpInTarget = 0;
kusano 7d535a
    dctxPtr->dict = dctxPtr->tmpOutBuffer;
kusano 7d535a
    dctxPtr->dictSize = 0;
kusano 7d535a
    dctxPtr->tmpOut = dctxPtr->tmpOutBuffer;
kusano 7d535a
    dctxPtr->tmpOutStart = 0;
kusano 7d535a
    dctxPtr->tmpOutSize = 0;
kusano 7d535a
kusano 7d535a
    dctxPtr->dStage = dstage_getCBlockSize;
kusano 7d535a
kusano 7d535a
    return frameHeaderSize;
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* LZ4F_getFrameInfo()
kusano 7d535a
* This function decodes frame header information, such as blockSize.
kusano 7d535a
* It is optional : you could start by calling directly LZ4F_decompress() instead.
kusano 7d535a
* The objective is to extract header information without starting decompression, typically for allocation purposes.
kusano 7d535a
* LZ4F_getFrameInfo() can also be used *after* starting decompression, on a valid LZ4F_decompressionContext_t.
kusano 7d535a
* The number of bytes read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).
kusano 7d535a
* You are expected to resume decompression from where it stopped (srcBuffer + *srcSizePtr)
kusano 7d535a
* The function result is an hint of the better srcSize to use for next call to LZ4F_decompress,
kusano 7d535a
* or an error code which can be tested using LZ4F_isError().
kusano 7d535a
*/
kusano 7d535a
LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_decompressionContext_t dCtx, LZ4F_frameInfo_t* frameInfoPtr,
kusano 7d535a
                                   const void* srcBuffer, size_t* srcSizePtr)
kusano 7d535a
{
kusano 7d535a
    LZ4F_dctx_t* dctxPtr = (LZ4F_dctx_t*)dCtx;
kusano 7d535a
kusano 7d535a
    if (dctxPtr->dStage > dstage_storeHeader)   /* note : requires dstage_* header related to be at beginning of enum */
kusano 7d535a
    {
kusano 7d535a
        size_t o=0, i=0;
kusano 7d535a
        /* frameInfo already decoded */
kusano 7d535a
        *srcSizePtr = 0;
kusano 7d535a
        *frameInfoPtr = dctxPtr->frameInfo;
kusano 7d535a
        return LZ4F_decompress(dCtx, NULL, &o, NULL, &i, NULL);
kusano 7d535a
    }
kusano 7d535a
    else
kusano 7d535a
    {
kusano 7d535a
        size_t o=0;
kusano 7d535a
        size_t nextSrcSize = LZ4F_decompress(dCtx, NULL, &o, srcBuffer, srcSizePtr, NULL);
kusano 7d535a
        if (dctxPtr->dStage <= dstage_storeHeader)   /* note : requires dstage_* header related to be at beginning of enum */
kusano 7d535a
            return (size_t)-LZ4F_ERROR_frameHeader_incomplete;
kusano 7d535a
        *frameInfoPtr = dctxPtr->frameInfo;
kusano 7d535a
        return nextSrcSize;
kusano 7d535a
    }
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* trivial redirector, for common prototype */
kusano 7d535a
static int LZ4F_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize)
kusano 7d535a
{
kusano 7d535a
    (void)dictStart; (void)dictSize;
kusano 7d535a
    return LZ4_decompress_safe (source, dest, compressedSize, maxDecompressedSize);
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
static void LZ4F_updateDict(LZ4F_dctx_t* dctxPtr, const BYTE* dstPtr, size_t dstSize, const BYTE* dstPtr0, unsigned withinTmp)
kusano 7d535a
{
kusano 7d535a
    if (dctxPtr->dictSize==0)
kusano 7d535a
        dctxPtr->dict = (const BYTE*)dstPtr;   /* priority to dictionary continuity */
kusano 7d535a
kusano 7d535a
    if (dctxPtr->dict + dctxPtr->dictSize == dstPtr)   /* dictionary continuity */
kusano 7d535a
    {
kusano 7d535a
        dctxPtr->dictSize += dstSize;
kusano 7d535a
        return;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    if (dstPtr - dstPtr0 + dstSize >= 64 KB)   /* dstBuffer large enough to become dictionary */
kusano 7d535a
    {
kusano 7d535a
        dctxPtr->dict = (const BYTE*)dstPtr0;
kusano 7d535a
        dctxPtr->dictSize = dstPtr - dstPtr0 + dstSize;
kusano 7d535a
        return;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    if ((withinTmp) && (dctxPtr->dict == dctxPtr->tmpOutBuffer))
kusano 7d535a
    {
kusano 7d535a
        /* assumption : dctxPtr->dict + dctxPtr->dictSize == dctxPtr->tmpOut + dctxPtr->tmpOutStart */
kusano 7d535a
        dctxPtr->dictSize += dstSize;
kusano 7d535a
        return;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    if (withinTmp) /* copy relevant dict portion in front of tmpOut within tmpOutBuffer */
kusano 7d535a
    {
kusano 7d535a
        size_t preserveSize = dctxPtr->tmpOut - dctxPtr->tmpOutBuffer;
kusano 7d535a
        size_t copySize = 64 KB - dctxPtr->tmpOutSize;
kusano 7d535a
        const BYTE* oldDictEnd = dctxPtr->dict + dctxPtr->dictSize - dctxPtr->tmpOutStart;
kusano 7d535a
        if (dctxPtr->tmpOutSize > 64 KB) copySize = 0;
kusano 7d535a
        if (copySize > preserveSize) copySize = preserveSize;
kusano 7d535a
kusano 7d535a
        memcpy(dctxPtr->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
kusano 7d535a
kusano 7d535a
        dctxPtr->dict = dctxPtr->tmpOutBuffer;
kusano 7d535a
        dctxPtr->dictSize = preserveSize + dctxPtr->tmpOutStart + dstSize;
kusano 7d535a
        return;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    if (dctxPtr->dict == dctxPtr->tmpOutBuffer)     /* copy dst into tmp to complete dict */
kusano 7d535a
    {
kusano 7d535a
        if (dctxPtr->dictSize + dstSize > dctxPtr->maxBufferSize)   /* tmp buffer not large enough */
kusano 7d535a
        {
kusano 7d535a
            size_t preserveSize = 64 KB - dstSize;   /* note : dstSize < 64 KB */
kusano 7d535a
            memcpy(dctxPtr->tmpOutBuffer, dctxPtr->dict + dctxPtr->dictSize - preserveSize, preserveSize);
kusano 7d535a
            dctxPtr->dictSize = preserveSize;
kusano 7d535a
        }
kusano 7d535a
        memcpy(dctxPtr->tmpOutBuffer + dctxPtr->dictSize, dstPtr, dstSize);
kusano 7d535a
        dctxPtr->dictSize += dstSize;
kusano 7d535a
        return;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* join dict & dest into tmp */
kusano 7d535a
    {
kusano 7d535a
        size_t preserveSize = 64 KB - dstSize;   /* note : dstSize < 64 KB */
kusano 7d535a
        if (preserveSize > dctxPtr->dictSize) preserveSize = dctxPtr->dictSize;
kusano 7d535a
        memcpy(dctxPtr->tmpOutBuffer, dctxPtr->dict + dctxPtr->dictSize - preserveSize, preserveSize);
kusano 7d535a
        memcpy(dctxPtr->tmpOutBuffer + preserveSize, dstPtr, dstSize);
kusano 7d535a
        dctxPtr->dict = dctxPtr->tmpOutBuffer;
kusano 7d535a
        dctxPtr->dictSize = preserveSize + dstSize;
kusano 7d535a
    }
kusano 7d535a
}
kusano 7d535a
kusano 7d535a
kusano 7d535a
kusano 7d535a
/* LZ4F_decompress()
kusano 7d535a
* Call this function repetitively to regenerate data compressed within srcBuffer.
kusano 7d535a
* The function will attempt to decode *srcSizePtr from srcBuffer, into dstBuffer of maximum size *dstSizePtr.
kusano 7d535a
*
kusano 7d535a
* The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr (necessarily <= original value).
kusano 7d535a
*
kusano 7d535a
* The number of bytes effectively read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).
kusano 7d535a
* If the number of bytes read is < number of bytes provided, then the decompression operation is not complete.
kusano 7d535a
* You will have to call it again, continuing from where it stopped.
kusano 7d535a
*
kusano 7d535a
* The function result is an hint of the better srcSize to use for next call to LZ4F_decompress.
kusano 7d535a
* Basically, it's the size of the current (or remaining) compressed block + header of next block.
kusano 7d535a
* Respecting the hint provides some boost to performance, since it allows less buffer shuffling.
kusano 7d535a
* Note that this is just a hint, you can always provide any srcSize you want.
kusano 7d535a
* When a frame is fully decoded, the function result will be 0.
kusano 7d535a
* If decompression failed, function result is an error code which can be tested using LZ4F_isError().
kusano 7d535a
*/
kusano 7d535a
size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext,
kusano 7d535a
                       void* dstBuffer, size_t* dstSizePtr,
kusano 7d535a
                       const void* srcBuffer, size_t* srcSizePtr,
kusano 7d535a
                       const LZ4F_decompressOptions_t* decompressOptionsPtr)
kusano 7d535a
{
kusano 7d535a
    LZ4F_dctx_t* dctxPtr = (LZ4F_dctx_t*)decompressionContext;
kusano 7d535a
    LZ4F_decompressOptions_t optionsNull;
kusano 7d535a
    const BYTE* const srcStart = (const BYTE*)srcBuffer;
kusano 7d535a
    const BYTE* const srcEnd = srcStart + *srcSizePtr;
kusano 7d535a
    const BYTE* srcPtr = srcStart;
kusano 7d535a
    BYTE* const dstStart = (BYTE*)dstBuffer;
kusano 7d535a
    BYTE* const dstEnd = dstStart + *dstSizePtr;
kusano 7d535a
    BYTE* dstPtr = dstStart;
kusano 7d535a
    const BYTE* selectedIn = NULL;
kusano 7d535a
    unsigned doAnotherStage = 1;
kusano 7d535a
    size_t nextSrcSizeHint = 1;
kusano 7d535a
kusano 7d535a
kusano 7d535a
    memset(&optionsNull, 0, sizeof(optionsNull));
kusano 7d535a
    if (decompressOptionsPtr==NULL) decompressOptionsPtr = &optionsNull;
kusano 7d535a
    *srcSizePtr = 0;
kusano 7d535a
    *dstSizePtr = 0;
kusano 7d535a
kusano 7d535a
    /* expect to continue decoding src buffer where it left previously */
kusano 7d535a
    if (dctxPtr->srcExpect != NULL)
kusano 7d535a
    {
kusano 7d535a
        if (srcStart != dctxPtr->srcExpect) return (size_t)-LZ4F_ERROR_srcPtr_wrong;
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* programmed as a state machine */
kusano 7d535a
kusano 7d535a
    while (doAnotherStage)
kusano 7d535a
    {
kusano 7d535a
kusano 7d535a
        switch(dctxPtr->dStage)
kusano 7d535a
        {
kusano 7d535a
kusano 7d535a
        case dstage_getHeader:
kusano 7d535a
            {
kusano 7d535a
                if ((size_t)(srcEnd-srcPtr) >= maxFHSize)   /* enough to decode - shortcut */
kusano 7d535a
                {
kusano 7d535a
                    LZ4F_errorCode_t errorCode = LZ4F_decodeHeader(dctxPtr, srcPtr, srcEnd-srcPtr);
kusano 7d535a
                    if (LZ4F_isError(errorCode)) return errorCode;
kusano 7d535a
                    srcPtr += errorCode;
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                dctxPtr->tmpInSize = 0;
kusano 7d535a
                dctxPtr->tmpInTarget = minFHSize;   /* minimum to attempt decode */
kusano 7d535a
                dctxPtr->dStage = dstage_storeHeader;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_storeHeader:
kusano 7d535a
            {
kusano 7d535a
                size_t sizeToCopy = dctxPtr->tmpInTarget - dctxPtr->tmpInSize;
kusano 7d535a
                if (sizeToCopy > (size_t)(srcEnd - srcPtr)) sizeToCopy =  srcEnd - srcPtr;
kusano 7d535a
                memcpy(dctxPtr->header + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
kusano 7d535a
                dctxPtr->tmpInSize += sizeToCopy;
kusano 7d535a
                srcPtr += sizeToCopy;
kusano 7d535a
                if (dctxPtr->tmpInSize < dctxPtr->tmpInTarget)
kusano 7d535a
                {
kusano 7d535a
                    nextSrcSizeHint = (dctxPtr->tmpInTarget - dctxPtr->tmpInSize) + BHSize;   /* rest of header + nextBlockHeader */
kusano 7d535a
                    doAnotherStage = 0;   /* not enough src data, ask for some more */
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                {
kusano 7d535a
                    LZ4F_errorCode_t errorCode = LZ4F_decodeHeader(dctxPtr, dctxPtr->header, dctxPtr->tmpInTarget);
kusano 7d535a
                    if (LZ4F_isError(errorCode)) return errorCode;
kusano 7d535a
                }
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_getCBlockSize:
kusano 7d535a
            {
kusano 7d535a
                if ((size_t)(srcEnd - srcPtr) >= BHSize)
kusano 7d535a
                {
kusano 7d535a
                    selectedIn = srcPtr;
kusano 7d535a
                    srcPtr += BHSize;
kusano 7d535a
                }
kusano 7d535a
                else
kusano 7d535a
                {
kusano 7d535a
                /* not enough input to read cBlockSize field */
kusano 7d535a
                    dctxPtr->tmpInSize = 0;
kusano 7d535a
                    dctxPtr->dStage = dstage_storeCBlockSize;
kusano 7d535a
                }
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
            if (dctxPtr->dStage == dstage_storeCBlockSize)
kusano 7d535a
        case dstage_storeCBlockSize:
kusano 7d535a
            {
kusano 7d535a
                size_t sizeToCopy = BHSize - dctxPtr->tmpInSize;
kusano 7d535a
                if (sizeToCopy > (size_t)(srcEnd - srcPtr)) sizeToCopy = srcEnd - srcPtr;
kusano 7d535a
                memcpy(dctxPtr->tmpIn + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
kusano 7d535a
                srcPtr += sizeToCopy;
kusano 7d535a
                dctxPtr->tmpInSize += sizeToCopy;
kusano 7d535a
                if (dctxPtr->tmpInSize < BHSize) /* not enough input to get full cBlockSize; wait for more */
kusano 7d535a
                {
kusano 7d535a
                    nextSrcSizeHint = BHSize - dctxPtr->tmpInSize;
kusano 7d535a
                    doAnotherStage  = 0;
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                selectedIn = dctxPtr->tmpIn;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        /* case dstage_decodeCBlockSize: */   /* no more direct access, to prevent scan-build warning */
kusano 7d535a
            {
kusano 7d535a
                size_t nextCBlockSize = LZ4F_readLE32(selectedIn) & 0x7FFFFFFFU;
kusano 7d535a
                if (nextCBlockSize==0)   /* frameEnd signal, no more CBlock */
kusano 7d535a
                {
kusano 7d535a
                    dctxPtr->dStage = dstage_getSuffix;
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                if (nextCBlockSize > dctxPtr->maxBlockSize) return (size_t)-LZ4F_ERROR_GENERIC;   /* invalid cBlockSize */
kusano 7d535a
                dctxPtr->tmpInTarget = nextCBlockSize;
kusano 7d535a
                if (LZ4F_readLE32(selectedIn) & LZ4F_BLOCKUNCOMPRESSED_FLAG)
kusano 7d535a
                {
kusano 7d535a
                    dctxPtr->dStage = dstage_copyDirect;
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                dctxPtr->dStage = dstage_getCBlock;
kusano 7d535a
                if (dstPtr==dstEnd)
kusano 7d535a
                {
kusano 7d535a
                    nextSrcSizeHint = nextCBlockSize + BHSize;
kusano 7d535a
                    doAnotherStage = 0;
kusano 7d535a
                }
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_copyDirect:   /* uncompressed block */
kusano 7d535a
            {
kusano 7d535a
                size_t sizeToCopy = dctxPtr->tmpInTarget;
kusano 7d535a
                if ((size_t)(srcEnd-srcPtr) < sizeToCopy) sizeToCopy = srcEnd - srcPtr;  /* not enough input to read full block */
kusano 7d535a
                if ((size_t)(dstEnd-dstPtr) < sizeToCopy) sizeToCopy = dstEnd - dstPtr;
kusano 7d535a
                memcpy(dstPtr, srcPtr, sizeToCopy);
kusano 7d535a
                if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), srcPtr, sizeToCopy);
kusano 7d535a
                if (dctxPtr->frameInfo.contentSize) dctxPtr->frameRemainingSize -= sizeToCopy;
kusano 7d535a
kusano 7d535a
                /* dictionary management */
kusano 7d535a
                if (dctxPtr->frameInfo.blockMode==LZ4F_blockLinked)
kusano 7d535a
                    LZ4F_updateDict(dctxPtr, dstPtr, sizeToCopy, dstStart, 0);
kusano 7d535a
kusano 7d535a
                srcPtr += sizeToCopy;
kusano 7d535a
                dstPtr += sizeToCopy;
kusano 7d535a
                if (sizeToCopy == dctxPtr->tmpInTarget)   /* all copied */
kusano 7d535a
                {
kusano 7d535a
                    dctxPtr->dStage = dstage_getCBlockSize;
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                dctxPtr->tmpInTarget -= sizeToCopy;   /* still need to copy more */
kusano 7d535a
                nextSrcSizeHint = dctxPtr->tmpInTarget + BHSize;
kusano 7d535a
                doAnotherStage = 0;
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_getCBlock:   /* entry from dstage_decodeCBlockSize */
kusano 7d535a
            {
kusano 7d535a
                if ((size_t)(srcEnd-srcPtr) < dctxPtr->tmpInTarget)
kusano 7d535a
                {
kusano 7d535a
                    dctxPtr->tmpInSize = 0;
kusano 7d535a
                    dctxPtr->dStage = dstage_storeCBlock;
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                selectedIn = srcPtr;
kusano 7d535a
                srcPtr += dctxPtr->tmpInTarget;
kusano 7d535a
                dctxPtr->dStage = dstage_decodeCBlock;
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_storeCBlock:
kusano 7d535a
            {
kusano 7d535a
                size_t sizeToCopy = dctxPtr->tmpInTarget - dctxPtr->tmpInSize;
kusano 7d535a
                if (sizeToCopy > (size_t)(srcEnd-srcPtr)) sizeToCopy = srcEnd-srcPtr;
kusano 7d535a
                memcpy(dctxPtr->tmpIn + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
kusano 7d535a
                dctxPtr->tmpInSize += sizeToCopy;
kusano 7d535a
                srcPtr += sizeToCopy;
kusano 7d535a
                if (dctxPtr->tmpInSize < dctxPtr->tmpInTarget)  /* need more input */
kusano 7d535a
                {
kusano 7d535a
                    nextSrcSizeHint = (dctxPtr->tmpInTarget - dctxPtr->tmpInSize) + BHSize;
kusano 7d535a
                    doAnotherStage=0;
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                selectedIn = dctxPtr->tmpIn;
kusano 7d535a
                dctxPtr->dStage = dstage_decodeCBlock;
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_decodeCBlock:
kusano 7d535a
            {
kusano 7d535a
                if ((size_t)(dstEnd-dstPtr) < dctxPtr->maxBlockSize)   /* not enough place into dst : decode into tmpOut */
kusano 7d535a
                    dctxPtr->dStage = dstage_decodeCBlock_intoTmp;
kusano 7d535a
                else
kusano 7d535a
                    dctxPtr->dStage = dstage_decodeCBlock_intoDst;
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_decodeCBlock_intoDst:
kusano 7d535a
            {
kusano 7d535a
                int (*decoder)(const char*, char*, int, int, const char*, int);
kusano 7d535a
                int decodedSize;
kusano 7d535a
kusano 7d535a
                if (dctxPtr->frameInfo.blockMode == LZ4F_blockLinked)
kusano 7d535a
                    decoder = LZ4_decompress_safe_usingDict;
kusano 7d535a
                else
kusano 7d535a
                    decoder = LZ4F_decompress_safe;
kusano 7d535a
kusano 7d535a
                decodedSize = decoder((const char*)selectedIn, (char*)dstPtr, (int)dctxPtr->tmpInTarget, (int)dctxPtr->maxBlockSize, (const char*)dctxPtr->dict, (int)dctxPtr->dictSize);
kusano 7d535a
                if (decodedSize < 0) return (size_t)-LZ4F_ERROR_GENERIC;   /* decompression failed */
kusano 7d535a
                if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), dstPtr, decodedSize);
kusano 7d535a
                if (dctxPtr->frameInfo.contentSize) dctxPtr->frameRemainingSize -= decodedSize;
kusano 7d535a
kusano 7d535a
                /* dictionary management */
kusano 7d535a
                if (dctxPtr->frameInfo.blockMode==LZ4F_blockLinked)
kusano 7d535a
                    LZ4F_updateDict(dctxPtr, dstPtr, decodedSize, dstStart, 0);
kusano 7d535a
kusano 7d535a
                dstPtr += decodedSize;
kusano 7d535a
                dctxPtr->dStage = dstage_getCBlockSize;
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_decodeCBlock_intoTmp:
kusano 7d535a
            {
kusano 7d535a
                /* not enough place into dst : decode into tmpOut */
kusano 7d535a
                int (*decoder)(const char*, char*, int, int, const char*, int);
kusano 7d535a
                int decodedSize;
kusano 7d535a
kusano 7d535a
                if (dctxPtr->frameInfo.blockMode == LZ4F_blockLinked)
kusano 7d535a
                    decoder = LZ4_decompress_safe_usingDict;
kusano 7d535a
                else
kusano 7d535a
                    decoder = LZ4F_decompress_safe;
kusano 7d535a
kusano 7d535a
                /* ensure enough place for tmpOut */
kusano 7d535a
                if (dctxPtr->frameInfo.blockMode == LZ4F_blockLinked)
kusano 7d535a
                {
kusano 7d535a
                    if (dctxPtr->dict == dctxPtr->tmpOutBuffer)
kusano 7d535a
                    {
kusano 7d535a
                        if (dctxPtr->dictSize > 128 KB)
kusano 7d535a
                        {
kusano 7d535a
                            memcpy(dctxPtr->tmpOutBuffer, dctxPtr->dict + dctxPtr->dictSize - 64 KB, 64 KB);
kusano 7d535a
                            dctxPtr->dictSize = 64 KB;
kusano 7d535a
                        }
kusano 7d535a
                        dctxPtr->tmpOut = dctxPtr->tmpOutBuffer + dctxPtr->dictSize;
kusano 7d535a
                    }
kusano 7d535a
                    else   /* dict not within tmp */
kusano 7d535a
                    {
kusano 7d535a
                        size_t reservedDictSpace = dctxPtr->dictSize;
kusano 7d535a
                        if (reservedDictSpace > 64 KB) reservedDictSpace = 64 KB;
kusano 7d535a
                        dctxPtr->tmpOut = dctxPtr->tmpOutBuffer + reservedDictSpace;
kusano 7d535a
                    }
kusano 7d535a
                }
kusano 7d535a
kusano 7d535a
                /* Decode */
kusano 7d535a
                decodedSize = decoder((const char*)selectedIn, (char*)dctxPtr->tmpOut, (int)dctxPtr->tmpInTarget, (int)dctxPtr->maxBlockSize, (const char*)dctxPtr->dict, (int)dctxPtr->dictSize);
kusano 7d535a
                if (decodedSize < 0) return (size_t)-LZ4F_ERROR_decompressionFailed;   /* decompression failed */
kusano 7d535a
                if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), dctxPtr->tmpOut, decodedSize);
kusano 7d535a
                if (dctxPtr->frameInfo.contentSize) dctxPtr->frameRemainingSize -= decodedSize;
kusano 7d535a
                dctxPtr->tmpOutSize = decodedSize;
kusano 7d535a
                dctxPtr->tmpOutStart = 0;
kusano 7d535a
                dctxPtr->dStage = dstage_flushOut;
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_flushOut:  /* flush decoded data from tmpOut to dstBuffer */
kusano 7d535a
            {
kusano 7d535a
                size_t sizeToCopy = dctxPtr->tmpOutSize - dctxPtr->tmpOutStart;
kusano 7d535a
                if (sizeToCopy > (size_t)(dstEnd-dstPtr)) sizeToCopy = dstEnd-dstPtr;
kusano 7d535a
                memcpy(dstPtr, dctxPtr->tmpOut + dctxPtr->tmpOutStart, sizeToCopy);
kusano 7d535a
kusano 7d535a
                /* dictionary management */
kusano 7d535a
                if (dctxPtr->frameInfo.blockMode==LZ4F_blockLinked)
kusano 7d535a
                    LZ4F_updateDict(dctxPtr, dstPtr, sizeToCopy, dstStart, 1);
kusano 7d535a
kusano 7d535a
                dctxPtr->tmpOutStart += sizeToCopy;
kusano 7d535a
                dstPtr += sizeToCopy;
kusano 7d535a
kusano 7d535a
                /* end of flush ? */
kusano 7d535a
                if (dctxPtr->tmpOutStart == dctxPtr->tmpOutSize)
kusano 7d535a
                {
kusano 7d535a
                    dctxPtr->dStage = dstage_getCBlockSize;
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                nextSrcSizeHint = BHSize;
kusano 7d535a
                doAnotherStage = 0;   /* still some data to flush */
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_getSuffix:
kusano 7d535a
            {
kusano 7d535a
                size_t suffixSize = dctxPtr->frameInfo.contentChecksumFlag * 4;
kusano 7d535a
                if (dctxPtr->frameRemainingSize) return (size_t)-LZ4F_ERROR_frameSize_wrong;   /* incorrect frame size decoded */
kusano 7d535a
                if (suffixSize == 0)   /* frame completed */
kusano 7d535a
                {
kusano 7d535a
                    nextSrcSizeHint = 0;
kusano 7d535a
                    dctxPtr->dStage = dstage_getHeader;
kusano 7d535a
                    doAnotherStage = 0;
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                if ((srcEnd - srcPtr) < 4)   /* not enough size for entire CRC */
kusano 7d535a
                {
kusano 7d535a
                    dctxPtr->tmpInSize = 0;
kusano 7d535a
                    dctxPtr->dStage = dstage_storeSuffix;
kusano 7d535a
                }
kusano 7d535a
                else
kusano 7d535a
                {
kusano 7d535a
                    selectedIn = srcPtr;
kusano 7d535a
                    srcPtr += 4;
kusano 7d535a
                }
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
            if (dctxPtr->dStage == dstage_storeSuffix)
kusano 7d535a
        case dstage_storeSuffix:
kusano 7d535a
            {
kusano 7d535a
                size_t sizeToCopy = 4 - dctxPtr->tmpInSize;
kusano 7d535a
                if (sizeToCopy > (size_t)(srcEnd - srcPtr)) sizeToCopy = srcEnd - srcPtr;
kusano 7d535a
                memcpy(dctxPtr->tmpIn + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
kusano 7d535a
                srcPtr += sizeToCopy;
kusano 7d535a
                dctxPtr->tmpInSize += sizeToCopy;
kusano 7d535a
                if (dctxPtr->tmpInSize < 4)  /* not enough input to read complete suffix */
kusano 7d535a
                {
kusano 7d535a
                    nextSrcSizeHint = 4 - dctxPtr->tmpInSize;
kusano 7d535a
                    doAnotherStage=0;
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                selectedIn = dctxPtr->tmpIn;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        /* case dstage_checkSuffix: */   /* no direct call, to avoid scan-build warning */
kusano 7d535a
            {
kusano 7d535a
                U32 readCRC = LZ4F_readLE32(selectedIn);
kusano 7d535a
                U32 resultCRC = XXH32_digest(&(dctxPtr->xxh));
kusano 7d535a
                if (readCRC != resultCRC) return (size_t)-LZ4F_ERROR_contentChecksum_invalid;
kusano 7d535a
                nextSrcSizeHint = 0;
kusano 7d535a
                dctxPtr->dStage = dstage_getHeader;
kusano 7d535a
                doAnotherStage = 0;
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_getSFrameSize:
kusano 7d535a
            {
kusano 7d535a
                if ((srcEnd - srcPtr) >= 4)
kusano 7d535a
                {
kusano 7d535a
                    selectedIn = srcPtr;
kusano 7d535a
                    srcPtr += 4;
kusano 7d535a
                }
kusano 7d535a
                else
kusano 7d535a
                {
kusano 7d535a
                /* not enough input to read cBlockSize field */
kusano 7d535a
                    dctxPtr->tmpInSize = 4;
kusano 7d535a
                    dctxPtr->tmpInTarget = 8;
kusano 7d535a
                    dctxPtr->dStage = dstage_storeSFrameSize;
kusano 7d535a
                }
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
            if (dctxPtr->dStage == dstage_storeSFrameSize)
kusano 7d535a
        case dstage_storeSFrameSize:
kusano 7d535a
            {
kusano 7d535a
                size_t sizeToCopy = dctxPtr->tmpInTarget - dctxPtr->tmpInSize;
kusano 7d535a
                if (sizeToCopy > (size_t)(srcEnd - srcPtr)) sizeToCopy = srcEnd - srcPtr;
kusano 7d535a
                memcpy(dctxPtr->header + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
kusano 7d535a
                srcPtr += sizeToCopy;
kusano 7d535a
                dctxPtr->tmpInSize += sizeToCopy;
kusano 7d535a
                if (dctxPtr->tmpInSize < dctxPtr->tmpInTarget) /* not enough input to get full sBlockSize; wait for more */
kusano 7d535a
                {
kusano 7d535a
                    nextSrcSizeHint = dctxPtr->tmpInTarget - dctxPtr->tmpInSize;
kusano 7d535a
                    doAnotherStage = 0;
kusano 7d535a
                    break;
kusano 7d535a
                }
kusano 7d535a
                selectedIn = dctxPtr->header + 4;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        /* case dstage_decodeSFrameSize: */   /* no direct access */
kusano 7d535a
            {
kusano 7d535a
                size_t SFrameSize = LZ4F_readLE32(selectedIn);
kusano 7d535a
                dctxPtr->frameInfo.contentSize = SFrameSize;
kusano 7d535a
                dctxPtr->tmpInTarget = SFrameSize;
kusano 7d535a
                dctxPtr->dStage = dstage_skipSkippable;
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
kusano 7d535a
        case dstage_skipSkippable:
kusano 7d535a
            {
kusano 7d535a
                size_t skipSize = dctxPtr->tmpInTarget;
kusano 7d535a
                if (skipSize > (size_t)(srcEnd-srcPtr)) skipSize = srcEnd-srcPtr;
kusano 7d535a
                srcPtr += skipSize;
kusano 7d535a
                dctxPtr->tmpInTarget -= skipSize;
kusano 7d535a
                doAnotherStage = 0;
kusano 7d535a
                nextSrcSizeHint = dctxPtr->tmpInTarget;
kusano 7d535a
                if (nextSrcSizeHint) break;
kusano 7d535a
                dctxPtr->dStage = dstage_getHeader;
kusano 7d535a
                break;
kusano 7d535a
            }
kusano 7d535a
        }
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* preserve dictionary within tmp if necessary */
kusano 7d535a
    if ( (dctxPtr->frameInfo.blockMode==LZ4F_blockLinked)
kusano 7d535a
        &&(dctxPtr->dict != dctxPtr->tmpOutBuffer)
kusano 7d535a
        &&(!decompressOptionsPtr->stableDst)
kusano 7d535a
        &&((unsigned)(dctxPtr->dStage-1) < (unsigned)(dstage_getSuffix-1))
kusano 7d535a
        )
kusano 7d535a
    {
kusano 7d535a
        if (dctxPtr->dStage == dstage_flushOut)
kusano 7d535a
        {
kusano 7d535a
            size_t preserveSize = dctxPtr->tmpOut - dctxPtr->tmpOutBuffer;
kusano 7d535a
            size_t copySize = 64 KB - dctxPtr->tmpOutSize;
kusano 7d535a
            const BYTE* oldDictEnd = dctxPtr->dict + dctxPtr->dictSize - dctxPtr->tmpOutStart;
kusano 7d535a
            if (dctxPtr->tmpOutSize > 64 KB) copySize = 0;
kusano 7d535a
            if (copySize > preserveSize) copySize = preserveSize;
kusano 7d535a
kusano 7d535a
            memcpy(dctxPtr->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
kusano 7d535a
kusano 7d535a
            dctxPtr->dict = dctxPtr->tmpOutBuffer;
kusano 7d535a
            dctxPtr->dictSize = preserveSize + dctxPtr->tmpOutStart;
kusano 7d535a
        }
kusano 7d535a
        else
kusano 7d535a
        {
kusano 7d535a
            size_t newDictSize = dctxPtr->dictSize;
kusano 7d535a
            const BYTE* oldDictEnd = dctxPtr->dict + dctxPtr->dictSize;
kusano 7d535a
            if ((newDictSize) > 64 KB) newDictSize = 64 KB;
kusano 7d535a
kusano 7d535a
            memcpy(dctxPtr->tmpOutBuffer, oldDictEnd - newDictSize, newDictSize);
kusano 7d535a
kusano 7d535a
            dctxPtr->dict = dctxPtr->tmpOutBuffer;
kusano 7d535a
            dctxPtr->dictSize = newDictSize;
kusano 7d535a
            dctxPtr->tmpOut = dctxPtr->tmpOutBuffer + newDictSize;
kusano 7d535a
        }
kusano 7d535a
    }
kusano 7d535a
kusano 7d535a
    /* require function to be called again from position where it stopped */
kusano 7d535a
    if (srcPtr
kusano 7d535a
        dctxPtr->srcExpect = srcPtr;
kusano 7d535a
    else
kusano 7d535a
        dctxPtr->srcExpect = NULL;
kusano 7d535a
kusano 7d535a
    *srcSizePtr = (srcPtr - srcStart);
kusano 7d535a
    *dstSizePtr = (dstPtr - dstStart);
kusano 7d535a
    return nextSrcSizeHint;
kusano 7d535a
}