1 /*
2 Lizard auto-framing library
3 Copyright (C) 2011-2016, Yann Collet
4 Copyright (C) 2016-2017, Przemyslaw Skibinski
5 
6 BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
7 
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are
10 met:
11 
12 * Redistributions of source code must retain the above copyright
13 notice, this list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above
15 copyright notice, this list of conditions and the following disclaimer
16 in the documentation and/or other materials provided with the
17 distribution.
18 
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 You can contact the author at :
32 - Lizard source repository : https://github.com/inikep/lizard
33 */
34 
35 /* LizardF is a stand-alone API to create Lizard-compressed Frames
36 *  in full conformance with specification v1.5.0
37 *  All related operations, including memory management, are handled by the library.
38 * */
39 
40 
41 /*-************************************
42 *  Compiler Options
43 **************************************/
44 #ifdef _MSC_VER    /* Visual Studio */
45 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
46 #endif
47 
48 
49 
50 /*-************************************
51 *  Includes
52 **************************************/
53 #include "lizard_frame_static.h"
54 #include "lizard_compress.h"
55 #include "lizard_decompress.h"
56 #include "lizard_common.h"  /* LIZARD_DICT_SIZE */
57 #define XXH_STATIC_LINKING_ONLY
58 #include "xxhash/xxhash.h"
59 #include <stdio.h>
60 
61 
62 
63 /* unoptimized version; solves endianess & alignment issues */
LizardF_readLE32(const void * src)64 static U32 LizardF_readLE32 (const void* src)
65 {
66     const BYTE* const srcPtr = (const BYTE*)src;
67     U32 value32 = srcPtr[0];
68     value32 += (srcPtr[1]<<8);
69     value32 += (srcPtr[2]<<16);
70     value32 += ((U32)srcPtr[3])<<24;
71     return value32;
72 }
73 
LizardF_writeLE32(BYTE * dstPtr,U32 value32)74 static void LizardF_writeLE32 (BYTE* dstPtr, U32 value32)
75 {
76     dstPtr[0] = (BYTE)value32;
77     dstPtr[1] = (BYTE)(value32 >> 8);
78     dstPtr[2] = (BYTE)(value32 >> 16);
79     dstPtr[3] = (BYTE)(value32 >> 24);
80 }
81 
LizardF_readLE64(const BYTE * srcPtr)82 static U64 LizardF_readLE64 (const BYTE* srcPtr)
83 {
84     U64 value64 = srcPtr[0];
85     value64 += ((U64)srcPtr[1]<<8);
86     value64 += ((U64)srcPtr[2]<<16);
87     value64 += ((U64)srcPtr[3]<<24);
88     value64 += ((U64)srcPtr[4]<<32);
89     value64 += ((U64)srcPtr[5]<<40);
90     value64 += ((U64)srcPtr[6]<<48);
91     value64 += ((U64)srcPtr[7]<<56);
92     return value64;
93 }
94 
LizardF_writeLE64(BYTE * dstPtr,U64 value64)95 static void LizardF_writeLE64 (BYTE* dstPtr, U64 value64)
96 {
97     dstPtr[0] = (BYTE)value64;
98     dstPtr[1] = (BYTE)(value64 >> 8);
99     dstPtr[2] = (BYTE)(value64 >> 16);
100     dstPtr[3] = (BYTE)(value64 >> 24);
101     dstPtr[4] = (BYTE)(value64 >> 32);
102     dstPtr[5] = (BYTE)(value64 >> 40);
103     dstPtr[6] = (BYTE)(value64 >> 48);
104     dstPtr[7] = (BYTE)(value64 >> 56);
105 }
106 
107 
108 /*-************************************
109 *  Constants
110 **************************************/
111 #define _1BIT  0x01
112 #define _2BITS 0x03
113 #define _3BITS 0x07
114 #define _4BITS 0x0F
115 #define _8BITS 0xFF
116 
117 #define LIZARDF_MAGIC_SKIPPABLE_START  0x184D2A50U
118 #define LIZARDF_MAGICNUMBER            0x184D2206U
119 #define LIZARDF_BLOCKUNCOMPRESSED_FLAG 0x80000000U
120 #define LIZARDF_BLOCKSIZEID_DEFAULT LizardF_max128KB
121 
122 static const size_t minFHSize = 7;
123 static const size_t maxFHSize = 15;
124 static const size_t BHSize = 4;
125 
126 
127 /*-************************************
128 *  Structures and local types
129 **************************************/
130 typedef struct LizardF_cctx_s
131 {
132     LizardF_preferences_t prefs;
133     U32    version;
134     U32    cStage;
135     size_t maxBlockSize;
136     size_t maxBufferSize;
137     BYTE*  tmpBuff;
138     BYTE*  tmpIn;
139     size_t tmpInSize;
140     U64    totalInSize;
141     XXH32_state_t xxh;
142     Lizard_stream_t* lizardCtxPtr;
143     U32    lizardCtxLevel;     /* 0: unallocated;  1: Lizard_stream_t;  */
144 } LizardF_cctx_t;
145 
146 typedef struct LizardF_dctx_s
147 {
148     LizardF_frameInfo_t frameInfo;
149     U32    version;
150     U32    dStage;
151     U64    frameRemainingSize;
152     size_t maxBlockSize;
153     size_t maxBufferSize;
154     const BYTE* srcExpect;
155     BYTE*  tmpIn;
156     size_t tmpInSize;
157     size_t tmpInTarget;
158     BYTE*  tmpOutBuffer;
159     const BYTE*  dict;
160     size_t dictSize;
161     BYTE*  tmpOut;
162     size_t tmpOutSize;
163     size_t tmpOutStart;
164     XXH32_state_t xxh;
165     BYTE   header[16];
166 } LizardF_dctx_t;
167 
168 
169 /*-************************************
170 *  Error management
171 **************************************/
172 #define LIZARDF_GENERATE_STRING(STRING) #STRING,
173 static const char* LizardF_errorStrings[] = { LIZARDF_LIST_ERRORS(LIZARDF_GENERATE_STRING) };
174 
175 
LizardF_isError(LizardF_errorCode_t code)176 unsigned LizardF_isError(LizardF_errorCode_t code)
177 {
178     return (code > (LizardF_errorCode_t)(-LizardF_ERROR_maxCode));
179 }
180 
LizardF_getErrorName(LizardF_errorCode_t code)181 const char* LizardF_getErrorName(LizardF_errorCode_t code)
182 {
183     static const char* codeError = "Unspecified error code";
184     if (LizardF_isError(code)) return LizardF_errorStrings[-(int)(code)];
185     return codeError;
186 }
187 
188 
189 /*-************************************
190 *  Private functions
191 **************************************/
LizardF_getBlockSize(unsigned blockSizeID)192 static size_t LizardF_getBlockSize(unsigned blockSizeID)
193 {
194     static const size_t blockSizes[7] = { 128 KB, 256 KB, 1 MB, 4 MB, 16 MB, 64 MB, 256 MB };
195 
196     if (blockSizeID == 0) blockSizeID = LIZARDF_BLOCKSIZEID_DEFAULT;
197     blockSizeID -= 1;
198     if (blockSizeID >= 7) return (size_t)-LizardF_ERROR_maxBlockSize_invalid;
199 
200     return blockSizes[blockSizeID];
201 }
202 
LizardF_optimalBSID(const LizardF_blockSizeID_t requestedBSID,const size_t srcSize)203 static LizardF_blockSizeID_t LizardF_optimalBSID(const LizardF_blockSizeID_t requestedBSID, const size_t srcSize)
204 {
205     LizardF_blockSizeID_t proposedBSID = LizardF_max128KB;
206     size_t maxBlockSize;
207 
208     while (requestedBSID > proposedBSID)
209     {
210         maxBlockSize = LizardF_getBlockSize(proposedBSID);
211         if (srcSize <= maxBlockSize) {
212             return proposedBSID;
213         }
214         proposedBSID = (LizardF_blockSizeID_t)((int)proposedBSID + 1);
215     }
216     return requestedBSID;
217 }
218 
219 
LizardF_headerChecksum(const void * header,size_t length)220 static BYTE LizardF_headerChecksum (const void* header, size_t length)
221 {
222     U32 xxh = XXH32(header, length, 0);
223     return (BYTE)(xxh >> 8);
224 }
225 
226 
227 /*-************************************
228 *  Simple compression functions
229 **************************************/
230 
LizardF_compressFrameBound(size_t srcSize,const LizardF_preferences_t * preferencesPtr)231 size_t LizardF_compressFrameBound(size_t srcSize, const LizardF_preferences_t* preferencesPtr)
232 {
233     LizardF_preferences_t prefs;
234     size_t headerSize;
235     size_t streamSize;
236 
237     if (preferencesPtr!=NULL) prefs = *preferencesPtr;
238     else memset(&prefs, 0, sizeof(prefs));
239 
240     prefs.frameInfo.blockSizeID = LizardF_optimalBSID(prefs.frameInfo.blockSizeID, srcSize);
241     prefs.autoFlush = 1;
242 
243     headerSize = maxFHSize;      /* header size, including magic number and frame content size*/
244     streamSize = LizardF_compressBound(srcSize, &prefs);
245 
246     return headerSize + streamSize;
247 }
248 
249 
250 
251 /*! LizardF_compressFrame() :
252 * Compress an entire srcBuffer into a valid Lizard frame, as defined by specification v1.5.0, in a single step.
253 * The most important rule is that dstBuffer MUST be large enough (dstMaxSize) to ensure compression completion even in worst case.
254 * You can get the minimum value of dstMaxSize by using LizardF_compressFrameBound()
255 * If this condition is not respected, LizardF_compressFrame() will fail (result is an errorCode)
256 * The LizardF_preferences_t structure is optional : you can provide NULL as argument. All preferences will then be set to default.
257 * The result of the function is the number of bytes written into dstBuffer.
258 * The function outputs an error code if it fails (can be tested using LizardF_isError())
259 */
LizardF_compressFrame(void * dstBuffer,size_t dstMaxSize,const void * srcBuffer,size_t srcSize,const LizardF_preferences_t * preferencesPtr)260 size_t LizardF_compressFrame(void* dstBuffer, size_t dstMaxSize, const void* srcBuffer, size_t srcSize, const LizardF_preferences_t* preferencesPtr)
261 {
262     LizardF_cctx_t cctxI;
263     LizardF_preferences_t prefs;
264     LizardF_compressOptions_t options;
265     LizardF_errorCode_t errorCode;
266     BYTE* const dstStart = (BYTE*) dstBuffer;
267     BYTE* dstPtr = dstStart;
268     BYTE* const dstEnd = dstStart + dstMaxSize;
269 
270     memset(&cctxI, 0, sizeof(cctxI));   /* works because no allocation */
271     memset(&options, 0, sizeof(options));
272 
273     cctxI.version = LIZARDF_VERSION;
274     cctxI.maxBufferSize = 5 MB;   /* mess with real buffer size to prevent allocation; works because autoflush==1 & stableSrc==1 */
275 
276     if (preferencesPtr!=NULL)
277         prefs = *preferencesPtr;
278     else
279         memset(&prefs, 0, sizeof(prefs));
280     if (prefs.frameInfo.contentSize != 0)
281         prefs.frameInfo.contentSize = (U64)srcSize;   /* auto-correct content size if selected (!=0) */
282 
283     prefs.frameInfo.blockSizeID = LizardF_optimalBSID(prefs.frameInfo.blockSizeID, srcSize);
284     prefs.autoFlush = 1;
285     if (srcSize <= LizardF_getBlockSize(prefs.frameInfo.blockSizeID))
286         prefs.frameInfo.blockMode = LizardF_blockIndependent;   /* no need for linked blocks */
287 
288     options.stableSrc = 1;
289 
290     if (dstMaxSize < LizardF_compressFrameBound(srcSize, &prefs))
291         return (size_t)-LizardF_ERROR_dstMaxSize_tooSmall;
292 
293     errorCode = LizardF_compressBegin(&cctxI, dstBuffer, dstMaxSize, &prefs);  /* write header */
294     if (LizardF_isError(errorCode)) goto error;
295     dstPtr += errorCode;   /* header size */
296 
297     errorCode = LizardF_compressUpdate(&cctxI, dstPtr, dstEnd-dstPtr, srcBuffer, srcSize, &options);
298     if (LizardF_isError(errorCode)) goto error;
299     dstPtr += errorCode;
300 
301     errorCode = LizardF_compressEnd(&cctxI, dstPtr, dstEnd-dstPtr, &options);   /* flush last block, and generate suffix */
302     if (LizardF_isError(errorCode)) goto error;
303     dstPtr += errorCode;
304 
305     Lizard_freeStream(cctxI.lizardCtxPtr);
306     FREEMEM(cctxI.tmpBuff);
307     return (dstPtr - dstStart);
308 error:
309     Lizard_freeStream(cctxI.lizardCtxPtr);
310     FREEMEM(cctxI.tmpBuff);
311     return errorCode;
312 }
313 
314 
315 /*-*********************************
316 *  Advanced compression functions
317 ***********************************/
318 
319 /* LizardF_createCompressionContext() :
320 * The first thing to do is to create a compressionContext object, which will be used in all compression operations.
321 * This is achieved using LizardF_createCompressionContext(), which takes as argument a version and an LizardF_preferences_t structure.
322 * The version provided MUST be LIZARDF_VERSION. It is intended to track potential version differences between different binaries.
323 * The function will provide a pointer to an allocated LizardF_compressionContext_t object.
324 * If the result LizardF_errorCode_t is not OK_NoError, there was an error during context creation.
325 * Object can release its memory using LizardF_freeCompressionContext();
326 */
LizardF_createCompressionContext(LizardF_compressionContext_t * LizardF_compressionContextPtr,unsigned version)327 LizardF_errorCode_t LizardF_createCompressionContext(LizardF_compressionContext_t* LizardF_compressionContextPtr, unsigned version)
328 {
329     LizardF_cctx_t* cctxPtr;
330 
331     cctxPtr = (LizardF_cctx_t*)ALLOCATOR(1, sizeof(LizardF_cctx_t));
332     if (cctxPtr==NULL) return (LizardF_errorCode_t)(-LizardF_ERROR_allocation_failed);
333 
334     cctxPtr->version = version;
335     cctxPtr->cStage = 0;   /* Next stage : write header */
336 
337     *LizardF_compressionContextPtr = (LizardF_compressionContext_t)cctxPtr;
338 
339     return LizardF_OK_NoError;
340 }
341 
342 
LizardF_freeCompressionContext(LizardF_compressionContext_t LizardF_compressionContext)343 LizardF_errorCode_t LizardF_freeCompressionContext(LizardF_compressionContext_t LizardF_compressionContext)
344 {
345     LizardF_cctx_t* cctxPtr = (LizardF_cctx_t*)LizardF_compressionContext;
346 
347     if (cctxPtr != NULL) {  /* null pointers can be safely provided to this function, like free() */
348        Lizard_freeStream(cctxPtr->lizardCtxPtr);
349        FREEMEM(cctxPtr->tmpBuff);
350        FREEMEM(LizardF_compressionContext);
351     }
352 
353     return LizardF_OK_NoError;
354 }
355 
356 
357 /*! LizardF_compressBegin() :
358 * will write the frame header into dstBuffer.
359 * dstBuffer must be large enough to accommodate a header (dstMaxSize). Maximum header size is LizardF_MAXHEADERFRAME_SIZE bytes.
360 * The result of the function is the number of bytes written into dstBuffer for the header
361 * or an error code (can be tested using LizardF_isError())
362 */
LizardF_compressBegin(LizardF_compressionContext_t compressionContext,void * dstBuffer,size_t dstMaxSize,const LizardF_preferences_t * preferencesPtr)363 size_t LizardF_compressBegin(LizardF_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const LizardF_preferences_t* preferencesPtr)
364 {
365     LizardF_preferences_t prefNull;
366     LizardF_cctx_t* cctxPtr = (LizardF_cctx_t*)compressionContext;
367     BYTE* const dstStart = (BYTE*)dstBuffer;
368     BYTE* dstPtr = dstStart;
369     BYTE* headerStart;
370     size_t requiredBuffSize;
371 
372     if (dstMaxSize < maxFHSize) return (size_t)-LizardF_ERROR_dstMaxSize_tooSmall;
373     if (cctxPtr->cStage != 0) return (size_t)-LizardF_ERROR_GENERIC;
374     memset(&prefNull, 0, sizeof(prefNull));
375     if (preferencesPtr == NULL) preferencesPtr = &prefNull;
376     cctxPtr->prefs = *preferencesPtr;
377 
378     /* ctx Management */
379     if (cctxPtr->lizardCtxLevel == 0) {
380         cctxPtr->lizardCtxPtr = Lizard_createStream(cctxPtr->prefs.compressionLevel);
381         cctxPtr->lizardCtxLevel = 1;
382     }
383 
384     /* Buffer Management */
385     if (cctxPtr->prefs.frameInfo.blockSizeID == 0) cctxPtr->prefs.frameInfo.blockSizeID = LIZARDF_BLOCKSIZEID_DEFAULT;
386     cctxPtr->maxBlockSize = LizardF_getBlockSize(cctxPtr->prefs.frameInfo.blockSizeID);
387     requiredBuffSize = cctxPtr->maxBlockSize + ((cctxPtr->prefs.frameInfo.blockMode == LizardF_blockLinked) * 2 * LIZARD_DICT_SIZE);
388 
389     if (preferencesPtr->autoFlush)
390         requiredBuffSize = (cctxPtr->prefs.frameInfo.blockMode == LizardF_blockLinked) * LIZARD_DICT_SIZE;   /* just needs dict */
391 
392     if (cctxPtr->maxBufferSize < requiredBuffSize) {
393         cctxPtr->maxBufferSize = requiredBuffSize;
394         FREEMEM(cctxPtr->tmpBuff);
395         cctxPtr->tmpBuff = (BYTE*)ALLOCATOR(1, requiredBuffSize);
396         if (cctxPtr->tmpBuff == NULL) { printf("ERROR in LizardF_compressBegin: Cannot allocate %d MB\n", (int)(requiredBuffSize>>20)); return (size_t)-LizardF_ERROR_allocation_failed; }
397     }
398     cctxPtr->tmpIn = cctxPtr->tmpBuff;
399     cctxPtr->tmpInSize = 0;
400     XXH32_reset(&(cctxPtr->xxh), 0);
401     cctxPtr->lizardCtxPtr = Lizard_resetStream((Lizard_stream_t*)(cctxPtr->lizardCtxPtr), cctxPtr->prefs.compressionLevel);
402     if (!cctxPtr->lizardCtxPtr) return (size_t)-LizardF_ERROR_allocation_failed;
403 
404     /* Magic Number */
405     LizardF_writeLE32(dstPtr, LIZARDF_MAGICNUMBER);
406     dstPtr += 4;
407     headerStart = dstPtr;
408 
409     /* FLG Byte */
410     *dstPtr++ = (BYTE)(((1 & _2BITS) << 6)    /* Version('01') */
411         + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5)    /* Block mode */
412         + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2)   /* Frame checksum */
413         + ((cctxPtr->prefs.frameInfo.contentSize > 0) << 3));   /* Frame content size */
414     /* BD Byte */
415     *dstPtr++ = (BYTE)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4);
416     /* Optional Frame content size field */
417     if (cctxPtr->prefs.frameInfo.contentSize) {
418         LizardF_writeLE64(dstPtr, cctxPtr->prefs.frameInfo.contentSize);
419         dstPtr += 8;
420         cctxPtr->totalInSize = 0;
421     }
422     /* CRC Byte */
423     *dstPtr = LizardF_headerChecksum(headerStart, dstPtr - headerStart);
424     dstPtr++;
425 
426     cctxPtr->cStage = 1;   /* header written, now request input data block */
427 
428     return (dstPtr - dstStart);
429 }
430 
431 
432 /* LizardF_compressBound() : gives the size of Dst buffer given a srcSize to handle worst case situations.
433 *                        The LizardF_frameInfo_t structure is optional :
434 *                        you can provide NULL as argument, preferences will then be set to cover worst case situations.
435 * */
LizardF_compressBound(size_t srcSize,const LizardF_preferences_t * preferencesPtr)436 size_t LizardF_compressBound(size_t srcSize, const LizardF_preferences_t* preferencesPtr)
437 {
438     LizardF_preferences_t prefsNull;
439     memset(&prefsNull, 0, sizeof(prefsNull));
440     prefsNull.frameInfo.contentChecksumFlag = LizardF_contentChecksumEnabled;   /* worst case */
441     {   const LizardF_preferences_t* prefsPtr = (preferencesPtr==NULL) ? &prefsNull : preferencesPtr;
442         LizardF_blockSizeID_t bid = prefsPtr->frameInfo.blockSizeID;
443         size_t blockSize = LizardF_getBlockSize(bid);
444         unsigned nbBlocks = (unsigned)(srcSize / blockSize) + 1;
445         size_t lastBlockSize = prefsPtr->autoFlush ? srcSize % blockSize : blockSize;
446         size_t blockInfo = 4;   /* default, without block CRC option */
447         size_t frameEnd = 4 + (prefsPtr->frameInfo.contentChecksumFlag*4);
448 
449         return (blockInfo * nbBlocks) + (blockSize * (nbBlocks-1)) + lastBlockSize + frameEnd;;
450     }
451 }
452 
453 
454 typedef int (*compressFunc_t)(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level);
455 
LizardF_compressBlock(void * dst,const void * src,size_t srcSize,compressFunc_t compress,void * lizardctx,int level)456 static size_t LizardF_compressBlock(void* dst, const void* src, size_t srcSize, compressFunc_t compress, void* lizardctx, int level)
457 {
458     /* compress one block */
459     BYTE* cSizePtr = (BYTE*)dst;
460     U32 cSize;
461     cSize = (U32)compress(lizardctx, (const char*)src, (char*)(cSizePtr+4), (int)(srcSize), (int)(srcSize-1), level);
462     LizardF_writeLE32(cSizePtr, cSize);
463     if (cSize == 0) {  /* compression failed */
464         cSize = (U32)srcSize;
465         LizardF_writeLE32(cSizePtr, cSize + LIZARDF_BLOCKUNCOMPRESSED_FLAG);
466         memcpy(cSizePtr+4, src, srcSize);
467     }
468     return cSize + 4;
469 }
470 
471 
472 
LizardF_localLizard_compress_continue(void * ctx,const char * src,char * dst,int srcSize,int dstSize,int level)473 static int LizardF_localLizard_compress_continue(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level)
474 {
475     (void)level;
476     return Lizard_compress_continue((Lizard_stream_t*)ctx, src, dst, srcSize, dstSize);
477 }
478 
LizardF_selectCompression(LizardF_blockMode_t blockMode)479 static compressFunc_t LizardF_selectCompression(LizardF_blockMode_t blockMode)
480 {
481     if (blockMode == LizardF_blockIndependent) return Lizard_compress_extState;
482     return LizardF_localLizard_compress_continue;
483 }
484 
LizardF_localSaveDict(LizardF_cctx_t * cctxPtr)485 static int LizardF_localSaveDict(LizardF_cctx_t* cctxPtr)
486 {
487     return Lizard_saveDict ((Lizard_stream_t*)(cctxPtr->lizardCtxPtr), (char*)(cctxPtr->tmpBuff), LIZARD_DICT_SIZE);
488 }
489 
490 typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LizardF_lastBlockStatus;
491 
492 /*! LizardF_compressUpdate() :
493 * LizardF_compressUpdate() can be called repetitively to compress as much data as necessary.
494 * The most important rule is that dstBuffer MUST be large enough (dstMaxSize) to ensure compression completion even in worst case.
495 * If this condition is not respected, LizardF_compress() will fail (result is an errorCode)
496 * You can get the minimum value of dstMaxSize by using LizardF_compressBound()
497 * The LizardF_compressOptions_t structure is optional : you can provide NULL as argument.
498 * The result of the function is the number of bytes written into dstBuffer : it can be zero, meaning input data was just buffered.
499 * The function outputs an error code if it fails (can be tested using LizardF_isError())
500 */
LizardF_compressUpdate(LizardF_compressionContext_t compressionContext,void * dstBuffer,size_t dstMaxSize,const void * srcBuffer,size_t srcSize,const LizardF_compressOptions_t * compressOptionsPtr)501 size_t LizardF_compressUpdate(LizardF_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const void* srcBuffer, size_t srcSize, const LizardF_compressOptions_t* compressOptionsPtr)
502 {
503     LizardF_compressOptions_t cOptionsNull;
504     LizardF_cctx_t* cctxPtr = (LizardF_cctx_t*)compressionContext;
505     size_t blockSize = cctxPtr->maxBlockSize;
506     const BYTE* srcPtr = (const BYTE*)srcBuffer;
507     const BYTE* const srcEnd = srcPtr + srcSize;
508     BYTE* const dstStart = (BYTE*)dstBuffer;
509     BYTE* dstPtr = dstStart;
510     LizardF_lastBlockStatus lastBlockCompressed = notDone;
511     compressFunc_t compress;
512 
513 
514     if (cctxPtr->cStage != 1) return (size_t)-LizardF_ERROR_GENERIC;
515     if (dstMaxSize < LizardF_compressBound(srcSize, &(cctxPtr->prefs))) return (size_t)-LizardF_ERROR_dstMaxSize_tooSmall;
516     memset(&cOptionsNull, 0, sizeof(cOptionsNull));
517     if (compressOptionsPtr == NULL) compressOptionsPtr = &cOptionsNull;
518 
519     /* select compression function */
520     compress = LizardF_selectCompression(cctxPtr->prefs.frameInfo.blockMode);
521 
522     /* complete tmp buffer */
523     if (cctxPtr->tmpInSize > 0) {   /* some data already within tmp buffer */
524         size_t sizeToCopy = blockSize - cctxPtr->tmpInSize;
525         if (sizeToCopy > srcSize) {
526             /* add src to tmpIn buffer */
527             memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize);
528             srcPtr = srcEnd;
529             cctxPtr->tmpInSize += srcSize;
530             /* still needs some CRC */
531         } else {
532             /* complete tmpIn block and then compress it */
533             lastBlockCompressed = fromTmpBuffer;
534             memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy);
535             srcPtr += sizeToCopy;
536 
537             dstPtr += LizardF_compressBlock(dstPtr, cctxPtr->tmpIn, blockSize, compress, cctxPtr->lizardCtxPtr, cctxPtr->prefs.compressionLevel);
538 
539             if (cctxPtr->prefs.frameInfo.blockMode==LizardF_blockLinked) cctxPtr->tmpIn += blockSize;
540             cctxPtr->tmpInSize = 0;
541         }
542     }
543 
544     while ((size_t)(srcEnd - srcPtr) >= blockSize) {
545         /* compress full block */
546         lastBlockCompressed = fromSrcBuffer;
547         dstPtr += LizardF_compressBlock(dstPtr, srcPtr, blockSize, compress, cctxPtr->lizardCtxPtr, cctxPtr->prefs.compressionLevel);
548         srcPtr += blockSize;
549     }
550 
551     if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd)) {
552         /* compress remaining input < blockSize */
553         lastBlockCompressed = fromSrcBuffer;
554         dstPtr += LizardF_compressBlock(dstPtr, srcPtr, srcEnd - srcPtr, compress, cctxPtr->lizardCtxPtr, cctxPtr->prefs.compressionLevel);
555         srcPtr  = srcEnd;
556     }
557 
558     /* preserve dictionary if necessary */
559     if ((cctxPtr->prefs.frameInfo.blockMode==LizardF_blockLinked) && (lastBlockCompressed==fromSrcBuffer)) {
560         if (compressOptionsPtr->stableSrc) {
561             cctxPtr->tmpIn = cctxPtr->tmpBuff;
562         } else {
563             int realDictSize = LizardF_localSaveDict(cctxPtr);
564             if (realDictSize==0) return (size_t)-LizardF_ERROR_GENERIC;
565             cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
566         }
567     }
568 
569     /* keep tmpIn within limits */
570     if ((cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)   /* necessarily LizardF_blockLinked && lastBlockCompressed==fromTmpBuffer */
571         && !(cctxPtr->prefs.autoFlush))
572     {
573         int realDictSize = LizardF_localSaveDict(cctxPtr);
574         cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
575     }
576 
577     /* some input data left, necessarily < blockSize */
578     if (srcPtr < srcEnd) {
579         /* fill tmp buffer */
580         size_t sizeToCopy = srcEnd - srcPtr;
581         memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy);
582         cctxPtr->tmpInSize = sizeToCopy;
583     }
584 
585     if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LizardF_contentChecksumEnabled)
586         XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize);
587 
588     cctxPtr->totalInSize += srcSize;
589     return dstPtr - dstStart;
590 }
591 
592 
593 /*! LizardF_flush() :
594 * Should you need to create compressed data immediately, without waiting for a block to be filled,
595 * you can call Lizard_flush(), which will immediately compress any remaining data stored within compressionContext.
596 * The result of the function is the number of bytes written into dstBuffer
597 * (it can be zero, this means there was no data left within compressionContext)
598 * The function outputs an error code if it fails (can be tested using LizardF_isError())
599 * The LizardF_compressOptions_t structure is optional : you can provide NULL as argument.
600 */
LizardF_flush(LizardF_compressionContext_t compressionContext,void * dstBuffer,size_t dstMaxSize,const LizardF_compressOptions_t * compressOptionsPtr)601 size_t LizardF_flush(LizardF_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const LizardF_compressOptions_t* compressOptionsPtr)
602 {
603     LizardF_cctx_t* cctxPtr = (LizardF_cctx_t*)compressionContext;
604     BYTE* const dstStart = (BYTE*)dstBuffer;
605     BYTE* dstPtr = dstStart;
606     compressFunc_t compress;
607 
608 
609     if (cctxPtr->tmpInSize == 0) return 0;   /* nothing to flush */
610     if (cctxPtr->cStage != 1) return (size_t)-LizardF_ERROR_GENERIC;
611     if (dstMaxSize < (cctxPtr->tmpInSize + 8)) return (size_t)-LizardF_ERROR_dstMaxSize_tooSmall;   /* +8 : block header(4) + block checksum(4) */
612     (void)compressOptionsPtr;   /* not yet useful */
613 
614     /* select compression function */
615     compress = LizardF_selectCompression(cctxPtr->prefs.frameInfo.blockMode);
616 
617     /* compress tmp buffer */
618     dstPtr += LizardF_compressBlock(dstPtr, cctxPtr->tmpIn, cctxPtr->tmpInSize, compress, cctxPtr->lizardCtxPtr, cctxPtr->prefs.compressionLevel);
619     if (cctxPtr->prefs.frameInfo.blockMode==LizardF_blockLinked) cctxPtr->tmpIn += cctxPtr->tmpInSize;
620     cctxPtr->tmpInSize = 0;
621 
622     /* keep tmpIn within limits */
623     if ((cctxPtr->tmpIn + cctxPtr->maxBlockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)) {  /* necessarily LizardF_blockLinked */
624         int realDictSize = LizardF_localSaveDict(cctxPtr);
625         cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
626     }
627 
628     return dstPtr - dstStart;
629 }
630 
631 
632 /*! LizardF_compressEnd() :
633 * When you want to properly finish the compressed frame, just call LizardF_compressEnd().
634 * It will flush whatever data remained within compressionContext (like Lizard_flush())
635 * but also properly finalize the frame, with an endMark and a checksum.
636 * The result of the function is the number of bytes written into dstBuffer (necessarily >= 4 (endMark size))
637 * The function outputs an error code if it fails (can be tested using LizardF_isError())
638 * The LizardF_compressOptions_t structure is optional : you can provide NULL as argument.
639 * compressionContext can then be used again, starting with LizardF_compressBegin(). The preferences will remain the same.
640 */
LizardF_compressEnd(LizardF_compressionContext_t compressionContext,void * dstBuffer,size_t dstMaxSize,const LizardF_compressOptions_t * compressOptionsPtr)641 size_t LizardF_compressEnd(LizardF_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const LizardF_compressOptions_t* compressOptionsPtr)
642 {
643     LizardF_cctx_t* cctxPtr = (LizardF_cctx_t*)compressionContext;
644     BYTE* const dstStart = (BYTE*)dstBuffer;
645     BYTE* dstPtr = dstStart;
646     size_t errorCode;
647 
648     errorCode = LizardF_flush(compressionContext, dstBuffer, dstMaxSize, compressOptionsPtr);
649     if (LizardF_isError(errorCode)) return errorCode;
650     dstPtr += errorCode;
651 
652     LizardF_writeLE32(dstPtr, 0);
653     dstPtr+=4;   /* endMark */
654 
655     if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LizardF_contentChecksumEnabled) {
656         U32 xxh = XXH32_digest(&(cctxPtr->xxh));
657         LizardF_writeLE32(dstPtr, xxh);
658         dstPtr+=4;   /* content Checksum */
659     }
660 
661     cctxPtr->cStage = 0;   /* state is now re-usable (with identical preferences) */
662     cctxPtr->maxBufferSize = 0;  /* reuse HC context */
663 
664     if (cctxPtr->prefs.frameInfo.contentSize) {
665         if (cctxPtr->prefs.frameInfo.contentSize != cctxPtr->totalInSize)
666             return (size_t)-LizardF_ERROR_frameSize_wrong;
667     }
668 
669     return dstPtr - dstStart;
670 }
671 
672 
673 /*-***************************************************
674 *   Frame Decompression
675 *****************************************************/
676 
677 /* Resource management */
678 
679 /*! LizardF_createDecompressionContext() :
680 *   Create a decompressionContext object, which will track all decompression operations.
681 *   Provides a pointer to a fully allocated and initialized LizardF_decompressionContext object.
682 *   Object can later be released using LizardF_freeDecompressionContext().
683 *   @return : if != 0, there was an error during context creation.
684 */
LizardF_createDecompressionContext(LizardF_decompressionContext_t * LizardF_decompressionContextPtr,unsigned versionNumber)685 LizardF_errorCode_t LizardF_createDecompressionContext(LizardF_decompressionContext_t* LizardF_decompressionContextPtr, unsigned versionNumber)
686 {
687     LizardF_dctx_t* const dctxPtr = (LizardF_dctx_t*)ALLOCATOR(1, sizeof(LizardF_dctx_t));
688     if (dctxPtr==NULL) return (LizardF_errorCode_t)-LizardF_ERROR_GENERIC;
689 
690     dctxPtr->version = versionNumber;
691     *LizardF_decompressionContextPtr = (LizardF_decompressionContext_t)dctxPtr;
692     return LizardF_OK_NoError;
693 }
694 
LizardF_freeDecompressionContext(LizardF_decompressionContext_t LizardF_decompressionContext)695 LizardF_errorCode_t LizardF_freeDecompressionContext(LizardF_decompressionContext_t LizardF_decompressionContext)
696 {
697     LizardF_errorCode_t result = LizardF_OK_NoError;
698     LizardF_dctx_t* const dctxPtr = (LizardF_dctx_t*)LizardF_decompressionContext;
699     if (dctxPtr != NULL) {   /* can accept NULL input, like free() */
700       result = (LizardF_errorCode_t)dctxPtr->dStage;
701       FREEMEM(dctxPtr->tmpIn);
702       FREEMEM(dctxPtr->tmpOutBuffer);
703       FREEMEM(dctxPtr);
704     }
705     return result;
706 }
707 
708 
709 /* ******************************************************************** */
710 /* ********************* Decompression ******************************** */
711 /* ******************************************************************** */
712 
713 typedef enum { dstage_getHeader=0, dstage_storeHeader,
714     dstage_getCBlockSize, dstage_storeCBlockSize,
715     dstage_copyDirect,
716     dstage_getCBlock, dstage_storeCBlock,
717     dstage_decodeCBlock, dstage_decodeCBlock_intoDst,
718     dstage_decodeCBlock_intoTmp, dstage_flushOut,
719     dstage_getSuffix, dstage_storeSuffix,
720     dstage_getSFrameSize, dstage_storeSFrameSize,
721     dstage_skipSkippable
722 } dStage_t;
723 
724 
725 /*! LizardF_headerSize() :
726 *   @return : size of frame header
727 *             or an error code, which can be tested using LizardF_isError()
728 */
LizardF_headerSize(const void * src,size_t srcSize)729 static size_t LizardF_headerSize(const void* src, size_t srcSize)
730 {
731     /* minimal srcSize to determine header size */
732     if (srcSize < 5) return (size_t)-LizardF_ERROR_frameHeader_incomplete;
733 
734     /* special case : skippable frames */
735     if ((LizardF_readLE32(src) & 0xFFFFFFF0U) == LIZARDF_MAGIC_SKIPPABLE_START) return 8;
736 
737     /* control magic number */
738     if (LizardF_readLE32(src) != LIZARDF_MAGICNUMBER) return (size_t)-LizardF_ERROR_frameType_unknown;
739 
740     /* Frame Header Size */
741     {   BYTE const FLG = ((const BYTE*)src)[4];
742         U32 const contentSizeFlag = (FLG>>3) & _1BIT;
743         return contentSizeFlag ? maxFHSize : minFHSize;
744     }
745 }
746 
747 
748 /*! LizardF_decodeHeader() :
749    input   : `srcVoidPtr` points at the **beginning of the frame**
750    output  : set internal values of dctx, such as
751              dctxPtr->frameInfo and dctxPtr->dStage.
752              Also allocates internal buffers.
753    @return : nb Bytes read from srcVoidPtr (necessarily <= srcSize)
754              or an error code (testable with LizardF_isError())
755 */
LizardF_decodeHeader(LizardF_dctx_t * dctxPtr,const void * srcVoidPtr,size_t srcSize)756 static size_t LizardF_decodeHeader(LizardF_dctx_t* dctxPtr, const void* srcVoidPtr, size_t srcSize)
757 {
758     BYTE FLG, BD, HC;
759     unsigned version, blockMode, blockChecksumFlag, contentSizeFlag, contentChecksumFlag, blockSizeID;
760     size_t bufferNeeded, currentBlockSize;
761     size_t frameHeaderSize;
762     const BYTE* srcPtr = (const BYTE*)srcVoidPtr;
763 
764     /* need to decode header to get frameInfo */
765     if (srcSize < minFHSize) return (size_t)-LizardF_ERROR_frameHeader_incomplete;   /* minimal frame header size */
766     memset(&(dctxPtr->frameInfo), 0, sizeof(dctxPtr->frameInfo));
767 
768     /* special case : skippable frames */
769     if ((LizardF_readLE32(srcPtr) & 0xFFFFFFF0U) == LIZARDF_MAGIC_SKIPPABLE_START) {
770         dctxPtr->frameInfo.frameType = LizardF_skippableFrame;
771         if (srcVoidPtr == (void*)(dctxPtr->header)) {
772             dctxPtr->tmpInSize = srcSize;
773             dctxPtr->tmpInTarget = 8;
774             dctxPtr->dStage = dstage_storeSFrameSize;
775             return srcSize;
776         } else {
777             dctxPtr->dStage = dstage_getSFrameSize;
778             return 4;
779         }
780     }
781 
782     /* control magic number */
783     if (LizardF_readLE32(srcPtr) != LIZARDF_MAGICNUMBER) return (size_t)-LizardF_ERROR_frameType_unknown;
784     dctxPtr->frameInfo.frameType = LizardF_frame;
785 
786     /* Flags */
787     FLG = srcPtr[4];
788     version = (FLG>>6) & _2BITS;
789     blockMode = (FLG>>5) & _1BIT;
790     blockChecksumFlag = (FLG>>4) & _1BIT;
791     contentSizeFlag = (FLG>>3) & _1BIT;
792     contentChecksumFlag = (FLG>>2) & _1BIT;
793 
794     /* Frame Header Size */
795     frameHeaderSize = contentSizeFlag ? maxFHSize : minFHSize;
796 
797     if (srcSize < frameHeaderSize) {
798         /* not enough input to fully decode frame header */
799         if (srcPtr != dctxPtr->header)
800             memcpy(dctxPtr->header, srcPtr, srcSize);
801         dctxPtr->tmpInSize = srcSize;
802         dctxPtr->tmpInTarget = frameHeaderSize;
803         dctxPtr->dStage = dstage_storeHeader;
804         return srcSize;
805     }
806 
807     BD = srcPtr[5];
808     blockSizeID = (BD>>4) & _3BITS;
809 
810     /* validate */
811     if (version != 1) return (size_t)-LizardF_ERROR_headerVersion_wrong;        /* Version Number, only supported value */
812     if (blockChecksumFlag != 0) return (size_t)-LizardF_ERROR_blockChecksum_unsupported; /* Not supported for the time being */
813     if (((FLG>>0)&_2BITS) != 0) return (size_t)-LizardF_ERROR_reservedFlag_set; /* Reserved bits */
814     if (((BD>>7)&_1BIT) != 0) return (size_t)-LizardF_ERROR_reservedFlag_set;   /* Reserved bit */
815     if (blockSizeID < 1) return (size_t)-LizardF_ERROR_maxBlockSize_invalid;    /* 1-7 only supported values for the time being */
816     if (((BD>>0)&_4BITS) != 0) return (size_t)-LizardF_ERROR_reservedFlag_set;  /* Reserved bits */
817 
818     /* check */
819     HC = LizardF_headerChecksum(srcPtr+4, frameHeaderSize-5);
820     if (HC != srcPtr[frameHeaderSize-1]) return (size_t)-LizardF_ERROR_headerChecksum_invalid;   /* Bad header checksum error */
821 
822     /* save */
823     dctxPtr->frameInfo.blockMode = (LizardF_blockMode_t)blockMode;
824     dctxPtr->frameInfo.contentChecksumFlag = (LizardF_contentChecksum_t)contentChecksumFlag;
825     dctxPtr->frameInfo.blockSizeID = (LizardF_blockSizeID_t)blockSizeID;
826     currentBlockSize = dctxPtr->maxBlockSize;
827     dctxPtr->maxBlockSize = LizardF_getBlockSize(blockSizeID);
828     if (contentSizeFlag)
829         dctxPtr->frameRemainingSize = dctxPtr->frameInfo.contentSize = LizardF_readLE64(srcPtr+6);
830 
831     /* init */
832     if (contentChecksumFlag) XXH32_reset(&(dctxPtr->xxh), 0);
833 
834     /* alloc */
835     bufferNeeded = dctxPtr->maxBlockSize + ((dctxPtr->frameInfo.blockMode==LizardF_blockLinked) * 2 * LIZARD_DICT_SIZE);
836     if (bufferNeeded > dctxPtr->maxBufferSize || dctxPtr->maxBlockSize > currentBlockSize) {   /* tmp buffers too small */
837         FREEMEM(dctxPtr->tmpIn);
838         FREEMEM(dctxPtr->tmpOutBuffer);
839         dctxPtr->maxBufferSize = 0;
840         dctxPtr->tmpIn = (BYTE*)ALLOCATOR(1, dctxPtr->maxBlockSize);
841         if (dctxPtr->tmpIn == NULL) return (size_t)-LizardF_ERROR_GENERIC;
842         dctxPtr->tmpOutBuffer= (BYTE*)ALLOCATOR(1, bufferNeeded);
843         if (dctxPtr->tmpOutBuffer== NULL) return (size_t)-LizardF_ERROR_GENERIC;
844         dctxPtr->maxBufferSize = bufferNeeded;
845     }
846     dctxPtr->tmpInSize = 0;
847     dctxPtr->tmpInTarget = 0;
848     dctxPtr->dict = dctxPtr->tmpOutBuffer;
849     dctxPtr->dictSize = 0;
850     dctxPtr->tmpOut = dctxPtr->tmpOutBuffer;
851     dctxPtr->tmpOutStart = 0;
852     dctxPtr->tmpOutSize = 0;
853 
854     dctxPtr->dStage = dstage_getCBlockSize;
855 
856     return frameHeaderSize;
857 }
858 
859 
860 /*! LizardF_getFrameInfo() :
861 *   Decodes frame header information, such as blockSize.
862 *   It is optional : you could start by calling directly LizardF_decompress() instead.
863 *   The objective is to extract header information without starting decompression, typically for allocation purposes.
864 *   LizardF_getFrameInfo() can also be used *after* starting decompression, on a valid LizardF_decompressionContext_t.
865 *   The number of bytes read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).
866 *   You are expected to resume decompression from where it stopped (srcBuffer + *srcSizePtr)
867 *   @return : hint of the better `srcSize` to use for next call to LizardF_decompress,
868 *             or an error code which can be tested using LizardF_isError().
869 */
LizardF_getFrameInfo(LizardF_decompressionContext_t dCtx,LizardF_frameInfo_t * frameInfoPtr,const void * srcBuffer,size_t * srcSizePtr)870 LizardF_errorCode_t LizardF_getFrameInfo(LizardF_decompressionContext_t dCtx, LizardF_frameInfo_t* frameInfoPtr,
871                                    const void* srcBuffer, size_t* srcSizePtr)
872 {
873     LizardF_dctx_t* dctxPtr = (LizardF_dctx_t*)dCtx;
874 
875     if (dctxPtr->dStage > dstage_storeHeader) {  /* note : requires dstage_* header related to be at beginning of enum */
876         /* frameInfo already decoded */
877         size_t o=0, i=0;
878         *srcSizePtr = 0;
879         *frameInfoPtr = dctxPtr->frameInfo;
880         return LizardF_decompress(dCtx, NULL, &o, NULL, &i, NULL);  /* returns : recommended nb of bytes for LizardF_decompress() */
881     } else {
882         size_t nextSrcSize, o=0;
883         size_t const hSize = LizardF_headerSize(srcBuffer, *srcSizePtr);
884         if (LizardF_isError(hSize)) { *srcSizePtr=0; return hSize; }
885         if (*srcSizePtr < hSize) { *srcSizePtr=0; return (size_t)-LizardF_ERROR_frameHeader_incomplete; }
886 
887         *srcSizePtr = hSize;
888         nextSrcSize = LizardF_decompress(dCtx, NULL, &o, srcBuffer, srcSizePtr, NULL);
889         if (dctxPtr->dStage <= dstage_storeHeader) return (size_t)-LizardF_ERROR_frameHeader_incomplete; /* should not happen, already checked */
890         *frameInfoPtr = dctxPtr->frameInfo;
891         return nextSrcSize;
892     }
893 }
894 
895 
896 /* trivial redirector, for common prototype */
LizardF_decompress_safe(const char * source,char * dest,int compressedSize,int maxDecompressedSize,const char * dictStart,int dictSize)897 static int LizardF_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize)
898 {
899     (void)dictStart; (void)dictSize;
900     return Lizard_decompress_safe (source, dest, compressedSize, maxDecompressedSize);
901 }
902 
903 
LizardF_updateDict(LizardF_dctx_t * dctxPtr,const BYTE * dstPtr,size_t dstSize,const BYTE * dstPtr0,unsigned withinTmp)904 static void LizardF_updateDict(LizardF_dctx_t* dctxPtr, const BYTE* dstPtr, size_t dstSize, const BYTE* dstPtr0, unsigned withinTmp)
905 {
906     if (dctxPtr->dictSize==0)
907         dctxPtr->dict = (const BYTE*)dstPtr;   /* priority to dictionary continuity */
908 
909     if (dctxPtr->dict + dctxPtr->dictSize == dstPtr) {  /* dictionary continuity */
910         dctxPtr->dictSize += dstSize;
911         return;
912     }
913 
914     if (dstPtr - dstPtr0 + dstSize >= LIZARD_DICT_SIZE) {  /* dstBuffer large enough to become dictionary */
915         dctxPtr->dict = (const BYTE*)dstPtr0;
916         dctxPtr->dictSize = dstPtr - dstPtr0 + dstSize;
917         return;
918     }
919 
920     if ((withinTmp) && (dctxPtr->dict == dctxPtr->tmpOutBuffer)) {
921         /* assumption : dctxPtr->dict + dctxPtr->dictSize == dctxPtr->tmpOut + dctxPtr->tmpOutStart */
922         dctxPtr->dictSize += dstSize;
923         return;
924     }
925 
926     if (withinTmp) { /* copy relevant dict portion in front of tmpOut within tmpOutBuffer */
927         size_t preserveSize = dctxPtr->tmpOut - dctxPtr->tmpOutBuffer;
928         size_t copySize = LIZARD_DICT_SIZE - dctxPtr->tmpOutSize;
929         const BYTE* oldDictEnd = dctxPtr->dict + dctxPtr->dictSize - dctxPtr->tmpOutStart;
930         if (dctxPtr->tmpOutSize > LIZARD_DICT_SIZE) copySize = 0;
931         if (copySize > preserveSize) copySize = preserveSize;
932 
933         memcpy(dctxPtr->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
934 
935         dctxPtr->dict = dctxPtr->tmpOutBuffer;
936         dctxPtr->dictSize = preserveSize + dctxPtr->tmpOutStart + dstSize;
937         return;
938     }
939 
940     if (dctxPtr->dict == dctxPtr->tmpOutBuffer) {    /* copy dst into tmp to complete dict */
941         if (dctxPtr->dictSize + dstSize > dctxPtr->maxBufferSize) {  /* tmp buffer not large enough */
942             size_t preserveSize = LIZARD_DICT_SIZE - dstSize;   /* note : dstSize < LIZARD_DICT_SIZE */
943             memcpy(dctxPtr->tmpOutBuffer, dctxPtr->dict + dctxPtr->dictSize - preserveSize, preserveSize);
944             dctxPtr->dictSize = preserveSize;
945         }
946         memcpy(dctxPtr->tmpOutBuffer + dctxPtr->dictSize, dstPtr, dstSize);
947         dctxPtr->dictSize += dstSize;
948         return;
949     }
950 
951     /* join dict & dest into tmp */
952     {   size_t preserveSize = LIZARD_DICT_SIZE - dstSize;   /* note : dstSize < LIZARD_DICT_SIZE */
953         if (preserveSize > dctxPtr->dictSize) preserveSize = dctxPtr->dictSize;
954         memcpy(dctxPtr->tmpOutBuffer, dctxPtr->dict + dctxPtr->dictSize - preserveSize, preserveSize);
955         memcpy(dctxPtr->tmpOutBuffer + preserveSize, dstPtr, dstSize);
956         dctxPtr->dict = dctxPtr->tmpOutBuffer;
957         dctxPtr->dictSize = preserveSize + dstSize;
958     }
959 }
960 
961 
962 
963 /*! LizardF_decompress() :
964 * Call this function repetitively to regenerate data compressed within srcBuffer.
965 * The function will attempt to decode *srcSizePtr from srcBuffer, into dstBuffer of maximum size *dstSizePtr.
966 *
967 * The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr (necessarily <= original value).
968 *
969 * The number of bytes effectively read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).
970 * If the number of bytes read is < number of bytes provided, then the decompression operation is not complete.
971 * You will have to call it again, continuing from where it stopped.
972 *
973 * The function result is an hint of the better srcSize to use for next call to LizardF_decompress.
974 * Basically, it's the size of the current (or remaining) compressed block + header of next block.
975 * Respecting the hint provides some boost to performance, since it allows less buffer shuffling.
976 * Note that this is just a hint, you can always provide any srcSize you want.
977 * When a frame is fully decoded, the function result will be 0.
978 * If decompression failed, function result is an error code which can be tested using LizardF_isError().
979 */
LizardF_decompress(LizardF_decompressionContext_t decompressionContext,void * dstBuffer,size_t * dstSizePtr,const void * srcBuffer,size_t * srcSizePtr,const LizardF_decompressOptions_t * decompressOptionsPtr)980 size_t LizardF_decompress(LizardF_decompressionContext_t decompressionContext,
981                        void* dstBuffer, size_t* dstSizePtr,
982                        const void* srcBuffer, size_t* srcSizePtr,
983                        const LizardF_decompressOptions_t* decompressOptionsPtr)
984 {
985     LizardF_dctx_t* dctxPtr = (LizardF_dctx_t*)decompressionContext;
986     LizardF_decompressOptions_t optionsNull;
987     const BYTE* const srcStart = (const BYTE*)srcBuffer;
988     const BYTE* const srcEnd = srcStart + *srcSizePtr;
989     const BYTE* srcPtr = srcStart;
990     BYTE* const dstStart = (BYTE*)dstBuffer;
991     BYTE* const dstEnd = dstStart + *dstSizePtr;
992     BYTE* dstPtr = dstStart;
993     const BYTE* selectedIn = NULL;
994     unsigned doAnotherStage = 1;
995     size_t nextSrcSizeHint = 1;
996 
997 
998     memset(&optionsNull, 0, sizeof(optionsNull));
999     if (decompressOptionsPtr==NULL) decompressOptionsPtr = &optionsNull;
1000     *srcSizePtr = 0;
1001     *dstSizePtr = 0;
1002 
1003     /* expect to continue decoding src buffer where it left previously */
1004     if (dctxPtr->srcExpect != NULL) {
1005         if (srcStart != dctxPtr->srcExpect) return (size_t)-LizardF_ERROR_srcPtr_wrong;
1006     }
1007 
1008     /* programmed as a state machine */
1009 
1010     while (doAnotherStage) {
1011 
1012         switch(dctxPtr->dStage)
1013         {
1014 
1015         case dstage_getHeader:
1016             if ((size_t)(srcEnd-srcPtr) >= maxFHSize) {  /* enough to decode - shortcut */
1017                 LizardF_errorCode_t const hSize = LizardF_decodeHeader(dctxPtr, srcPtr, srcEnd-srcPtr);
1018                 if (LizardF_isError(hSize)) return hSize;
1019                 srcPtr += hSize;
1020                 break;
1021             }
1022             dctxPtr->tmpInSize = 0;
1023             dctxPtr->tmpInTarget = minFHSize;   /* minimum to attempt decode */
1024             dctxPtr->dStage = dstage_storeHeader;
1025             /* pass-through */
1026 
1027         case dstage_storeHeader:
1028             {   size_t sizeToCopy = dctxPtr->tmpInTarget - dctxPtr->tmpInSize;
1029                 if (sizeToCopy > (size_t)(srcEnd - srcPtr)) sizeToCopy =  srcEnd - srcPtr;
1030                 memcpy(dctxPtr->header + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
1031                 dctxPtr->tmpInSize += sizeToCopy;
1032                 srcPtr += sizeToCopy;
1033                 if (dctxPtr->tmpInSize < dctxPtr->tmpInTarget) {
1034                     nextSrcSizeHint = (dctxPtr->tmpInTarget - dctxPtr->tmpInSize) + BHSize;   /* rest of header + nextBlockHeader */
1035                     doAnotherStage = 0;   /* not enough src data, ask for some more */
1036                     break;
1037                 }
1038                 {   LizardF_errorCode_t const hSize = LizardF_decodeHeader(dctxPtr, dctxPtr->header, dctxPtr->tmpInTarget);
1039                     if (LizardF_isError(hSize)) return hSize;
1040                 }
1041                 break;
1042             }
1043 
1044         case dstage_getCBlockSize:
1045             if ((size_t)(srcEnd - srcPtr) >= BHSize) {
1046                 selectedIn = srcPtr;
1047                 srcPtr += BHSize;
1048             } else {
1049                 /* not enough input to read cBlockSize field */
1050                 dctxPtr->tmpInSize = 0;
1051                 dctxPtr->dStage = dstage_storeCBlockSize;
1052             }
1053 
1054             if (dctxPtr->dStage == dstage_storeCBlockSize)   /* can be skipped */
1055         case dstage_storeCBlockSize:
1056             {
1057                 size_t sizeToCopy = BHSize - dctxPtr->tmpInSize;
1058                 if (sizeToCopy > (size_t)(srcEnd - srcPtr)) sizeToCopy = srcEnd - srcPtr;
1059                 memcpy(dctxPtr->tmpIn + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
1060                 srcPtr += sizeToCopy;
1061                 dctxPtr->tmpInSize += sizeToCopy;
1062                 if (dctxPtr->tmpInSize < BHSize) {   /* not enough input to get full cBlockSize; wait for more */
1063                     nextSrcSizeHint = BHSize - dctxPtr->tmpInSize;
1064                     doAnotherStage  = 0;
1065                     break;
1066                 }
1067                 selectedIn = dctxPtr->tmpIn;
1068             }
1069 
1070         /* case dstage_decodeCBlockSize: */   /* no more direct access, to prevent scan-build warning */
1071             {   size_t const nextCBlockSize = LizardF_readLE32(selectedIn) & 0x7FFFFFFFU;
1072                 if (nextCBlockSize==0) {  /* frameEnd signal, no more CBlock */
1073                     dctxPtr->dStage = dstage_getSuffix;
1074                     break;
1075                 }
1076                 if (nextCBlockSize > dctxPtr->maxBlockSize) return (size_t)-LizardF_ERROR_GENERIC;   /* invalid cBlockSize */
1077                 dctxPtr->tmpInTarget = nextCBlockSize;
1078                 if (LizardF_readLE32(selectedIn) & LIZARDF_BLOCKUNCOMPRESSED_FLAG) {
1079                     dctxPtr->dStage = dstage_copyDirect;
1080                     break;
1081                 }
1082                 dctxPtr->dStage = dstage_getCBlock;
1083                 if (dstPtr==dstEnd) {
1084                     nextSrcSizeHint = nextCBlockSize + BHSize;
1085                     doAnotherStage = 0;
1086                 }
1087                 break;
1088             }
1089 
1090         case dstage_copyDirect:   /* uncompressed block */
1091             {   size_t sizeToCopy = dctxPtr->tmpInTarget;
1092                 if ((size_t)(srcEnd-srcPtr) < sizeToCopy) sizeToCopy = srcEnd - srcPtr;  /* not enough input to read full block */
1093                 if ((size_t)(dstEnd-dstPtr) < sizeToCopy) sizeToCopy = dstEnd - dstPtr;
1094                 memcpy(dstPtr, srcPtr, sizeToCopy);
1095                 if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), srcPtr, sizeToCopy);
1096                 if (dctxPtr->frameInfo.contentSize) dctxPtr->frameRemainingSize -= sizeToCopy;
1097 
1098                 /* dictionary management */
1099                 if (dctxPtr->frameInfo.blockMode==LizardF_blockLinked)
1100                     LizardF_updateDict(dctxPtr, dstPtr, sizeToCopy, dstStart, 0);
1101 
1102                 srcPtr += sizeToCopy;
1103                 dstPtr += sizeToCopy;
1104                 if (sizeToCopy == dctxPtr->tmpInTarget) {  /* all copied */
1105                     dctxPtr->dStage = dstage_getCBlockSize;
1106                     break;
1107                 }
1108                 dctxPtr->tmpInTarget -= sizeToCopy;   /* still need to copy more */
1109                 nextSrcSizeHint = dctxPtr->tmpInTarget + BHSize;
1110                 doAnotherStage = 0;
1111                 break;
1112             }
1113 
1114         case dstage_getCBlock:   /* entry from dstage_decodeCBlockSize */
1115             if ((size_t)(srcEnd-srcPtr) < dctxPtr->tmpInTarget) {
1116                 dctxPtr->tmpInSize = 0;
1117                 dctxPtr->dStage = dstage_storeCBlock;
1118                 break;
1119             }
1120             selectedIn = srcPtr;
1121             srcPtr += dctxPtr->tmpInTarget;
1122             dctxPtr->dStage = dstage_decodeCBlock;
1123             break;
1124 
1125         case dstage_storeCBlock:
1126             {   size_t sizeToCopy = dctxPtr->tmpInTarget - dctxPtr->tmpInSize;
1127                 if (sizeToCopy > (size_t)(srcEnd-srcPtr)) sizeToCopy = srcEnd-srcPtr;
1128                 memcpy(dctxPtr->tmpIn + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
1129                 dctxPtr->tmpInSize += sizeToCopy;
1130                 srcPtr += sizeToCopy;
1131                 if (dctxPtr->tmpInSize < dctxPtr->tmpInTarget) { /* need more input */
1132                     nextSrcSizeHint = (dctxPtr->tmpInTarget - dctxPtr->tmpInSize) + BHSize;
1133                     doAnotherStage=0;
1134                     break;
1135                 }
1136                 selectedIn = dctxPtr->tmpIn;
1137                 dctxPtr->dStage = dstage_decodeCBlock;
1138                 /* pass-through */
1139             }
1140 
1141         case dstage_decodeCBlock:
1142             if ((size_t)(dstEnd-dstPtr) < dctxPtr->maxBlockSize)   /* not enough place into dst : decode into tmpOut */
1143                 dctxPtr->dStage = dstage_decodeCBlock_intoTmp;
1144             else
1145                 dctxPtr->dStage = dstage_decodeCBlock_intoDst;
1146             break;
1147 
1148         case dstage_decodeCBlock_intoDst:
1149             {   int (*decoder)(const char*, char*, int, int, const char*, int);
1150                 int decodedSize;
1151 
1152                 if (dctxPtr->frameInfo.blockMode == LizardF_blockLinked)
1153                     decoder = Lizard_decompress_safe_usingDict;
1154                 else
1155                     decoder = LizardF_decompress_safe;
1156 
1157                 decodedSize = decoder((const char*)selectedIn, (char*)dstPtr, (int)dctxPtr->tmpInTarget, (int)dctxPtr->maxBlockSize, (const char*)dctxPtr->dict, (int)dctxPtr->dictSize);
1158                 if (decodedSize < 0) return (size_t)-LizardF_ERROR_GENERIC;   /* decompression failed */
1159                 if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), dstPtr, decodedSize);
1160                 if (dctxPtr->frameInfo.contentSize) dctxPtr->frameRemainingSize -= decodedSize;
1161 
1162                 /* dictionary management */
1163                 if (dctxPtr->frameInfo.blockMode==LizardF_blockLinked)
1164                     LizardF_updateDict(dctxPtr, dstPtr, decodedSize, dstStart, 0);
1165 
1166                 dstPtr += decodedSize;
1167                 dctxPtr->dStage = dstage_getCBlockSize;
1168                 break;
1169             }
1170 
1171         case dstage_decodeCBlock_intoTmp:
1172             /* not enough place into dst : decode into tmpOut */
1173             {   int (*decoder)(const char*, char*, int, int, const char*, int);
1174                 int decodedSize;
1175 
1176                 if (dctxPtr->frameInfo.blockMode == LizardF_blockLinked)
1177                     decoder = Lizard_decompress_safe_usingDict;
1178                 else
1179                     decoder = LizardF_decompress_safe;
1180 
1181                 /* ensure enough place for tmpOut */
1182                 if (dctxPtr->frameInfo.blockMode == LizardF_blockLinked) {
1183                     if (dctxPtr->dict == dctxPtr->tmpOutBuffer) {
1184                         if (dctxPtr->dictSize > 2 * LIZARD_DICT_SIZE) {
1185                             memcpy(dctxPtr->tmpOutBuffer, dctxPtr->dict + dctxPtr->dictSize - LIZARD_DICT_SIZE, LIZARD_DICT_SIZE);
1186                             dctxPtr->dictSize = LIZARD_DICT_SIZE;
1187                         }
1188                         dctxPtr->tmpOut = dctxPtr->tmpOutBuffer + dctxPtr->dictSize;
1189                     } else {  /* dict not within tmp */
1190                         size_t reservedDictSpace = dctxPtr->dictSize;
1191                         if (reservedDictSpace > LIZARD_DICT_SIZE) reservedDictSpace = LIZARD_DICT_SIZE;
1192                         dctxPtr->tmpOut = dctxPtr->tmpOutBuffer + reservedDictSpace;
1193                     }
1194                 }
1195 
1196                 /* Decode */
1197                 decodedSize = decoder((const char*)selectedIn, (char*)dctxPtr->tmpOut, (int)dctxPtr->tmpInTarget, (int)dctxPtr->maxBlockSize, (const char*)dctxPtr->dict, (int)dctxPtr->dictSize);
1198                 if (decodedSize < 0) return (size_t)-LizardF_ERROR_decompressionFailed;   /* decompression failed */
1199                 if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), dctxPtr->tmpOut, decodedSize);
1200                 if (dctxPtr->frameInfo.contentSize) dctxPtr->frameRemainingSize -= decodedSize;
1201                 dctxPtr->tmpOutSize = decodedSize;
1202                 dctxPtr->tmpOutStart = 0;
1203                 dctxPtr->dStage = dstage_flushOut;
1204                 break;
1205             }
1206 
1207         case dstage_flushOut:  /* flush decoded data from tmpOut to dstBuffer */
1208             {   size_t sizeToCopy = dctxPtr->tmpOutSize - dctxPtr->tmpOutStart;
1209                 if (sizeToCopy > (size_t)(dstEnd-dstPtr)) sizeToCopy = dstEnd-dstPtr;
1210                 memcpy(dstPtr, dctxPtr->tmpOut + dctxPtr->tmpOutStart, sizeToCopy);
1211 
1212                 /* dictionary management */
1213                 if (dctxPtr->frameInfo.blockMode==LizardF_blockLinked)
1214                     LizardF_updateDict(dctxPtr, dstPtr, sizeToCopy, dstStart, 1);
1215 
1216                 dctxPtr->tmpOutStart += sizeToCopy;
1217                 dstPtr += sizeToCopy;
1218 
1219                 /* end of flush ? */
1220                 if (dctxPtr->tmpOutStart == dctxPtr->tmpOutSize) {
1221                     dctxPtr->dStage = dstage_getCBlockSize;
1222                     break;
1223                 }
1224                 nextSrcSizeHint = BHSize;
1225                 doAnotherStage = 0;   /* still some data to flush */
1226                 break;
1227             }
1228 
1229         case dstage_getSuffix:
1230             {   size_t const suffixSize = dctxPtr->frameInfo.contentChecksumFlag * 4;
1231                 if (dctxPtr->frameRemainingSize) return (size_t)-LizardF_ERROR_frameSize_wrong;   /* incorrect frame size decoded */
1232                 if (suffixSize == 0) {  /* frame completed */
1233                     nextSrcSizeHint = 0;
1234                     dctxPtr->dStage = dstage_getHeader;
1235                     doAnotherStage = 0;
1236                     break;
1237                 }
1238                 if ((srcEnd - srcPtr) < 4) {  /* not enough size for entire CRC */
1239                     dctxPtr->tmpInSize = 0;
1240                     dctxPtr->dStage = dstage_storeSuffix;
1241                 } else {
1242                     selectedIn = srcPtr;
1243                     srcPtr += 4;
1244                 }
1245             }
1246 
1247             if (dctxPtr->dStage == dstage_storeSuffix)   /* can be skipped */
1248         case dstage_storeSuffix:
1249             {
1250                 size_t sizeToCopy = 4 - dctxPtr->tmpInSize;
1251                 if (sizeToCopy > (size_t)(srcEnd - srcPtr)) sizeToCopy = srcEnd - srcPtr;
1252                 memcpy(dctxPtr->tmpIn + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
1253                 srcPtr += sizeToCopy;
1254                 dctxPtr->tmpInSize += sizeToCopy;
1255                 if (dctxPtr->tmpInSize < 4) { /* not enough input to read complete suffix */
1256                     nextSrcSizeHint = 4 - dctxPtr->tmpInSize;
1257                     doAnotherStage=0;
1258                     break;
1259                 }
1260                 selectedIn = dctxPtr->tmpIn;
1261             }
1262 
1263         /* case dstage_checkSuffix: */   /* no direct call, to avoid scan-build warning */
1264             {   U32 const readCRC = LizardF_readLE32(selectedIn);
1265                 U32 const resultCRC = XXH32_digest(&(dctxPtr->xxh));
1266                 if (readCRC != resultCRC) return (size_t)-LizardF_ERROR_contentChecksum_invalid;
1267                 nextSrcSizeHint = 0;
1268                 dctxPtr->dStage = dstage_getHeader;
1269                 doAnotherStage = 0;
1270                 break;
1271             }
1272 
1273         case dstage_getSFrameSize:
1274             if ((srcEnd - srcPtr) >= 4) {
1275                 selectedIn = srcPtr;
1276                 srcPtr += 4;
1277             } else {
1278                 /* not enough input to read cBlockSize field */
1279                 dctxPtr->tmpInSize = 4;
1280                 dctxPtr->tmpInTarget = 8;
1281                 dctxPtr->dStage = dstage_storeSFrameSize;
1282             }
1283 
1284             if (dctxPtr->dStage == dstage_storeSFrameSize)
1285         case dstage_storeSFrameSize:
1286             {
1287                 size_t sizeToCopy = dctxPtr->tmpInTarget - dctxPtr->tmpInSize;
1288                 if (sizeToCopy > (size_t)(srcEnd - srcPtr)) sizeToCopy = srcEnd - srcPtr;
1289                 memcpy(dctxPtr->header + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
1290                 srcPtr += sizeToCopy;
1291                 dctxPtr->tmpInSize += sizeToCopy;
1292                 if (dctxPtr->tmpInSize < dctxPtr->tmpInTarget) { /* not enough input to get full sBlockSize; wait for more */
1293                     nextSrcSizeHint = dctxPtr->tmpInTarget - dctxPtr->tmpInSize;
1294                     doAnotherStage = 0;
1295                     break;
1296                 }
1297                 selectedIn = dctxPtr->header + 4;
1298             }
1299 
1300         /* case dstage_decodeSFrameSize: */   /* no direct access */
1301             {   size_t const SFrameSize = LizardF_readLE32(selectedIn);
1302                 dctxPtr->frameInfo.contentSize = SFrameSize;
1303                 dctxPtr->tmpInTarget = SFrameSize;
1304                 dctxPtr->dStage = dstage_skipSkippable;
1305                 break;
1306             }
1307 
1308         case dstage_skipSkippable:
1309             {   size_t skipSize = dctxPtr->tmpInTarget;
1310                 if (skipSize > (size_t)(srcEnd-srcPtr)) skipSize = srcEnd-srcPtr;
1311                 srcPtr += skipSize;
1312                 dctxPtr->tmpInTarget -= skipSize;
1313                 doAnotherStage = 0;
1314                 nextSrcSizeHint = dctxPtr->tmpInTarget;
1315                 if (nextSrcSizeHint) break;
1316                 dctxPtr->dStage = dstage_getHeader;
1317                 break;
1318             }
1319         }
1320     }
1321 
1322     /* preserve dictionary within tmp if necessary */
1323     if ( (dctxPtr->frameInfo.blockMode==LizardF_blockLinked)
1324         &&(dctxPtr->dict != dctxPtr->tmpOutBuffer)
1325         &&(!decompressOptionsPtr->stableDst)
1326         &&((unsigned)(dctxPtr->dStage-1) < (unsigned)(dstage_getSuffix-1))
1327         )
1328     {
1329         if (dctxPtr->dStage == dstage_flushOut) {
1330             size_t preserveSize = dctxPtr->tmpOut - dctxPtr->tmpOutBuffer;
1331             size_t copySize = LIZARD_DICT_SIZE - dctxPtr->tmpOutSize;
1332             const BYTE* oldDictEnd = dctxPtr->dict + dctxPtr->dictSize - dctxPtr->tmpOutStart;
1333             if (dctxPtr->tmpOutSize > LIZARD_DICT_SIZE) copySize = 0;
1334             if (copySize > preserveSize) copySize = preserveSize;
1335 
1336             memcpy(dctxPtr->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
1337 
1338             dctxPtr->dict = dctxPtr->tmpOutBuffer;
1339             dctxPtr->dictSize = preserveSize + dctxPtr->tmpOutStart;
1340         } else {
1341             size_t newDictSize = dctxPtr->dictSize;
1342             const BYTE* oldDictEnd = dctxPtr->dict + dctxPtr->dictSize;
1343             if ((newDictSize) > LIZARD_DICT_SIZE) newDictSize = LIZARD_DICT_SIZE;
1344 
1345             memcpy(dctxPtr->tmpOutBuffer, oldDictEnd - newDictSize, newDictSize);
1346 
1347             dctxPtr->dict = dctxPtr->tmpOutBuffer;
1348             dctxPtr->dictSize = newDictSize;
1349             dctxPtr->tmpOut = dctxPtr->tmpOutBuffer + newDictSize;
1350         }
1351     }
1352 
1353     /* require function to be called again from position where it stopped */
1354     if (srcPtr<srcEnd)
1355         dctxPtr->srcExpect = srcPtr;
1356     else
1357         dctxPtr->srcExpect = NULL;
1358 
1359     *srcSizePtr = (srcPtr - srcStart);
1360     *dstSizePtr = (dstPtr - dstStart);
1361     return nextSrcSizeHint;
1362 }
1363