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