| File: | root/firefox-clang/mozglue/static/lz4/lz4frame.c |
| Warning: | line 3080, column 9 Null pointer passed to 2nd parameter expecting 'nonnull' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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 | ||||
| 128 | static 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 | ||||
| 144 | static 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 | ||||
| 154 | static 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> | |||
| 182 | static 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 */ | |||
| 217 | static 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 | ||||
| 227 | static 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 | ||||
| 236 | static 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 | ||||
| 250 | static 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 | ||||
| 282 | static const size_t minFHSize = LZ4F_HEADER_SIZE_MIN7; /* 7 */ | |||
| 283 | static const size_t maxFHSize = LZ4F_HEADER_SIZE_MAX19; /* 19 */ | |||
| 284 | static const size_t BHSize = LZ4F_BLOCK_HEADER_SIZE4; /* block header : size, and compress flag */ | |||
| 285 | static const size_t BFSize = LZ4F_BLOCK_CHECKSUM_SIZE4; /* block footer : checksum (optional) */ | |||
| 286 | ||||
| 287 | ||||
| 288 | /*-************************************ | |||
| 289 | * Structures and local types | |||
| 290 | **************************************/ | |||
| 291 | ||||
| 292 | typedef enum { LZ4B_COMPRESSED, LZ4B_UNCOMPRESSED} LZ4F_BlockCompressMode_e; | |||
| 293 | typedef enum { ctxNone, ctxFast, ctxHC } LZ4F_CtxType_e; | |||
| 294 | ||||
| 295 | struct 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 | ||||
| 302 | typedef 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 */ | |||
| 336 | typedef struct LZ4F_mtCtx_s LZ4F_mtCtx_t; | |||
| 337 | static void LZ4F_mt_abort(LZ4F_mtCtx_t* mt); | |||
| 338 | static size_t LZ4F_mt_begin(LZ4F_cctx_t* cctx); | |||
| 339 | static void LZ4F_mt_free(LZ4F_mtCtx_t* mt, LZ4F_CustomMem cmem); | |||
| 340 | static 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, | |||
| 347 | static 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 | ||||
| 350 | unsigned LZ4F_isError(LZ4F_errorCode_t code) | |||
| 351 | { | |||
| 352 | return (code > (LZ4F_errorCode_t)(-LZ4F_ERROR_maxCode)); | |||
| 353 | } | |||
| 354 | ||||
| 355 | const 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 | ||||
| 362 | LZ4F_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 | ||||
| 368 | static 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 */ | |||
| 393 | static 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 | ||||
| 407 | unsigned LZ4F_getVersion(void) { return LZ4F_VERSION100; } | |||
| 408 | ||||
| 409 | int LZ4F_compressionLevel_max(void) { return LZ4HC_CLEVEL_MAX12; } | |||
| 410 | ||||
| 411 | size_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 | ||||
| 428 | static 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 | **************************************/ | |||
| 438 | static 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 | */ | |||
| 457 | static 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 | ||||
| 470 | size_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 | ||||
| 504 | static 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 | ||||
| 510 | unsigned 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() */ | |||
| 520 | static 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 */ | |||
| 547 | static 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. */ | |||
| 560 | static 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 | */ | |||
| 580 | size_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 | */ | |||
| 649 | size_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 | ||||
| 698 | LZ4F_CDict* | |||
| 699 | LZ4F_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 */ | |||
| 742 | LZ4F_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 | ||||
| 748 | void 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 | ||||
| 762 | LZ4F_cctx* | |||
| 763 | LZ4F_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 | **/ | |||
| 784 | LZ4F_errorCode_t | |||
| 785 | LZ4F_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 | ||||
| 796 | LZ4F_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 | */ | |||
| 819 | static 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()). */ | |||
| 855 | static 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. */ | |||
| 881 | static 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 */ | |||
| 894 | static 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 | ||||
| 905 | size_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 */ | |||
| 921 | static 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 | */ | |||
| 952 | static 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 | ||||
| 1093 | size_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 | ||||
| 1102 | size_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 | ||||
| 1112 | size_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 | */ | |||
| 1128 | size_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 | ||||
| 1162 | typedef 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 | */ | |||
| 1172 | static 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 | */ | |||
| 1215 | static 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. */ | |||
| 1244 | static 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 | ||||
| 1251 | static 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 | ||||
| 1263 | static 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 | ||||
| 1271 | static 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 | ||||
| 1280 | static 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 | ||||
| 1286 | static 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 | ||||
| 1292 | static 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 */ | |||
| 1305 | static 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 | ||||
| 1313 | typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus; | |||
| 1314 | ||||
| 1315 | static 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) */ | |||
| 1323 | static 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 */ | |||
| 1337 | static 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) */ | |||
| 1352 | static 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) */ | |||
| 1362 | static 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) */ | |||
| 1399 | static 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 */ | |||
| 1436 | static 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 | ||||
| 1465 | typedef void (*LZ4F_job_f)(void* arg, unsigned workerID); | |||
| 1466 | typedef 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. */ | |||
| 1531 | typedef struct { LZ4F_job_f fn; void* arg; } LZ4F_poolJob_t; | |||
| 1532 | typedef struct { LZ4F_pool_t* pool; unsigned workerID; } LZ4F_poolWorker_t; | |||
| 1533 | ||||
| 1534 | struct 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 */ | |||
| 1553 | static 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 | ||||
| 1562 | static 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) | |||
| 1585 | static DWORD WINAPI LZ4F_pool_thread(LPVOID arg) | |||
| 1586 | { | |||
| 1587 | LZ4F_pool_work((LZ4F_poolWorker_t*)arg); | |||
| 1588 | return 0; | |||
| 1589 | } | |||
| 1590 | #else | |||
| 1591 | static 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) */ | |||
| 1600 | static 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 | ||||
| 1641 | static 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 | ||||
| 1649 | static 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 */ | |||
| 1674 | static 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 | ||||
| 1717 | static 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) */ | |||
| 1724 | static 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()) */ | |||
| 1732 | static 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 | ||||
| 1749 | static 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 | ||||
| 1759 | static 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 | ||||
| 1767 | static 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 | ||||
| 1777 | static 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 */ | |||
| 1788 | static 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 | ||||
| 1796 | static 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 */ | |||
| 1807 | static 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 | ||||
| 1817 | static unsigned LZ4F_pool_nbThreads(const LZ4F_pool_t* pool) { (void)pool; return 0; } | |||
| 1818 | static size_t LZ4F_pool_sizeof(const LZ4F_pool_t* pool) { (void)pool; return 0; } | |||
| 1819 | static void LZ4F_pool_free(LZ4F_pool_t* pool, LZ4F_CustomMem cmem) { (void)pool; (void)cmem; } | |||
| 1820 | static int LZ4F_pool_submit(LZ4F_pool_t* pool, LZ4F_job_f fn, void* arg) { (void)pool; fn(arg, 0); return 0; } | |||
| 1821 | static unsigned LZ4F_pool_startThreads(LZ4F_pool_t* pool, unsigned nbThreads) { (void)pool; (void)nbThreads; return 0; } | |||
| 1822 | static void LZ4F_pool_setDone(LZ4F_pool_t* pool, int* doneFlag) { (void)pool; *doneFlag = 1; } | |||
| 1823 | static void LZ4F_pool_setCancel(LZ4F_pool_t* pool, int cancel) { (void)pool; (void)cancel; } | |||
| 1824 | static int LZ4F_pool_isCancelled(LZ4F_pool_t* pool) { (void)pool; return 0; } | |||
| 1825 | static int LZ4F_pool_isDone(LZ4F_pool_t* pool, const int* doneFlag) { (void)pool; return *doneFlag; } | |||
| 1826 | static void LZ4F_pool_waitDone(LZ4F_pool_t* pool, const int* doneFlag) { (void)pool; (void)doneFlag; assert(*doneFlag)((void)0); } | |||
| 1827 | static unsigned LZ4F_pool_nbDone(LZ4F_pool_t* pool) { (void)pool; return 0; } | |||
| 1828 | static void LZ4F_pool_waitAny(LZ4F_pool_t* pool, unsigned nbDone) { (void)pool; (void)nbDone; assert(0)((void)0); } | |||
| 1829 | ||||
| 1830 | #endif /* LZ4F_MULTITHREAD */ | |||
| 1831 | ||||
| 1832 | typedef 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 | ||||
| 1844 | struct 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 | ||||
| 1876 | static 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) */ | |||
| 1892 | static 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 */ | |||
| 1900 | static 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 */ | |||
| 1913 | static 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()) */ | |||
| 1922 | static LZ4F_mtJob_t* LZ4F_mt_job(LZ4F_mtCtx_t* mt, unsigned n) | |||
| 1923 | { | |||
| 1924 | return mt->jobs + (mt->oldest + n) % mt->nbJobSlots; | |||
| 1925 | } | |||
| 1926 | ||||
| 1927 | static 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 */ | |||
| 1933 | static 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) */ | |||
| 1948 | static 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 | ||||
| 1964 | static 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) */ | |||
| 1968 | static 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 */ | |||
| 1971 | static 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 */ | |||
| 1980 | static 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 */ | |||
| 1992 | static 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) */ | |||
| 2018 | static 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) */ | |||
| 2036 | static 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 */ | |||
| 2053 | static 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 | ||||
| 2067 | static 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 | ||||
| 2076 | static 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 | ||||
| 2087 | static 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 | ||||
| 2099 | static 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 | ||||
| 2111 | static 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 */ | |||
| 2131 | static 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(). */ | |||
| 2240 | static 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 */ | |||
| 2252 | static 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). */ | |||
| 2281 | static 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. */ | |||
| 2387 | static 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 | */ | |||
| 2538 | size_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 | */ | |||
| 2554 | size_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) */ | |||
| 2568 | static 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 | ||||
| 2579 | static 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 */ | |||
| 2585 | static 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) */ | |||
| 2594 | static 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 | */ | |||
| 2619 | size_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 | */ | |||
| 2656 | size_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 | ||||
| 2716 | typedef 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 | ||||
| 2728 | struct 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 | ||||
| 2754 | LZ4F_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 | */ | |||
| 2770 | LZ4F_errorCode_t | |||
| 2771 | LZ4F_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 | ||||
| 2783 | LZ4F_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 | ||||
| 2795 | size_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 ---==*/ | |||
| 2804 | void 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 | */ | |||
| 2823 | static 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 | */ | |||
| 2920 | size_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 | */ | |||
| 2961 | LZ4F_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 | */ | |||
| 3009 | static 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 | */ | |||
| 3028 | static 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 */ | |||
| 3034 | assert(dctx->dict != NULL)((void)0); | |||
| 3035 | ||||
| 3036 | if (dctx->dict + dctx->dictSize == dstPtr) { /* prefix mode, everything within dstBuffer */ | |||
| 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 */ | |||
| 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
| |||
| 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
| |||
| 3061 | LZ4F_moveDictBeforeTmpOut(dctx, dstSize); | |||
| 3062 | return; | |||
| 3063 | } | |||
| 3064 | ||||
| 3065 | if (dctx->dict == dctx->tmpOutBuffer) { /* copy dst into tmp to complete dict */ | |||
| 3066 | if (dctx->dictSize + dstSize > dctx->tmpOutBufferCapacity) { /* tmp buffer not large enough */ | |||
| 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); | |||
| 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. */ | |||
| 3091 | static 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 | */ | |||
| 3101 | static 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 | */ | |||
| 3131 | static const BYTE* | |||
| 3132 | LZ4F_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 | */ | |||
| 3170 | size_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; | |||
| ||||
| 3179 | const BYTE* const srcEnd = srcBuffer
| |||
| 3180 | const BYTE* srcPtr = srcStart; | |||
| 3181 | BYTE* const dstStart = (BYTE*)dstBuffer; | |||
| 3182 | BYTE* const dstEnd = dstStart ? dstStart + *dstSizePtr : NULL((void*)0); | |||
| 3183 | BYTE* dstPtr = dstStart; | |||
| 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
| |||
| 3192 | if (srcBuffer
| |||
| 3193 | MEM_INIT(&optionsNull, 0, sizeof(optionsNull))memset((&optionsNull),(0),(sizeof(optionsNull))); | |||
| 3194 | if (decompressOptionsPtr==NULL((void*)0)) decompressOptionsPtr = &optionsNull; | |||
| 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 */ | |||
| 3199 | dctx->skipBlockChecksum = 1; | |||
| 3200 | dctx->skipContentChecksum = 1; | |||
| 3201 | } | |||
| 3202 | ||||
| 3203 | /* behaves as a state machine */ | |||
| 3204 | ||||
| 3205 | while (doAnotherStage) { | |||
| 3206 | ||||
| 3207 | switch(dctx->dStage) | |||
| 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
| |||
| 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) { | |||
| 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) | |||
| 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) ) | |||
| 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); | |||
| 3412 | ||||
| 3413 | /* dictionary management, unless the frame ends with this block */ | |||
| 3414 | if ( (dctx->frameInfo.blockMode==LZ4F_blockLinked) | |||
| 3415 | && !LZ4F_endMarkFollows(srcPtr, srcEnd, 0) ) { | |||
| 3416 | LZ4F_updateDict(dctx, dstPtr, decodedSize, dstStart, 0); | |||
| 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 | */ | |||
| 3563 | size_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) */ | |||
| 3580 | size_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 | } |