Bug Summary

File:root/firefox-clang/mozglue/static/lz4/lz4frame.c
Warning:line 3071, column 9
Null pointer passed to 2nd parameter expecting 'nonnull'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name lz4frame.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/mozglue/static -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/mozglue/static -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D LZ4LIB_VISIBILITY= -D LZ4FLIB_VISIBILITY= -D EXPORT_JS_API -D MOZ_HAS_MOZGLUE -D MOZ_SUPPORT_LEAKCHECKING -I /root/firefox-clang/mozglue/static -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozglue/static -I /root/firefox-clang/mozglue/static/xxhash -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=tautological-type-limit-compare -Wno-range-loop-analysis -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-unknown-warning-option -Wno-character-conversion -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-11-12-001017-1011516-1 -x c /root/firefox-clang/mozglue/static/lz4/lz4frame.c
1/*
2 * LZ4 auto-framing library
3 * Copyright (c) Yann Collet. All rights reserved.
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 homepage : http://www.lz4.org
32 * - LZ4 source repository : https://github.com/lz4/lz4
33 */
34
35/* LZ4F is a stand-alone API to create LZ4-compressed Frames
36 * in full conformance with specification v1.6.1 .
37 * This library rely upon memory management capabilities (malloc, free)
38 * provided either by <stdlib.h>,
39 * or redirected towards another library of user's choice
40 * (see Memory Routines below).
41 */
42
43
44/*-************************************
45* Compiler Options
46**************************************/
47#include <limits.h>
48#ifdef _MSC_VER /* Visual Studio */
49# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
50#endif
51
52
53/*-************************************
54* Tuning parameters
55**************************************/
56/*
57 * LZ4F_HEAPMODE :
58 * Control how LZ4F_compressFrame allocates the Compression State,
59 * either on stack (0:default, fastest), or in memory heap (1:requires malloc()).
60 */
61#ifndef LZ4F_HEAPMODE0
62# define LZ4F_HEAPMODE0 0
63#endif
64
65/*
66 * LZ4F_MULTITHREAD :
67 * Enables multi-threaded compression, requested with LZ4F_preferences_t.nbWorkers.
68 * Requires <pthread.h> on POSIX systems (compile and link with -pthread), or Windows threads.
69 * Default is 0 : no threading code is compiled,
70 * and multi-threaded requests are served serially by the calling thread, producing the same output.
71 */
72#ifndef LZ4F_MULTITHREAD0
73# define LZ4F_MULTITHREAD0 0
74#endif
75#if defined(LZ4_FREESTANDING0) && (LZ4_FREESTANDING0 == 1)
76# undef LZ4F_MULTITHREAD0
77# define LZ4F_MULTITHREAD0 0
78#endif
79#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) /* MinGW.org, unlike mingw-w64, lacks Windows Vista API (condition variables) */
80# undef LZ4F_MULTITHREAD0
81# define LZ4F_MULTITHREAD0 0
82#endif
83
84
85/*-************************************
86* Library declarations
87**************************************/
88#define LZ4F_STATIC_LINKING_ONLY
89#include "lz4frame.h"
90#define LZ4_STATIC_LINKING_ONLY
91#include "lz4.h"
92#define LZ4_HC_STATIC_LINKING_ONLY
93#include "lz4hc.h"
94#define XXH_STATIC_LINKING_ONLY
95#include "xxhash.h"
96
97
98/*-************************************
99* Memory routines
100**************************************/
101/*
102 * Default allocation functions, employed when LZ4F_CustomMem doesn't define them :
103 * malloc(), calloc() and free(), or LZ4_malloc(), LZ4_calloc() and LZ4_free()
104 * when lz4 is compiled with LZ4_USER_MEMORY_FUNCTIONS (see lz4.c).
105**/
106
107#include <string.h> /* memset, memcpy, memmove */
108#ifndef LZ4_SRC_INCLUDED /* avoid redefinition when sources are coalesced */
109# define MEM_INIT(p,v,s)memset((p),(v),(s)) memset((p),(v),(s))
110#endif
111
112#ifndef LZ4_SRC_INCLUDED /* avoid redefinition when sources are coalesced */
113# if defined(LZ4_USER_MEMORY_FUNCTIONS)
114 void* LZ4_malloc(size_t s);
115 void* LZ4_calloc(size_t n, size_t s);
116 void LZ4_free(void* p);
117# define ALLOC(s)malloc(s) LZ4_malloc(s)
118# define ALLOC_AND_ZERO(s)calloc(1,(s)) LZ4_calloc(1,(s))
119# define FREEMEM(p)free(p) LZ4_free(p)
120# else
121# include <stdlib.h> /* malloc, calloc, free */
122# define ALLOC(s)malloc(s) malloc(s)
123# define ALLOC_AND_ZERO(s)calloc(1,(s)) calloc(1,(s))
124# define FREEMEM(p)free(p) free(p)
125# endif
126#endif
127
128static void* LZ4F_calloc(size_t s, LZ4F_CustomMem cmem)
129{
130 /* custom calloc defined : use it */
131 if (cmem.customCalloc != NULL((void*)0)) {
132 return cmem.customCalloc(cmem.opaqueState, s);
133 }
134 /* nothing defined : use default <stdlib.h>'s calloc() */
135 if (cmem.customAlloc == NULL((void*)0)) {
136 return ALLOC_AND_ZERO(s)calloc(1,(s));
137 }
138 /* only custom alloc defined : use it, and combine it with memset() */
139 { void* const p = cmem.customAlloc(cmem.opaqueState, s);
140 if (p != NULL((void*)0)) MEM_INIT(p, 0, s)memset((p),(0),(s));
141 return p;
142} }
143
144static void* LZ4F_malloc(size_t s, LZ4F_CustomMem cmem)
145{
146 /* custom malloc defined : use it */
147 if (cmem.customAlloc != NULL((void*)0)) {
148 return cmem.customAlloc(cmem.opaqueState, s);
149 }
150 /* nothing defined : use default <stdlib.h>'s malloc() */
151 return ALLOC(s)malloc(s);
152}
153
154static void LZ4F_free(void* p, LZ4F_CustomMem cmem)
155{
156 if (p == NULL((void*)0)) return;
157 if (cmem.customFree != NULL((void*)0)) {
158 /* custom allocation defined : use it */
159 cmem.customFree(cmem.opaqueState, p);
160 return;
161 }
162 /* nothing defined : use default <stdlib.h>'s free() */
163 FREEMEM(p)free(p);
164}
165
166
167/*-************************************
168* Debug
169**************************************/
170#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)
171# include <assert.h>
172#else
173# ifndef assert
174# define assert(condition)((void)0) ((void)0)
175# endif
176#endif
177
178#define LZ4F_STATIC_ASSERT(c){ enum { LZ4F_static_assert = 1/(int)(!!(c)) }; } { enum { LZ4F_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
179
180#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) && !defined(DEBUGLOG)
181# include <stdio.h>
182static int g_debuglog_enable = 1;
183# define DEBUGLOG(l, ...){} { \
184 if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \
185 fprintf(stderr, __FILE__"/root/firefox-clang/mozglue/static/lz4/lz4frame.c" " %i: ", __LINE__185 ); \
186 fprintf(stderr, __VA_ARGS__); \
187 fprintf(stderr, " \n"); \
188 } }
189#endif
190#ifndef DEBUGLOG /* may already be defined, e.g. by lz4.c in an amalgamated build */
191# define DEBUGLOG(l, ...){} {} /* disabled */
192#endif
193
194
195/*-************************************
196* Basic Types
197**************************************/
198#ifndef LZ4_SRC_INCLUDED
199# if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__201710L) && (__STDC_VERSION__201710L >= 199901L) /* C99 */) )
200# include <stdint.h>
201 typedef uint8_t BYTE;
202 typedef uint16_t U16;
203 typedef uint32_t U32;
204 typedef int32_t S32;
205 typedef uint64_t U64;
206# else
207 typedef unsigned char BYTE;
208 typedef unsigned short U16;
209 typedef unsigned int U32;
210 typedef signed int S32;
211 typedef unsigned long long U64;
212# endif
213#endif
214
215
216/* unoptimized version; solves endianness & alignment issues */
217static U32 LZ4F_readLE32 (const void* src)
218{
219 const BYTE* const srcPtr = (const BYTE*)src;
220 U32 value32 = srcPtr[0];
221 value32 |= ((U32)srcPtr[1])<< 8;
222 value32 |= ((U32)srcPtr[2])<<16;
223 value32 |= ((U32)srcPtr[3])<<24;
224 return value32;
225}
226
227static void LZ4F_writeLE32 (void* dst, U32 value32)
228{
229 BYTE* const dstPtr = (BYTE*)dst;
230 dstPtr[0] = (BYTE)value32;
231 dstPtr[1] = (BYTE)(value32 >> 8);
232 dstPtr[2] = (BYTE)(value32 >> 16);
233 dstPtr[3] = (BYTE)(value32 >> 24);
234}
235
236static U64 LZ4F_readLE64 (const void* src)
237{
238 const BYTE* const srcPtr = (const BYTE*)src;
239 U64 value64 = srcPtr[0];
240 value64 |= ((U64)srcPtr[1]<<8);
241 value64 |= ((U64)srcPtr[2]<<16);
242 value64 |= ((U64)srcPtr[3]<<24);
243 value64 |= ((U64)srcPtr[4]<<32);
244 value64 |= ((U64)srcPtr[5]<<40);
245 value64 |= ((U64)srcPtr[6]<<48);
246 value64 |= ((U64)srcPtr[7]<<56);
247 return value64;
248}
249
250static void LZ4F_writeLE64 (void* dst, U64 value64)
251{
252 BYTE* const dstPtr = (BYTE*)dst;
253 dstPtr[0] = (BYTE)value64;
254 dstPtr[1] = (BYTE)(value64 >> 8);
255 dstPtr[2] = (BYTE)(value64 >> 16);
256 dstPtr[3] = (BYTE)(value64 >> 24);
257 dstPtr[4] = (BYTE)(value64 >> 32);
258 dstPtr[5] = (BYTE)(value64 >> 40);
259 dstPtr[6] = (BYTE)(value64 >> 48);
260 dstPtr[7] = (BYTE)(value64 >> 56);
261}
262
263
264/*-************************************
265* Constants
266**************************************/
267#ifndef LZ4_SRC_INCLUDED /* avoid double definition */
268# define KB*(1<<10) *(1<<10)
269# define MB*(1<<20) *(1<<20)
270# define GB*(1<<30) *(1<<30)
271#endif
272
273#define _1BIT0x01 0x01
274#define _2BITS0x03 0x03
275#define _3BITS0x07 0x07
276#define _4BITS0x0F 0x0F
277#define _8BITS0xFF 0xFF
278
279#define LZ4F_BLOCKUNCOMPRESSED_FLAG0x80000000U 0x80000000U
280#define LZ4F_BLOCKSIZEID_DEFAULTLZ4F_max64KB LZ4F_max64KB
281
282static const size_t minFHSize = LZ4F_HEADER_SIZE_MIN7; /* 7 */
283static const size_t maxFHSize = LZ4F_HEADER_SIZE_MAX19; /* 19 */
284static const size_t BHSize = LZ4F_BLOCK_HEADER_SIZE4; /* block header : size, and compress flag */
285static const size_t BFSize = LZ4F_BLOCK_CHECKSUM_SIZE4; /* block footer : checksum (optional) */
286
287
288/*-************************************
289* Structures and local types
290**************************************/
291
292typedef enum { LZ4B_COMPRESSED, LZ4B_UNCOMPRESSED} LZ4F_BlockCompressMode_e;
293typedef enum { ctxNone, ctxFast, ctxHC } LZ4F_CtxType_e;
294
295struct LZ4F_CDict_s {
296 LZ4F_CustomMem cmem;
297 void* dictContent;
298 LZ4_stream_t* fastCtx;
299 LZ4_streamHC_t* HCCtx;
300}; /* typedef'd to LZ4F_CDict within lz4frame_static.h */
301
302typedef struct LZ4F_cctx_s
303{
304 LZ4F_CustomMem cmem;
305 LZ4F_preferences_t prefs;
306 U32 version;
307 U32 cStage; /* 0 : compression uninitialized ; 1 : initialized, can compress ; 2 : frame ending (LZ4F_compressEnd() in progress) */
308 const LZ4F_CDict* cdict;
309 size_t maxBlockSize;
310 size_t tmpBuffCapacity;
311 BYTE* tmpBuff; /* internal buffer, for streaming */
312 BYTE* tmpIn; /* starting position of data compress within internal buffer (>= tmpBuff) */
313 size_t tmpInSize; /* amount of data to compress after tmpIn */
314 U64 totalInSize;
315 XXH32_state_t xxh;
316 void* lz4CtxPtr;
317 U16 lz4CtxAlloc; /* sized for: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */
318 U16 lz4CtxType; /* in use as: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */
319 LZ4F_BlockCompressMode_e blockCompressMode;
320 struct LZ4F_mtCtx_s* mt; /* multi-threading resources, kept across frames; NULL when never needed */
321 U32 mtActive; /* current frame is cut into jobs (prefs.nbWorkers >= 1) */
322 /* LZ4F_flush() and LZ4F_compressEnd() : output not fitting into dstBuffer yet, see LZ4F_emitPending() */
323 U32 flushStage; /* 0 : none ; 1 : flush point set, emitting ; LZ4F_compressEnd() only : 2 : footer ; 3 : completed */
324 const BYTE* pending; /* remaining output of a block (stageBuff), or of the frame footer */
325 size_t pendingSize;
326 BYTE* stageBuff; /* a block not fitting into dstBuffer : allocated on first need */
327 size_t stageCapacity;
328 BYTE footer[8]; /* endMark + optional content checksum */
329 /* raw dictionary (LZ4F_compressBegin_usingDict()) digested for current frame, then employed as a CDict */
330 struct LZ4F_CDict_s rawDict; /* fastCtx or HCCtx, the other one NULL ; no dictContent : not owned */
331 LZ4_stream_t* rawDictFast; /* allocated on first need */
332 LZ4_streamHC_t* rawDictHC; /* allocated on first need */
333} LZ4F_cctx_t;
334
335/* multi-threaded compression, see LZ4F_preferences_t.nbWorkers */
336typedef struct LZ4F_mtCtx_s LZ4F_mtCtx_t;
337static void LZ4F_mt_abort(LZ4F_mtCtx_t* mt);
338static size_t LZ4F_mt_begin(LZ4F_cctx_t* cctx);
339static void LZ4F_mt_free(LZ4F_mtCtx_t* mt, LZ4F_CustomMem cmem);
340static size_t LZ4F_mt_sizeof(const LZ4F_mtCtx_t* mt);
341
342
343/*-************************************
344* Error management
345**************************************/
346#define LZ4F_GENERATE_STRING(STRING)"STRING", #STRING,
347static const char* LZ4F_errorStrings[] = { LZ4F_LIST_ERRORS(LZ4F_GENERATE_STRING)"OK_NoError", "ERROR_GENERIC", "ERROR_maxBlockSize_invalid", "ERROR_blockMode_invalid"
, "ERROR_parameter_invalid", "ERROR_compressionLevel_invalid"
, "ERROR_headerVersion_wrong", "ERROR_blockChecksum_invalid",
"ERROR_reservedFlag_set", "ERROR_allocation_failed", "ERROR_srcSize_tooLarge"
, "ERROR_dstCapacity_tooSmall", "ERROR_frameHeader_incomplete"
, "ERROR_frameType_unknown", "ERROR_frameSize_wrong", "ERROR_srcPtr_wrong"
, "ERROR_decompressionFailed", "ERROR_headerChecksum_invalid"
, "ERROR_contentChecksum_invalid", "ERROR_frameDecoding_alreadyStarted"
, "ERROR_compressionState_uninitialized", "ERROR_parameter_null"
, "ERROR_io_write", "ERROR_io_read", "ERROR_maxCode",
};
348
349
350unsigned LZ4F_isError(LZ4F_errorCode_t code)
351{
352 return (code > (LZ4F_errorCode_t)(-LZ4F_ERROR_maxCode));
353}
354
355const char* LZ4F_getErrorName(LZ4F_errorCode_t code)
356{
357 static const char* codeError = "Unspecified error code";
358 if (LZ4F_isError(code)) return LZ4F_errorStrings[-(int)(code)];
359 return codeError;
360}
361
362LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult)
363{
364 if (!LZ4F_isError(functionResult)) return LZ4F_OK_NoError;
365 return (LZ4F_errorCodes)(-(ptrdiff_t)functionResult);
366}
367
368static LZ4F_errorCode_t LZ4F_returnErrorCode(LZ4F_errorCodes code)
369{
370 /* A compilation error here means sizeof(ptrdiff_t) is not large enough */
371 LZ4F_STATIC_ASSERT(sizeof(ptrdiff_t) >= sizeof(size_t)){ enum { LZ4F_static_assert = 1/(int)(!!(sizeof(ptrdiff_t) >=
sizeof(size_t))) }; }
;
372 return (LZ4F_errorCode_t)-(ptrdiff_t)code;
373}
374
375#define RETURN_ERROR(e)return LZ4F_returnErrorCode(LZ4F_ERROR_e) return LZ4F_returnErrorCode(LZ4F_ERROR_ ## e)
376
377#define RETURN_ERROR_IF(c,e)do { if (c) { {}; return LZ4F_returnErrorCode(LZ4F_ERROR_e); }
} while (0)
do { \
378 if (c) { \
379 DEBUGLOG(3, "Error: " #c){}; \
380 RETURN_ERROR(e)return LZ4F_returnErrorCode(LZ4F_ERROR_e); \
381 } \
382 } while (0)
383
384/* note : @r is evaluated only once, so it can be an expression with side effects */
385#define FORWARD_IF_ERROR(r)do { size_t const err_code = (r); if (LZ4F_isError(err_code))
return err_code; } while (0)
do { \
386 size_t const err_code = (r); \
387 if (LZ4F_isError(err_code)) return err_code; \
388 } while (0)
389
390/* a failed operation invalidates the frame, and discards its jobs (multi-threaded mode) :
391 * next operations fail with compressionState_uninitialized, until LZ4F_compressBegin().
392 * Idempotent : also employed to forward errors of functions which already invalidated the frame */
393static void LZ4F_invalidateFrame(LZ4F_cctx_t* cctx)
394{
395 if (cctx->mtActive) LZ4F_mt_abort(cctx->mt);
396 cctx->cStage = 0;
397}
398
399#define FORWARD_IF_ERROR_INVALIDATE(r, cctx)do { size_t const err_code = (r); if (LZ4F_isError(err_code))
{ LZ4F_invalidateFrame(cctx); return err_code; } } while (0)
do { \
400 size_t const err_code = (r); \
401 if (LZ4F_isError(err_code)) { \
402 LZ4F_invalidateFrame(cctx); \
403 return err_code; \
404 } \
405 } while (0)
406
407unsigned LZ4F_getVersion(void) { return LZ4F_VERSION100; }
408
409int LZ4F_compressionLevel_max(void) { return LZ4HC_CLEVEL_MAX12; }
410
411size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID)
412{
413 static const size_t blockSizes[4] = { 64 KB*(1<<10), 256 KB*(1<<10), 1 MB*(1<<20), 4 MB*(1<<20) };
414
415 if (blockSizeID == 0) blockSizeID = LZ4F_BLOCKSIZEID_DEFAULTLZ4F_max64KB;
416 if (blockSizeID < LZ4F_max64KB || blockSizeID > LZ4F_max4MB)
417 RETURN_ERROR(maxBlockSize_invalid)return LZ4F_returnErrorCode(LZ4F_ERROR_maxBlockSize_invalid);
418 { int const blockSizeIdx = (int)blockSizeID - (int)LZ4F_max64KB;
419 return blockSizes[blockSizeIdx];
420} }
421
422/*-************************************
423* Private functions
424**************************************/
425#undef MIN
426#define MIN(a,b)( (a) < (b) ? (a) : (b) ) ( (a) < (b) ? (a) : (b) )
427
428static BYTE LZ4F_headerChecksum (const void* header, size_t length)
429{
430 U32 const xxh = XXH32(header, length, 0);
431 return (BYTE)(xxh >> 8);
432}
433
434
435/*-************************************
436* Simple-pass compression functions
437**************************************/
438static LZ4F_blockSizeID_t LZ4F_optimalBSID(const LZ4F_blockSizeID_t requestedBSID,
439 const size_t srcSize)
440{
441 LZ4F_blockSizeID_t proposedBSID = LZ4F_max64KB;
442 size_t maxBlockSize = 64 KB*(1<<10);
443 while (requestedBSID > proposedBSID) {
444 if (srcSize <= maxBlockSize)
445 return proposedBSID;
446 proposedBSID = (LZ4F_blockSizeID_t)((int)proposedBSID + 1);
447 maxBlockSize <<= 2;
448 }
449 return requestedBSID;
450}
451
452/*! LZ4F_compressBound_autoFlush() :
453 * worst case : @srcSize as blocks stored uncompressed, the last one partial, then the frame footer.
454 * Also bounds one-shot compression (LZ4F_compressFrameBound()), which compresses all its input at once.
455 * When srcSize==0, it bounds LZ4F_flush() and LZ4F_compressEnd() operations.
456 */
457static size_t LZ4F_compressBound_autoFlush(size_t srcSize, const LZ4F_preferences_t* prefsPtr)
458{
459 size_t const blockSize = LZ4F_getBlockSize(prefsPtr->frameInfo.blockSizeID);
460 size_t const nbFullBlocks = srcSize / blockSize;
461 size_t const lastBlockSize = srcSize & (blockSize-1);
462 size_t const nbBlocks = nbFullBlocks + (lastBlockSize>0);
463 size_t const blockCRCSize = BFSize * prefsPtr->frameInfo.blockChecksumFlag;
464 size_t const frameEnd = BHSize + (prefsPtr->frameInfo.contentChecksumFlag*BFSize);
465 assert(prefsPtr->autoFlush)((void)0);
466 return ((BHSize + blockCRCSize) * nbBlocks) +
467 (blockSize * nbFullBlocks) + lastBlockSize + frameEnd;
468}
469
470size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
471{
472 LZ4F_preferences_t prefs;
473 size_t const headerSize = maxFHSize; /* max header size, including optional fields */
474
475 if (preferencesPtr!=NULL((void*)0)) prefs = *preferencesPtr;
476 else MEM_INIT(&prefs, 0, sizeof(prefs))memset((&prefs),(0),(sizeof(prefs)));
477 prefs.autoFlush = 1;
478
479 return headerSize + LZ4F_compressBound_autoFlush(srcSize, &prefs);
480}
481
482
483/*-************************************
484* Multi-threading parameters
485**************************************/
486/* see LZ4F_preferences_t.nbWorkers */
487/* sanity limit (ex: uninitialized preferences) ; on 32-bit systems, keeps LZ4F_compressBound() within address space */
488#ifndef LZ4F_NBWORKERS_MAX((sizeof(size_t) >= 8) ? 1024u : 128u)
489# define LZ4F_NBWORKERS_MAX((sizeof(size_t) >= 8) ? 1024u : 128u) ((sizeof(size_t) >= 8) ? 1024u : 128u)
490#endif
491#define LZ4F_MT_PREFIX_SIZE(64 *(1<<10)) (64 KB*(1<<10)) /* history employed by linked blocks */
492
493/* Job size :
494 * Input is cut into jobs at fixed positions of the frame's content (restarting after each flush) :
495 * a whole nb of blocks, up to LZ4F_MT_JOBSIZE, or a single block when larger.
496 * With linked blocks, each job restarts the stream, using previous 64 KB as dictionary.
497 * Job boundaries are therefore part of the output definition : this size must remain frozen.
498 * With independent blocks, output only depends on block boundaries, not on job boundaries.
499 * Small jobs balance load among threads, and engage multi-threading on smaller inputs. */
500#ifndef LZ4F_MT_JOBSIZE(1 *(1<<20)) /* override for testing only : changes output of linked blocks */
501# define LZ4F_MT_JOBSIZE(1 *(1<<20)) (1 MB*(1<<20))
502#endif
503
504static size_t LZ4F_mtJobSize(size_t blockSize)
505{
506 if (blockSize >= LZ4F_MT_JOBSIZE(1 *(1<<20))) return blockSize;
507 return (LZ4F_MT_JOBSIZE(1 *(1<<20)) / blockSize) * blockSize; /* whole nb of blocks */
508}
509
510unsigned LZ4F_getMaxNbWorkers(void)
511{
512#if LZ4F_MULTITHREAD0
513 return LZ4F_NBWORKERS_MAX((sizeof(size_t) >= 8) ? 1024u : 128u);
514#else
515 return 0;
516#endif
517}
518
519/* frames are cut into jobs only with valid block modes, see LZ4F_mt_begin() */
520static int LZ4F_mtEligible(const LZ4F_preferences_t* prefs)
521{
522 if (prefs->nbWorkers == 0) return 0;
523 /* linked blocks : jobs define the output, so frames are cut into jobs even without threads */
524 if (prefs->frameInfo.blockMode == LZ4F_blockLinked) return 1;
525#if LZ4F_MULTITHREAD0
526 return (prefs->frameInfo.blockMode == LZ4F_blockIndependent);
527#else
528 /* independent blocks : the output is identical to single-threaded mode, which is faster without threads */
529 return 0;
530#endif
531}
532
533/* nb of job slots for @nbThreads worker threads :
534 * one being filled, one per worker, plus room for jobs completed out of order,
535 * since their outputs are emitted in order */
536#ifndef LZ4F_MT_SLOTS_PER_THREAD2
537# define LZ4F_MT_SLOTS_PER_THREAD2 2
538#endif
539#define LZ4F_MT_NBSLOTS(nbThreads)((size_t)(nbThreads) * 2 + 1) ((size_t)(nbThreads) * LZ4F_MT_SLOTS_PER_THREAD2 + 1)
540#ifndef LZ4F_MT_EXTRA_INBUFFS1
541# define LZ4F_MT_EXTRA_INBUFFS1 1
542#endif
543
544
545/*! LZ4F_blocksBound() :
546 * @return worst case size of LZ4F_compressBlocks_internal() output */
547static size_t LZ4F_blocksBound(size_t srcSize, size_t blockSize, LZ4F_blockChecksum_t crcFlag)
548{
549 size_t const nbBlocks = (srcSize + blockSize - 1) / blockSize;
550 return srcSize + nbBlocks * (BHSize + (size_t)crcFlag * BFSize);
551}
552
553/*! LZ4F_mtCompressBound() :
554 * LZ4F_compressBound() for frames cut into jobs, without autoFlush : the job is the unit, instead of the block.
555 * LZ4F_compressUpdate() defers completed jobs not fitting into dstBuffer :
556 * output is only required to start a job while all slots are busy (the oldest one),
557 * and an invocation starts at most ceil(srcSize / jobSize) jobs, since the job being filled holds < jobSize bytes.
558 * @srcSize == 0 : LZ4F_flush() and LZ4F_compressEnd() emit at least one whole job per invocation, then the frame footer.
559 * The frame footer is always included, so that the bound grows with @srcSize. */
560static size_t LZ4F_mtCompressBound(size_t srcSize, const LZ4F_preferences_t* prefs)
561{
562 size_t const bs = LZ4F_getBlockSize(prefs->frameInfo.blockSizeID);
563 size_t const blockSize = LZ4F_isError(bs) ? 4 MB*(1<<20) : bs;
564 size_t const jobSize = LZ4F_mtJobSize(blockSize);
565 size_t const nbJobs = srcSize ? (srcSize / jobSize) + ((srcSize % jobSize) != 0) : 1;
566 size_t const frameEnd = BHSize + (prefs->frameInfo.contentChecksumFlag*BFSize);
567 return nbJobs * LZ4F_blocksBound(jobSize, blockSize, prefs->frameInfo.blockChecksumFlag) + frameEnd;
568}
569
570/*! LZ4F_compressFrame_usingCDict() :
571 * Compress srcBuffer using a dictionary, in a single step.
572 * cdict can be NULL, in which case, no dictionary is used.
573 * Success is guaranteed when dstCapacity >= LZ4F_compressFrameBound(srcSize, preferencesPtr).
574 * Otherwise, compression fails with dstCapacity_tooSmall if the frame doesn't fit.
575 * The LZ4F_preferences_t structure is optional : you may provide NULL as argument,
576 * however, it's the only way to provide a dictID, so it's not recommended.
577 * @return : number of bytes written into dstBuffer,
578 * or an error code if it fails (can be tested using LZ4F_isError())
579 */
580size_t LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx,
581 void* dstBuffer, size_t dstCapacity,
582 const void* srcBuffer, size_t srcSize,
583 const LZ4F_CDict* cdict,
584 const LZ4F_preferences_t* preferencesPtr)
585{
586 LZ4F_preferences_t prefs;
587 LZ4F_compressOptions_t options;
588 BYTE* const dstStart = (BYTE*) dstBuffer;
589 BYTE* dstPtr = dstStart;
590 BYTE* const dstEnd = dstStart + dstCapacity;
591
592 DEBUGLOG(4, "LZ4F_compressFrame_usingCDict (srcSize=%u)", (unsigned)srcSize){};
593 if (preferencesPtr!=NULL((void*)0))
594 prefs = *preferencesPtr;
595 else
596 MEM_INIT(&prefs, 0, sizeof(prefs))memset((&prefs),(0),(sizeof(prefs)));
597 if (prefs.frameInfo.contentSize != 0)
598 prefs.frameInfo.contentSize = (U64)srcSize; /* auto-correct content size if selected (!=0) */
599
600 prefs.frameInfo.blockSizeID = LZ4F_optimalBSID(prefs.frameInfo.blockSizeID, srcSize);
601 prefs.autoFlush = 1;
602 if (srcSize <= LZ4F_getBlockSize(prefs.frameInfo.blockSizeID))
603 prefs.frameInfo.blockMode = LZ4F_blockIndependent; /* only one block => no need for inter-block link */
604
605 if (LZ4F_mtEligible(&prefs)) {
606 /* a single job produces the same output as single-threaded compression, without the setup cost */
607 size_t const blockSize = LZ4F_getBlockSize(prefs.frameInfo.blockSizeID);
608 if (LZ4F_isError(blockSize) || srcSize <= LZ4F_mtJobSize(blockSize))
609 prefs.nbWorkers = 0;
610 }
611
612 MEM_INIT(&options, 0, sizeof(options))memset((&options),(0),(sizeof(options)));
613 options.stableSrc = LZ4F_srcStableUntilFlush; /* src remains until LZ4F_compressEnd() below */
614
615 { size_t const headerSize = LZ4F_compressBegin_usingCDict(cctx, dstBuffer, dstCapacity, cdict, &prefs); /* write header */
616 FORWARD_IF_ERROR(headerSize)do { size_t const err_code = (headerSize); if (LZ4F_isError(err_code
)) return err_code; } while (0)
;
617 dstPtr += headerSize; /* header size */ }
618
619 assert(dstEnd >= dstPtr)((void)0);
620 { size_t const cSize = LZ4F_compressUpdate(cctx, dstPtr, (size_t)(dstEnd-dstPtr), srcBuffer, srcSize, &options);
621 FORWARD_IF_ERROR(cSize)do { size_t const err_code = (cSize); if (LZ4F_isError(err_code
)) return err_code; } while (0)
;
622 dstPtr += cSize; }
623
624 assert(dstEnd >= dstPtr)((void)0);
625 { size_t const tailSize = LZ4F_compressEnd(cctx, dstPtr, (size_t)(dstEnd-dstPtr), &options); /* flush last block, and generate suffix */
626 int const completed = !LZ4F_isError(tailSize) && ((cctx->cStage == 0) || (cctx->flushStage == 3));
627 if (!completed) {
628 /* the frame doesn't fit */
629 LZ4F_invalidateFrame(cctx);
630 return LZ4F_isError(tailSize) ? tailSize : LZ4F_returnErrorCode(LZ4F_ERROR_dstCapacity_tooSmall);
631 }
632 cctx->cStage = 0;
633 cctx->flushStage = 0;
634 dstPtr += tailSize; }
635
636 assert(dstEnd >= dstStart)((void)0);
637 return (size_t)(dstPtr - dstStart);
638}
639
640
641/*! LZ4F_compressFrame() :
642 * Compress an entire srcBuffer into a valid LZ4 frame, in a single step.
643 * Success is guaranteed when dstCapacity >= LZ4F_compressFrameBound(srcSize, preferencesPtr).
644 * Otherwise, compression fails with dstCapacity_tooSmall if the frame doesn't fit.
645 * The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default.
646 * @return : number of bytes written into dstBuffer.
647 * or an error code if it fails (can be tested using LZ4F_isError())
648 */
649size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity,
650 const void* srcBuffer, size_t srcSize,
651 const LZ4F_preferences_t* preferencesPtr)
652{
653 size_t result;
654#if (LZ4F_HEAPMODE0)
655 LZ4F_cctx_t* cctxPtr;
656 result = LZ4F_createCompressionContext(&cctxPtr, LZ4F_VERSION100);
657 FORWARD_IF_ERROR(result)do { size_t const err_code = (result); if (LZ4F_isError(err_code
)) return err_code; } while (0)
;
658#else
659 LZ4F_cctx_t cctx;
660 LZ4_stream_t lz4ctx;
661 LZ4F_cctx_t* const cctxPtr = &cctx;
662
663 MEM_INIT(&cctx, 0, sizeof(cctx))memset((&cctx),(0),(sizeof(cctx)));
664 cctx.version = LZ4F_VERSION100;
665 cctx.tmpBuffCapacity = 5 MB*(1<<20); /* mess with real buffer size to prevent dynamic allocation; works only because autoflush==1 & stableSrc */
666 if ( preferencesPtr == NULL((void*)0)
667 || preferencesPtr->compressionLevel < LZ4HC_CLEVEL_MIN2 ) {
668 LZ4_initStream(&lz4ctx, sizeof(lz4ctx));
669 cctxPtr->lz4CtxPtr = &lz4ctx;
670 cctxPtr->lz4CtxAlloc = ctxFast;
671 cctxPtr->lz4CtxType = ctxFast;
672 }
673#endif
674 DEBUGLOG(4, "LZ4F_compressFrame"){};
675
676 result = LZ4F_compressFrame_usingCDict(cctxPtr, dstBuffer, dstCapacity,
677 srcBuffer, srcSize,
678 NULL((void*)0), preferencesPtr);
679
680#if (LZ4F_HEAPMODE0)
681 LZ4F_freeCompressionContext(cctxPtr);
682#else
683 if ( preferencesPtr != NULL((void*)0)
684 && preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN2 ) {
685 LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);
686 }
687 LZ4F_mt_free(cctxPtr->mt, cctxPtr->cmem);
688 LZ4F_free(cctxPtr->stageBuff, cctxPtr->cmem);
689#endif
690 return result;
691}
692
693
694/*-***************************************************
695* Dictionary compression
696*****************************************************/
697
698LZ4F_CDict*
699LZ4F_createCDict_advanced(LZ4F_CustomMem cmem, const void* dictBuffer, size_t dictSize)
700{
701 const char* dictStart = (const char*)dictBuffer;
702 LZ4F_CDict* cdict = NULL((void*)0);
703
704 DEBUGLOG(4, "LZ4F_createCDict_advanced"){};
705
706 if (!dictStart)
707 return NULL((void*)0);
708 cdict = (LZ4F_CDict*)LZ4F_malloc(sizeof(*cdict), cmem);
709 if (!cdict)
710 return NULL((void*)0);
711
712 cdict->cmem = cmem;
713 if (dictSize > 64 KB*(1<<10)) {
714 dictStart += dictSize - 64 KB*(1<<10);
715 dictSize = 64 KB*(1<<10);
716 }
717 cdict->dictContent = LZ4F_malloc(dictSize, cmem);
718 /* note: using @cmem to allocate => can't use default create */
719 cdict->fastCtx = (LZ4_stream_t*)LZ4F_malloc(sizeof(LZ4_stream_t), cmem);
720 cdict->HCCtx = (LZ4_streamHC_t*)LZ4F_malloc(sizeof(LZ4_streamHC_t), cmem);
721 if (!cdict->dictContent || !cdict->fastCtx || !cdict->HCCtx) {
722 LZ4F_freeCDict(cdict);
723 return NULL((void*)0);
724 }
725 memcpy(cdict->dictContent, dictStart, dictSize);
726 LZ4_initStream(cdict->fastCtx, sizeof(LZ4_stream_t));
727 LZ4_loadDictSlow(cdict->fastCtx, (const char*)cdict->dictContent, (int)dictSize);
728 LZ4_initStreamHC(cdict->HCCtx, sizeof(LZ4_streamHC_t));
729 /* note: we don't know at this point which compression level is going to be used
730 * as a consequence, HCCtx is created for the more common HC mode */
731 LZ4_setCompressionLevel(cdict->HCCtx, LZ4HC_CLEVEL_DEFAULT9);
732 LZ4_loadDictHC(cdict->HCCtx, (const char*)cdict->dictContent, (int)dictSize);
733 return cdict;
734}
735
736/*! LZ4F_createCDict() :
737 * When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once.
738 * LZ4F_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay.
739 * LZ4F_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.
740 * @dictBuffer can be released after LZ4F_CDict creation, since its content is copied within CDict
741 * @return : digested dictionary for compression, or NULL if failed */
742LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize)
743{
744 DEBUGLOG(4, "LZ4F_createCDict"){};
745 return LZ4F_createCDict_advanced(LZ4F_defaultCMem, dictBuffer, dictSize);
746}
747
748void LZ4F_freeCDict(LZ4F_CDict* cdict)
749{
750 if (cdict==NULL((void*)0)) return; /* support free on NULL */
751 LZ4F_free(cdict->dictContent, cdict->cmem);
752 LZ4F_free(cdict->fastCtx, cdict->cmem);
753 LZ4F_free(cdict->HCCtx, cdict->cmem);
754 LZ4F_free(cdict, cdict->cmem);
755}
756
757
758/*-*********************************
759* Advanced compression functions
760***********************************/
761
762LZ4F_cctx*
763LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version)
764{
765 LZ4F_cctx* const cctxPtr =
766 (LZ4F_cctx*)LZ4F_calloc(sizeof(LZ4F_cctx), customMem);
767 if (cctxPtr==NULL((void*)0)) return NULL((void*)0);
768
769 cctxPtr->cmem = customMem;
770 cctxPtr->version = version;
771 cctxPtr->cStage = 0; /* Uninitialized. Next stage : init cctx */
772
773 return cctxPtr;
774}
775
776/*! LZ4F_createCompressionContext() :
777 * The first thing to do is to create a compressionContext object, which will be used in all compression operations.
778 * This is achieved using LZ4F_createCompressionContext(), which takes as argument a version and an LZ4F_preferences_t structure.
779 * The version provided MUST be LZ4F_VERSION. It is intended to track potential incompatible differences between different binaries.
780 * The function will provide a pointer to an allocated LZ4F_compressionContext_t object.
781 * If the result LZ4F_errorCode_t is not OK_NoError, there was an error during context creation.
782 * Object can release its memory using LZ4F_freeCompressionContext();
783**/
784LZ4F_errorCode_t
785LZ4F_createCompressionContext(LZ4F_cctx** LZ4F_compressionContextPtr, unsigned version)
786{
787 assert(LZ4F_compressionContextPtr != NULL)((void)0); /* considered a violation of narrow contract */
788 /* in case it nonetheless happen in production */
789 RETURN_ERROR_IF(LZ4F_compressionContextPtr == NULL, parameter_null)do { if (LZ4F_compressionContextPtr == ((void*)0)) { {}; return
LZ4F_returnErrorCode(LZ4F_ERROR_parameter_null); } } while (
0)
;
790
791 *LZ4F_compressionContextPtr = LZ4F_createCompressionContext_advanced(LZ4F_defaultCMem, version);
792 RETURN_ERROR_IF(*LZ4F_compressionContextPtr==NULL, allocation_failed)do { if (*LZ4F_compressionContextPtr==((void*)0)) { {}; return
LZ4F_returnErrorCode(LZ4F_ERROR_allocation_failed); } } while
(0)
;
793 return LZ4F_OK_NoError;
794}
795
796LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctxPtr)
797{
798 if (cctxPtr != NULL((void*)0)) { /* support free on NULL */
799 LZ4F_mt_free(cctxPtr->mt, cctxPtr->cmem); /* first : waits for jobs still in flight */
800 LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); /* note: LZ4_streamHC_t and LZ4_stream_t are simple POD types */
801 LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem);
802 LZ4F_free(cctxPtr->stageBuff, cctxPtr->cmem);
803 LZ4F_free(cctxPtr->rawDictFast, cctxPtr->cmem);
804 LZ4F_free(cctxPtr->rawDictHC, cctxPtr->cmem);
805 LZ4F_free(cctxPtr, cctxPtr->cmem);
806 }
807 return LZ4F_OK_NoError;
808}
809
810
811/**
812 * This function prepares the internal LZ4(HC) stream for a new compression,
813 * resetting the context and attaching the dictionary, if there is one.
814 *
815 * It needs to be called at the beginning of each independent compression
816 * stream (i.e., at the beginning of a frame in blockLinked mode, or at the
817 * beginning of each block in blockIndependent mode).
818 */
819static void LZ4F_initStream(void* ctx,
820 const LZ4F_CDict* cdict,
821 int level,
822 LZ4F_blockMode_t blockMode) {
823 if (level < LZ4HC_CLEVEL_MIN2) {
824 /* In these cases, we will call LZ4_compress_fast_continue(),
825 * which needs an already reset context. Otherwise, we'll call a
826 * one-shot API. The non-continued APIs internally perform their own
827 * resets at the beginning of their calls, where they know what
828 * tableType they need the context to be in. So in that case this
829 * would be misguided / wasted work. */
830 if (cdict) {
831 LZ4_resetStream_fast((LZ4_stream_t*)ctx);
832 LZ4_attach_dictionary((LZ4_stream_t*)ctx, cdict->fastCtx);
833 } else if (blockMode == LZ4F_blockLinked) {
834 /* Clear the table, rather than LZ4_resetStream_fast() :
835 * output doesn't depend on the context's history,
836 * and it's faster, unless the frame is tiny. */
837 LZ4_initStream(ctx, sizeof(LZ4_stream_t));
838 }
839 /* In these cases, we'll call a one-shot API.
840 * The non-continued APIs internally perform their own resets
841 * at the beginning of their calls, where they know
842 * which tableType they need the context to be in.
843 * Therefore, a reset here would be wasted work. */
844 } else {
845 LZ4_resetStreamHC_fast((LZ4_streamHC_t*)ctx, level);
846 if (cdict)
847 LZ4_attach_HC_dictionary((LZ4_streamHC_t*)ctx, cdict->HCCtx);
848 }
849}
850
851/* LZ4F_startStream() :
852 * starts the LZ4 stream @ctx at the compression level of @prefs : for a frame, a job, or after uncompressed blocks.
853 * With linked blocks, the stream starts from dictionary @cdict, or from history @dict (@dictSize bytes) ;
854 * independent blocks start their own stream (see LZ4F_compressBlock()). */
855static void LZ4F_startStream(void* ctx, const LZ4F_preferences_t* prefs,
856 const LZ4F_CDict* cdict, const void* dict, size_t dictSize)
857{
858 int const level = prefs->compressionLevel;
859 if (prefs->frameInfo.blockMode == LZ4F_blockLinked)
860 LZ4F_initStream(ctx, cdict, level, LZ4F_blockLinked);
861 if (level >= LZ4HC_CLEVEL_MIN2)
862 LZ4_favorDecompressionSpeed((LZ4_streamHC_t*)ctx, (int)prefs->favorDecSpeed);
863 if (dict) {
864 assert(cdict == NULL)((void)0);
865 assert(prefs->frameInfo.blockMode == LZ4F_blockLinked)((void)0);
866 assert(dictSize <= INT_MAX)((void)0);
867 if (level < LZ4HC_CLEVEL_MIN2) {
868 LZ4_loadDict((LZ4_stream_t*)ctx, (const char*)dict, (int)dictSize);
869 } else {
870 LZ4_loadDictHC((LZ4_streamHC_t*)ctx, (const char*)dict, (int)dictSize);
871 }
872 }
873}
874
875/* LZ4F_sizeofLZ4State() :
876 * @return the size of the inner LZ4 state (pointed by lz4CtxPtr) for @ctxType :
877 * LZ4_stream_t for fast levels, LZ4_streamHC_t for HC levels.
878 * ctxNone means no state allocated, hence a size of 0.
879 * Note : the larger of fast and HC states depends on LZ4_MEMORY_USAGE,
880 * so reusing an allocated state requires comparing sizes. */
881static size_t LZ4F_sizeofLZ4State(LZ4F_CtxType_e ctxType)
882{
883 switch (ctxType) {
884 case ctxNone: return 0;
885 case ctxFast: return sizeof(LZ4_stream_t);
886 case ctxHC: return LZ4_STREAMHC_EXTSIZE(262200 + 65584); /* extended : levels >= 10 allocate nothing */
887 }
888 assert(0)((void)0); /* invalid ctxType */
889 return 0;
890}
891
892/* LZ4F_initLZ4State() :
893 * initializes the inner LZ4 state @state for @ctxType (fast or HC), at compression @level */
894static void LZ4F_initLZ4State(void* state, LZ4F_CtxType_e ctxType, int level)
895{
896 if (ctxType == ctxFast) {
897 LZ4_initStream(state, sizeof(LZ4_stream_t));
898 } else {
899 assert(ctxType == ctxHC)((void)0);
900 LZ4_initStreamHC(state, LZ4_STREAMHC_EXTSIZE(262200 + 65584));
901 LZ4_setCompressionLevel((LZ4_streamHC_t*)state, level);
902 }
903}
904
905size_t LZ4F_cctx_size(const LZ4F_cctx* cctx) {
906 if (cctx == NULL((void*)0)) {
907 return 0;
908 }
909 return sizeof(*cctx) + cctx->tmpBuffCapacity + cctx->stageCapacity + LZ4F_sizeofLZ4State((LZ4F_CtxType_e)cctx->lz4CtxAlloc)
910 + (cctx->rawDictFast ? sizeof(LZ4_stream_t) : 0) + (cctx->rawDictHC ? sizeof(LZ4_streamHC_t) : 0)
911 + LZ4F_mt_sizeof(cctx->mt);
912}
913
914/* LZ4F_digestRawDict() :
915 * digests @dictBuffer within states owned by @cctx, without copying it (it must outlive the compression session),
916 * for the compression level of current frame, which then employs it as a CDict.
917 * Since it's done for each frame, fast levels employ LZ4_loadDict(), faster than LZ4F_createCDict()'s LZ4_loadDictSlow().
918 * HC levels digest it for the frame's level : level 2 (lz4mid) has its own format,
919 * which a CDict (digested at default level) lacks.
920 * @return : CDict, or NULL if allocation failed */
921static const LZ4F_CDict* LZ4F_digestRawDict(LZ4F_cctx_t* cctx, const void* dictBuffer, size_t dictSize)
922{
923 const char* dictStart = (const char*)dictBuffer;
924 if (dictSize > 64 KB*(1<<10)) {
925 dictStart += dictSize - 64 KB*(1<<10);
926 dictSize = 64 KB*(1<<10);
927 }
928 cctx->rawDict.cmem = cctx->cmem;
929 cctx->rawDict.dictContent = NULL((void*)0);
930 cctx->rawDict.fastCtx = NULL((void*)0);
931 cctx->rawDict.HCCtx = NULL((void*)0);
932 if (cctx->prefs.compressionLevel < LZ4HC_CLEVEL_MIN2) {
933 if (cctx->rawDictFast == NULL((void*)0)) cctx->rawDictFast = (LZ4_stream_t*)LZ4F_malloc(sizeof(LZ4_stream_t), cctx->cmem);
934 if (cctx->rawDictFast == NULL((void*)0)) return NULL((void*)0);
935 LZ4_initStream(cctx->rawDictFast, sizeof(LZ4_stream_t));
936 LZ4_loadDict(cctx->rawDictFast, dictStart, (int)dictSize);
937 cctx->rawDict.fastCtx = cctx->rawDictFast;
938 } else {
939 if (cctx->rawDictHC == NULL((void*)0)) cctx->rawDictHC = (LZ4_streamHC_t*)LZ4F_malloc(sizeof(LZ4_streamHC_t), cctx->cmem);
940 if (cctx->rawDictHC == NULL((void*)0)) return NULL((void*)0);
941 LZ4_initStreamHC(cctx->rawDictHC, sizeof(LZ4_streamHC_t));
942 LZ4_setCompressionLevel(cctx->rawDictHC, cctx->prefs.compressionLevel);
943 LZ4_loadDictHC(cctx->rawDictHC, dictStart, (int)dictSize);
944 cctx->rawDict.HCCtx = cctx->rawDictHC;
945 }
946 return &cctx->rawDict;
947}
948
949/* LZ4F_compressBegin_internal()
950 * Note: only accepts @cdict _or_ @dictBuffer as non NULL.
951 */
952static size_t LZ4F_compressBegin_internal(LZ4F_cctx* cctx,
953 void* dstBuffer, size_t dstCapacity,
954 const void* dictBuffer, size_t dictSize,
955 const LZ4F_CDict* cdict,
956 const LZ4F_preferences_t* preferencesPtr)
957{
958 LZ4F_preferences_t const prefNull = LZ4F_INIT_PREFERENCES{ { LZ4F_max64KB, LZ4F_blockLinked, LZ4F_noContentChecksum, LZ4F_frame
, 0ULL, 0U, LZ4F_noBlockChecksum }, 0, 0u, 0u, 0u, 0u, 0u }
;
959 BYTE* const dstStart = (BYTE*)dstBuffer;
960 BYTE* dstPtr = dstStart;
961 int mtFrame; /* frame cut into jobs (multi-threaded compression) */
962
963 if (preferencesPtr == NULL((void*)0)) preferencesPtr = &prefNull;
964 /* validate arguments before modifying @cctx : when they are rejected, a frame in progress continues */
965 { size_t const headerSize = minFHSize
966 + (preferencesPtr->frameInfo.contentSize ? 8 : 0)
967 + (preferencesPtr->frameInfo.dictID ? 4 : 0);
968 RETURN_ERROR_IF(dstCapacity < headerSize, dstCapacity_tooSmall)do { if (dstCapacity < headerSize) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_dstCapacity_tooSmall); } } while (0)
;
969 }
970 FORWARD_IF_ERROR( LZ4F_getBlockSize(preferencesPtr->frameInfo.blockSizeID) )do { size_t const err_code = (LZ4F_getBlockSize(preferencesPtr
->frameInfo.blockSizeID)); if (LZ4F_isError(err_code)) return
err_code; } while (0)
;
971 RETURN_ERROR_IF(dictSize > INT_MAX, parameter_invalid)do { if (dictSize > 2147483647) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_parameter_invalid); } } while (0)
;
972 /* from now on, a failure (allocation) leaves no frame in progress */
973 LZ4F_invalidateFrame(cctx); /* jobs of an abandoned frame : must complete before @cctx is modified */
974 cctx->mtActive = 0;
975 cctx->flushStage = 0;
976 cctx->pendingSize = 0;
977 cctx->prefs = *preferencesPtr;
978 mtFrame = LZ4F_mtEligible(&cctx->prefs);
979 DEBUGLOG(5, "LZ4F_compressBegin_internal: Independent_blocks=%u", cctx->prefs.frameInfo.blockMode){};
980
981 /* cctx Management */
982 { LZ4F_CtxType_e const ctxType = (cctx->prefs.compressionLevel < LZ4HC_CLEVEL_MIN2) ? ctxFast : ctxHC;
983 size_t const requiredSize = LZ4F_sizeofLZ4State(ctxType);
984 size_t const allocatedSize = LZ4F_sizeofLZ4State((LZ4F_CtxType_e)cctx->lz4CtxAlloc);
985 if (allocatedSize < requiredSize) {
986 /* not enough space allocated : must take ownership of memory allocation,
987 * in order to respect custom allocator contract */
988 LZ4F_free(cctx->lz4CtxPtr, cctx->cmem);
989 cctx->lz4CtxAlloc = (U16)ctxNone; /* in case allocation fails */
990 cctx->lz4CtxPtr = LZ4F_malloc(requiredSize, cctx->cmem);
991 RETURN_ERROR_IF(cctx->lz4CtxPtr == NULL, allocation_failed)do { if (cctx->lz4CtxPtr == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_allocation_failed); } } while (0)
;
992 cctx->lz4CtxAlloc = (U16)ctxType;
993 cctx->lz4CtxType = (U16)ctxNone; /* not initialized yet */
994 }
995 if (cctx->lz4CtxType != ctxType) {
996 /* initialize the state for the correct context type */
997 LZ4F_initLZ4State(cctx->lz4CtxPtr, ctxType, cctx->prefs.compressionLevel);
998 cctx->lz4CtxType = (U16)ctxType;
999 } }
1000
1001 /* raw dictionary : employed by each block as a CDict (independent blocks, or jobs of multi-threaded frames) ;
1002 * single-threaded linked blocks keep the historical behavior : dictionary loaded as history, preceding the first block,
1003 * from a copy within @tmpBuff (see below) */
1004 if ( dictBuffer
1005 && ((cctx->prefs.frameInfo.blockMode == LZ4F_blockIndependent) || mtFrame) ) {
1006 assert(cdict == NULL)((void)0);
1007 cdict = LZ4F_digestRawDict(cctx, dictBuffer, dictSize);
1008 RETURN_ERROR_IF(cdict == NULL, allocation_failed)do { if (cdict == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_allocation_failed); } } while (0)
;
1009 dictBuffer = NULL((void*)0);
1010 dictSize = 0;
1011 }
1012
1013 /* Buffer Management */
1014 if (cctx->prefs.frameInfo.blockSizeID == 0)
1015 cctx->prefs.frameInfo.blockSizeID = LZ4F_BLOCKSIZEID_DEFAULTLZ4F_max64KB;
1016 cctx->maxBlockSize = LZ4F_getBlockSize(cctx->prefs.frameInfo.blockSizeID);
1017
1018 { size_t const requiredBuffSize = mtFrame ? 0 : /* jobs employ their own buffers */
1019 preferencesPtr->autoFlush ?
1020 ((cctx->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 128 KB*(1<<10) : 0) : /* history, then inputs < 64 KB (see LZ4F_st_update()) */
1021 cctx->maxBlockSize + ((cctx->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 128 KB*(1<<10) : 0);
1022
1023 if (cctx->tmpBuffCapacity < requiredBuffSize) {
1024 cctx->tmpBuffCapacity = 0;
1025 LZ4F_free(cctx->tmpBuff, cctx->cmem);
1026 cctx->tmpBuff = (BYTE*)LZ4F_malloc(requiredBuffSize, cctx->cmem);
1027 RETURN_ERROR_IF(cctx->tmpBuff == NULL, allocation_failed)do { if (cctx->tmpBuff == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_allocation_failed); } } while (0)
;
1028 cctx->tmpBuffCapacity = requiredBuffSize;
1029 } }
1030 cctx->tmpIn = cctx->tmpBuff;
1031 cctx->tmpInSize = 0;
1032 if (dictBuffer) {
1033 /* single-threaded linked blocks : the dictionary is copied into @tmpBuff, as saved history :
1034 * buffered input follows it, and references it in prefix mode
1035 * (faster than an external dictionary, which fast levels also lose after the first block) */
1036 if (dictSize > 64 KB*(1<<10)) {
1037 dictBuffer = (const BYTE*)dictBuffer + dictSize - 64 KB*(1<<10);
1038 dictSize = 64 KB*(1<<10);
1039 }
1040 assert(cctx->tmpBuffCapacity >= 128 KB)((void)0);
1041 memcpy(cctx->tmpBuff, dictBuffer, dictSize);
1042 dictBuffer = cctx->tmpBuff;
1043 cctx->tmpIn = cctx->tmpBuff + dictSize;
1044 }
1045 (void)XXH32_reset(&(cctx->xxh), 0);
1046
1047 /* context init */
1048 cctx->cdict = cdict;
1049 cctx->blockCompressMode = LZ4B_COMPRESSED; /* a frame starts with the LZ4 state just initialized (dictionary included) */
1050 LZ4F_startStream(cctx->lz4CtxPtr, &cctx->prefs, cdict, dictBuffer, dictSize);
1051
1052 /* Stage 2 : Write Frame Header */
1053
1054 /* Magic Number */
1055 LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER0x184D2204U);
1056 dstPtr += 4;
1057 { BYTE* const headerStart = dstPtr;
1058
1059 /* FLG Byte */
1060 *dstPtr++ = (BYTE)(((1 & _2BITS0x03) << 6) /* Version('01') */
1061 + ((cctx->prefs.frameInfo.blockMode & _1BIT0x01 ) << 5)
1062 + ((cctx->prefs.frameInfo.blockChecksumFlag & _1BIT0x01 ) << 4)
1063 + ((unsigned)(cctx->prefs.frameInfo.contentSize > 0) << 3)
1064 + ((cctx->prefs.frameInfo.contentChecksumFlag & _1BIT0x01 ) << 2)
1065 + (cctx->prefs.frameInfo.dictID > 0) );
1066 /* BD Byte */
1067 *dstPtr++ = (BYTE)((cctx->prefs.frameInfo.blockSizeID & _3BITS0x07) << 4);
1068 /* Optional Frame content size field */
1069 if (cctx->prefs.frameInfo.contentSize) {
1070 LZ4F_writeLE64(dstPtr, cctx->prefs.frameInfo.contentSize);
1071 dstPtr += 8;
1072 cctx->totalInSize = 0;
1073 }
1074 /* Optional dictionary ID field */
1075 if (cctx->prefs.frameInfo.dictID) {
1076 LZ4F_writeLE32(dstPtr, cctx->prefs.frameInfo.dictID);
1077 dstPtr += 4;
1078 }
1079 /* Header CRC Byte */
1080 *dstPtr = LZ4F_headerChecksum(headerStart, (size_t)(dstPtr - headerStart));
1081 dstPtr++;
1082 }
1083
1084 if (mtFrame) {
1085 assert(dictBuffer == NULL)((void)0);
1086 FORWARD_IF_ERROR( LZ4F_mt_begin(cctx) )do { size_t const err_code = (LZ4F_mt_begin(cctx)); if (LZ4F_isError
(err_code)) return err_code; } while (0)
;
1087 }
1088
1089 cctx->cStage = 1; /* header written, now request input data block */
1090 return (size_t)(dstPtr - dstStart);
1091}
1092
1093size_t LZ4F_compressBegin(LZ4F_cctx* cctx,
1094 void* dstBuffer, size_t dstCapacity,
1095 const LZ4F_preferences_t* preferencesPtr)
1096{
1097 return LZ4F_compressBegin_internal(cctx, dstBuffer, dstCapacity,
1098 NULL((void*)0), 0,
1099 NULL((void*)0), preferencesPtr);
1100}
1101
1102size_t LZ4F_compressBegin_usingDict(LZ4F_cctx* cctx,
1103 void* dstBuffer, size_t dstCapacity,
1104 const void* dict, size_t dictSize,
1105 const LZ4F_preferences_t* preferencesPtr)
1106{
1107 return LZ4F_compressBegin_internal(cctx, dstBuffer, dstCapacity,
1108 dict, dictSize,
1109 NULL((void*)0), preferencesPtr);
1110}
1111
1112size_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctx,
1113 void* dstBuffer, size_t dstCapacity,
1114 const LZ4F_CDict* cdict,
1115 const LZ4F_preferences_t* preferencesPtr)
1116{
1117 return LZ4F_compressBegin_internal(cctx, dstBuffer, dstCapacity,
1118 NULL((void*)0), 0,
1119 cdict, preferencesPtr);
1120}
1121
1122
1123/* LZ4F_compressBound() :
1124 * @return minimum capacity of dstBuffer for a given srcSize to handle worst case scenario.
1125 * LZ4F_preferences_t structure is optional : if NULL, preferences will be set to cover worst case scenario.
1126 * This function cannot fail.
1127 */
1128size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
1129{
1130 size_t bound;
1131 size_t const blockCRCSize = preferencesPtr
1132 ? BFSize * preferencesPtr->frameInfo.blockChecksumFlag
1133 : BFSize;
1134 if (preferencesPtr && !preferencesPtr->autoFlush && LZ4F_mtEligible(preferencesPtr)) {
1135 /* frame cut into jobs : output can lag behind input */
1136 return LZ4F_mtCompressBound(srcSize, preferencesPtr);
1137 }
1138 if (preferencesPtr && preferencesPtr->autoFlush) {
1139 /* + 1 : output of LZ4F_flush() and LZ4F_compressEnd() remains strictly below LZ4F_compressBound(0),
1140 * since a filled dstBuffer means more output may remain */
1141 return LZ4F_compressBound_autoFlush(srcSize, preferencesPtr) + (srcSize == 0);
1142 }
1143 /* single-threaded : full blocks only, each one stored uncompressed in worst case.
1144 * Up to (blockSize-1) bytes can already be buffered : srcSize completes at most srcSize/blockSize + 1 blocks.
1145 * The buffered block can also be emitted separately (mode switch), or remain from an incomplete LZ4F_flush() :
1146 * then, srcSize completes at most srcSize/blockSize blocks.
1147 * @srcSize == 0 : LZ4F_flush() and LZ4F_compressEnd() emit the buffered block (< blockSize bytes : output
1148 * remains strictly below the bound, so that a single invocation completes them), then the frame footer.
1149 * The frame footer is always included, so that the bound grows with @srcSize. */
1150 { size_t const bs = LZ4F_getBlockSize(preferencesPtr ? preferencesPtr->frameInfo.blockSizeID : LZ4F_default);
1151 size_t const blockSize = LZ4F_isError(bs) ? 4 MB*(1<<20) : bs;
1152 size_t const contentCRCSize = preferencesPtr
1153 ? BFSize * preferencesPtr->frameInfo.contentChecksumFlag
1154 : BFSize;
1155 size_t const nbBlocks = srcSize / blockSize + 1;
1156 bound = nbBlocks * (blockSize + BHSize + blockCRCSize) + BHSize + contentCRCSize;
1157 }
1158 return bound;
1159}
1160
1161
1162typedef int (*compressFunc_t)(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level, const LZ4F_CDict* cdict);
1163
1164
1165/*! LZ4F_makeBlock():
1166 * compress a single block, add header and optional checksum.
1167 * The block is stored uncompressed when compression doesn't save space.
1168 * @return : size of the block written into @dst,
1169 * or an error code if it doesn't fit into @dstCapacity.
1170 * Note : success is guaranteed when dstCapacity >= BHSize + srcSize + crcSize
1171 */
1172static size_t LZ4F_makeBlock(void* dst, size_t dstCapacity,
1173 const void* src, size_t srcSize,
1174 compressFunc_t compress, void* lz4ctx, int level,
1175 const LZ4F_CDict* cdict,
1176 LZ4F_blockChecksum_t crcFlag)
1177{
1178 BYTE* const cSizePtr = (BYTE*)dst;
1179 size_t const overhead = BHSize + (size_t)crcFlag * BFSize;
1180 int cCapacity = (srcSize > 1) ? (int)srcSize - 1 : 1;
1181 U32 cSize;
1182 assert(compress != NULL)((void)0);
1183 assert(srcSize > 0)((void)0);
1184 RETURN_ERROR_IF(dstCapacity <= overhead, dstCapacity_tooSmall)do { if (dstCapacity <= overhead) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_dstCapacity_tooSmall); } } while (0)
;
1185 if (dstCapacity - overhead < (size_t)cCapacity)
1186 cCapacity = (int)(dstCapacity - overhead);
1187 cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+BHSize),
1188 (int)srcSize, cCapacity,
1189 level, cdict);
1190
1191 if (cSize == 0 || cSize >= srcSize) {
1192 RETURN_ERROR_IF(dstCapacity - overhead < srcSize, dstCapacity_tooSmall)do { if (dstCapacity - overhead < srcSize) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_dstCapacity_tooSmall); } } while (0)
;
1193 cSize = (U32)srcSize;
1194 LZ4F_writeLE32(cSizePtr, cSize | LZ4F_BLOCKUNCOMPRESSED_FLAG0x80000000U);
1195 memcpy(cSizePtr+BHSize, src, srcSize);
1196 } else {
1197 LZ4F_writeLE32(cSizePtr, cSize);
1198 }
1199 if (crcFlag) {
1200 U32 const crc32 = XXH32(cSizePtr+BHSize, cSize, 0); /* checksum of compressed data */
1201 LZ4F_writeLE32(cSizePtr+BHSize+cSize, crc32);
1202 }
1203 return BHSize + cSize + ((U32)crcFlag)*BFSize;
1204}
1205
1206/*! LZ4F_compressBlocks_internal():
1207 * compress @src as a sequence of blocks of @blockSize bytes (the last one can be smaller),
1208 * each with its header and optional checksum, but without frame header, endMark nor content checksum.
1209 * @lz4ctx must be prepared for @compress :
1210 * linked blocks continue the stream, so they need a state initialized at frame start (or with a prefix).
1211 * @return : nb of bytes written into @dst,
1212 * or an error code if it doesn't fit into @dstCapacity.
1213 * Note : success is guaranteed when dstCapacity >= srcSize + nbBlocks * (BHSize + crcFlag * BFSize)
1214 */
1215static size_t LZ4F_compressBlocks_internal(void* dst, size_t dstCapacity,
1216 const void* src, size_t srcSize, size_t blockSize,
1217 compressFunc_t compress, void* lz4ctx, int level,
1218 const LZ4F_CDict* cdict,
1219 LZ4F_blockChecksum_t crcFlag)
1220{
1221 const BYTE* ip = (const BYTE*)src;
1222 const BYTE* const iend = srcSize ? ip + srcSize : ip;
1223 BYTE* const ostart = (BYTE*)dst;
1224 BYTE* op = ostart;
1225 BYTE* const oend = dstCapacity ? ostart + dstCapacity : ostart;
1226
1227 while (ip < iend) {
1228 size_t const bSize = MIN(blockSize, (size_t)(iend - ip))( (blockSize) < ((size_t)(iend - ip)) ? (blockSize) : ((size_t
)(iend - ip)) )
;
1229 size_t const cSize = LZ4F_makeBlock(op, (size_t)(oend - op),
1230 ip, bSize,
1231 compress, lz4ctx, level,
1232 cdict, crcFlag);
1233 FORWARD_IF_ERROR(cSize)do { size_t const err_code = (cSize); if (LZ4F_isError(err_code
)) return err_code; } while (0)
;
1234 ip += bSize;
1235 op += cSize;
1236 }
1237 return (size_t)(op - ostart);
1238}
1239
1240
1241/* Negative compression levels select LZ4 "fast" mode, with acceleration = -level + 1.
1242 * Acceleration is capped at LZ4_ACCELERATION_MAX before this computation,
1243 * which would overflow for INT_MIN and INT_MIN+1. */
1244static int LZ4F_levelToAcceleration(int level)
1245{
1246 if (level >= 0) return 1;
1247 if (level < 1 - LZ4_ACCELERATION_MAX65537) return LZ4_ACCELERATION_MAX65537;
1248 return -level + 1;
1249}
1250
1251static int LZ4F_compressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
1252{
1253 int const acceleration = LZ4F_levelToAcceleration(level);
1254 DEBUGLOG(5, "LZ4F_compressBlock (srcSize=%i)", srcSize){};
1255 LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent);
1256 if (cdict) {
1257 return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration);
1258 } else {
1259 return LZ4_compress_fast_extState_fastReset(ctx, src, dst, srcSize, dstCapacity, acceleration);
1260 }
1261}
1262
1263static int LZ4F_compressBlock_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
1264{
1265 int const acceleration = LZ4F_levelToAcceleration(level);
1266 (void)cdict; /* init once at beginning of frame */
1267 DEBUGLOG(5, "LZ4F_compressBlock_continue (srcSize=%i)", srcSize){};
1268 return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration);
1269}
1270
1271static int LZ4F_compressBlockHC(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
1272{
1273 LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent);
1274 if (cdict) {
1275 return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity);
1276 }
1277 return LZ4_compress_HC_extStateHC_fastReset(ctx, src, dst, srcSize, dstCapacity, level);
1278}
1279
1280static int LZ4F_compressBlockHC_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
1281{
1282 (void)level; (void)cdict; /* init once at beginning of frame */
1283 return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity);
1284}
1285
1286static int LZ4F_doNotCompressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
1287{
1288 (void)ctx; (void)src; (void)dst; (void)srcSize; (void)dstCapacity; (void)level; (void)cdict;
1289 return 0;
1290}
1291
1292static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int level, LZ4F_BlockCompressMode_e compressMode)
1293{
1294 if (compressMode == LZ4B_UNCOMPRESSED)
1295 return LZ4F_doNotCompressBlock;
1296 if (level < LZ4HC_CLEVEL_MIN2) {
1297 if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlock;
1298 return LZ4F_compressBlock_continue;
1299 }
1300 if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlockHC;
1301 return LZ4F_compressBlockHC_continue;
1302}
1303
1304/* Save or shorten history (up to 64KB) into @tmpBuff */
1305static void LZ4F_localSaveDict(LZ4F_cctx_t* cctxPtr)
1306{
1307 int const dictSize = (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN2) ?
1308 LZ4_saveDict ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB*(1<<10)) :
1309 LZ4_saveDictHC ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB*(1<<10));
1310 cctxPtr->tmpIn = cctxPtr->tmpBuff + dictSize;
1311}
1312
1313typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus;
1314
1315static const LZ4F_compressOptions_t k_cOptionsNull = { 0, 0, 0, 0 };
1316
1317/*-***************************************************
1318* Output not fitting into dstBuffer yet (LZ4F_flush(), LZ4F_compressEnd())
1319*****************************************************/
1320
1321/* stageBuff holds one block, stored uncompressed in worst case.
1322 * @return : 0, or an error code (allocation_failed) */
1323static size_t LZ4F_reserveStage(LZ4F_cctx_t* cctx)
1324{
1325 size_t const needed = cctx->maxBlockSize + BHSize + BFSize;
1326 if (cctx->stageCapacity >= needed) return 0;
1327 LZ4F_free(cctx->stageBuff, cctx->cmem);
1328 cctx->stageCapacity = 0;
1329 cctx->stageBuff = (BYTE*)LZ4F_malloc(needed, cctx->cmem);
1330 RETURN_ERROR_IF(cctx->stageBuff == NULL, allocation_failed)do { if (cctx->stageBuff == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_allocation_failed); } } while (0)
;
1331 cctx->stageCapacity = needed;
1332 return 0;
1333}
1334
1335/* copies pending output into @dst + @pos, as much as fits.
1336 * @return : nb of bytes copied */
1337static size_t LZ4F_copyPending(LZ4F_cctx_t* cctx, BYTE* dst, size_t dstCapacity, size_t pos)
1338{
1339 size_t const n = MIN(cctx->pendingSize, dstCapacity - pos)( (cctx->pendingSize) < (dstCapacity - pos) ? (cctx->
pendingSize) : (dstCapacity - pos) )
;
1340 assert(pos <= dstCapacity)((void)0);
1341 if (n) {
1342 memcpy(dst + pos, cctx->pending, n);
1343 cctx->pending += n;
1344 }
1345 cctx->pendingSize -= n;
1346 return n;
1347}
1348
1349/* compression operations first emit output left pending by an incomplete LZ4F_flush(),
1350 * entirely, since they must succeed : the flush ends there.
1351 * @return : nb of bytes written into @dst, or an error code (callers invalidate the frame) */
1352static size_t LZ4F_emitPendingWhole(LZ4F_cctx_t* cctx, BYTE* dst, size_t dstCapacity)
1353{
1354 cctx->flushStage = 0;
1355 RETURN_ERROR_IF(cctx->pendingSize > dstCapacity, dstCapacity_tooSmall)do { if (cctx->pendingSize > dstCapacity) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_dstCapacity_tooSmall); } } while (0)
;
1356 return LZ4F_copyPending(cctx, dst, dstCapacity, 0);
1357}
1358
1359/*! LZ4F_flushBlock() :
1360 * compresses input buffered within cctx (single-threaded mode), if any, into @dst.
1361 * @return : nb of bytes written into @dst, or an error code (the frame is then invalidated) */
1362static size_t LZ4F_flushBlock(LZ4F_cctx_t* cctxPtr, void* dstBuffer, size_t dstCapacity)
1363{
1364 compressFunc_t compress;
1365 size_t cSize;
1366
1367 DEBUGLOG(5, "LZ4F_flushBlock: %zu buffered bytes (saved dict size = %i) (dstCapacity=%u)",{}
1368 cctxPtr->tmpInSize, (int)(cctxPtr->tmpIn - cctxPtr->tmpBuff), (unsigned)dstCapacity){};
1369 if (cctxPtr->tmpInSize == 0) return 0; /* nothing to flush */
1370
1371 /* select compression function */
1372 compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, cctxPtr->blockCompressMode);
1373
1374 /* compress tmp buffer */
1375 cSize = LZ4F_makeBlock(dstBuffer, dstCapacity,
1376 cctxPtr->tmpIn, cctxPtr->tmpInSize,
1377 compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
1378 cctxPtr->cdict,
1379 cctxPtr->prefs.frameInfo.blockChecksumFlag);
1380 FORWARD_IF_ERROR_INVALIDATE(cSize, cctxPtr)do { size_t const err_code = (cSize); if (LZ4F_isError(err_code
)) { LZ4F_invalidateFrame(cctxPtr); return err_code; } } while
(0)
;
1381 assert(((void)"flush overflows dstBuffer!", cSize <= dstCapacity))((void)0);
1382
1383 if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked)
1384 cctxPtr->tmpIn += cctxPtr->tmpInSize;
1385 cctxPtr->tmpInSize = 0;
1386
1387 /* keep tmpIn within limits */
1388 if ((cctxPtr->tmpIn + cctxPtr->maxBlockSize) > (cctxPtr->tmpBuff + cctxPtr->tmpBuffCapacity)) {
1389 assert(cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked)((void)0);
1390 LZ4F_localSaveDict(cctxPtr);
1391 }
1392
1393 return cSize;
1394}
1395
1396/* flush point, single-threaded mode : the buffered block is compressed into @dst when it fits in any case,
1397 * otherwise into stageBuff, as pending output. Same output : compression capacity is the same.
1398 * @return : nb of bytes written into @dst, or an error code (the frame is then invalidated) */
1399static size_t LZ4F_flushBlockPartial(LZ4F_cctx_t* cctx, BYTE* dst, size_t dstCapacity)
1400{
1401 size_t const worstCase = cctx->tmpInSize + BHSize + (size_t)cctx->prefs.frameInfo.blockChecksumFlag * BFSize;
1402 size_t cSize;
1403 if ((cctx->tmpInSize == 0) || (dstCapacity >= worstCase))
1404 return LZ4F_flushBlock(cctx, dst, dstCapacity);
1405 FORWARD_IF_ERROR_INVALIDATE(LZ4F_reserveStage(cctx), cctx)do { size_t const err_code = (LZ4F_reserveStage(cctx)); if (LZ4F_isError
(err_code)) { LZ4F_invalidateFrame(cctx); return err_code; } }
while (0)
;
1406 cSize = LZ4F_flushBlock(cctx, cctx->stageBuff, cctx->stageCapacity);
1407 FORWARD_IF_ERROR(cSize)do { size_t const err_code = (cSize); if (LZ4F_isError(err_code
)) return err_code; } while (0)
; /* already invalidated */
1408 cctx->pending = cctx->stageBuff;
1409 cctx->pendingSize = cSize;
1410 return 0;
1411}
1412
1413
1414/*-***************************************************
1415* Multi-threaded compression
1416*****************************************************/
1417/* When prefs.nbWorkers >= 1, input is cut into jobs (see LZ4F_mtJobSize()).
1418 * Jobs are compressed by a pool of worker threads, kept within cctx,
1419 * and their outputs are emitted in order by the calling thread.
1420 * Output only depends on content and flush points :
1421 * never on nb of workers, scheduling, input slicing, nor stableSrc.
1422 * nbWorkers is the nb of worker threads compressing jobs : the calling thread fills and emits them.
1423 * At a flush point (LZ4F_flush(), LZ4F_compressEnd(), autoFlush), it waits for all jobs : when no other job is running,
1424 * it compresses the flushed job itself. So at most nbWorkers threads compress at any time.
1425 * Without multi-threading support (LZ4F_MULTITHREAD), the calling thread processes jobs alone, producing the same output.
1426 * If no worker thread can be created, frame compression fails (allocation_failed) ;
1427 * with fewer threads than requested (ex: system limits), it proceeds with them. */
1428
1429/*! LZ4F_compressJob() :
1430 * compress one job, as a sequence of blocks (stored uncompressed when @mode == LZ4B_UNCOMPRESSED).
1431 * With linked blocks, each job restarts the stream, using as dictionary
1432 * the @prefixSize bytes of history right before @src,
1433 * or @cdict for the first job of the frame (@prefixSize == 0).
1434 * Output doesn't depend on @lz4ctx history.
1435 * @return : nb of bytes written into @dst, or an error code */
1436static size_t LZ4F_compressJob(void* lz4ctx, const LZ4F_preferences_t* prefs,
1437 const LZ4F_CDict* cdict, size_t blockSize,
1438 void* dst, size_t dstCapacity,
1439 const BYTE* src, size_t srcSize, size_t prefixSize,
1440 LZ4F_BlockCompressMode_e mode)
1441{
1442 int const level = prefs->compressionLevel;
1443 LZ4F_blockMode_t const blockMode = prefs->frameInfo.blockMode;
1444
1445 if (mode == LZ4B_UNCOMPRESSED) { /* LZ4F_compressOptions_t.uncompressed */
1446 return LZ4F_compressBlocks_internal(dst, dstCapacity, src, srcSize, blockSize,
1447 LZ4F_doNotCompressBlock, NULL((void*)0), 0, NULL((void*)0),
1448 prefs->frameInfo.blockChecksumFlag);
1449 }
1450
1451 assert(prefixSize <= LZ4F_MT_PREFIX_SIZE)((void)0);
1452 if (prefixSize) {
1453 LZ4F_startStream(lz4ctx, prefs, NULL((void*)0), src - prefixSize, prefixSize);
1454 } else {
1455 LZ4F_startStream(lz4ctx, prefs, cdict, NULL((void*)0), 0);
1456 }
1457
1458 return LZ4F_compressBlocks_internal(dst, dstCapacity,
1459 src, srcSize, blockSize,
1460 LZ4F_selectCompression(blockMode, level, LZ4B_COMPRESSED),
1461 lz4ctx, level, cdict,
1462 prefs->frameInfo.blockChecksumFlag);
1463}
1464
1465typedef void (*LZ4F_job_f)(void* arg, unsigned workerID);
1466typedef struct LZ4F_pool_s LZ4F_pool_t;
1467
1468#if LZ4F_MULTITHREAD0
1469
1470#if defined(_WIN32)
1471# ifndef WIN32_LEAN_AND_MEAN
1472# define WIN32_LEAN_AND_MEAN
1473# endif
1474 /* condition variables require Windows Vista+ ; _WIN32_WINNT may already be defined lower,
1475 * ex: by <_mingw.h> (included by standard headers) on older mingw-w64 (0x502) */
1476# if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0600)
1477# if defined(_WINDOWS_) /* <windows.h> already included (ex: amalgamated sources), without them */
1478# error "LZ4F_MULTITHREAD requires Windows Vista API : define _WIN32_WINNT >= 0x0600 before including <windows.h>, or disable LZ4F_MULTITHREAD"
1479# endif
1480# undef _WIN32_WINNT
1481# define _WIN32_WINNT 0x0600
1482# endif
1483# include <windows.h>
1484 typedef HANDLE LZ4F_thread_t;
1485 typedef CRITICAL_SECTION LZ4F_mutex_t;
1486 typedef CONDITION_VARIABLE LZ4F_cond_t;
1487# define LZ4F_mutex_init(m) (InitializeCriticalSection(m), 0)
1488# define LZ4F_mutex_destroy(m) DeleteCriticalSection(m)
1489# define LZ4F_mutex_lock(m) EnterCriticalSection(m)
1490# define LZ4F_mutex_unlock(m) LeaveCriticalSection(m)
1491# define LZ4F_cond_init(c) (InitializeConditionVariable(c), 0)
1492# define LZ4F_cond_destroy(c) ((void)(c))
1493# define LZ4F_cond_wait(c, m) SleepConditionVariableCS((c), (m), INFINITE)
1494# define LZ4F_cond_signal(c) WakeConditionVariable(c)
1495# define LZ4F_cond_broadcast(c) WakeAllConditionVariable(c)
1496#else
1497# include <pthread.h>
1498# include <signal.h>
1499 typedef pthread_t LZ4F_thread_t;
1500 typedef pthread_mutex_t LZ4F_mutex_t;
1501 typedef pthread_cond_t LZ4F_cond_t;
1502# define LZ4F_mutex_init(m) pthread_mutex_init((m), NULL((void*)0))
1503# define LZ4F_mutex_destroy(m) pthread_mutex_destroy(m)
1504# define LZ4F_mutex_lock(m) pthread_mutex_lock(m)
1505# define LZ4F_mutex_unlock(m) pthread_mutex_unlock(m)
1506# define LZ4F_cond_init(c) pthread_cond_init((c), NULL((void*)0))
1507# define LZ4F_cond_destroy(c) pthread_cond_destroy(c)
1508# define LZ4F_cond_wait(c, m) pthread_cond_wait((c), (m))
1509# define LZ4F_cond_signal(c) pthread_cond_signal(c)
1510# define LZ4F_cond_broadcast(c) pthread_cond_broadcast(c)
1511 /* worker threads must not receive process-directed signals intended for the application,
1512 * but synchronous signals (faults) remain deliverable : when blocked, a fault kills the process,
1513 * bypassing the application's handlers (ex: SIGBUS from a memory-mapped input truncated during compression) */
1514# if defined(SIG_SETMASK) && (!defined(_POSIX_C_SOURCE202405L) || (_POSIX_C_SOURCE202405L >= 199506L))
1515# define LZ4F_MT_BLOCK_SIGNALS 1
1516# else
1517# define LZ4F_MT_BLOCK_SIGNALS 0
1518# endif
1519#endif
1520
1521/* some compression functions employ large stack buffers when not in HEAPMODE,
1522 * while default thread stacks can be small (ex: 128 KB on musl) */
1523#define LZ4F_MT_STACKSIZE_MIN (1 MB*(1<<20))
1524
1525/* Persistent pool of worker threads, started on demand (see LZ4F_pool_submit()), up to maxThreads.
1526 * Queued jobs are run by worker threads (workerID 1 to nbThreads) :
1527 * the calling thread only submits jobs and waits for them (without pool, it runs jobs itself : workerID 0).
1528 * Allocations happen in the calling thread, since @cmem might not be thread-safe :
1529 * HC states are extended (LZ4_STREAMHC_EXTSIZE), so lz4hc allocates nothing within worker threads.
1530 * Note : like any thread, workers don't survive fork() : a forked child must not employ this pool. */
1531typedef struct { LZ4F_job_f fn; void* arg; } LZ4F_poolJob_t;
1532typedef struct { LZ4F_pool_t* pool; unsigned workerID; } LZ4F_poolWorker_t;
1533
1534struct LZ4F_pool_s {
1535 LZ4F_mutex_t lock;
1536 LZ4F_cond_t jobQueued; /* to worker threads */
1537 LZ4F_cond_t jobDone; /* to the calling thread */
1538 LZ4F_poolJob_t* queue; /* circular */
1539 unsigned queueCapacity;
1540 unsigned queueHead;
1541 unsigned queueSize;
1542 int shutdown;
1543 int cancel; /* jobs of an abandoned frame : not started yet, they end without running */
1544 unsigned nbDone; /* incremented at each job completion */
1545 unsigned nbBusy; /* worker threads running a job */
1546 LZ4F_thread_t* threads;
1547 LZ4F_poolWorker_t* workers;
1548 unsigned nbThreads; /* started on demand, up to maxThreads */
1549 unsigned maxThreads;
1550};
1551
1552/* requires lock */
1553static LZ4F_poolJob_t LZ4F_pool_pop(LZ4F_pool_t* pool)
1554{
1555 LZ4F_poolJob_t const job = pool->queue[pool->queueHead];
1556 assert(pool->queueSize > 0)((void)0);
1557 pool->queueHead = (pool->queueHead + 1) % pool->queueCapacity;
1558 pool->queueSize--;
1559 return job;
1560}
1561
1562static void LZ4F_pool_work(LZ4F_poolWorker_t* worker)
1563{
1564 LZ4F_pool_t* const pool = worker->pool;
1565 int busy = 0;
1566 for (;;) {
1567 LZ4F_poolJob_t job;
1568 LZ4F_mutex_lock(&pool->lock);
1569 if (busy) pool->nbBusy--;
1570 while ((pool->queueSize == 0) && !pool->shutdown)
1571 LZ4F_cond_wait(&pool->jobQueued, &pool->lock);
1572 if (pool->queueSize == 0) { /* shutdown */
1573 LZ4F_mutex_unlock(&pool->lock);
1574 return;
1575 }
1576 job = LZ4F_pool_pop(pool);
1577 pool->nbBusy++;
1578 busy = 1;
1579 LZ4F_mutex_unlock(&pool->lock);
1580 job.fn(job.arg, worker->workerID);
1581 }
1582}
1583
1584#if defined(_WIN32)
1585static DWORD WINAPI LZ4F_pool_thread(LPVOID arg)
1586{
1587 LZ4F_pool_work((LZ4F_poolWorker_t*)arg);
1588 return 0;
1589}
1590#else
1591static void* LZ4F_pool_thread(void* arg)
1592{
1593 LZ4F_pool_work((LZ4F_poolWorker_t*)arg);
1594 return NULL((void*)0);
1595}
1596#endif
1597
1598/* starts threads, up to @nbThreads in total
1599 * @return : nb of threads of @pool (fewer than @nbThreads when a creation fails) */
1600static unsigned LZ4F_pool_startThreads(LZ4F_pool_t* pool, unsigned nbThreads)
1601{
1602 unsigned n;
1603#if defined(_WIN32)
1604 assert(nbThreads <= pool->maxThreads)((void)0);
1605 for (n = pool->nbThreads; n < nbThreads; n++) {
1606 /* stack size : reserved, and committed on demand (otherwise, CreateThread() commits it upfront) */
1607 pool->threads[n] = CreateThread(NULL((void*)0), LZ4F_MT_STACKSIZE_MIN, LZ4F_pool_thread, &pool->workers[n],
1608 STACK_SIZE_PARAM_IS_A_RESERVATION, NULL((void*)0));
1609 if (pool->threads[n] == NULL((void*)0)) break;
1610 }
1611#else
1612 pthread_attr_t attr;
1613 int const attrOK = !pthread_attr_init(&attr);
1614# if LZ4F_MT_BLOCK_SIGNALS
1615 sigset_t allSignals, previousMask;
1616 sigfillset(&allSignals);
1617 sigdelset(&allSignals, SIGBUS);
1618 sigdelset(&allSignals, SIGSEGV);
1619 sigdelset(&allSignals, SIGFPE);
1620 sigdelset(&allSignals, SIGILL);
1621 pthread_sigmask(SIG_SETMASK, &allSignals, &previousMask); /* new threads inherit this mask */
1622# endif
1623 if (attrOK) {
1624 size_t stackSize;
1625 if (!pthread_attr_getstacksize(&attr, &stackSize) && (stackSize < LZ4F_MT_STACKSIZE_MIN))
1626 (void)pthread_attr_setstacksize(&attr, LZ4F_MT_STACKSIZE_MIN);
1627 }
1628 assert(nbThreads <= pool->maxThreads)((void)0);
1629 for (n = pool->nbThreads; n < nbThreads; n++) {
1630 if (pthread_create(&pool->threads[n], attrOK ? &attr : NULL((void*)0), LZ4F_pool_thread, &pool->workers[n])) break;
1631 }
1632# if LZ4F_MT_BLOCK_SIGNALS
1633 pthread_sigmask(SIG_SETMASK, &previousMask, NULL((void*)0));
1634# endif
1635 if (attrOK) pthread_attr_destroy(&attr);
1636#endif
1637 pool->nbThreads = n;
1638 return n;
1639}
1640
1641static void LZ4F_pool_freeMem(LZ4F_pool_t* pool, LZ4F_CustomMem cmem)
1642{
1643 LZ4F_free(pool->queue, cmem);
1644 LZ4F_free(pool->threads, cmem);
1645 LZ4F_free(pool->workers, cmem);
1646 LZ4F_free(pool, cmem);
1647}
1648
1649static void LZ4F_pool_free(LZ4F_pool_t* pool, LZ4F_CustomMem cmem)
1650{
1651 unsigned n;
1652 if (pool == NULL((void*)0)) return;
1653 LZ4F_mutex_lock(&pool->lock);
1654 pool->shutdown = 1;
1655 LZ4F_cond_broadcast(&pool->jobQueued);
1656 LZ4F_mutex_unlock(&pool->lock);
1657 for (n = 0; n < pool->nbThreads; n++) {
1658#if defined(_WIN32)
1659 WaitForSingleObject(pool->threads[n], INFINITE); /* never ends under the loader lock (see lz4frame.h) */
1660 CloseHandle(pool->threads[n]);
1661#else
1662 pthread_join(pool->threads[n], NULL((void*)0));
1663#endif
1664 }
1665 LZ4F_cond_destroy(&pool->jobDone);
1666 LZ4F_cond_destroy(&pool->jobQueued);
1667 LZ4F_mutex_destroy(&pool->lock);
1668 LZ4F_pool_freeMem(pool, cmem);
1669}
1670
1671/*! LZ4F_pool_create() :
1672 * threads are started on demand (see LZ4F_pool_submit()), up to @maxThreads.
1673 * @return : a pool with one thread started, or NULL */
1674static LZ4F_pool_t* LZ4F_pool_create(unsigned maxThreads, unsigned queueCapacity, LZ4F_CustomMem cmem)
1675{
1676 LZ4F_pool_t* const pool = (LZ4F_pool_t*)LZ4F_calloc(sizeof(*pool), cmem);
1677 if (pool == NULL((void*)0)) return NULL((void*)0);
1678 pool->queue = (LZ4F_poolJob_t*)LZ4F_malloc(queueCapacity * sizeof(*pool->queue), cmem);
1679 pool->threads = (LZ4F_thread_t*)LZ4F_malloc(maxThreads * sizeof(*pool->threads), cmem);
1680 pool->workers = (LZ4F_poolWorker_t*)LZ4F_malloc(maxThreads * sizeof(*pool->workers), cmem);
1681 if (pool->queue == NULL((void*)0) || pool->threads == NULL((void*)0) || pool->workers == NULL((void*)0)) {
1682 LZ4F_pool_freeMem(pool, cmem);
1683 return NULL((void*)0);
1684 }
1685 if (LZ4F_mutex_init(&pool->lock)) {
1686 LZ4F_pool_freeMem(pool, cmem);
1687 return NULL((void*)0);
1688 }
1689 if (LZ4F_cond_init(&pool->jobQueued)) {
1690 LZ4F_mutex_destroy(&pool->lock);
1691 LZ4F_pool_freeMem(pool, cmem);
1692 return NULL((void*)0);
1693 }
1694 if (LZ4F_cond_init(&pool->jobDone)) {
1695 LZ4F_cond_destroy(&pool->jobQueued);
1696 LZ4F_mutex_destroy(&pool->lock);
1697 LZ4F_pool_freeMem(pool, cmem);
1698 return NULL((void*)0);
1699 }
1700 pool->queueCapacity = queueCapacity;
1701 pool->maxThreads = maxThreads;
1702 { unsigned n;
1703 for (n = 0; n < maxThreads; n++) {
1704 pool->workers[n].pool = pool;
1705 pool->workers[n].workerID = n+1;
1706 } }
1707 if (LZ4F_pool_startThreads(pool, 1) == 0) {
1708 LZ4F_pool_free(pool, cmem);
1709 return NULL((void*)0);
1710 }
1711 return pool;
1712}
1713
1714/* A frame cut into jobs has a pool : its creation failure fails the frame (see LZ4F_mt_begin()).
1715 * Only LZ4F_pool_free() and LZ4F_pool_setCancel() accept a NULL pool, left by such a failure (see LZ4F_mt_free()). */
1716
1717static unsigned LZ4F_pool_nbThreads(const LZ4F_pool_t* pool)
1718{
1719 assert(pool != NULL)((void)0);
1720 return pool->nbThreads;
1721}
1722
1723/* memory allocated with the custom allocator (thread stacks excluded) */
1724static size_t LZ4F_pool_sizeof(const LZ4F_pool_t* pool)
1725{
1726 assert(pool != NULL)((void)0);
1727 return sizeof(*pool) + pool->queueCapacity * sizeof(*pool->queue)
1728 + pool->maxThreads * (sizeof(*pool->threads) + sizeof(*pool->workers));
1729}
1730
1731/* @return : 1 when queued jobs outnumber idle threads, and more threads can be started (see LZ4F_pool_startThreads()) */
1732static int LZ4F_pool_submit(LZ4F_pool_t* pool, LZ4F_job_f fn, void* arg)
1733{
1734 int moreThreads;
1735 assert(pool != NULL)((void)0);
1736 LZ4F_mutex_lock(&pool->lock);
1737 assert(pool->queueSize < pool->queueCapacity)((void)0);
1738 { LZ4F_poolJob_t* const job = pool->queue + (pool->queueHead + pool->queueSize) % pool->queueCapacity;
1739 job->fn = fn;
1740 job->arg = arg;
1741 }
1742 pool->queueSize++;
1743 moreThreads = (pool->queueSize > pool->nbThreads - pool->nbBusy) && (pool->nbThreads < pool->maxThreads);
1744 LZ4F_cond_signal(&pool->jobQueued);
1745 LZ4F_mutex_unlock(&pool->lock);
1746 return moreThreads;
1747}
1748
1749static void LZ4F_pool_setDone(LZ4F_pool_t* pool, int* doneFlag)
1750{
1751 assert(pool != NULL)((void)0);
1752 LZ4F_mutex_lock(&pool->lock);
1753 *doneFlag = 1;
1754 pool->nbDone++;
1755 LZ4F_cond_broadcast(&pool->jobDone);
1756 LZ4F_mutex_unlock(&pool->lock);
1757}
1758
1759static void LZ4F_pool_setCancel(LZ4F_pool_t* pool, int cancel)
1760{
1761 if (pool == NULL((void*)0)) return;
1762 LZ4F_mutex_lock(&pool->lock);
1763 pool->cancel = cancel;
1764 LZ4F_mutex_unlock(&pool->lock);
1765}
1766
1767static int LZ4F_pool_isCancelled(LZ4F_pool_t* pool)
1768{
1769 int cancel;
1770 assert(pool != NULL)((void)0);
1771 LZ4F_mutex_lock(&pool->lock);
1772 cancel = pool->cancel;
1773 LZ4F_mutex_unlock(&pool->lock);
1774 return cancel;
1775}
1776
1777static int LZ4F_pool_isDone(LZ4F_pool_t* pool, const int* doneFlag)
1778{
1779 int done;
1780 assert(pool != NULL)((void)0);
1781 LZ4F_mutex_lock(&pool->lock);
1782 done = *doneFlag;
1783 LZ4F_mutex_unlock(&pool->lock);
1784 return done;
1785}
1786
1787/* waits for *doneFlag */
1788static void LZ4F_pool_waitDone(LZ4F_pool_t* pool, const int* doneFlag)
1789{
1790 assert(pool != NULL)((void)0);
1791 LZ4F_mutex_lock(&pool->lock);
1792 while (!*doneFlag) LZ4F_cond_wait(&pool->jobDone, &pool->lock);
1793 LZ4F_mutex_unlock(&pool->lock);
1794}
1795
1796static unsigned LZ4F_pool_nbDone(LZ4F_pool_t* pool)
1797{
1798 unsigned nbDone;
1799 assert(pool != NULL)((void)0);
1800 LZ4F_mutex_lock(&pool->lock);
1801 nbDone = pool->nbDone;
1802 LZ4F_mutex_unlock(&pool->lock);
1803 return nbDone;
1804}
1805
1806/* waits for any job completion after @nbDone was observed */
1807static void LZ4F_pool_waitAny(LZ4F_pool_t* pool, unsigned nbDone)
1808{
1809 assert(pool != NULL)((void)0);
1810 LZ4F_mutex_lock(&pool->lock);
1811 while (pool->nbDone == nbDone) LZ4F_cond_wait(&pool->jobDone, &pool->lock);
1812 LZ4F_mutex_unlock(&pool->lock);
1813}
1814
1815#else /* !LZ4F_MULTITHREAD : no pool is ever created, jobs run within the calling thread */
1816
1817static unsigned LZ4F_pool_nbThreads(const LZ4F_pool_t* pool) { (void)pool; return 0; }
1818static size_t LZ4F_pool_sizeof(const LZ4F_pool_t* pool) { (void)pool; return 0; }
1819static void LZ4F_pool_free(LZ4F_pool_t* pool, LZ4F_CustomMem cmem) { (void)pool; (void)cmem; }
1820static int LZ4F_pool_submit(LZ4F_pool_t* pool, LZ4F_job_f fn, void* arg) { (void)pool; fn(arg, 0); return 0; }
1821static unsigned LZ4F_pool_startThreads(LZ4F_pool_t* pool, unsigned nbThreads) { (void)pool; (void)nbThreads; return 0; }
1822static void LZ4F_pool_setDone(LZ4F_pool_t* pool, int* doneFlag) { (void)pool; *doneFlag = 1; }
1823static void LZ4F_pool_setCancel(LZ4F_pool_t* pool, int cancel) { (void)pool; (void)cancel; }
1824static int LZ4F_pool_isCancelled(LZ4F_pool_t* pool) { (void)pool; return 0; }
1825static int LZ4F_pool_isDone(LZ4F_pool_t* pool, const int* doneFlag) { (void)pool; return *doneFlag; }
1826static void LZ4F_pool_waitDone(LZ4F_pool_t* pool, const int* doneFlag) { (void)pool; (void)doneFlag; assert(*doneFlag)((void)0); }
1827static unsigned LZ4F_pool_nbDone(LZ4F_pool_t* pool) { (void)pool; return 0; }
1828static void LZ4F_pool_waitAny(LZ4F_pool_t* pool, unsigned nbDone) { (void)pool; (void)nbDone; assert(0)((void)0); }
1829
1830#endif /* LZ4F_MULTITHREAD */
1831
1832typedef struct {
1833 LZ4F_mtCtx_t* mt;
1834 const BYTE* src; /* job content, right after its prefixSize bytes of history */
1835 size_t srcSize;
1836 size_t prefixSize;
1837 BYTE* inBuff; /* input buffer (LZ4F_MT_PREFIX_SIZE + jobSize), when content is copied : NULL when read in place */
1838 BYTE* outBuff; /* compressed blocks */
1839 LZ4F_BlockCompressMode_e mode; /* LZ4B_UNCOMPRESSED : uncompressed input (LZ4F_compressOptions_t.uncompressed) */
1840 size_t cSize; /* result : compressed size, or error code */
1841 int done; /* result available (protected by pool's lock) */
1842} LZ4F_mtJob_t;
1843
1844struct LZ4F_mtCtx_s {
1845 LZ4F_CustomMem cmem;
1846 LZ4F_pool_t* pool; /* NULL without multi-threading support (jobs run within the calling thread), or after a failed creation */
1847 unsigned poolRequested; /* nbWorkers requested when pool was created : threads are started on demand, up to it */
1848 void** states; /* one LZ4 state per worker (allocated when it starts) ; states[0] : cctx's own state, without pool */
1849 unsigned nbStates; /* capacity of states[] */
1850 LZ4F_CtxType_e statesType;
1851 LZ4F_mtJob_t* jobs; /* ring buffer of job slots, for the maximum nb of threads : outBuff allocated on first use */
1852 unsigned nbJobSlots;
1853 size_t inCapacity; /* of each input buffer */
1854 size_t outCapacity; /* of each outBuff */
1855 /* input buffers are only needed until their job is compressed : they are shared by job slots */
1856 BYTE** inBuffs; /* all allocated input buffers */
1857 BYTE** inFree; /* free input buffers (stack) */
1858 unsigned nbInBuffs;
1859 unsigned nbInFree;
1860 unsigned maxInBuffs; /* capacity of inBuffs[] and inFree[] : one per thread, plus the one being filled */
1861 unsigned oldest; /* oldest submitted job, not emitted yet */
1862 size_t emitted; /* part of the oldest job already emitted, see LZ4F_mt_emitPartial() */
1863 unsigned nbSubmitted;
1864 int filling; /* the slot following submitted jobs is being filled */
1865 BYTE* history; /* last bytes of content, up to LZ4F_MT_PREFIX_SIZE (linked blocks) */
1866 size_t historySize;
1867 const BYTE* stableEnd; /* end of previous input, when stable (LZ4F_srcStableUntilFlush) */
1868 size_t stableRun; /* stable input contiguously preceding stableEnd, up to LZ4F_MT_PREFIX_SIZE */
1869 /* current frame */
1870 const LZ4F_preferences_t* prefs;
1871 const LZ4F_CDict* cdict;
1872 size_t blockSize;
1873 size_t jobSize;
1874};
1875
1876static void LZ4F_mt_runJob(void* arg, unsigned workerID)
1877{
1878 LZ4F_mtJob_t* const job = (LZ4F_mtJob_t*)arg;
1879 LZ4F_mtCtx_t* const mt = job->mt;
1880 assert(mt->states[workerID] != NULL)((void)0);
1881 if (LZ4F_pool_isCancelled(mt->pool)) {
1882 job->cSize = 0; /* abandoned frame : result discarded */
1883 } else {
1884 job->cSize = LZ4F_compressJob(mt->states[workerID], mt->prefs, mt->cdict, mt->blockSize,
1885 job->outBuff, mt->outCapacity,
1886 job->src, job->srcSize, job->prefixSize, job->mode);
1887 }
1888 LZ4F_pool_setDone(mt->pool, &job->done);
1889}
1890
1891/* jobs in flight : up to LZ4F_MT_NBSLOTS() of current nb of threads (within ring buffer of job slots) */
1892static unsigned LZ4F_mt_nbUsableSlots(const LZ4F_mtCtx_t* mt)
1893{
1894 unsigned const nbThreads = LZ4F_pool_nbThreads(mt->pool);
1895 return nbThreads ? (unsigned)LZ4F_MT_NBSLOTS(nbThreads)((size_t)(nbThreads) * 2 + 1) : 1;
1896}
1897
1898/* allocates the LZ4 state of worker @workerID, unless already done
1899 * @return : 1 when the state is ready, 0 if allocation failed */
1900static int LZ4F_mt_allocState(LZ4F_mtCtx_t* mt, unsigned workerID, int level)
1901{
1902 if (mt->states[workerID] == NULL((void*)0)) {
1903 void* const state = LZ4F_malloc(LZ4F_sizeofLZ4State(mt->statesType), mt->cmem);
1904 if (state == NULL((void*)0)) return 0;
1905 LZ4F_initLZ4State(state, mt->statesType, level);
1906 mt->states[workerID] = state;
1907 }
1908 return 1;
1909}
1910
1911/* starts one more worker thread, with its LZ4 state (allocated by the calling thread) :
1912 * on failure (allocation, thread creation), compression proceeds with current threads */
1913static void LZ4F_mt_addWorker(LZ4F_mtCtx_t* mt)
1914{
1915 unsigned const workerID = LZ4F_pool_nbThreads(mt->pool) + 1;
1916 assert(workerID < mt->nbStates)((void)0);
1917 if (LZ4F_mt_allocState(mt, workerID, mt->prefs->compressionLevel))
1918 (void)LZ4F_pool_startThreads(mt->pool, workerID);
1919}
1920
1921/* @n-th submitted job, from the oldest one : @n == nbSubmitted is the slot following them (see LZ4F_mt_fillingSlot()) */
1922static LZ4F_mtJob_t* LZ4F_mt_job(LZ4F_mtCtx_t* mt, unsigned n)
1923{
1924 return mt->jobs + (mt->oldest + n) % mt->nbJobSlots;
1925}
1926
1927static LZ4F_mtJob_t* LZ4F_mt_fillingSlot(LZ4F_mtCtx_t* mt)
1928{
1929 return LZ4F_mt_job(mt, mt->nbSubmitted);
1930}
1931
1932/* keeps the last LZ4F_MT_PREFIX_SIZE bytes of content */
1933static void LZ4F_mt_addHistory(LZ4F_mtCtx_t* mt, const BYTE* content, size_t size)
1934{
1935 if (size >= LZ4F_MT_PREFIX_SIZE(64 *(1<<10))) {
1936 memcpy(mt->history, content + size - LZ4F_MT_PREFIX_SIZE(64 *(1<<10)), LZ4F_MT_PREFIX_SIZE(64 *(1<<10)));
1937 mt->historySize = LZ4F_MT_PREFIX_SIZE(64 *(1<<10));
1938 } else {
1939 size_t const kept = MIN(mt->historySize, LZ4F_MT_PREFIX_SIZE - size)( (mt->historySize) < ((64 *(1<<10)) - size) ? (mt
->historySize) : ((64 *(1<<10)) - size) )
;
1940 memmove(mt->history, mt->history + mt->historySize - kept, kept);
1941 memcpy(mt->history + kept, content, size);
1942 mt->historySize = kept + size;
1943 }
1944}
1945
1946/* submits the job being filled : to worker threads,
1947 * or, @inCallingThread, runs it right away with the cctx's own state (states[0], unused by workers) */
1948static void LZ4F_mt_submit_internal(LZ4F_mtCtx_t* mt, int inCallingThread)
1949{
1950 LZ4F_mtJob_t* const job = LZ4F_mt_fillingSlot(mt);
1951 assert(mt->filling)((void)0);
1952 assert(job->srcSize > 0)((void)0);
1953 job->done = 0;
1954 mt->filling = 0;
1955 mt->nbSubmitted++;
1956 if (inCallingThread)
1957 LZ4F_mt_runJob(job, 0);
1958 else if (LZ4F_pool_submit(mt->pool, LZ4F_mt_runJob, job))
1959 LZ4F_mt_addWorker(mt); /* more jobs than idle threads */
1960 if (mt->prefs->frameInfo.blockMode == LZ4F_blockLinked)
1961 LZ4F_mt_addHistory(mt, job->src, job->srcSize); /* job content is only read while job runs */
1962}
1963
1964static void LZ4F_mt_submit(LZ4F_mtCtx_t* mt) { LZ4F_mt_submit_internal(mt, 0); }
1965
1966/* the calling thread is about to wait for all jobs : when no other job is running, it compresses the last one itself,
1967 * which saves the synchronization with a worker (same output : jobs don't depend on their state) */
1968static void LZ4F_mt_submitLast(LZ4F_mtCtx_t* mt) { LZ4F_mt_submit_internal(mt, mt->nbSubmitted == 0); }
1969
1970/* input buffer of a completed job returns to the free stack */
1971static void LZ4F_mt_releaseInBuff(LZ4F_mtCtx_t* mt, LZ4F_mtJob_t* job)
1972{
1973 if (job->inBuff == NULL((void*)0)) return;
1974 assert(mt->nbInFree < mt->nbInBuffs)((void)0);
1975 mt->inFree[mt->nbInFree++] = job->inBuff;
1976 job->inBuff = NULL((void*)0);
1977}
1978
1979/* the oldest job, completed, is emitted or discarded : its slot is free */
1980static void LZ4F_mt_retireOldest(LZ4F_mtCtx_t* mt)
1981{
1982 LZ4F_mt_releaseInBuff(mt, mt->jobs + mt->oldest);
1983 mt->oldest = (mt->oldest + 1) % mt->nbJobSlots;
1984 mt->nbSubmitted--;
1985 mt->emitted = 0;
1986}
1987
1988/*! LZ4F_mt_acquireInBuff() :
1989 * When all input buffers are employed, one is reclaimed from a submitted job,
1990 * the first one to complete (job durations vary).
1991 * @return : an input buffer, or NULL if allocation failed */
1992static BYTE* LZ4F_mt_acquireInBuff(LZ4F_mtCtx_t* mt, LZ4F_CustomMem cmem)
1993{
1994 if (mt->nbInFree) return mt->inFree[--mt->nbInFree];
1995 if (mt->nbInBuffs < LZ4F_pool_nbThreads(mt->pool) + LZ4F_MT_EXTRA_INBUFFS1) {
1996 BYTE* const buff = (BYTE*)LZ4F_malloc(mt->inCapacity, cmem);
1997 assert(mt->nbInBuffs < mt->maxInBuffs)((void)0);
1998 if (buff != NULL((void*)0)) mt->inBuffs[mt->nbInBuffs++] = buff;
1999 return buff;
2000 }
2001 for (;;) {
2002 unsigned const nbDone = LZ4F_pool_nbDone(mt->pool);
2003 unsigned n;
2004 for (n = 0; n < mt->nbSubmitted; n++) {
2005 LZ4F_mtJob_t* const job = LZ4F_mt_job(mt, n);
2006 if ((job->inBuff != NULL((void*)0)) && LZ4F_pool_isDone(mt->pool, &job->done)) {
2007 BYTE* const buff = job->inBuff;
2008 job->inBuff = NULL((void*)0);
2009 return buff;
2010 } }
2011 /* buffers are held by submitted jobs, still running : wait for one of them */
2012 LZ4F_pool_waitAny(mt->pool, nbDone);
2013 }
2014}
2015
2016/* emits oldest submitted job into @dst + @pos, waiting for it if needed.
2017 * On error, the job remains submitted : callers invalidate the frame, discarding its jobs (FORWARD_IF_ERROR_INVALIDATE) */
2018static size_t LZ4F_mt_emitOldest(LZ4F_mtCtx_t* mt, BYTE* dst, size_t dstCapacity, size_t pos)
2019{
2020 LZ4F_mtJob_t* const job = mt->jobs + mt->oldest;
2021 size_t toEmit;
2022 assert(mt->nbSubmitted > 0)((void)0);
2023 assert(pos <= dstCapacity)((void)0);
2024 LZ4F_pool_waitDone(mt->pool, &job->done);
2025 FORWARD_IF_ERROR(job->cSize)do { size_t const err_code = (job->cSize); if (LZ4F_isError
(err_code)) return err_code; } while (0)
;
2026 toEmit = job->cSize - mt->emitted; /* part already emitted by an incomplete LZ4F_flush() */
2027 RETURN_ERROR_IF(toEmit > dstCapacity - pos, dstCapacity_tooSmall)do { if (toEmit > dstCapacity - pos) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_dstCapacity_tooSmall); } } while (0)
;
2028 memcpy(dst + pos, job->outBuff + mt->emitted, toEmit);
2029 LZ4F_mt_retireOldest(mt);
2030 return toEmit;
2031}
2032
2033/* emits submitted jobs in order : all of them (@all), or only the already completed ones fitting into @dst
2034 * (their emission isn't required yet : the others are emitted later).
2035 * On error, callers invalidate the frame (FORWARD_IF_ERROR_INVALIDATE) */
2036static size_t LZ4F_mt_emit(LZ4F_mtCtx_t* mt, BYTE* dst, size_t dstCapacity, size_t pos, int all)
2037{
2038 size_t written = 0;
2039 while (mt->nbSubmitted > 0) {
2040 const LZ4F_mtJob_t* const job = mt->jobs + mt->oldest;
2041 if (!all) {
2042 if (!LZ4F_pool_isDone(mt->pool, &job->done)) break;
2043 if (job->cSize - mt->emitted > dstCapacity - (pos + written)) break; /* also defers an error code */
2044 }
2045 { size_t const r = LZ4F_mt_emitOldest(mt, dst, dstCapacity, pos + written);
2046 FORWARD_IF_ERROR(r)do { size_t const err_code = (r); if (LZ4F_isError(err_code))
return err_code; } while (0)
;
2047 written += r;
2048 } }
2049 return written;
2050}
2051
2052/* discards submitted jobs : those not started yet end without running, others are waited for */
2053static void LZ4F_mt_abort(LZ4F_mtCtx_t* mt)
2054{
2055 LZ4F_pool_setCancel(mt->pool, 1);
2056 while (mt->nbSubmitted > 0) {
2057 LZ4F_pool_waitDone(mt->pool, &mt->jobs[mt->oldest].done);
2058 LZ4F_mt_retireOldest(mt);
2059 }
2060 LZ4F_pool_setCancel(mt->pool, 0);
2061 if (mt->filling) LZ4F_mt_releaseInBuff(mt, LZ4F_mt_fillingSlot(mt));
2062 mt->filling = 0;
2063 mt->emitted = 0;
2064 mt->stableEnd = NULL((void*)0);
2065}
2066
2067static void LZ4F_mt_freeStates(LZ4F_mtCtx_t* mt, LZ4F_CustomMem cmem)
2068{
2069 unsigned n;
2070 for (n = 1; n < mt->nbStates; n++) LZ4F_free(mt->states[n], cmem); /* states[0] belongs to cctx ; NULL : not allocated */
2071 LZ4F_free(mt->states, cmem);
2072 mt->states = NULL((void*)0);
2073 mt->nbStates = 0;
2074}
2075
2076static void LZ4F_mt_freeInBuffs(LZ4F_mtCtx_t* mt, LZ4F_CustomMem cmem)
2077{
2078 unsigned n;
2079 assert(mt->nbInFree == mt->nbInBuffs)((void)0); /* no job in flight */
2080 for (n = 0; n < mt->nbInBuffs; n++) LZ4F_free(mt->inBuffs[n], cmem);
2081 LZ4F_free(mt->inBuffs, cmem);
2082 LZ4F_free(mt->inFree, cmem);
2083 mt->inBuffs = mt->inFree = NULL((void*)0);
2084 mt->nbInBuffs = mt->nbInFree = mt->maxInBuffs = 0;
2085}
2086
2087static void LZ4F_mt_freeJobs(LZ4F_mtCtx_t* mt, LZ4F_CustomMem cmem)
2088{
2089 unsigned n;
2090 for (n = 0; n < mt->nbJobSlots; n++) {
2091 assert(mt->jobs[n].inBuff == NULL)((void)0);
2092 LZ4F_free(mt->jobs[n].outBuff, cmem);
2093 }
2094 LZ4F_free(mt->jobs, cmem);
2095 mt->jobs = NULL((void*)0);
2096 mt->nbJobSlots = 0;
2097}
2098
2099static void LZ4F_mt_free(LZ4F_mtCtx_t* mt, LZ4F_CustomMem cmem)
2100{
2101 if (mt == NULL((void*)0)) return;
2102 LZ4F_mt_abort(mt);
2103 LZ4F_pool_free(mt->pool, cmem);
2104 LZ4F_mt_freeStates(mt, cmem);
2105 LZ4F_mt_freeJobs(mt, cmem);
2106 LZ4F_mt_freeInBuffs(mt, cmem);
2107 LZ4F_free(mt->history, cmem);
2108 LZ4F_free(mt, cmem);
2109}
2110
2111static size_t LZ4F_mt_sizeof(const LZ4F_mtCtx_t* mt)
2112{
2113 size_t size;
2114 unsigned n;
2115 if (mt == NULL((void*)0)) return 0;
2116 size = sizeof(*mt) + LZ4F_MT_PREFIX_SIZE(64 *(1<<10))
2117 + mt->nbStates * sizeof(void*)
2118 + mt->nbJobSlots * sizeof(LZ4F_mtJob_t)
2119 + mt->maxInBuffs * 2 * sizeof(BYTE*) + mt->nbInBuffs * mt->inCapacity
2120 + (mt->pool ? LZ4F_pool_sizeof(mt->pool) : 0);
2121 for (n = 1; n < mt->nbStates; n++) if (mt->states[n] != NULL((void*)0)) size += LZ4F_sizeofLZ4State(mt->statesType);
2122 for (n = 0; n < mt->nbJobSlots; n++) if (mt->jobs[n].outBuff != NULL((void*)0)) size += mt->outCapacity;
2123 return size;
2124}
2125
2126/*! LZ4F_mt_begin() :
2127 * prepares multi-threading resources for a new frame, cut into jobs.
2128 * Resources are kept within cctx, and reused by next frames.
2129 * Requires a cctx prepared by LZ4F_compressBegin_internal(), with no job in flight.
2130 * @return : 0, or an error code */
2131static size_t LZ4F_mt_begin(LZ4F_cctx_t* cctx)
2132{
2133 LZ4F_CustomMem const cmem = cctx->cmem;
2134 unsigned const nbWorkers = MIN(cctx->prefs.nbWorkers, LZ4F_NBWORKERS_MAX)( (cctx->prefs.nbWorkers) < (((sizeof(size_t) >= 8) ?
1024u : 128u)) ? (cctx->prefs.nbWorkers) : (((sizeof(size_t
) >= 8) ? 1024u : 128u)) )
;
2135 LZ4F_CtxType_e const ctxType = (LZ4F_CtxType_e)cctx->lz4CtxType;
2136 size_t const blockSize = cctx->maxBlockSize;
2137 size_t const jobSize = LZ4F_mtJobSize(blockSize);
2138 size_t const inCapacity = LZ4F_MT_PREFIX_SIZE(64 *(1<<10)) + jobSize;
2139 size_t const outCapacity = LZ4F_blocksBound(jobSize, blockSize, LZ4F_blockChecksumEnabled);
2140 LZ4F_mtCtx_t* mt = cctx->mt;
2141 unsigned nbThreads, maxThreads, nbJobSlots;
2142
2143 assert(nbWorkers >= 1)((void)0);
2144 (void)nbWorkers; /* only employed by multi-threading support */
2145 if (mt == NULL((void*)0)) {
2146 mt = (LZ4F_mtCtx_t*)LZ4F_calloc(sizeof(*mt), cmem);
2147 RETURN_ERROR_IF(mt == NULL, allocation_failed)do { if (mt == ((void*)0)) { {}; return LZ4F_returnErrorCode(
LZ4F_ERROR_allocation_failed); } } while (0)
;
2148 mt->history = (BYTE*)LZ4F_malloc(LZ4F_MT_PREFIX_SIZE(64 *(1<<10)), cmem);
2149 if (mt->history == NULL((void*)0)) {
2150 LZ4F_free(mt, cmem);
2151 RETURN_ERROR(allocation_failed)return LZ4F_returnErrorCode(LZ4F_ERROR_allocation_failed);
2152 }
2153 mt->cmem = cmem;
2154 cctx->mt = mt;
2155 }
2156 assert(mt->nbSubmitted == 0 && !mt->filling)((void)0);
2157
2158 /* worker threads */
2159#if LZ4F_MULTITHREAD0
2160 /* an incomplete pool (fewer threads than requested) is kept ;
2161 * without any worker thread, frame compression fails, and next frames try again */
2162 if ((mt->pool == NULL((void*)0)) || (mt->poolRequested != nbWorkers)) {
2163 LZ4F_pool_free(mt->pool, cmem);
2164 mt->pool = LZ4F_pool_create(nbWorkers, (unsigned)LZ4F_MT_NBSLOTS(nbWorkers)((size_t)(nbWorkers) * 2 + 1), cmem); /* queue : all job slots */
2165 mt->poolRequested = nbWorkers;
2166 RETURN_ERROR_IF(mt->pool == NULL, allocation_failed)do { if (mt->pool == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_allocation_failed); } } while (0)
;
2167 }
2168#endif
2169 /* resources are sized for the maximum nb of threads, but only allocated when needed :
2170 * threads with their LZ4 state, job slots' outputs and input buffers */
2171 nbThreads = LZ4F_pool_nbThreads(mt->pool); /* already started */
2172 maxThreads = mt->pool ? mt->poolRequested : 0;
2173 nbJobSlots = maxThreads ? (unsigned)LZ4F_MT_NBSLOTS(maxThreads)((size_t)(maxThreads) * 2 + 1) : 1;
2174
2175 /* one LZ4 state per worker */
2176 if ((mt->nbStates != maxThreads + 1) || (mt->statesType != ctxType)) {
2177 LZ4F_mt_freeStates(mt, cmem);
2178 mt->states = (void**)LZ4F_calloc((maxThreads + 1) * sizeof(void*), cmem);
2179 RETURN_ERROR_IF(mt->states == NULL, allocation_failed)do { if (mt->states == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_allocation_failed); } } while (0)
;
2180 mt->nbStates = maxThreads + 1;
2181 mt->statesType = ctxType;
2182 }
2183 { unsigned n;
2184 for (n = 1; n <= nbThreads; n++) /* threads already started (previous frames) */
2185 RETURN_ERROR_IF(!LZ4F_mt_allocState(mt, n, cctx->prefs.compressionLevel), allocation_failed)do { if (!LZ4F_mt_allocState(mt, n, cctx->prefs.compressionLevel
)) { {}; return LZ4F_returnErrorCode(LZ4F_ERROR_allocation_failed
); } } while (0)
;
2186 }
2187 mt->states[0] = cctx->lz4CtxPtr;
2188
2189 /* job slots (outputs allocated on first use) */
2190 if (mt->nbJobSlots != nbJobSlots) {
2191 LZ4F_mt_freeJobs(mt, cmem);
2192 mt->jobs = (LZ4F_mtJob_t*)LZ4F_calloc(nbJobSlots * sizeof(LZ4F_mtJob_t), cmem);
2193 RETURN_ERROR_IF(mt->jobs == NULL, allocation_failed)do { if (mt->jobs == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_allocation_failed); } } while (0)
;
2194 mt->nbJobSlots = nbJobSlots;
2195 }
2196 if (mt->outCapacity < outCapacity) {
2197 unsigned n;
2198 for (n = 0; n < mt->nbJobSlots; n++) {
2199 LZ4F_free(mt->jobs[n].outBuff, cmem);
2200 mt->jobs[n].outBuff = NULL((void*)0);
2201 }
2202 mt->outCapacity = outCapacity;
2203 }
2204
2205 /* input buffers : one per thread, plus the one being filled (allocated on first need) */
2206 if ((mt->maxInBuffs != maxThreads + LZ4F_MT_EXTRA_INBUFFS1) || (mt->inCapacity < inCapacity)) {
2207 LZ4F_mt_freeInBuffs(mt, cmem);
2208 mt->inBuffs = (BYTE**)LZ4F_malloc((maxThreads + LZ4F_MT_EXTRA_INBUFFS1) * sizeof(BYTE*), cmem);
2209 mt->inFree = (BYTE**)LZ4F_malloc((maxThreads + LZ4F_MT_EXTRA_INBUFFS1) * sizeof(BYTE*), cmem);
2210 if ((mt->inBuffs == NULL((void*)0)) || (mt->inFree == NULL((void*)0))) {
2211 LZ4F_mt_freeInBuffs(mt, cmem);
2212 RETURN_ERROR(allocation_failed)return LZ4F_returnErrorCode(LZ4F_ERROR_allocation_failed);
2213 }
2214 mt->maxInBuffs = maxThreads + LZ4F_MT_EXTRA_INBUFFS1;
2215 mt->inCapacity = inCapacity;
2216 }
2217
2218 /* current frame */
2219 { unsigned n;
2220 for (n = 0; n < nbJobSlots; n++) mt->jobs[n].mt = mt;
2221 }
2222 mt->prefs = &cctx->prefs;
2223 mt->cdict = cctx->cdict;
2224 mt->blockSize = blockSize;
2225 mt->jobSize = jobSize;
2226 mt->oldest = 0;
2227 mt->emitted = 0;
2228 mt->nbSubmitted = 0;
2229 mt->filling = 0;
2230 mt->historySize = 0;
2231 mt->stableEnd = NULL((void*)0);
2232 mt->stableRun = 0;
2233 cctx->mtActive = 1;
2234 return 0;
2235}
2236
2237/*! LZ4F_mt_flushPoint() :
2238 * LZ4F_flush() and LZ4F_compressEnd(), multi-threaded mode : the job being filled is submitted (see LZ4F_mt_submitLast()),
2239 * and all jobs are waited for : input is no longer read, and their output is emitted by LZ4F_mt_emitPartial(). */
2240static void LZ4F_mt_flushPoint(LZ4F_mtCtx_t* mt)
2241{
2242 unsigned n;
2243 if (mt->filling) LZ4F_mt_submitLast(mt);
2244 for (n = 0; n < mt->nbSubmitted; n++)
2245 LZ4F_pool_waitDone(mt->pool, &LZ4F_mt_job(mt, n)->done);
2246}
2247
2248/*! LZ4F_mt_emitPartial() :
2249 * emits completed jobs in order into @dst + @pos, as much as fits : the oldest one can be emitted partially.
2250 * Requires all submitted jobs completed, see LZ4F_mt_flushPoint().
2251 * @return : nb of bytes written, or an error code */
2252static size_t LZ4F_mt_emitPartial(LZ4F_mtCtx_t* mt, BYTE* dst, size_t dstCapacity, size_t pos)
2253{
2254 size_t const start = pos;
2255 while (mt->nbSubmitted > 0) {
2256 LZ4F_mtJob_t* const job = mt->jobs + mt->oldest;
2257 size_t n;
2258 assert(LZ4F_pool_isDone(mt->pool, &job->done))((void)0);
2259 FORWARD_IF_ERROR(job->cSize)do { size_t const err_code = (job->cSize); if (LZ4F_isError
(err_code)) return err_code; } while (0)
;
2260 n = MIN(job->cSize - mt->emitted, dstCapacity - pos)( (job->cSize - mt->emitted) < (dstCapacity - pos) ?
(job->cSize - mt->emitted) : (dstCapacity - pos) )
;
2261 if (n) memcpy(dst + pos, job->outBuff + mt->emitted, n);
2262 pos += n;
2263 mt->emitted += n;
2264 if (mt->emitted < job->cSize) break; /* dst is full */
2265 LZ4F_mt_retireOldest(mt);
2266 }
2267 return pos - start;
2268}
2269
2270/*! LZ4F_mt_update() :
2271 * LZ4F_compressUpdate(), multi-threaded mode (@mode : compressed or uncompressed input ; @cOptPtr : not NULL).
2272 * Input is copied into jobs, submitted once full.
2273 * Job content is read in place instead, when it remains available until the job completes :
2274 * with stableSrc == LZ4F_srcStableUntilFlush, or with autoFlush, since all jobs complete within the invocation.
2275 * LZ4F_srcStable (historical value 1) only guarantees the last 64 KB : content is copied.
2276 * Uncompressed segments are jobs too : a mode switch submits the job being filled,
2277 * so that blocks are cut as in single-threaded mode (the buffered block is flushed on a mode switch).
2278 * Completed jobs are emitted in order, as long as they fit. With autoFlush, all input is compressed and emitted.
2279 * Success is guaranteed when @dstCapacity >= LZ4F_compressBound(srcSize, prefs) :
2280 * without autoFlush, output is only required to start a job while all slots are busy (oldest job). */
2281static size_t LZ4F_mt_update(LZ4F_cctx_t* cctx,
2282 void* dstBuffer, size_t dstCapacity,
2283 const void* srcBuffer, size_t srcSize,
2284 const LZ4F_compressOptions_t* cOptPtr,
2285 LZ4F_BlockCompressMode_e mode)
2286{
2287 LZ4F_mtCtx_t* const mt = cctx->mt;
2288 const BYTE* const istart = (const BYTE*)srcBuffer;
2289 const BYTE* ip = istart;
2290 const BYTE* const iend = srcSize ? istart + srcSize : istart;
2291 BYTE* const dst = (BYTE*)dstBuffer;
2292 size_t pos = 0;
2293 int const checksum = (cctx->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled);
2294 int const linked = (cctx->prefs.frameInfo.blockMode == LZ4F_blockLinked);
2295 int const stableUntilFlush = (cOptPtr->stableSrc == LZ4F_srcStableUntilFlush);
2296 int const inPlace = cctx->prefs.autoFlush || stableUntilFlush;
2297 /* stable input remains available : when contiguous with previous input, history can be read in place */
2298 size_t const prevRun = (stableUntilFlush && srcSize && (istart == mt->stableEnd)) ? mt->stableRun : 0;
2299
2300 DEBUGLOG(4, "LZ4F_mt_update (srcSize=%u, mode=%u)", (unsigned)srcSize, (unsigned)mode){};
2301 RETURN_ERROR_IF(cctx->cStage != 1, compressionState_uninitialized)do { if (cctx->cStage != 1) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_compressionState_uninitialized); } } while (0)
;
2302 assert(cctx->pendingSize == 0)((void)0); /* multi-threaded mode : pending output is within jobs */
2303 cctx->flushStage = 0; /* an incomplete LZ4F_flush() ends here : remaining jobs are emitted as usual */
2304 if (mt->filling && (LZ4F_mt_fillingSlot(mt)->mode != mode))
2305 LZ4F_mt_submit(mt); /* mode switch */
2306
2307 while (ip < iend) {
2308 LZ4F_mtJob_t* const job = LZ4F_mt_fillingSlot(mt);
2309 if (!mt->filling) {
2310 /* new job */
2311 size_t const remaining = (size_t)(iend - ip);
2312 size_t const prefixSize = linked ? mt->historySize : 0;
2313 if (mt->nbSubmitted >= LZ4F_mt_nbUsableSlots(mt)) {
2314 /* all slots busy : emit oldest job, to free a slot */
2315 size_t const r = LZ4F_mt_emitOldest(mt, dst, dstCapacity, pos);
2316 FORWARD_IF_ERROR_INVALIDATE(r, cctx)do { size_t const err_code = (r); if (LZ4F_isError(err_code))
{ LZ4F_invalidateFrame(cctx); return err_code; } } while (0)
;
2317 pos += r;
2318 assert(job == LZ4F_mt_fillingSlot(mt))((void)0);
2319 }
2320 if (job->outBuff == NULL((void*)0)) { /* first use of this slot */
2321 job->outBuff = (BYTE*)LZ4F_malloc(mt->outCapacity, cctx->cmem);
2322 if (job->outBuff == NULL((void*)0)) {
2323 LZ4F_invalidateFrame(cctx);
2324 RETURN_ERROR(allocation_failed)return LZ4F_returnErrorCode(LZ4F_ERROR_allocation_failed);
2325 }
2326 }
2327 mt->filling = 1;
2328 job->mode = mode;
2329 job->prefixSize = prefixSize;
2330 if ( inPlace
2331 && ((remaining >= mt->jobSize) || cctx->prefs.autoFlush)
2332 && (prevRun + (size_t)(ip - istart) >= prefixSize) ) {
2333 /* read in place : whole job is within @srcBuffer, and its history right before */
2334 size_t const jobSize = MIN(remaining, mt->jobSize)( (remaining) < (mt->jobSize) ? (remaining) : (mt->jobSize
) )
;
2335 job->src = ip;
2336 job->srcSize = jobSize;
2337 if (cctx->prefs.autoFlush && (jobSize == remaining))
2338 LZ4F_mt_submitLast(mt); /* all jobs are waited for before returning */
2339 else
2340 LZ4F_mt_submit(mt);
2341 if (checksum) (void)XXH32_update(&(cctx->xxh), ip, jobSize);
2342 ip += jobSize;
2343 continue;
2344 }
2345 /* copy : history first, right before content */
2346 assert(job->inBuff == NULL)((void)0);
2347 job->inBuff = LZ4F_mt_acquireInBuff(mt, cctx->cmem);
2348 if (job->inBuff == NULL((void*)0)) {
2349 mt->filling = 0;
2350 LZ4F_invalidateFrame(cctx);
2351 RETURN_ERROR(allocation_failed)return LZ4F_returnErrorCode(LZ4F_ERROR_allocation_failed);
2352 }
2353 job->src = job->inBuff + LZ4F_MT_PREFIX_SIZE(64 *(1<<10));
2354 job->srcSize = 0;
2355 memcpy(job->inBuff + LZ4F_MT_PREFIX_SIZE(64 *(1<<10)) - prefixSize, mt->history + mt->historySize - prefixSize, prefixSize);
2356 }
2357 { size_t const toCopy = MIN(mt->jobSize - job->srcSize, (size_t)(iend - ip))( (mt->jobSize - job->srcSize) < ((size_t)(iend - ip
)) ? (mt->jobSize - job->srcSize) : ((size_t)(iend - ip
)) )
;
2358 assert(job->src == job->inBuff + LZ4F_MT_PREFIX_SIZE)((void)0);
2359 memcpy(job->inBuff + LZ4F_MT_PREFIX_SIZE(64 *(1<<10)) + job->srcSize, ip, toCopy);
2360 job->srcSize += toCopy;
2361 if (job->srcSize == mt->jobSize) LZ4F_mt_submit(mt); /* before checksum : workers start sooner */
2362 if (checksum) (void)XXH32_update(&(cctx->xxh), ip, toCopy);
2363 ip += toCopy;
2364 }
2365 }
2366 cctx->totalInSize += srcSize;
2367 if (srcSize) {
2368 mt->stableEnd = stableUntilFlush ? iend : NULL((void*)0);
2369 mt->stableRun = MIN(prevRun + srcSize, LZ4F_MT_PREFIX_SIZE)( (prevRun + srcSize) < ((64 *(1<<10))) ? (prevRun +
srcSize) : ((64 *(1<<10))) )
;
2370 }
2371
2372 /* autoFlush : the last job is submitted too (see LZ4F_mt_submitLast()), and all jobs are emitted */
2373 if (cctx->prefs.autoFlush && mt->filling) LZ4F_mt_submitLast(mt);
2374 { size_t const r = LZ4F_mt_emit(mt, dst, dstCapacity, pos, cctx->prefs.autoFlush != 0);
2375 FORWARD_IF_ERROR_INVALIDATE(r, cctx)do { size_t const err_code = (r); if (LZ4F_isError(err_code))
{ LZ4F_invalidateFrame(cctx); return err_code; } } while (0)
;
2376 return pos + r;
2377 }
2378}
2379
2380
2381/*! LZ4F_st_update() :
2382 * LZ4F_compressUpdate(), single-threaded mode (@blockCompression : compressed or uncompressed input ;
2383 * @compressOptionsPtr : not NULL).
2384 * Input first completes the block buffered within cctx, then full blocks are compressed directly from @srcBuffer
2385 * (all remaining input with autoFlush), and the remainder is buffered.
2386 * A change of @blockCompression first flushes the buffered block. */
2387static size_t LZ4F_st_update(LZ4F_cctx* cctxPtr,
2388 void* dstBuffer, size_t dstCapacity,
2389 const void* srcBuffer, size_t srcSize,
2390 const LZ4F_compressOptions_t* compressOptionsPtr,
2391 LZ4F_BlockCompressMode_e blockCompression)
2392 {
2393 size_t const blockSize = cctxPtr->maxBlockSize;
2394 const BYTE* srcPtr = (const BYTE*)srcBuffer;
2395 const BYTE* const srcEnd = srcSize ? (assert(srcPtr!=NULL)((void)0), srcPtr + srcSize) : srcPtr;
2396 BYTE* const dstStart = (BYTE*)dstBuffer;
2397 BYTE* const dstEnd = dstStart + dstCapacity;
2398 BYTE* dstPtr = dstStart;
2399 LZ4F_lastBlockStatus lastBlockCompressed = notDone;
2400 compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, blockCompression);
2401 size_t bytesWritten;
2402 DEBUGLOG(4, "LZ4F_compressUpdate (srcSize=%zu)", srcSize){};
2403
2404 RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized)do { if (cctxPtr->cStage != 1) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_compressionState_uninitialized); } } while (0)
; /* state must be initialized and waiting for next block */
2405
2406 /* output left pending by an incomplete LZ4F_flush() */
2407 bytesWritten = LZ4F_emitPendingWhole(cctxPtr, dstPtr, dstCapacity);
2408 FORWARD_IF_ERROR_INVALIDATE(bytesWritten, cctxPtr)do { size_t const err_code = (bytesWritten); if (LZ4F_isError
(err_code)) { LZ4F_invalidateFrame(cctxPtr); return err_code;
} } while (0)
;
2409 dstPtr += bytesWritten;
2410
2411 /* flush currently written block, to continue with new block compression */
2412 if (cctxPtr->blockCompressMode != blockCompression) {
2413 bytesWritten = LZ4F_flushBlock(cctxPtr, dstPtr, (size_t)(dstEnd - dstPtr));
2414 FORWARD_IF_ERROR(bytesWritten)do { size_t const err_code = (bytesWritten); if (LZ4F_isError
(err_code)) return err_code; } while (0)
; /* note : a failed flush invalidates the frame */
2415 dstPtr += bytesWritten;
2416 cctxPtr->blockCompressMode = blockCompression;
2417 if ((blockCompression == LZ4B_UNCOMPRESSED) && (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked)) {
2418 /* linked blocks : the decoder's history will contain uncompressed data, unknown to the LZ4 state :
2419 * history is dropped, blocks compressed next don't reference anything before them */
2420 LZ4F_startStream(cctxPtr->lz4CtxPtr, &cctxPtr->prefs, NULL((void*)0), NULL((void*)0), 0);
2421 }
2422 }
2423
2424 /* complete tmp buffer */
2425 if (cctxPtr->tmpInSize > 0) { /* some data already within tmp buffer */
2426 size_t const sizeToCopy = blockSize - cctxPtr->tmpInSize;
2427 assert(blockSize > cctxPtr->tmpInSize)((void)0);
2428 if (sizeToCopy > srcSize) {
2429 /* add src to tmpIn buffer */
2430 memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize);
2431 srcPtr = srcEnd;
2432 cctxPtr->tmpInSize += srcSize;
2433 /* still needs some CRC */
2434 } else {
2435 /* complete tmpIn block and then compress it */
2436 lastBlockCompressed = fromTmpBuffer;
2437 memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy);
2438 srcPtr += sizeToCopy;
2439
2440 bytesWritten = LZ4F_makeBlock(dstPtr, (size_t)(dstEnd - dstPtr),
2441 cctxPtr->tmpIn, blockSize,
2442 compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
2443 cctxPtr->cdict,
2444 cctxPtr->prefs.frameInfo.blockChecksumFlag);
2445 FORWARD_IF_ERROR_INVALIDATE(bytesWritten, cctxPtr)do { size_t const err_code = (bytesWritten); if (LZ4F_isError
(err_code)) { LZ4F_invalidateFrame(cctxPtr); return err_code;
} } while (0)
;
2446 dstPtr += bytesWritten;
2447 if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += blockSize;
2448 cctxPtr->tmpInSize = 0;
2449 } }
2450
2451 /* compress full blocks; with autoFlush, remaining input (< blockSize) is compressed too */
2452 { size_t const remaining = (size_t)(srcEnd - srcPtr);
2453 size_t const toCompress = cctxPtr->prefs.autoFlush ? remaining : remaining - (remaining % blockSize);
2454 /* autoFlush, linked blocks, unstable input smaller than the history window (64 KB) :
2455 * compressed from @srcBuffer, it would become the only history of next blocks.
2456 * It's compressed from tmpBuff instead, right after the history, which remains whole. */
2457 int const viaTmp = cctxPtr->prefs.autoFlush
2458 && (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked)
2459 && !compressOptionsPtr->stableSrc
2460 && (blockCompression == LZ4B_COMPRESSED)
2461 && (toCompress > 0) && (toCompress < 64 KB*(1<<10));
2462 if (viaTmp) {
2463 if (cctxPtr->tmpIn + toCompress > cctxPtr->tmpBuff + cctxPtr->tmpBuffCapacity)
2464 LZ4F_localSaveDict(cctxPtr); /* tmpBuff >= 128 KB : room after the history */
2465 memcpy(cctxPtr->tmpIn, srcPtr, toCompress);
2466 lastBlockCompressed = fromTmpBuffer;
2467 bytesWritten = LZ4F_makeBlock(dstPtr, (size_t)(dstEnd - dstPtr),
2468 cctxPtr->tmpIn, toCompress,
2469 compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
2470 cctxPtr->cdict,
2471 cctxPtr->prefs.frameInfo.blockChecksumFlag);
2472 FORWARD_IF_ERROR_INVALIDATE(bytesWritten, cctxPtr)do { size_t const err_code = (bytesWritten); if (LZ4F_isError
(err_code)) { LZ4F_invalidateFrame(cctxPtr); return err_code;
} } while (0)
;
2473 dstPtr += bytesWritten;
2474 srcPtr += toCompress;
2475 cctxPtr->tmpIn += toCompress;
2476 } else if (toCompress) {
2477 lastBlockCompressed = fromSrcBuffer;
2478 bytesWritten = LZ4F_compressBlocks_internal(dstPtr, (size_t)(dstEnd - dstPtr),
2479 srcPtr, toCompress, blockSize,
2480 compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
2481 cctxPtr->cdict,
2482 cctxPtr->prefs.frameInfo.blockChecksumFlag);
2483 FORWARD_IF_ERROR_INVALIDATE(bytesWritten, cctxPtr)do { size_t const err_code = (bytesWritten); if (LZ4F_isError
(err_code)) { LZ4F_invalidateFrame(cctxPtr); return err_code;
} } while (0)
;
2484 dstPtr += bytesWritten;
2485 srcPtr += toCompress;
2486 } }
2487
2488 /* preserve dictionary within @tmpBuff whenever necessary */
2489 if ((cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) && (lastBlockCompressed==fromSrcBuffer)) {
2490 /* note : after uncompressed blocks, the LZ4 state is empty (history dropped), so is the saved dictionary */
2491 if (compressOptionsPtr->stableSrc) {
2492 cctxPtr->tmpIn = cctxPtr->tmpBuff; /* src is stable : dictionary remains in src across invocations */
2493 } else {
2494 LZ4F_localSaveDict(cctxPtr);
2495 }
2496 }
2497
2498 /* keep tmpIn within limits */
2499 if (!(cctxPtr->prefs.autoFlush) /* no autoflush : there may be some data left within internal buffer */
2500 && (cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->tmpBuffCapacity) ) /* not enough room to store next block */
2501 {
2502 /* only preserve 64KB within internal buffer. Ensures there is enough room for next block.
2503 * note: this situation necessarily implies lastBlockCompressed==fromTmpBuffer */
2504 LZ4F_localSaveDict(cctxPtr);
2505 assert((cctxPtr->tmpIn + blockSize) <= (cctxPtr->tmpBuff + cctxPtr->tmpBuffCapacity))((void)0);
2506 }
2507
2508 /* some input data left, necessarily < blockSize */
2509 if (srcPtr < srcEnd) {
2510 /* fill tmp buffer */
2511 size_t const sizeToCopy = (size_t)(srcEnd - srcPtr);
2512 memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy);
2513 cctxPtr->tmpInSize = sizeToCopy;
2514 }
2515
2516 if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled)
2517 (void)XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize);
2518
2519 cctxPtr->totalInSize += srcSize;
2520 return (size_t)(dstPtr - dstStart);
2521}
2522
2523/*! LZ4F_compressUpdate() :
2524 * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.
2525 * When successful, the function always entirely consumes @srcBuffer.
2526 * src data is either buffered or compressed into @dstBuffer.
2527 * With @compressOptionsPtr->uncompressed == 1, src data is stored as uncompressed blocks instead.
2528 * With linked blocks, blocks compressed next don't reference data preceding them (single-threaded mode),
2529 * or reference the uncompressed data (multi-threaded mode, see LZ4F_compressJob()).
2530 * A switch between compressed and uncompressed input first flushes buffered data.
2531 * Success is guaranteed when @dstCapacity >= LZ4F_compressBound(srcSize, preferencesPtr).
2532 * Otherwise, the operation fails with dstCapacity_tooSmall if its output doesn't fit.
2533 * @compressOptionsPtr is optional : provide NULL to mean "default".
2534 * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.
2535 * or an error code if it fails (which can be tested using LZ4F_isError())
2536 * After an error, the frame is invalidated, and must be restarted with LZ4F_compressBegin().
2537 */
2538size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr,
2539 void* dstBuffer, size_t dstCapacity,
2540 const void* srcBuffer, size_t srcSize,
2541 const LZ4F_compressOptions_t* compressOptionsPtr)
2542{
2543 LZ4F_BlockCompressMode_e mode;
2544 if (compressOptionsPtr == NULL((void*)0)) compressOptionsPtr = &k_cOptionsNull;
2545 mode = (compressOptionsPtr->uncompressed == 1) ? LZ4B_UNCOMPRESSED : LZ4B_COMPRESSED;
2546 if (cctxPtr->mtActive)
2547 return LZ4F_mt_update(cctxPtr, dstBuffer, dstCapacity, srcBuffer, srcSize, compressOptionsPtr, mode);
2548 return LZ4F_st_update(cctxPtr, dstBuffer, dstCapacity, srcBuffer, srcSize, compressOptionsPtr, mode);
2549}
2550
2551/*! LZ4F_uncompressedUpdate() : deprecated
2552 * LZ4F_compressUpdate() with option uncompressed.
2553 */
2554size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr,
2555 void* dstBuffer, size_t dstCapacity,
2556 const void* srcBuffer, size_t srcSize,
2557 const LZ4F_compressOptions_t* compressOptionsPtr)
2558{
2559 LZ4F_compressOptions_t cOpts = k_cOptionsNull;
2560 if (compressOptionsPtr != NULL((void*)0)) cOpts = *compressOptionsPtr;
2561 cOpts.uncompressed = 1;
2562 return LZ4F_compressUpdate(cctxPtr, dstBuffer, dstCapacity, srcBuffer, srcSize, &cOpts);
2563}
2564
2565
2566/* emits pending output into @dst + @pos, as much as fits : block or footer (pending), then jobs (multi-threaded mode).
2567 * @return : new position, or an error code (the frame is then invalidated) */
2568static size_t LZ4F_emitPending(LZ4F_cctx_t* cctx, BYTE* dst, size_t dstCapacity, size_t pos)
2569{
2570 pos += LZ4F_copyPending(cctx, dst, dstCapacity, pos);
2571 if (cctx->mtActive && (cctx->pendingSize == 0)) {
2572 size_t const r = LZ4F_mt_emitPartial(cctx->mt, dst, dstCapacity, pos);
2573 FORWARD_IF_ERROR_INVALIDATE(r, cctx)do { size_t const err_code = (r); if (LZ4F_isError(err_code))
{ LZ4F_invalidateFrame(cctx); return err_code; } } while (0)
;
2574 pos += r;
2575 }
2576 return pos;
2577}
2578
2579static int LZ4F_outputPending(const LZ4F_cctx_t* cctx)
2580{
2581 return (cctx->pendingSize > 0) || (cctx->mtActive && (cctx->mt->nbSubmitted > 0));
2582}
2583
2584/* @return : 1 if LZ4F_flush() has output to produce */
2585static int LZ4F_somethingToFlush(const LZ4F_cctx_t* cctx)
2586{
2587 if (LZ4F_outputPending(cctx)) return 1;
2588 if (cctx->mtActive) return cctx->mt->filling;
2589 return cctx->tmpInSize > 0;
2590}
2591
2592/* flush point : input retained within cctx is compressed, and no longer read.
2593 * @return : nb of bytes written into @dst, or an error code (the frame is then invalidated) */
2594static size_t LZ4F_flushPoint(LZ4F_cctx_t* cctx, BYTE* dst, size_t dstCapacity)
2595{
2596 size_t r = 0;
2597 if (cctx->mtActive) {
2598 LZ4F_mt_flushPoint(cctx->mt);
2599 } else {
2600 r = LZ4F_flushBlockPartial(cctx, dst, dstCapacity);
2601 }
2602 if (!LZ4F_isError(r)) cctx->flushStage = 1;
2603 return r;
2604}
2605
2606/*! LZ4F_flush() :
2607 * When compressed data must be sent immediately, without waiting for a block to be filled,
2608 * invoke LZ4F_flush(), which will immediately compress any remaining data stored within LZ4F_cctx.
2609 * The invocation fills @dstBuffer, or completes the flush :
2610 * as long as it returns @dstCapacity, output may remain : invoke LZ4F_flush() again.
2611 * The first invocation sets the flush point : input retained within cctx is compressed (all jobs completed).
2612 * The result of the function is the number of bytes written into dstBuffer.
2613 * It can be zero, this means there was no data left within LZ4F_cctx.
2614 * The function outputs an error code if it fails (can be tested using LZ4F_isError())
2615 * A single invocation completes the flush when dstCapacity >= LZ4F_compressBound(0, preferencesPtr).
2616 * dstCapacity == 0 is rejected (dstCapacity_tooSmall) when there is something to flush, without modifying the frame.
2617 * LZ4F_compressOptions_t* is optional : NULL is a valid argument.
2618 */
2619size_t LZ4F_flush(LZ4F_cctx* cctxPtr,
2620 void* dstBuffer, size_t dstCapacity,
2621 const LZ4F_compressOptions_t* compressOptionsPtr)
2622{
2623 BYTE* const dst = (BYTE*)dstBuffer;
2624 size_t pos = 0;
2625 DEBUGLOG(5, "LZ4F_flush (dstCapacity=%u)", (unsigned)dstCapacity){};
2626 (void)compressOptionsPtr; /* not useful (yet) */
2627 RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized)do { if (cctxPtr->cStage != 1) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_compressionState_uninitialized); } } while (0)
;
2628 if (dstCapacity == 0) {
2629 /* no progress possible : rejected, the frame continues */
2630 RETURN_ERROR_IF(LZ4F_somethingToFlush(cctxPtr), dstCapacity_tooSmall)do { if (LZ4F_somethingToFlush(cctxPtr)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_dstCapacity_tooSmall); } } while (0)
;
2631 cctxPtr->flushStage = 0;
2632 return 0;
2633 }
2634 if (cctxPtr->flushStage == 0) {
2635 pos = LZ4F_flushPoint(cctxPtr, dst, dstCapacity);
2636 FORWARD_IF_ERROR(pos)do { size_t const err_code = (pos); if (LZ4F_isError(err_code
)) return err_code; } while (0)
; /* already invalidated */
2637 }
2638 pos = LZ4F_emitPending(cctxPtr, dst, dstCapacity, pos);
2639 FORWARD_IF_ERROR(pos)do { size_t const err_code = (pos); if (LZ4F_isError(err_code
)) return err_code; } while (0)
; /* already invalidated */
2640 if (pos < dstCapacity) cctxPtr->flushStage = 0; /* completed */
2641 return pos;
2642}
2643
2644
2645/*! LZ4F_compressEnd() :
2646 * When you want to properly finish the compressed frame, just call LZ4F_compressEnd().
2647 * It will flush whatever data remained within compressionContext (like LZ4_flush())
2648 * but also properly finalize the frame, with an endMark and an (optional) checksum.
2649 * The invocation fills @dstBuffer, or completes the frame :
2650 * as long as it returns @dstCapacity, output may remain : invoke LZ4F_compressEnd() again.
2651 * LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
2652 * @return: the number of bytes written into dstBuffer (the last invocation writes the endMark, unless already written)
2653 * or an error code if it fails (can be tested using LZ4F_isError())
2654 * Once completed, the context can be used again to compress a new frame, starting with LZ4F_compressBegin().
2655 */
2656size_t LZ4F_compressEnd(LZ4F_cctx* cctxPtr,
2657 void* dstBuffer, size_t dstCapacity,
2658 const LZ4F_compressOptions_t* compressOptionsPtr)
2659{
2660 BYTE* const dst = (BYTE*)dstBuffer;
2661 size_t pos = 0;
2662 DEBUGLOG(5,"LZ4F_compressEnd: dstCapacity=%u", (unsigned)dstCapacity){};
2663 (void)compressOptionsPtr; /* not useful (yet) */
2664 RETURN_ERROR_IF((cctxPtr->cStage != 1) && (cctxPtr->cStage != 2), compressionState_uninitialized)do { if ((cctxPtr->cStage != 1) && (cctxPtr->cStage
!= 2)) { {}; return LZ4F_returnErrorCode(LZ4F_ERROR_compressionState_uninitialized
); } } while (0)
;
2665 RETURN_ERROR_IF(dstCapacity == 0, dstCapacity_tooSmall)do { if (dstCapacity == 0) { {}; return LZ4F_returnErrorCode(
LZ4F_ERROR_dstCapacity_tooSmall); } } while (0)
; /* no progress possible : rejected, the frame continues */
2666
2667 if (cctxPtr->cStage == 1) {
2668 /* end of frame : no more input */
2669 if (cctxPtr->prefs.frameInfo.contentSize && (cctxPtr->prefs.frameInfo.contentSize != cctxPtr->totalInSize)) {
2670 LZ4F_invalidateFrame(cctxPtr);
2671 RETURN_ERROR(frameSize_wrong)return LZ4F_returnErrorCode(LZ4F_ERROR_frameSize_wrong);
2672 }
2673 cctxPtr->cStage = 2;
2674 if (cctxPtr->flushStage == 0) {
2675 pos = LZ4F_flushPoint(cctxPtr, dst, dstCapacity);
2676 FORWARD_IF_ERROR(pos)do { size_t const err_code = (pos); if (LZ4F_isError(err_code
)) return err_code; } while (0)
; /* already invalidated */
2677 } }
2678
2679 if (cctxPtr->flushStage == 1) {
2680 pos = LZ4F_emitPending(cctxPtr, dst, dstCapacity, pos);
2681 FORWARD_IF_ERROR(pos)do { size_t const err_code = (pos); if (LZ4F_isError(err_code
)) return err_code; } while (0)
; /* already invalidated */
2682 if (LZ4F_outputPending(cctxPtr)) return pos; /* dstBuffer is full */
2683 /* footer : endMark, then optional content checksum */
2684 LZ4F_writeLE32(cctxPtr->footer, 0);
2685 cctxPtr->pendingSize = 4;
2686 if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled) {
2687 U32 const xxh = XXH32_digest(&(cctxPtr->xxh));
2688 DEBUGLOG(5,"Writing 32-bit content checksum (0x%0X)", xxh){};
2689 LZ4F_writeLE32(cctxPtr->footer + 4, xxh);
2690 cctxPtr->pendingSize = 8;
2691 }
2692 cctxPtr->pending = cctxPtr->footer;
2693 cctxPtr->flushStage = 2;
2694 }
2695
2696 if (cctxPtr->flushStage == 2) {
2697 pos += LZ4F_copyPending(cctxPtr, dst, dstCapacity, pos);
2698 if (cctxPtr->pendingSize) return pos; /* dstBuffer is full */
2699 cctxPtr->flushStage = 3;
2700 }
2701
2702 assert(cctxPtr->flushStage == 3)((void)0);
2703 if (pos < dstCapacity) {
2704 /* completed : the context is available for a new frame */
2705 cctxPtr->cStage = 0;
2706 cctxPtr->flushStage = 0;
2707 } /* otherwise, completed, but dstBuffer is full : next invocation returns 0 */
2708 return pos;
2709}
2710
2711
2712/*-***************************************************
2713* Frame Decompression
2714*****************************************************/
2715
2716typedef enum {
2717 dstage_getFrameHeader=0, dstage_storeFrameHeader,
2718 dstage_init,
2719 dstage_getBlockHeader,
2720 dstage_copyDirect, dstage_getBlockChecksum,
2721 dstage_getCBlock,
2722 dstage_flushOut,
2723 dstage_getSuffix,
2724 dstage_getSFrameSize,
2725 dstage_skipSkippable
2726} dStage_t;
2727
2728struct LZ4F_dctx_s {
2729 LZ4F_CustomMem cmem;
2730 LZ4F_frameInfo_t frameInfo;
2731 U32 version;
2732 dStage_t dStage;
2733 U64 frameRemainingSize;
2734 size_t maxBlockSize;
2735 size_t tmpOutBufferCapacity;
2736 BYTE* tmpIn;
2737 size_t tmpInCapacity;
2738 size_t tmpInSize;
2739 size_t tmpInTarget;
2740 BYTE* tmpOutBuffer;
2741 const BYTE* dict;
2742 size_t dictSize;
2743 BYTE* tmpOut;
2744 size_t tmpOutSize;
2745 size_t tmpOutStart;
2746 XXH32_state_t xxh;
2747 XXH32_state_t blockChecksum;
2748 int skipBlockChecksum;
2749 int skipContentChecksum;
2750 BYTE header[LZ4F_HEADER_SIZE_MAX19];
2751}; /* typedef'd to LZ4F_dctx in lz4frame.h */
2752
2753
2754LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version)
2755{
2756 LZ4F_dctx* const dctx = (LZ4F_dctx*)LZ4F_calloc(sizeof(LZ4F_dctx), customMem);
2757 if (dctx == NULL((void*)0)) return NULL((void*)0);
2758
2759 dctx->cmem = customMem;
2760 dctx->version = version;
2761 return dctx;
2762}
2763
2764/*! LZ4F_createDecompressionContext() :
2765 * Create a decompressionContext object, which will track all decompression operations.
2766 * Provides a pointer to a fully allocated and initialized LZ4F_decompressionContext object.
2767 * Object can later be released using LZ4F_freeDecompressionContext().
2768 * @return : if != 0, there was an error during context creation.
2769 */
2770LZ4F_errorCode_t
2771LZ4F_createDecompressionContext(LZ4F_dctx** LZ4F_decompressionContextPtr, unsigned versionNumber)
2772{
2773 assert(LZ4F_decompressionContextPtr != NULL)((void)0); /* violation of narrow contract */
2774 RETURN_ERROR_IF(LZ4F_decompressionContextPtr == NULL, parameter_null)do { if (LZ4F_decompressionContextPtr == ((void*)0)) { {}; return
LZ4F_returnErrorCode(LZ4F_ERROR_parameter_null); } } while (
0)
; /* in case it nonetheless happen in production */
2775
2776 *LZ4F_decompressionContextPtr = LZ4F_createDecompressionContext_advanced(LZ4F_defaultCMem, versionNumber);
2777 if (*LZ4F_decompressionContextPtr == NULL((void*)0)) { /* failed allocation */
2778 RETURN_ERROR(allocation_failed)return LZ4F_returnErrorCode(LZ4F_ERROR_allocation_failed);
2779 }
2780 return LZ4F_OK_NoError;
2781}
2782
2783LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx)
2784{
2785 LZ4F_errorCode_t result = LZ4F_OK_NoError;
2786 if (dctx != NULL((void*)0)) { /* can accept NULL input, like free() */
2787 result = (LZ4F_errorCode_t)dctx->dStage;
2788 LZ4F_free(dctx->tmpIn, dctx->cmem);
2789 LZ4F_free(dctx->tmpOutBuffer, dctx->cmem);
2790 LZ4F_free(dctx, dctx->cmem);
2791 }
2792 return result;
2793}
2794
2795size_t LZ4F_dctx_size(const LZ4F_dctx* dctx) {
2796 if (dctx == NULL((void*)0)) {
2797 return 0;
2798 }
2799 return sizeof(*dctx) + dctx->tmpInCapacity + dctx->tmpOutBufferCapacity;
2800}
2801
2802
2803/*==--- Streaming Decompression operations ---==*/
2804void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx)
2805{
2806 DEBUGLOG(5, "LZ4F_resetDecompressionContext"){};
2807 dctx->dStage = dstage_getFrameHeader;
2808 dctx->dict = NULL((void*)0);
2809 dctx->dictSize = 0;
2810 dctx->skipBlockChecksum = 0;
2811 dctx->skipContentChecksum = 0;
2812 dctx->frameRemainingSize = 0;
2813}
2814
2815
2816/*! LZ4F_decodeHeader() :
2817 * input : `src` points at the **beginning of the frame**, srcSize >= minFHSize
2818 * output : set internal values of dctx, such as
2819 * dctx->frameInfo and dctx->dStage.
2820 * @return : nb Bytes read from src (necessarily <= srcSize)
2821 * or an error code (testable with LZ4F_isError())
2822 */
2823static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize)
2824{
2825 unsigned blockMode, blockChecksumFlag, contentSizeFlag, contentChecksumFlag, dictIDFlag, blockSizeID;
2826 size_t frameHeaderSize;
2827 const BYTE* srcPtr = (const BYTE*)src;
2828
2829 DEBUGLOG(5, "LZ4F_decodeHeader"){};
2830 assert(srcSize >= minFHSize)((void)0); /* minimal frame header size */
2831 MEM_INIT(&(dctx->frameInfo), 0, sizeof(dctx->frameInfo))memset((&(dctx->frameInfo)),(0),(sizeof(dctx->frameInfo
)))
;
2832
2833 /* special case : skippable frames */
2834 if ((LZ4F_readLE32(srcPtr) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START0x184D2A50U) {
2835 dctx->frameInfo.frameType = LZ4F_skippableFrame;
2836 dctx->dStage = dstage_getSFrameSize;
2837 if (src == (void*)(dctx->header)) {
2838 /* the beginning of the frame size field is already gathered in header+4 */
2839 assert(srcSize <= 8)((void)0);
2840 dctx->tmpInSize = srcSize - 4;
2841 return srcSize;
2842 } else {
2843 dctx->tmpInSize = 0;
2844 return 4;
2845 } }
2846
2847 /* control magic number */
2848#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2849 if (LZ4F_readLE32(srcPtr) != LZ4F_MAGICNUMBER0x184D2204U) {
2850 DEBUGLOG(4, "frame header error : unknown magic number"){};
2851 RETURN_ERROR(frameType_unknown)return LZ4F_returnErrorCode(LZ4F_ERROR_frameType_unknown);
2852 }
2853#endif
2854 dctx->frameInfo.frameType = LZ4F_frame;
2855
2856 /* Flags */
2857 { U32 const FLG = srcPtr[4];
2858 U32 const version = (FLG>>6) & _2BITS0x03;
2859 blockChecksumFlag = (FLG>>4) & _1BIT0x01;
2860 blockMode = (FLG>>5) & _1BIT0x01;
2861 contentSizeFlag = (FLG>>3) & _1BIT0x01;
2862 contentChecksumFlag = (FLG>>2) & _1BIT0x01;
2863 dictIDFlag = FLG & _1BIT0x01;
2864 /* validate */
2865 if (((FLG>>1)&_1BIT0x01) != 0) RETURN_ERROR(reservedFlag_set)return LZ4F_returnErrorCode(LZ4F_ERROR_reservedFlag_set); /* Reserved bit */
2866 if (version != 1) RETURN_ERROR(headerVersion_wrong)return LZ4F_returnErrorCode(LZ4F_ERROR_headerVersion_wrong); /* Version Number, only supported value */
2867 }
2868 DEBUGLOG(6, "contentSizeFlag: %u", contentSizeFlag){};
2869
2870 /* Frame Header Size */
2871 frameHeaderSize = minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0);
2872
2873 if (srcSize < frameHeaderSize) {
2874 /* only the minimal header is gathered into dctx->header : gather the rest */
2875 assert(srcPtr == dctx->header)((void)0);
2876 assert(dctx->dStage == dstage_storeFrameHeader)((void)0);
2877 assert(dctx->tmpInSize == srcSize)((void)0);
2878 dctx->tmpInTarget = frameHeaderSize;
2879 return srcSize;
2880 }
2881
2882 { U32 const BD = srcPtr[5];
2883 blockSizeID = (BD>>4) & _3BITS0x07;
2884 /* validate */
2885 if (((BD>>7)&_1BIT0x01) != 0) RETURN_ERROR(reservedFlag_set)return LZ4F_returnErrorCode(LZ4F_ERROR_reservedFlag_set); /* Reserved bit */
2886 if (blockSizeID < 4) RETURN_ERROR(maxBlockSize_invalid)return LZ4F_returnErrorCode(LZ4F_ERROR_maxBlockSize_invalid); /* 4-7 only supported values for the time being */
2887 if (((BD>>0)&_4BITS0x0F) != 0) RETURN_ERROR(reservedFlag_set)return LZ4F_returnErrorCode(LZ4F_ERROR_reservedFlag_set); /* Reserved bits */
2888 }
2889
2890 /* check header */
2891 assert(frameHeaderSize > 5)((void)0);
2892#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2893 { BYTE const HC = LZ4F_headerChecksum(srcPtr+4, frameHeaderSize-5);
2894 RETURN_ERROR_IF(HC != srcPtr[frameHeaderSize-1], headerChecksum_invalid)do { if (HC != srcPtr[frameHeaderSize-1]) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_headerChecksum_invalid); } } while (0)
;
2895 }
2896#endif
2897
2898 /* save */
2899 dctx->frameInfo.blockMode = (LZ4F_blockMode_t)blockMode;
2900 dctx->frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)blockChecksumFlag;
2901 dctx->frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)contentChecksumFlag;
2902 dctx->frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID;
2903 dctx->maxBlockSize = LZ4F_getBlockSize((LZ4F_blockSizeID_t)blockSizeID);
2904 if (contentSizeFlag) {
2905 dctx->frameRemainingSize = dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6);
2906 }
2907 if (dictIDFlag)
2908 dctx->frameInfo.dictID = LZ4F_readLE32(srcPtr + frameHeaderSize - 5);
2909
2910 dctx->dStage = dstage_init;
2911
2912 return frameHeaderSize;
2913}
2914
2915
2916/*! LZ4F_headerSize() :
2917 * @return : size of frame header
2918 * or an error code, which can be tested using LZ4F_isError()
2919 */
2920size_t LZ4F_headerSize(const void* src, size_t srcSize)
2921{
2922 RETURN_ERROR_IF(src == NULL, srcPtr_wrong)do { if (src == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_srcPtr_wrong); } } while (0)
;
2923
2924 /* minimal srcSize to determine header size */
2925 if (srcSize < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH5)
2926 RETURN_ERROR(frameHeader_incomplete)return LZ4F_returnErrorCode(LZ4F_ERROR_frameHeader_incomplete
)
;
2927
2928 /* special case : skippable frames */
2929 if ((LZ4F_readLE32(src) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START0x184D2A50U)
2930 return 8;
2931
2932 /* control magic number */
2933#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2934 if (LZ4F_readLE32(src) != LZ4F_MAGICNUMBER0x184D2204U)
2935 RETURN_ERROR(frameType_unknown)return LZ4F_returnErrorCode(LZ4F_ERROR_frameType_unknown);
2936#endif
2937
2938 /* Frame Header Size */
2939 { BYTE const FLG = ((const BYTE*)src)[4];
2940 U32 const contentSizeFlag = (FLG>>3) & _1BIT0x01;
2941 U32 const dictIDFlag = FLG & _1BIT0x01;
2942 return minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0);
2943 }
2944}
2945
2946/*! LZ4F_getFrameInfo() :
2947 * This function extracts frame parameters (max blockSize, frame checksum, etc.).
2948 * Usage is optional. Objective is to provide relevant information for allocation purposes.
2949 * This function works in 2 situations :
2950 * - At the beginning of a new frame, in which case it will decode this information from `srcBuffer`, and start the decoding process.
2951 * Amount of input data provided must be large enough to successfully decode the frame header.
2952 * A header size is variable, but is guaranteed to be <= LZ4F_HEADER_SIZE_MAX bytes. It's possible to provide more input data than this minimum.
2953 * - After decoding has been started. In which case, no input is read, frame parameters are extracted from dctx.
2954 * The number of bytes consumed from srcBuffer will be updated within *srcSizePtr (necessarily <= original value).
2955 * Decompression must resume from (srcBuffer + *srcSizePtr).
2956 * @return : an hint about how many srcSize bytes LZ4F_decompress() expects for next call,
2957 * or an error code which can be tested using LZ4F_isError()
2958 * note 1 : in case of error, dctx is not modified. Decoding operations can resume from where they stopped.
2959 * note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure.
2960 */
2961LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctx,
2962 LZ4F_frameInfo_t* frameInfoPtr,
2963 const void* srcBuffer, size_t* srcSizePtr)
2964{
2965 assert(dctx != NULL)((void)0);
2966 RETURN_ERROR_IF(frameInfoPtr == NULL, parameter_null)do { if (frameInfoPtr == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_parameter_null); } } while (0)
;
2967 RETURN_ERROR_IF(srcSizePtr == NULL, parameter_null)do { if (srcSizePtr == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_parameter_null); } } while (0)
;
2968
2969 LZ4F_STATIC_ASSERT(dstage_getFrameHeader < dstage_storeFrameHeader){ enum { LZ4F_static_assert = 1/(int)(!!(dstage_getFrameHeader
< dstage_storeFrameHeader)) }; }
;
2970 if (dctx->dStage > dstage_storeFrameHeader) {
2971 /* frameInfo already decoded */
2972 size_t o=0, i=0;
2973 *srcSizePtr = 0;
2974 *frameInfoPtr = dctx->frameInfo;
2975 /* returns : recommended nb of bytes for LZ4F_decompress() */
2976 return LZ4F_decompress(dctx, NULL((void*)0), &o, NULL((void*)0), &i, NULL((void*)0));
2977 } else {
2978 if (dctx->dStage == dstage_storeFrameHeader) {
2979 /* frame decoding already started, in the middle of header => automatic fail */
2980 *srcSizePtr = 0;
2981 RETURN_ERROR(frameDecoding_alreadyStarted)return LZ4F_returnErrorCode(LZ4F_ERROR_frameDecoding_alreadyStarted
)
;
2982 } else {
2983 size_t const hSize = LZ4F_headerSize(srcBuffer, *srcSizePtr);
2984 if (LZ4F_isError(hSize)) { *srcSizePtr=0; return hSize; }
2985 if (*srcSizePtr < hSize) {
2986 *srcSizePtr=0;
2987 RETURN_ERROR(frameHeader_incomplete)return LZ4F_returnErrorCode(LZ4F_ERROR_frameHeader_incomplete
)
;
2988 }
2989
2990 { size_t decodeResult = LZ4F_decodeHeader(dctx, srcBuffer, hSize);
2991 if (LZ4F_isError(decodeResult)) {
2992 *srcSizePtr = 0;
2993 } else {
2994 *srcSizePtr = decodeResult;
2995 decodeResult = BHSize; /* block header size */
2996 }
2997 *frameInfoPtr = dctx->frameInfo;
2998 return decodeResult;
2999 } } }
3000}
3001
3002
3003/* LZ4F_moveDictBeforeTmpOut() :
3004 * The block decoded in tmpOut is being flushed, while the history preceding it is elsewhere.
3005 * Copy the end of this history in front of tmpOut (up to 64 KB with the block), so that the dictionary
3006 * becomes contiguous within tmpOutBuffer, ending with the flushed part of tmpOut.
3007 * @flushSize : bytes being flushed, not yet counted in tmpOutStart
3008 */
3009static void LZ4F_moveDictBeforeTmpOut(LZ4F_dctx* dctx, size_t flushSize)
3010{
3011 size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer);
3012 size_t copySize = 64 KB*(1<<10) - dctx->tmpOutSize;
3013 const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart;
3014 if (dctx->tmpOutSize > 64 KB*(1<<10)) copySize = 0;
3015 if (copySize > preserveSize) copySize = preserveSize;
3016 assert(dctx->tmpOutBuffer != NULL)((void)0);
3017
3018 memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
3019
3020 dctx->dict = dctx->tmpOutBuffer;
3021 dctx->dictSize = preserveSize + dctx->tmpOutStart + flushSize;
3022}
3023
3024/* LZ4F_updateDict() :
3025 * only used for LZ4F_blockLinked mode
3026 * Condition : @dstPtr != NULL
3027 */
3028static void LZ4F_updateDict(LZ4F_dctx* dctx,
3029 const BYTE* dstPtr, size_t dstSize, const BYTE* dstBufferStart,
3030 unsigned withinTmp)
3031{
3032 assert(dstPtr != NULL)((void)0);
3033 if (dctx->dictSize==0) dctx->dict = (const BYTE*)dstPtr; /* will lead to prefix mode */
27
Assuming field 'dictSize' is not equal to 0
28
Taking false branch
3034 assert(dctx->dict != NULL)((void)0);
3035
3036 if (dctx->dict + dctx->dictSize == dstPtr) { /* prefix mode, everything within dstBuffer */
29
Assuming the condition is false
30
Taking false branch
3037 dctx->dictSize += dstSize;
3038 return;
3039 }
3040
3041 assert(dstPtr >= dstBufferStart)((void)0);
3042 if ((size_t)(dstPtr - dstBufferStart) + dstSize >= 64 KB*(1<<10)) { /* history in dstBuffer becomes large enough to become dictionary */
31
Assuming the condition is false
32
Taking false branch
3043 dctx->dict = (const BYTE*)dstBufferStart;
3044 dctx->dictSize = (size_t)(dstPtr - dstBufferStart) + dstSize;
3045 return;
3046 }
3047
3048 assert(dstSize < 64 KB)((void)0); /* if dstSize >= 64 KB, dictionary would be set into dstBuffer directly */
3049
3050 /* dstBuffer does not contain whole useful history (64 KB), so it must be saved within tmpOutBuffer */
3051 assert(dctx->tmpOutBuffer != NULL)((void)0);
3052
3053 if (withinTmp
32.1
'withinTmp' is 0
&& (dctx->dict == dctx->tmpOutBuffer)) { /* continue history within tmpOutBuffer */
3054 /* withinTmp expectation : content of [dstPtr,dstSize] is same as [dict+dictSize,dstSize], so we just extend it */
3055 assert(dctx->dict + dctx->dictSize == dctx->tmpOut + dctx->tmpOutStart)((void)0);
3056 dctx->dictSize += dstSize;
3057 return;
3058 }
3059
3060 if (withinTmp
32.2
'withinTmp' is 0
) { /* copy relevant dict portion in front of tmpOut within tmpOutBuffer */
33
Taking false branch
3061 LZ4F_moveDictBeforeTmpOut(dctx, dstSize);
3062 return;
3063 }
3064
3065 if (dctx->dict == dctx->tmpOutBuffer) { /* copy dst into tmp to complete dict */
34
Assuming field 'dict' is equal to field 'tmpOutBuffer'
35
Taking true branch
3066 if (dctx->dictSize + dstSize > dctx->tmpOutBufferCapacity) { /* tmp buffer not large enough */
36
Assuming the condition is false
37
Taking false branch
3067 size_t const preserveSize = 64 KB*(1<<10) - dstSize;
3068 memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);
3069 dctx->dictSize = preserveSize;
3070 }
3071 memcpy(dctx->tmpOutBuffer + dctx->dictSize, dstPtr, dstSize);
38
Null pointer passed to 2nd parameter expecting 'nonnull'
3072 dctx->dictSize += dstSize;
3073 return;
3074 }
3075
3076 /* join dict & dest into tmp */
3077 { size_t preserveSize = 64 KB*(1<<10) - dstSize;
3078 if (preserveSize > dctx->dictSize) preserveSize = dctx->dictSize;
3079 memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);
3080 memcpy(dctx->tmpOutBuffer + preserveSize, dstPtr, dstSize);
3081 dctx->dict = dctx->tmpOutBuffer;
3082 dctx->dictSize = preserveSize + dstSize;
3083 }
3084}
3085
3086
3087/* LZ4F_endMarkFollows() :
3088 * the frame's endMark is within input, right after the block just output (after @skip bytes : its block checksum, if not consumed yet) :
3089 * no block follows, so the history of linked blocks is no longer needed (saving it can cost as much as decoding a small block).
3090 * Only valid once the block is entirely output : the same invocation then reads the endMark, which ends the frame. */
3091static int LZ4F_endMarkFollows(const BYTE* srcPtr, const BYTE* srcEnd, size_t skip)
3092{
3093 return ((size_t)(srcEnd - srcPtr) >= skip + BHSize) && (LZ4F_readLE32(srcPtr + skip) == 0);
3094}
3095
3096/* LZ4F_decodeBlock() :
3097 * Decode compressed block @src (block checksum excluded) into @dst (capacity >= maxBlockSize),
3098 * using current dictionary, and update content checksum and remaining frame size.
3099 * @return : decoded size, or an error code (testable with LZ4F_isError())
3100 */
3101static size_t LZ4F_decodeBlock(LZ4F_dctx* dctx, const BYTE* src, size_t srcSize, BYTE* dst)
3102{
3103 const char* dict = (const char*)dctx->dict;
3104 size_t dictSize = dctx->dictSize;
3105 int decodedSize;
3106 assert(dst != NULL)((void)0);
3107 if (dict && dictSize > 1 GB*(1<<30)) {
3108 /* the dictSize param is an int, avoid truncation / sign issues */
3109 dict += dictSize - 64 KB*(1<<10);
3110 dictSize = 64 KB*(1<<10);
3111 }
3112 decodedSize = LZ4_decompress_safe_usingDict(
3113 (const char*)src, (char*)dst,
3114 (int)srcSize, (int)dctx->maxBlockSize,
3115 dict, (int)dictSize);
3116 RETURN_ERROR_IF(decodedSize < 0, decompressionFailed)do { if (decodedSize < 0) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_decompressionFailed); } } while (0)
;
3117 if (dctx->frameInfo.contentChecksumFlag && !dctx->skipContentChecksum)
3118 XXH32_update(&(dctx->xxh), dst, (size_t)decodedSize);
3119 if (dctx->frameInfo.contentSize)
3120 dctx->frameRemainingSize -= (size_t)decodedSize;
3121 return (size_t)decodedSize;
3122}
3123
3124
3125/* LZ4F_gatherInput() :
3126 * Provides @wanted contiguous input bytes : directly from src if they are all available and none is buffered yet,
3127 * otherwise accumulated into @buffer, over as many calls as needed (dctx->tmpInSize bytes buffered so far).
3128 * @return : pointer to the @wanted bytes (*srcPtrPtr is moved past consumed input),
3129 * or NULL if input is exhausted before (all of it is then buffered).
3130 */
3131static const BYTE*
3132LZ4F_gatherInput(LZ4F_dctx* dctx, BYTE* buffer, size_t wanted,
3133 const BYTE** srcPtrPtr, const BYTE* srcEnd)
3134{
3135 const BYTE* const srcPtr = *srcPtrPtr;
3136 size_t const available = (size_t)(srcEnd - srcPtr);
3137 if ((dctx->tmpInSize == 0) && (available >= wanted)) {
3138 *srcPtrPtr = srcPtr + wanted;
3139 return srcPtr;
3140 }
3141 { size_t const sizeToCopy = MIN(wanted - dctx->tmpInSize, available)( (wanted - dctx->tmpInSize) < (available) ? (wanted - dctx
->tmpInSize) : (available) )
;
3142 memcpy(buffer + dctx->tmpInSize, srcPtr, sizeToCopy);
3143 dctx->tmpInSize += sizeToCopy;
3144 *srcPtrPtr = srcPtr + sizeToCopy;
3145 }
3146 if (dctx->tmpInSize < wanted) return NULL((void*)0);
3147 dctx->tmpInSize = 0; /* ready for next gathering */
3148 return buffer;
3149}
3150
3151
3152/*! LZ4F_decompress() :
3153 * Call this function repetitively to regenerate compressed data in srcBuffer.
3154 * The function will attempt to decode up to *srcSizePtr bytes from srcBuffer
3155 * into dstBuffer of capacity *dstSizePtr.
3156 *
3157 * The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr (necessarily <= original value).
3158 *
3159 * The number of bytes effectively read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).
3160 * If number of bytes read is < number of bytes provided, then decompression operation is not complete.
3161 * Remaining data will have to be presented again in a subsequent invocation.
3162 *
3163 * The function result is an hint of the better srcSize to use for next call to LZ4F_decompress.
3164 * Schematically, it's the size of the current (or remaining) compressed block + header of next block.
3165 * Respecting the hint provides a small boost to performance, since it allows less buffer shuffling.
3166 * Note that this is just a hint, and it's always possible to any srcSize value.
3167 * When a frame is fully decoded, @return will be 0.
3168 * If decompression failed, @return is an error code which can be tested using LZ4F_isError().
3169 */
3170size_t LZ4F_decompress(LZ4F_dctx* dctx,
3171 void* dstBuffer, size_t* dstSizePtr,
3172 const void* srcBuffer, size_t* srcSizePtr,
3173 const LZ4F_decompressOptions_t* decompressOptionsPtr)
3174{
3175 static const BYTE emptySrc = 0; /* NULL srcBuffer (no input, used by LZ4F_getFrameInfo()) points here instead :
3176 * NULL+0 and memcpy(dst, NULL, 0) are UB */
3177 LZ4F_decompressOptions_t optionsNull;
3178 const BYTE* const srcStart = srcBuffer ? (const BYTE*)srcBuffer : &emptySrc;
1
Assuming 'srcBuffer' is null
2
'?' condition is false
3179 const BYTE* const srcEnd = srcBuffer
2.1
'srcBuffer' is null
? srcStart + *srcSizePtr : srcStart;
3
'?' condition is false
3180 const BYTE* srcPtr = srcStart;
3181 BYTE* const dstStart = (BYTE*)dstBuffer;
4
'dstStart' initialized here
3182 BYTE* const dstEnd = dstStart ? dstStart + *dstSizePtr : NULL((void*)0);
5
Assuming 'dstStart' is null
6
'?' condition is false
3183 BYTE* dstPtr = dstStart;
7
'dstPtr' initialized to a null pointer value
3184 const BYTE* selectedIn = NULL((void*)0);
3185 unsigned doAnotherStage = 1;
3186 size_t nextSrcSizeHint = 1;
3187
3188
3189 DEBUGLOG(5, "LZ4F_decompress: src[%p](%u) => dst[%p](%u)",{}
3190 srcBuffer, (unsigned)*srcSizePtr, dstBuffer, (unsigned)*dstSizePtr){};
3191 if (dstBuffer
7.1
'dstBuffer' is equal to NULL
== NULL((void*)0)) assert(*dstSizePtr == 0)((void)0);
8
Taking true branch
3192 if (srcBuffer
8.1
'srcBuffer' is equal to NULL
== NULL((void*)0)) assert(*srcSizePtr == 0)((void)0);
9
Taking true branch
3193 MEM_INIT(&optionsNull, 0, sizeof(optionsNull))memset((&optionsNull),(0),(sizeof(optionsNull)));
3194 if (decompressOptionsPtr==NULL((void*)0)) decompressOptionsPtr = &optionsNull;
10
Assuming 'decompressOptionsPtr' is not equal to NULL
11
Taking false branch
3195 *srcSizePtr = 0;
3196 *dstSizePtr = 0;
3197 assert(dctx != NULL)((void)0);
3198 if (decompressOptionsPtr->skipChecksums) { /* once set, disable for the remainder of the frame */
12
Assuming field 'skipChecksums' is 0
13
Taking false branch
3199 dctx->skipBlockChecksum = 1;
3200 dctx->skipContentChecksum = 1;
3201 }
3202
3203 /* behaves as a state machine */
3204
3205 while (doAnotherStage) {
14
Loop condition is true. Entering loop body
3206
3207 switch(dctx->dStage)
15
Control jumps to 'case dstage_getCBlock:' at line 3377
3208 {
3209
3210 case dstage_getFrameHeader:
3211 DEBUGLOG(6, "dstage_getFrameHeader"){};
3212 if ((size_t)(srcEnd-srcPtr) >= maxFHSize) { /* enough to decode - shortcut */
3213 size_t const hSize = LZ4F_decodeHeader(dctx, srcPtr, (size_t)(srcEnd-srcPtr)); /* will update dStage appropriately */
3214 FORWARD_IF_ERROR(hSize)do { size_t const err_code = (hSize); if (LZ4F_isError(err_code
)) return err_code; } while (0)
;
3215 srcPtr += hSize;
3216 break;
3217 }
3218 dctx->tmpInSize = 0;
3219 if (srcEnd-srcPtr == 0) return minFHSize; /* 0-size input */
3220 dctx->tmpInTarget = minFHSize; /* minimum size to decode header */
3221 dctx->dStage = dstage_storeFrameHeader;
3222 /* fall-through */
3223
3224 case dstage_storeFrameHeader:
3225 DEBUGLOG(6, "dstage_storeFrameHeader"){};
3226 { size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize, (size_t)(srcEnd - srcPtr))( (dctx->tmpInTarget - dctx->tmpInSize) < ((size_t)(
srcEnd - srcPtr)) ? (dctx->tmpInTarget - dctx->tmpInSize
) : ((size_t)(srcEnd - srcPtr)) )
;
3227 memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);
3228 dctx->tmpInSize += sizeToCopy;
3229 srcPtr += sizeToCopy;
3230 }
3231 if (dctx->tmpInSize < dctx->tmpInTarget) {
3232 nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) + BHSize; /* rest of header + nextBlockHeader */
3233 doAnotherStage = 0; /* not enough src data, ask for some more */
3234 break;
3235 }
3236 FORWARD_IF_ERROR( LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget) )do { size_t const err_code = (LZ4F_decodeHeader(dctx, dctx->
header, dctx->tmpInTarget)); if (LZ4F_isError(err_code)) return
err_code; } while (0)
; /* will update dStage appropriately */
3237 break;
3238
3239 case dstage_init:
3240 DEBUGLOG(6, "dstage_init"){};
3241 if (dctx->frameInfo.contentChecksumFlag) (void)XXH32_reset(&(dctx->xxh), 0);
3242 /* internal buffers allocation */
3243 { size_t const tmpInNeeded = dctx->maxBlockSize + BFSize /* block checksum */;
3244 size_t const tmpOutNeeded = dctx->maxBlockSize
3245 + ((dctx->frameInfo.blockMode==LZ4F_blockLinked) ? 128 KB*(1<<10) : 0);
3246 if ( (tmpInNeeded > dctx->tmpInCapacity)
3247 || (tmpOutNeeded > dctx->tmpOutBufferCapacity) ) { /* tmp buffers too small */
3248 /* capacities remain 0 until allocation succeeds,
3249 * so that allocation will be re-attempted on next entry */
3250 LZ4F_free(dctx->tmpIn, dctx->cmem);
3251 dctx->tmpInCapacity = 0;
3252 dctx->tmpIn = (BYTE*)LZ4F_malloc(tmpInNeeded, dctx->cmem);
3253 RETURN_ERROR_IF(dctx->tmpIn == NULL, allocation_failed)do { if (dctx->tmpIn == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_allocation_failed); } } while (0)
;
3254 dctx->tmpInCapacity = tmpInNeeded;
3255 LZ4F_free(dctx->tmpOutBuffer, dctx->cmem);
3256 dctx->tmpOutBufferCapacity = 0;
3257 dctx->tmpOutBuffer= (BYTE*)LZ4F_malloc(tmpOutNeeded, dctx->cmem);
3258 RETURN_ERROR_IF(dctx->tmpOutBuffer== NULL, allocation_failed)do { if (dctx->tmpOutBuffer== ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_allocation_failed); } } while (0)
;
3259 dctx->tmpOutBufferCapacity = tmpOutNeeded;
3260 } }
3261 dctx->tmpInSize = 0;
3262 dctx->tmpInTarget = 0;
3263 dctx->tmpOut = dctx->tmpOutBuffer;
3264 dctx->tmpOutStart = 0;
3265 dctx->tmpOutSize = 0;
3266
3267 dctx->dStage = dstage_getBlockHeader;
3268 /* fall-through */
3269
3270 case dstage_getBlockHeader:
3271 selectedIn = LZ4F_gatherInput(dctx, dctx->tmpIn, BHSize, &srcPtr, srcEnd);
3272 if (selectedIn == NULL((void*)0)) { /* not enough input for cBlockSize */
3273 nextSrcSizeHint = BHSize - dctx->tmpInSize;
3274 doAnotherStage = 0;
3275 break;
3276 }
3277
3278 /* decode block header */
3279 { U32 const blockHeader = LZ4F_readLE32(selectedIn);
3280 size_t const nextCBlockSize = blockHeader & 0x7FFFFFFFU;
3281 size_t const crcSize = dctx->frameInfo.blockChecksumFlag * BFSize;
3282 if (blockHeader==0) { /* frameEnd signal, no more block */
3283 DEBUGLOG(5, "end of frame"){};
3284 dctx->dStage = dstage_getSuffix;
3285 break;
3286 }
3287 if (nextCBlockSize > dctx->maxBlockSize) {
3288 RETURN_ERROR(maxBlockSize_invalid)return LZ4F_returnErrorCode(LZ4F_ERROR_maxBlockSize_invalid);
3289 }
3290 if (blockHeader & LZ4F_BLOCKUNCOMPRESSED_FLAG0x80000000U) {
3291 /* next block is uncompressed */
3292 dctx->tmpInTarget = nextCBlockSize;
3293 DEBUGLOG(5, "next block is uncompressed (size %u)", (U32)nextCBlockSize){};
3294 if (dctx->frameInfo.blockChecksumFlag) {
3295 (void)XXH32_reset(&dctx->blockChecksum, 0);
3296 }
3297 dctx->dStage = dstage_copyDirect;
3298 break;
3299 }
3300 /* next block is a compressed block */
3301 dctx->tmpInTarget = nextCBlockSize + crcSize;
3302 dctx->dStage = dstage_getCBlock;
3303 if (dstPtr==dstEnd || srcPtr==srcEnd) {
3304 nextSrcSizeHint = BHSize + nextCBlockSize + crcSize;
3305 doAnotherStage = 0;
3306 }
3307 break;
3308 }
3309
3310 case dstage_copyDirect: /* uncompressed block */
3311 DEBUGLOG(6, "dstage_copyDirect"){};
3312 { size_t sizeToCopy;
3313 if (dstPtr == NULL((void*)0)) {
3314 sizeToCopy = 0;
3315 } else {
3316 size_t const minBuffSize = MIN((size_t)(srcEnd-srcPtr), (size_t)(dstEnd-dstPtr))( ((size_t)(srcEnd-srcPtr)) < ((size_t)(dstEnd-dstPtr)) ? (
(size_t)(srcEnd-srcPtr)) : ((size_t)(dstEnd-dstPtr)) )
;
3317 sizeToCopy = MIN(dctx->tmpInTarget, minBuffSize)( (dctx->tmpInTarget) < (minBuffSize) ? (dctx->tmpInTarget
) : (minBuffSize) )
;
3318 memcpy(dstPtr, srcPtr, sizeToCopy);
3319 if (dctx->frameInfo.blockChecksumFlag && !dctx->skipBlockChecksum)
3320 (void)XXH32_update(&dctx->blockChecksum, srcPtr, sizeToCopy);
3321 if (dctx->frameInfo.contentChecksumFlag && !dctx->skipContentChecksum)
3322 (void)XXH32_update(&dctx->xxh, srcPtr, sizeToCopy);
3323 if (dctx->frameInfo.contentSize)
3324 dctx->frameRemainingSize -= sizeToCopy;
3325
3326 /* history management (linked blocks only), unless the frame ends with this block */
3327 if ( (dctx->frameInfo.blockMode == LZ4F_blockLinked)
3328 && !( (sizeToCopy == dctx->tmpInTarget)
3329 && LZ4F_endMarkFollows(srcPtr + sizeToCopy, srcEnd, dctx->frameInfo.blockChecksumFlag * BFSize) ) ) {
3330 LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 0);
3331 }
3332 srcPtr += sizeToCopy;
3333 dstPtr += sizeToCopy;
3334 }
3335 if (sizeToCopy == dctx->tmpInTarget) { /* all done */
3336 if (dctx->frameInfo.blockChecksumFlag)
3337 dctx->dStage = dstage_getBlockChecksum;
3338 else
3339 dctx->dStage = dstage_getBlockHeader; /* new block */
3340 break;
3341 }
3342 dctx->tmpInTarget -= sizeToCopy; /* need to copy more */
3343 }
3344 nextSrcSizeHint = dctx->tmpInTarget
3345 + (dctx->frameInfo.blockChecksumFlag ? BFSize : 0)
3346 + BHSize /* next header size */;
3347 doAnotherStage = 0;
3348 break;
3349
3350 /* check block checksum for recently transferred uncompressed block */
3351 case dstage_getBlockChecksum:
3352 DEBUGLOG(6, "dstage_getBlockChecksum"){};
3353 selectedIn = LZ4F_gatherInput(dctx, dctx->tmpIn, BFSize, &srcPtr, srcEnd);
3354 if (selectedIn == NULL((void*)0)) { /* all input consumed */
3355 nextSrcSizeHint = (BFSize - dctx->tmpInSize) + BHSize /* next header size */;
3356 doAnotherStage = 0;
3357 break;
3358 }
3359 if (!dctx->skipBlockChecksum) {
3360 U32 const readCRC = LZ4F_readLE32(selectedIn);
3361 U32 const calcCRC = XXH32_digest(&dctx->blockChecksum);
3362#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
3363 DEBUGLOG(6, "compare block checksum"){};
3364 if (readCRC != calcCRC) {
3365 DEBUGLOG(4, "incorrect block checksum: %08X != %08X",{}
3366 readCRC, calcCRC){};
3367 RETURN_ERROR(blockChecksum_invalid)return LZ4F_returnErrorCode(LZ4F_ERROR_blockChecksum_invalid);
3368 }
3369#else
3370 (void)readCRC;
3371 (void)calcCRC;
3372#endif
3373 }
3374 dctx->dStage = dstage_getBlockHeader; /* new block */
3375 break;
3376
3377 case dstage_getCBlock:
3378 DEBUGLOG(6, "dstage_getCBlock"){};
3379 selectedIn = LZ4F_gatherInput(dctx, dctx->tmpIn, dctx->tmpInTarget, &srcPtr, srcEnd);
3380 if (selectedIn
15.1
'selectedIn' is not equal to NULL
== NULL((void*)0)) { /* need more input */
16
Taking false branch
3381 nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) /* block checksum included */
3382 + BHSize /* next header size */;
3383 doAnotherStage = 0;
3384 break;
3385 }
3386
3387 /* At this stage, input is large enough to decode a block */
3388
3389 /* First, decode and control block checksum if it exists */
3390 if (dctx->frameInfo.blockChecksumFlag) {
17
Assuming field 'blockChecksumFlag' is 0
3391 assert(dctx->tmpInTarget >= 4)((void)0);
3392 dctx->tmpInTarget -= 4;
3393 if (!dctx->skipBlockChecksum) {
3394 U32 const readBlockCrc = LZ4F_readLE32(selectedIn + dctx->tmpInTarget);
3395 U32 const calcBlockCrc = XXH32(selectedIn, dctx->tmpInTarget, 0);
3396#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
3397 RETURN_ERROR_IF(readBlockCrc != calcBlockCrc, blockChecksum_invalid)do { if (readBlockCrc != calcBlockCrc) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_blockChecksum_invalid); } } while (0)
;
3398#else
3399 (void)readBlockCrc;
3400 (void)calcBlockCrc;
3401#endif
3402 } }
3403
3404 /* decode directly into destination buffer if there is enough room */
3405 if ( ((size_t)(dstEnd-dstPtr) >= dctx->maxBlockSize)
18
Assuming the condition is true
20
Taking true branch
3406 /* unless the dictionary is stored in tmpOut:
3407 * in which case it's faster to decode within tmpOut
3408 * to benefit from prefix speedup */
3409 && !(dctx->dict!= NULL((void*)0) && (const BYTE*)dctx->dict + dctx->dictSize == dctx->tmpOut) )
19
Assuming field 'dict' is equal to NULL
3410 { size_t const decodedSize = LZ4F_decodeBlock(dctx, selectedIn, dctx->tmpInTarget, dstPtr);
3411 FORWARD_IF_ERROR(decodedSize)do { size_t const err_code = (decodedSize); if (LZ4F_isError(
err_code)) return err_code; } while (0)
;
21
Taking false branch
3412
3413 /* dictionary management, unless the frame ends with this block */
3414 if ( (dctx->frameInfo.blockMode==LZ4F_blockLinked)
22
Loop condition is false. Exiting loop
23
Assuming field 'blockMode' is equal to LZ4F_blockLinked
24
Taking true branch
3415 && !LZ4F_endMarkFollows(srcPtr, srcEnd, 0) ) {
3416 LZ4F_updateDict(dctx, dstPtr, decodedSize, dstStart, 0);
25
Passing null pointer value via 2nd parameter 'dstPtr'
26
Calling 'LZ4F_updateDict'
3417 }
3418
3419 dstPtr += decodedSize;
3420 dctx->dStage = dstage_getBlockHeader; /* end of block, let's get another one */
3421 break;
3422 }
3423
3424 /* not enough place into dst : decode into tmpOut */
3425
3426 /* manage dictionary */
3427 if (dctx->frameInfo.blockMode == LZ4F_blockLinked) {
3428 if (dctx->dict == dctx->tmpOutBuffer) {
3429 /* truncate dictionary to 64 KB if too big */
3430 if (dctx->dictSize > 128 KB*(1<<10)) {
3431 memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - 64 KB*(1<<10), 64 KB*(1<<10));
3432 dctx->dictSize = 64 KB*(1<<10);
3433 }
3434 dctx->tmpOut = dctx->tmpOutBuffer + dctx->dictSize;
3435 } else { /* dict not within tmpOut */
3436 size_t const reservedDictSpace = MIN(dctx->dictSize, 64 KB)( (dctx->dictSize) < (64 *(1<<10)) ? (dctx->dictSize
) : (64 *(1<<10)) )
;
3437 dctx->tmpOut = dctx->tmpOutBuffer + reservedDictSpace;
3438 } }
3439
3440 /* Decode block into tmpOut */
3441 { size_t const decodedSize = LZ4F_decodeBlock(dctx, selectedIn, dctx->tmpInTarget, dctx->tmpOut);
3442 FORWARD_IF_ERROR(decodedSize)do { size_t const err_code = (decodedSize); if (LZ4F_isError(
err_code)) return err_code; } while (0)
;
3443 dctx->tmpOutSize = decodedSize;
3444 dctx->tmpOutStart = 0;
3445 dctx->dStage = dstage_flushOut;
3446 }
3447 /* fall-through */
3448
3449 case dstage_flushOut: /* flush decoded data from tmpOut to dstBuffer */
3450 DEBUGLOG(6, "dstage_flushOut"){};
3451 if (dstPtr != NULL((void*)0)) {
3452 size_t const sizeToCopy = MIN(dctx->tmpOutSize - dctx->tmpOutStart, (size_t)(dstEnd-dstPtr))( (dctx->tmpOutSize - dctx->tmpOutStart) < ((size_t)
(dstEnd-dstPtr)) ? (dctx->tmpOutSize - dctx->tmpOutStart
) : ((size_t)(dstEnd-dstPtr)) )
;
3453 memcpy(dstPtr, dctx->tmpOut + dctx->tmpOutStart, sizeToCopy);
3454
3455 /* dictionary management, unless the frame ends with this block */
3456 if ( (dctx->frameInfo.blockMode == LZ4F_blockLinked)
3457 && !( (dctx->tmpOutStart + sizeToCopy == dctx->tmpOutSize)
3458 && LZ4F_endMarkFollows(srcPtr, srcEnd, 0) ) )
3459 LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 1 /*withinTmp*/);
3460
3461 dctx->tmpOutStart += sizeToCopy;
3462 dstPtr += sizeToCopy;
3463 }
3464 if (dctx->tmpOutStart == dctx->tmpOutSize) { /* all flushed */
3465 dctx->dStage = dstage_getBlockHeader; /* get next block */
3466 break;
3467 }
3468 /* could not flush everything : stop there, just request a block header */
3469 doAnotherStage = 0;
3470 nextSrcSizeHint = BHSize;
3471 break;
3472
3473 case dstage_getSuffix:
3474 RETURN_ERROR_IF(dctx->frameRemainingSize, frameSize_wrong)do { if (dctx->frameRemainingSize) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_frameSize_wrong); } } while (0)
; /* incorrect frame size decoded */
3475 if (!dctx->frameInfo.contentChecksumFlag) { /* no checksum, frame is completed */
3476 nextSrcSizeHint = 0;
3477 LZ4F_resetDecompressionContext(dctx);
3478 doAnotherStage = 0;
3479 break;
3480 }
3481 selectedIn = LZ4F_gatherInput(dctx, dctx->tmpIn, 4, &srcPtr, srcEnd);
3482 if (selectedIn == NULL((void*)0)) { /* not enough input to read complete suffix */
3483 nextSrcSizeHint = 4 - dctx->tmpInSize;
3484 doAnotherStage = 0;
3485 break;
3486 }
3487 if (!dctx->skipContentChecksum) {
3488 U32 const readCRC = LZ4F_readLE32(selectedIn);
3489 U32 const resultCRC = XXH32_digest(&(dctx->xxh));
3490 DEBUGLOG(4, "frame checksum: stored 0x%0X vs 0x%0X processed", readCRC, resultCRC){};
3491#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
3492 RETURN_ERROR_IF(readCRC != resultCRC, contentChecksum_invalid)do { if (readCRC != resultCRC) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_contentChecksum_invalid); } } while (0)
;
3493#else
3494 (void)readCRC;
3495 (void)resultCRC;
3496#endif
3497 }
3498 nextSrcSizeHint = 0;
3499 LZ4F_resetDecompressionContext(dctx);
3500 doAnotherStage = 0;
3501 break;
3502
3503 case dstage_getSFrameSize:
3504 selectedIn = LZ4F_gatherInput(dctx, dctx->header + 4, 4, &srcPtr, srcEnd);
3505 if (selectedIn == NULL((void*)0)) { /* not enough input to read the skippable frame size */
3506 nextSrcSizeHint = 4 - dctx->tmpInSize;
3507 doAnotherStage = 0;
3508 break;
3509 }
3510 { size_t const SFrameSize = LZ4F_readLE32(selectedIn);
3511 dctx->frameInfo.contentSize = SFrameSize;
3512 dctx->tmpInTarget = SFrameSize;
3513 dctx->dStage = dstage_skipSkippable;
3514 break;
3515 }
3516
3517 case dstage_skipSkippable:
3518 { size_t const skipSize = MIN(dctx->tmpInTarget, (size_t)(srcEnd-srcPtr))( (dctx->tmpInTarget) < ((size_t)(srcEnd-srcPtr)) ? (dctx
->tmpInTarget) : ((size_t)(srcEnd-srcPtr)) )
;
3519 srcPtr += skipSize;
3520 dctx->tmpInTarget -= skipSize;
3521 doAnotherStage = 0;
3522 nextSrcSizeHint = dctx->tmpInTarget;
3523 if (nextSrcSizeHint) break; /* still more to skip */
3524 /* frame fully skipped : prepare context for a new frame */
3525 LZ4F_resetDecompressionContext(dctx);
3526 break;
3527 }
3528 } /* switch (dctx->dStage) */
3529 } /* while (doAnotherStage) */
3530
3531 /* preserve history within tmpOut whenever necessary */
3532 LZ4F_STATIC_ASSERT((unsigned)dstage_init == 2){ enum { LZ4F_static_assert = 1/(int)(!!((unsigned)dstage_init
== 2)) }; }
;
3533 if ( (dctx->frameInfo.blockMode==LZ4F_blockLinked) /* next block will use up to 64KB from previous ones */
3534 && (dctx->dict != dctx->tmpOutBuffer) /* dictionary is not already within tmp */
3535 && (dctx->dict != NULL((void*)0)) /* dictionary exists */
3536 && (!decompressOptionsPtr->stableDst) /* cannot rely on dst data to remain there for next call */
3537 && ((unsigned)(dctx->dStage)-2 < (unsigned)(dstage_getSuffix)-2) ) /* valid stages : [init ... getSuffix[ */
3538 {
3539 if (dctx->dStage == dstage_flushOut) {
3540 LZ4F_moveDictBeforeTmpOut(dctx, 0);
3541 } else {
3542 const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize;
3543 size_t const newDictSize = MIN(dctx->dictSize, 64 KB)( (dctx->dictSize) < (64 *(1<<10)) ? (dctx->dictSize
) : (64 *(1<<10)) )
;
3544
3545 memcpy(dctx->tmpOutBuffer, oldDictEnd - newDictSize, newDictSize);
3546
3547 dctx->dict = dctx->tmpOutBuffer;
3548 dctx->dictSize = newDictSize;
3549 dctx->tmpOut = dctx->tmpOutBuffer + newDictSize;
3550 }
3551 }
3552
3553 *srcSizePtr = (size_t)(srcPtr - srcStart);
3554 *dstSizePtr = (size_t)(dstPtr - dstStart);
3555 return nextSrcSizeHint;
3556}
3557
3558/*! LZ4F_decompress_usingDict() :
3559 * Same as LZ4F_decompress(), using a predefined dictionary.
3560 * Dictionary is used "in place", without any preprocessing.
3561 * It must remain accessible throughout the entire frame decoding.
3562 */
3563size_t LZ4F_decompress_usingDict(LZ4F_dctx* dctx,
3564 void* dstBuffer, size_t* dstSizePtr,
3565 const void* srcBuffer, size_t* srcSizePtr,
3566 const void* dict, size_t dictSize,
3567 const LZ4F_decompressOptions_t* decompressOptionsPtr)
3568{
3569 if (dctx->dStage <= dstage_init) {
3570 dctx->dict = (const BYTE*)dict;
3571 dctx->dictSize = dictSize;
3572 }
3573 return LZ4F_decompress(dctx, dstBuffer, dstSizePtr,
3574 srcBuffer, srcSizePtr,
3575 decompressOptionsPtr);
3576}
3577
3578/*! LZ4F_resumeFrame() :
3579 * see lz4frame.h (static section) */
3580size_t LZ4F_resumeFrame(LZ4F_dctx* dctx, const LZ4F_frameInfo_t* frameInfo,
3581 unsigned long long decodedPos,
3582 const void* dict, size_t dictSize)
3583{
3584 assert(dctx != NULL)((void)0);
3585 RETURN_ERROR_IF(frameInfo == NULL, parameter_null)do { if (frameInfo == ((void*)0)) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_parameter_null); } } while (0)
;
3586 RETURN_ERROR_IF(dict == NULL && dictSize > 0, parameter_null)do { if (dict == ((void*)0) && dictSize > 0) { {};
return LZ4F_returnErrorCode(LZ4F_ERROR_parameter_null); } } while
(0)
;
3587 RETURN_ERROR_IF(frameInfo->frameType != LZ4F_frame, frameType_unknown)do { if (frameInfo->frameType != LZ4F_frame) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_frameType_unknown); } } while (0)
;
3588 RETURN_ERROR_IF(frameInfo->blockSizeID < LZ4F_max64KB || frameInfo->blockSizeID > LZ4F_max4MB, maxBlockSize_invalid)do { if (frameInfo->blockSizeID < LZ4F_max64KB || frameInfo
->blockSizeID > LZ4F_max4MB) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_maxBlockSize_invalid); } } while (0)
;
3589 RETURN_ERROR_IF((unsigned)frameInfo->blockMode > 1, blockMode_invalid)do { if ((unsigned)frameInfo->blockMode > 1) { {}; return
LZ4F_returnErrorCode(LZ4F_ERROR_blockMode_invalid); } } while
(0)
;
3590 RETURN_ERROR_IF((unsigned)frameInfo->blockChecksumFlag > 1, parameter_invalid)do { if ((unsigned)frameInfo->blockChecksumFlag > 1) { {
}; return LZ4F_returnErrorCode(LZ4F_ERROR_parameter_invalid);
} } while (0)
;
3591 RETURN_ERROR_IF((unsigned)frameInfo->contentChecksumFlag > 1, parameter_invalid)do { if ((unsigned)frameInfo->contentChecksumFlag > 1) {
{}; return LZ4F_returnErrorCode(LZ4F_ERROR_parameter_invalid
); } } while (0)
;
3592 RETURN_ERROR_IF(frameInfo->contentSize && decodedPos > frameInfo->contentSize, frameSize_wrong)do { if (frameInfo->contentSize && decodedPos >
frameInfo->contentSize) { {}; return LZ4F_returnErrorCode
(LZ4F_ERROR_frameSize_wrong); } } while (0)
;
3593
3594 LZ4F_resetDecompressionContext(dctx);
3595 dctx->frameInfo = *frameInfo;
3596 dctx->maxBlockSize = LZ4F_getBlockSize(frameInfo->blockSizeID);
3597 if (frameInfo->contentSize)
3598 dctx->frameRemainingSize = frameInfo->contentSize - decodedPos;
3599 dctx->dict = (const BYTE*)dict;
3600 dctx->dictSize = dictSize;
3601 dctx->skipContentChecksum = 1; /* content checksum covers the whole frame : can't be verified */
3602 dctx->dStage = dstage_init;
3603 return 0;
3604}