packages feed

lz4-hs 0.1.5.1 → 0.1.5.2

raw patch · 11 files changed

+1229/−671 lines, 11 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Codec.Lz4.Foreign: type LzCtxPtr = ForeignPtr (LzCtx)
+ Codec.Lz4.Foreign: type LzCtxPtr = ForeignPtr LzCtx
- Codec.Lz4.Foreign: type LzDecompressionCtxPtr = ForeignPtr (LzDecompressionCtx)
+ Codec.Lz4.Foreign: type LzDecompressionCtxPtr = ForeignPtr LzDecompressionCtx
- Codec.Lz4.Foreign: type LzPreferencesPtr = ForeignPtr (LzPreferences)
+ Codec.Lz4.Foreign: type LzPreferencesPtr = ForeignPtr LzPreferences

Files

CHANGELOG.md view
@@ -1,5 +1,9 @@ # lz4 +## 0.1.5.2++  * Upgrade bundled sources to 1.9.4+ ## 0.1.5.1    * Upgrade to lz4 1.9.3 under the hood
README.md view
@@ -1,7 +1,7 @@ # lz4-hs  Haskell bindings to [lz4](https://github.com/lz4/lz4). Includes a bundled copy-of lz4 1.9.2+of lz4 1.9.3  ## Comparison 
bench/Bench.hs view
@@ -8,19 +8,16 @@  decompressDump :: FilePath -> FilePath -> IO () decompressDump dir fp' =-    BSL.writeFile (dir </> "dump.tar") =<<-        (decompress <$> BSL.readFile fp')+    BSL.writeFile (dir </> "dump.tar") . decompress =<< BSL.readFile fp'  decompressFile :: FilePath -> IO ()-decompressFile fp' = forceBSL =<<-        (decompress <$> BSL.readFile fp')+decompressFile fp' = forceBSL . decompress =<< BSL.readFile fp'      where forceBSL = (`seq` mempty) . last . BSL.toChunks  compressDump :: FilePath -> FilePath -> IO () compressDump dir fp' =-    BSL.writeFile (dir </> "dump.tar.lz4") =<<-        (compress <$> BSL.readFile fp')+    BSL.writeFile (dir </> "dump.tar.lz4") . compress =<< BSL.readFile fp'  main :: IO () main =
cbits/lz4.c view
@@ -1,6 +1,6 @@ /*    LZ4 - Fast LZ compression algorithm-   Copyright (C) 2011-present, Yann Collet.+   Copyright (C) 2011-2020, Yann Collet.     BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) @@ -124,6 +124,7 @@ #if defined(_MSC_VER) && (_MSC_VER >= 1400)  /* Visual Studio 2005+ */ #  include <intrin.h>               /* only present in VS2005+ */ #  pragma warning(disable : 4127)   /* disable: C4127: conditional expression is constant */+#  pragma warning(disable : 6237)   /* disable: C6237: conditional expression is always 0 */ #endif  /* _MSC_VER */  #ifndef LZ4_FORCE_INLINE@@ -187,7 +188,27 @@ /*-************************************ *  Memory routines **************************************/-#ifdef LZ4_USER_MEMORY_FUNCTIONS++/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION :+ *  Disable relatively high-level LZ4/HC functions that use dynamic memory+ *  allocation functions (malloc(), calloc(), free()).+ *+ *  Note that this is a compile-time switch. And since it disables+ *  public/stable LZ4 v1 API functions, we don't recommend using this+ *  symbol to generate a library for distribution.+ *+ *  The following public functions are removed when this symbol is defined.+ *  - lz4   : LZ4_createStream, LZ4_freeStream,+ *            LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated)+ *  - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC,+ *            LZ4_createHC (deprecated), LZ4_freeHC  (deprecated)+ *  - lz4frame, lz4file : All LZ4F_* functions+ */+#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)+#  define ALLOC(s)          lz4_error_memory_allocation_is_disabled+#  define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled+#  define FREEMEM(p)        lz4_error_memory_allocation_is_disabled+#elif defined(LZ4_USER_MEMORY_FUNCTIONS) /* memory management functions can be customized by user project.  * Below functions must exist somewhere in the Project  * and be available at link time */@@ -204,8 +225,13 @@ # define FREEMEM(p)        free(p) #endif -#include <string.h>   /* memset, memcpy */-#define MEM_INIT(p,v,s)   memset((p),(v),(s))+#if ! LZ4_FREESTANDING+#  include <string.h>   /* memset, memcpy */+#endif+#if !defined(LZ4_memset)+#  define LZ4_memset(p,v,s) memset((p),(v),(s))+#endif+#define MEM_INIT(p,v,s)   LZ4_memset((p),(v),(s))   /*-************************************@@ -316,12 +342,22 @@  * memcpy() as if it were standard compliant, so it can inline it in freestanding  * environments. This is needed when decompressing the Linux Kernel, for example.  */-#if defined(__GNUC__) && (__GNUC__ >= 4)-#define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size)-#else-#define LZ4_memcpy(dst, src, size) memcpy(dst, src, size)+#if !defined(LZ4_memcpy)+#  if defined(__GNUC__) && (__GNUC__ >= 4)+#    define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size)+#  else+#    define LZ4_memcpy(dst, src, size) memcpy(dst, src, size)+#  endif #endif +#if !defined(LZ4_memmove)+#  if defined(__GNUC__) && (__GNUC__ >= 4)+#    define LZ4_memmove __builtin_memmove+#  else+#    define LZ4_memmove memmove+#  endif+#endif+ static unsigned LZ4_isLittleEndian(void) {     const union { U32 u; BYTE c[4]; } one = { 1 };   /* don't use static : performance detrimental */@@ -343,14 +379,14 @@  /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ /* currently only defined for gcc and icc */-typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign;+typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) LZ4_unalign; -static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; }-static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }-static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; }+static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign*)ptr)->u16; }+static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign*)ptr)->u32; }+static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalign*)ptr)->uArch; } -static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; }-static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; }+static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign*)memPtr)->u16 = value; }+static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign*)memPtr)->u32 = value; }  #else  /* safe and portable access using memcpy() */ @@ -421,10 +457,12 @@ #ifndef LZ4_FAST_DEC_LOOP #  if defined __i386__ || defined _M_IX86 || defined __x86_64__ || defined _M_X64 #    define LZ4_FAST_DEC_LOOP 1+#  elif defined(__aarch64__) && defined(__APPLE__)+#    define LZ4_FAST_DEC_LOOP 1 #  elif defined(__aarch64__) && !defined(__clang__)-     /* On aarch64, we disable this optimization for clang because on certain-      * mobile chipsets, performance is reduced with clang. For information-      * refer to https://github.com/lz4/lz4/pull/707 */+     /* On non-Apple aarch64, we disable this optimization for clang because+      * on certain mobile chipsets, performance is reduced with clang. For+      * more information refer to https://github.com/lz4/lz4/pull/707 */ #    define LZ4_FAST_DEC_LOOP 1 #  else #    define LZ4_FAST_DEC_LOOP 0@@ -486,7 +524,14 @@     case 2:         LZ4_memcpy(v, srcPtr, 2);         LZ4_memcpy(&v[2], srcPtr, 2);+#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */+#  pragma warning(push)+#  pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */+#endif         LZ4_memcpy(&v[4], v, 4);+#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */+#  pragma warning(pop)+#endif         break;     case 4:         LZ4_memcpy(v, srcPtr, 4);@@ -515,9 +560,20 @@     assert(val != 0);     if (LZ4_isLittleEndian()) {         if (sizeof(val) == 8) {-#       if defined(_MSC_VER) && (_MSC_VER >= 1800) && defined(_M_AMD64) && !defined(LZ4_FORCE_SW_BITCOUNT)+#       if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT)+/*-*************************************************************************************************+* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11.+* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics+* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC.+****************************************************************************************************/+#         if defined(__clang__) && (__clang_major__ < 10)+            /* Avoid undefined clang-cl intrinsics issue.+             * See https://github.com/lz4/lz4/pull/1017 for details. */+            return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3;+#         else             /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */             return (unsigned)_tzcnt_u64(val) >> 3;+#         endif #       elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)             unsigned long r = 0;             _BitScanForward64(&r, (U64)val);@@ -652,10 +708,10 @@  * - usingExtDict  : Like withPrefix64k, but the preceding content is somewhere  *                   else in memory, starting at ctx->dictionary with length  *                   ctx->dictSize.- * - usingDictCtx  : Like usingExtDict, but everything concerning the preceding- *                   content is in a separate context, pointed to by- *                   ctx->dictCtx. ctx->dictionary, ctx->dictSize, and table- *                   entries in the current context that refer to positions+ * - usingDictCtx  : Everything concerning the preceding content is+ *                   in a separate context, pointed to by ctx->dictCtx.+ *                   ctx->dictionary, ctx->dictSize, and table entries+ *                   in the current context that refer to positions  *                   preceding the beginning of the current compression are  *                   ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx  *                   ->dictSize describe the location and size of the preceding@@ -672,12 +728,12 @@ int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } int LZ4_compressBound(int isize)  { return LZ4_COMPRESSBOUND(isize); }-int LZ4_sizeofState(void) { return LZ4_STREAMSIZE; }+int LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); }  -/*-************************************-*  Internal Definitions used in Tests-**************************************/+/*-****************************************+*  Internal Definitions, used only in Tests+*******************************************/ #if defined (__cplusplus) extern "C" { #endif@@ -687,7 +743,9 @@ int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,                                      int compressedSize, int maxOutputSize,                                      const void* dictStart, size_t dictSize);-+int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,+                                     int compressedSize, int targetOutputSize, int dstCapacity,+                                     const void* dictStart, size_t dictSize); #if defined (__cplusplus) } #endif@@ -827,9 +885,10 @@         }     } -    /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, is faster-     * than compressing without a gap. However, compressing with-     * currentOffset == 0 is faster still, so we preserve that case.+    /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back,+     * is faster than compressing without a gap.+     * However, compressing with currentOffset == 0 is faster still,+     * so we preserve that case.      */     if (cctx->currentOffset != 0 && tableType == byU32) {         DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset");@@ -853,7 +912,7 @@                  const char* const source,                  char* const dest,                  const int inputSize,-                 int *inputConsumed, /* only written when outputDirective == fillOutput */+                 int*  inputConsumed, /* only written when outputDirective == fillOutput */                  const int maxOutputSize,                  const limitedOutput_directive outputDirective,                  const tableType_t tableType,@@ -885,7 +944,8 @@      /* the dictCtx currentOffset is indexed on the start of the dictionary,      * while a dictionary in the current context precedes the currentOffset */-    const BYTE* dictBase = !dictionary ? NULL : (dictDirective == usingDictCtx) ?+    const BYTE* dictBase = (dictionary == NULL) ? NULL :+                           (dictDirective == usingDictCtx) ?                             dictionary + dictSize - dictCtx->currentOffset :                             dictionary + dictSize - startIndex; @@ -981,10 +1041,11 @@                         match = base + matchIndex;                         lowLimit = (const BYTE*)source;                     }-                } else if (dictDirective==usingExtDict) {+                } else if (dictDirective == usingExtDict) {                     if (matchIndex < startIndex) {                         DEBUGLOG(7, "extDict candidate: matchIndex=%5u  <  startIndex=%5u", matchIndex, startIndex);                         assert(startIndex - matchIndex >= MINMATCH);+                        assert(dictBase);                         match = dictBase + matchIndex;                         lowLimit = dictionary;                     } else {@@ -1048,7 +1109,7 @@ _next_match:         /* at this stage, the following variables must be correctly set :          * - ip : at start of LZ operation-         * - match : at start of previous pattern occurence; can be within current prefix, or within extDict+         * - match : at start of previous pattern occurrence; can be within current prefix, or within extDict          * - offset : if maybe_ext_memSegment==1 (constant)          * - lowLimit : must be == dictionary to mean "match is within extDict"; must be == source otherwise          * - token and *token : position to write 4-bits for match length; higher 4-bits for literal length supposed already written@@ -1173,6 +1234,7 @@                 }             } else if (dictDirective==usingExtDict) {                 if (matchIndex < startIndex) {+                    assert(dictBase);                     match = dictBase + matchIndex;                     lowLimit = dictionary;   /* required for match length counter */                 } else {@@ -1355,7 +1417,7 @@ {     int result; #if (LZ4_HEAPMODE)-    LZ4_stream_t* ctxPtr = ALLOC(sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */+    LZ4_stream_t* ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */     if (ctxPtr == NULL) return 0; #else     LZ4_stream_t ctx;@@ -1420,15 +1482,17 @@ *  Streaming functions ********************************/ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4_stream_t* LZ4_createStream(void) {     LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));-    LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal));    /* A compilation error here means LZ4_STREAMSIZE is not large enough */+    LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal));     DEBUGLOG(4, "LZ4_createStream %p", lz4s);     if (lz4s == NULL) return NULL;     LZ4_initStream(lz4s, sizeof(*lz4s));     return lz4s; }+#endif  static size_t LZ4_stream_t_alignment(void) {@@ -1462,6 +1526,7 @@     LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32); } +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) int LZ4_freeStream (LZ4_stream_t* LZ4_stream) {     if (!LZ4_stream) return 0;   /* support free on NULL */@@ -1469,6 +1534,7 @@     FREEMEM(LZ4_stream);     return (0); }+#endif   #define HASH_UNIT sizeof(reg_t)@@ -1514,8 +1580,9 @@     return (int)dict->dictSize; } -void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) {-    const LZ4_stream_t_internal* dictCtx = dictionaryStream == NULL ? NULL :+void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream)+{+    const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL :         &(dictionaryStream->internal_donotuse);      DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)",@@ -1568,36 +1635,40 @@                                 int acceleration) {     const tableType_t tableType = byU32;-    LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse;-    const BYTE* dictEnd = streamPtr->dictionary + streamPtr->dictSize;+    LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse;+    const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL; -    DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i)", inputSize);+    DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)", inputSize, streamPtr->dictSize); -    LZ4_renormDictT(streamPtr, inputSize);   /* avoid index overflow */+    LZ4_renormDictT(streamPtr, inputSize);   /* fix index overflow */     if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;     if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;      /* invalidate tiny dictionaries */-    if ( (streamPtr->dictSize-1 < 4-1)   /* intentional underflow */-      && (dictEnd != (const BYTE*)source) ) {+    if ( (streamPtr->dictSize < 4)     /* tiny dictionary : not enough for a hash */+      && (dictEnd != source)           /* prefix mode */+      && (inputSize > 0)               /* tolerance : don't lose history, in case next invocation would use prefix mode */+      && (streamPtr->dictCtx == NULL)  /* usingDictCtx */+      ) {         DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary);+        /* remove dictionary existence from history, to employ faster prefix mode */         streamPtr->dictSize = 0;         streamPtr->dictionary = (const BYTE*)source;-        dictEnd = (const BYTE*)source;+        dictEnd = source;     }      /* Check overlapping input/dictionary space */-    {   const BYTE* sourceEnd = (const BYTE*) source + inputSize;-        if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) {+    {   const char* const sourceEnd = source + inputSize;+        if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) {             streamPtr->dictSize = (U32)(dictEnd - sourceEnd);             if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;             if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;-            streamPtr->dictionary = dictEnd - streamPtr->dictSize;+            streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize;         }     }      /* prefix mode : source data follows dictionary */-    if (dictEnd == (const BYTE*)source) {+    if (dictEnd == source) {         if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))             return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration);         else@@ -1623,7 +1694,7 @@             } else {                 result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);             }-        } else {+        } else {  /* small data <= 4 KB */             if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {                 result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration);             } else {@@ -1661,21 +1732,25 @@ /*! LZ4_saveDict() :  *  If previously compressed data block is not guaranteed to remain available at its memory location,  *  save it into a safer place (char* safeBuffer).- *  Note : you don't need to call LZ4_loadDict() afterwards,- *         dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue().- *  Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.+ *  Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable,+ *         one can therefore call LZ4_compress_fast_continue() right after.+ * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.  */ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) {     LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;-    const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; +    DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer);+     if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */     if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; }      if (safeBuffer == NULL) assert(dictSize == 0);-    if (dictSize > 0)-        memmove(safeBuffer, previousDictEnd - dictSize, dictSize);+    if (dictSize > 0) {+        const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;+        assert(dict->dictionary);+        LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize);+    }      dict->dictionary = (const BYTE*)safeBuffer;     dict->dictSize = (U32)dictSize;@@ -1689,40 +1764,164 @@  *  Decompression functions  ********************************/ -typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive;  #undef MIN #define MIN(a,b)    ( (a) < (b) ? (a) : (b) ) ++/* variant for decompress_unsafe()+ * does not know end of input+ * presumes input is well formed+ * note : will consume at least one byte */+size_t read_long_length_no_check(const BYTE** pp)+{+    size_t b, l = 0;+    do { b = **pp; (*pp)++; l += b; } while (b==255);+    DEBUGLOG(6, "read_long_length_no_check: +length=%zu using %zu input bytes", l, l/255 + 1)+    return l;+}++/* core decoder variant for LZ4_decompress_fast*()+ * for legacy support only : these entry points are deprecated.+ * - Presumes input is correctly formed (no defense vs malformed inputs)+ * - Does not know input size (presume input buffer is "large enough")+ * - Decompress a full block (only)+ * @return : nb of bytes read from input.+ * Note : this variant is not optimized for speed, just for maintenance.+ *        the goal is to remove support of decompress_fast*() variants by v2.0+**/+LZ4_FORCE_INLINE int+LZ4_decompress_unsafe_generic(+                 const BYTE* const istart,+                 BYTE* const ostart,+                 int decompressedSize,++                 size_t prefixSize,+                 const BYTE* const dictStart,  /* only if dict==usingExtDict */+                 const size_t dictSize         /* note: =0 if dictStart==NULL */+                 )+{+    const BYTE* ip = istart;+    BYTE* op = (BYTE*)ostart;+    BYTE* const oend = ostart + decompressedSize;+    const BYTE* const prefixStart = ostart - prefixSize;++    DEBUGLOG(5, "LZ4_decompress_unsafe_generic");+    if (dictStart == NULL) assert(dictSize == 0);++    while (1) {+        /* start new sequence */+        unsigned token = *ip++;++        /* literals */+        {   size_t ll = token >> ML_BITS;+            if (ll==15) {+                /* long literal length */+                ll += read_long_length_no_check(&ip);+            }+            if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */+            LZ4_memmove(op, ip, ll); /* support in-place decompression */+            op += ll;+            ip += ll;+            if ((size_t)(oend-op) < MFLIMIT) {+                if (op==oend) break;  /* end of block */+                DEBUGLOG(5, "invalid: literals end at distance %zi from end of block", oend-op);+                /* incorrect end of block :+                 * last match must start at least MFLIMIT==12 bytes before end of output block */+                return -1;+        }   }++        /* match */+        {   size_t ml = token & 15;+            size_t const offset = LZ4_readLE16(ip);+            ip+=2;++            if (ml==15) {+                /* long literal length */+                ml += read_long_length_no_check(&ip);+            }+            ml += MINMATCH;++            if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */++            {   const BYTE* match = op - offset;++                /* out of range */+                if (offset > (size_t)(op - prefixStart) + dictSize) {+                    DEBUGLOG(6, "offset out of range");+                    return -1;+                }++                /* check special case : extDict */+                if (offset > (size_t)(op - prefixStart)) {+                    /* extDict scenario */+                    const BYTE* const dictEnd = dictStart + dictSize;+                    const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart));+                    size_t const extml = (size_t)(dictEnd - extMatch);+                    if (extml > ml) {+                        /* match entirely within extDict */+                        LZ4_memmove(op, extMatch, ml);+                        op += ml;+                        ml = 0;+                    } else {+                        /* match split between extDict & prefix */+                        LZ4_memmove(op, extMatch, extml);+                        op += extml;+                        ml -= extml;+                    }+                    match = prefixStart;+                }++                /* match copy - slow variant, supporting overlap copy */+                {   size_t u;+                    for (u=0; u<ml; u++) {+                        op[u] = match[u];+            }   }   }+            op += ml;+            if ((size_t)(oend-op) < LASTLITERALS) {+                DEBUGLOG(5, "invalid: match ends at distance %zi from end of block", oend-op);+                /* incorrect end of block :+                 * last match must stop at least LASTLITERALS==5 bytes before end of output block */+                return -1;+            }+        } /* match */+    } /* main loop */+    return (int)(ip - istart);+}++ /* Read the variable-length literal or match length.  *- * ip - pointer to use as input.- * lencheck - end ip.  Return an error if ip advances >= lencheck.- * loop_check - check ip >= lencheck in body of loop.  Returns loop_error if so.- * initial_check - check ip >= lencheck before start of loop.  Returns initial_error if so.- * error (output) - error code.  Should be set to 0 before call.- */-typedef enum { loop_error = -2, initial_error = -1, ok = 0 } variable_length_error;-LZ4_FORCE_INLINE unsigned-read_variable_length(const BYTE**ip, const BYTE* lencheck,-                     int loop_check, int initial_check,-                     variable_length_error* error)+ * @ip : input pointer+ * @ilimit : position after which if length is not decoded, the input is necessarily corrupted.+ * @initial_check - check ip >= ipmax before start of loop.  Returns initial_error if so.+ * @error (output) - error code.  Must be set to 0 before call.+**/+typedef size_t Rvl_t;+static const Rvl_t rvl_error = (Rvl_t)(-1);+LZ4_FORCE_INLINE Rvl_t+read_variable_length(const BYTE** ip, const BYTE* ilimit,+                     int initial_check) {-    U32 length = 0;-    U32 s;-    if (initial_check && unlikely((*ip) >= lencheck)) {    /* overflow detection */-        *error = initial_error;-        return length;+    Rvl_t s, length = 0;+    assert(ip != NULL);+    assert(*ip !=  NULL);+    assert(ilimit != NULL);+    if (initial_check && unlikely((*ip) >= ilimit)) {    /* read limit reached */+        return rvl_error;     }     do {         s = **ip;         (*ip)++;         length += s;-        if (loop_check && unlikely((*ip) >= lencheck)) {    /* overflow detection */-            *error = loop_error;-            return length;+        if (unlikely((*ip) > ilimit)) {    /* read limit reached */+            return rvl_error;         }+        /* accumulator overflow detection (32-bit mode only) */+        if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) {+            return rvl_error;+        }     } while (s==255);      return length;@@ -1741,7 +1940,6 @@                  int srcSize,                  int outputSize,         /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ -                 endCondition_directive endOnInput,   /* endOnOutputSize, endOnInputSize */                  earlyEnd_directive partialDecoding,  /* full, partial */                  dict_directive dict,                 /* noDict, withPrefix64k, usingExtDict */                  const BYTE* const lowPrefix,  /* always <= dst, == dst when no prefix */@@ -1749,7 +1947,7 @@                  const size_t dictSize         /* note : = 0 if noDict */                  ) {-    if (src == NULL) { return -1; }+    if ((src == NULL) || (outputSize < 0)) { return -1; }      {   const BYTE* ip = (const BYTE*) src;         const BYTE* const iend = ip + srcSize;@@ -1760,13 +1958,12 @@          const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; -        const int safeDecode = (endOnInput==endOnInputSize);-        const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));+        const int checkOffset = (dictSize < (int)(64 KB));           /* Set up the "end" pointers for the shortcut. */-        const BYTE* const shortiend = iend - (endOnInput ? 14 : 8) /*maxLL*/ - 2 /*offset*/;-        const BYTE* const shortoend = oend - (endOnInput ? 14 : 8) /*maxLL*/ - 18 /*maxML*/;+        const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/;+        const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/;          const BYTE* match;         size_t offset;@@ -1778,83 +1975,70 @@          /* Special cases */         assert(lowPrefix <= op);-        if ((endOnInput) && (unlikely(outputSize==0))) {+        if (unlikely(outputSize==0)) {             /* Empty output buffer */             if (partialDecoding) return 0;             return ((srcSize==1) && (*ip==0)) ? 0 : -1;         }-        if ((!endOnInput) && (unlikely(outputSize==0))) { return (*ip==0 ? 1 : -1); }-        if ((endOnInput) && unlikely(srcSize==0)) { return -1; }+        if (unlikely(srcSize==0)) { return -1; } -	/* Currently the fast loop shows a regression on qualcomm arm chips. */+    /* LZ4_FAST_DEC_LOOP:+     * designed for modern OoO performance cpus,+     * where copying reliably 32-bytes is preferable to an unpredictable branch.+     * note : fast loop may show a regression for some client arm chips. */ #if LZ4_FAST_DEC_LOOP         if ((oend - op) < FASTLOOP_SAFE_DISTANCE) {             DEBUGLOG(6, "skip fast decode loop");             goto safe_decode;         } -        /* Fast loop : decode sequences as long as output < iend-FASTLOOP_SAFE_DISTANCE */+        /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */         while (1) {             /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */             assert(oend - op >= FASTLOOP_SAFE_DISTANCE);-            if (endOnInput) { assert(ip < iend); }+            assert(ip < iend);             token = *ip++;             length = token >> ML_BITS;  /* literal length */ -            assert(!endOnInput || ip <= iend); /* ip < iend before the increment */-             /* decode literal length */             if (length == RUN_MASK) {-                variable_length_error error = ok;-                length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error);-                if (error == initial_error) { goto _output_error; }-                if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */-                if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */+                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);+                if (addl == rvl_error) { goto _output_error; }+                length += addl;+                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */+                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */                  /* copy literals */                 cpy = op+length;                 LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);-                if (endOnInput) {  /* LZ4_decompress_safe() */-                    if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }-                    LZ4_wildCopy32(op, ip, cpy);-                } else {   /* LZ4_decompress_fast() */-                    if (cpy>oend-8) { goto safe_literal_copy; }-                    LZ4_wildCopy8(op, ip, cpy); /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time :-                                                 * it doesn't know input length, and only relies on end-of-block properties */-                }+                if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }+                LZ4_wildCopy32(op, ip, cpy);                 ip += length; op = cpy;             } else {                 cpy = op+length;-                if (endOnInput) {  /* LZ4_decompress_safe() */-                    DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length);-                    /* We don't need to check oend, since we check it once for each loop below */-                    if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; }-                    /* Literals can only be 14, but hope compilers optimize if we copy by a register size */-                    LZ4_memcpy(op, ip, 16);-                } else {  /* LZ4_decompress_fast() */-                    /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time :-                     * it doesn't know input length, and relies on end-of-block properties */-                    LZ4_memcpy(op, ip, 8);-                    if (length > 8) { LZ4_memcpy(op+8, ip+8, 8); }-                }+                DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length);+                /* We don't need to check oend, since we check it once for each loop below */+                if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; }+                /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */+                LZ4_memcpy(op, ip, 16);                 ip += length; op = cpy;             }              /* get offset */             offset = LZ4_readLE16(ip); ip+=2;             match = op - offset;-            assert(match <= op);+            assert(match <= op);  /* overflow check */              /* get matchlength */             length = token & ML_MASK;              if (length == ML_MASK) {-                variable_length_error error = ok;-                if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */-                length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error);-                if (error != ok) { goto _output_error; }-                if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */+                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);+                if (addl == rvl_error) { goto _output_error; }+                length += addl;                 length += MINMATCH;+                if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */+                if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */                 if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {                     goto safe_match_copy;                 }@@ -1864,7 +2048,7 @@                     goto safe_match_copy;                 } -                /* Fastpath check: Avoids a branch in LZ4_wildCopy32 if true */+                /* Fastpath check: skip LZ4_wildCopy32 when true */                 if ((dict == withPrefix64k) || (match >= lowPrefix)) {                     if (offset >= 8) {                         assert(match >= lowPrefix);@@ -1881,6 +2065,7 @@             if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */             /* match starting within external dictionary */             if ((dict==usingExtDict) && (match < lowPrefix)) {+                assert(dictEnd != NULL);                 if (unlikely(op+length > oend-LASTLITERALS)) {                     if (partialDecoding) {                         DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd");@@ -1891,7 +2076,7 @@                  if (length <= (size_t)(lowPrefix-match)) {                     /* match fits entirely within external dictionary : just copy */-                    memmove(op, dictEnd - (lowPrefix-match), length);+                    LZ4_memmove(op, dictEnd - (lowPrefix-match), length);                     op += length;                 } else {                     /* match stretches into both external dictionary and current block */@@ -1927,11 +2112,10 @@          /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */         while (1) {+            assert(ip < iend);             token = *ip++;             length = token >> ML_BITS;  /* literal length */ -            assert(!endOnInput || ip <= iend); /* ip < iend before the increment */-             /* A two-stage shortcut for the most common case:              * 1) If the literal length is 0..14, and there is enough space,              * enter the shortcut and copy 16 bytes on behalf of the literals@@ -1941,11 +2125,11 @@              * those 18 bytes earlier, upon entering the shortcut (in other words,              * there is a combined check for both stages).              */-            if ( (endOnInput ? length != RUN_MASK : length <= 8)+            if ( (length != RUN_MASK)                 /* strictly "less than" on input, to re-enter the loop with at least one byte */-              && likely((endOnInput ? ip < shortiend : 1) & (op <= shortoend)) ) {+              && likely((ip < shortiend) & (op <= shortoend)) ) {                 /* Copy the literals */-                LZ4_memcpy(op, ip, endOnInput ? 16 : 8);+                LZ4_memcpy(op, ip, 16);                 op += length; ip += length;                  /* The second stage: prepare for match copying, decode full info.@@ -1975,11 +2159,11 @@              /* decode literal length */             if (length == RUN_MASK) {-                variable_length_error error = ok;-                length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error);-                if (error == initial_error) { goto _output_error; }-                if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */-                if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */+                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);+                if (addl == rvl_error) { goto _output_error; }+                length += addl;+                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */+                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */             }              /* copy literals */@@ -1988,9 +2172,7 @@         safe_literal_copy: #endif             LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);-            if ( ((endOnInput) && ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) )-              || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )-            {+            if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) {                 /* We've either hit the input parsing restriction or the output parsing restriction.                  * In the normal scenario, decoding a full block, it must be the last sequence,                  * otherwise it's an error (invalid input or dimensions).@@ -2000,7 +2182,6 @@                     /* Since we are partial decoding we may be in this block because of the output parsing                      * restriction, which is not valid since the output buffer is allowed to be undersized.                      */-                    assert(endOnInput);                     DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end")                     DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length);                     DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op));@@ -2021,21 +2202,17 @@                         length = (size_t)(oend-op);                     }                 } else {-                    /* We must be on the last sequence because of the parsing limitations so check-                     * that we exactly regenerate the original size (must be exact when !endOnInput).-                     */-                    if ((!endOnInput) && (cpy != oend)) { goto _output_error; }                      /* We must be on the last sequence (or invalid) because of the parsing limitations                       * so check that we exactly consume the input and don't overrun the output buffer.                       */-                    if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) {+                    if ((ip+length != iend) || (cpy > oend)) {                         DEBUGLOG(6, "should have been last run of literals")                         DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend);                         DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend);                         goto _output_error;                     }                 }-                memmove(op, ip, length);  /* supports overlapping memory regions; only matters for in-place decompression scenarios */+                LZ4_memmove(op, ip, length);  /* supports overlapping memory regions, for in-place decompression scenarios */                 ip += length;                 op += length;                 /* Necessarily EOF when !partialDecoding.@@ -2047,7 +2224,7 @@                     break;                 }             } else {-                LZ4_wildCopy8(op, ip, cpy);   /* may overwrite up to WILDCOPYLENGTH beyond cpy */+                LZ4_wildCopy8(op, ip, cpy);   /* can overwrite up to 8 bytes beyond cpy */                 ip += length; op = cpy;             } @@ -2060,10 +2237,10 @@      _copy_match:             if (length == ML_MASK) {-              variable_length_error error = ok;-              length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error);-              if (error != ok) goto _output_error;-                if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error;   /* overflow detection */+                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);+                if (addl == rvl_error) { goto _output_error; }+                length += addl;+                if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error;   /* overflow detection */             }             length += MINMATCH; @@ -2073,6 +2250,7 @@             if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error;   /* Error : offset outside buffers */             /* match starting within external dictionary */             if ((dict==usingExtDict) && (match < lowPrefix)) {+                assert(dictEnd != NULL);                 if (unlikely(op+length > oend-LASTLITERALS)) {                     if (partialDecoding) length = MIN(length, (size_t)(oend-op));                     else goto _output_error;   /* doesn't respect parsing restriction */@@ -2080,7 +2258,7 @@                  if (length <= (size_t)(lowPrefix-match)) {                     /* match fits entirely within external dictionary : just copy */-                    memmove(op, dictEnd - (lowPrefix-match), length);+                    LZ4_memmove(op, dictEnd - (lowPrefix-match), length);                     op += length;                 } else {                     /* match stretches into both external dictionary and current block */@@ -2151,12 +2329,8 @@         }          /* end of decoding */-        if (endOnInput) {-            DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst));-           return (int) (((char*)op)-dst);     /* Nb of output bytes decoded */-       } else {-           return (int) (((const char*)ip)-src);   /* Nb of input bytes read */-       }+        DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst));+        return (int) (((char*)op)-dst);     /* Nb of output bytes decoded */          /* Overflow error detected */     _output_error:@@ -2171,7 +2345,7 @@ int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) {     return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,-                                  endOnInputSize, decode_full_block, noDict,+                                  decode_full_block, noDict,                                   (BYTE*)dest, NULL, 0); } @@ -2180,16 +2354,17 @@ {     dstCapacity = MIN(targetOutputSize, dstCapacity);     return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity,-                                  endOnInputSize, partial_decode,+                                  partial_decode,                                   noDict, (BYTE*)dst, NULL, 0); }  LZ4_FORCE_O2 int LZ4_decompress_fast(const char* source, char* dest, int originalSize) {-    return LZ4_decompress_generic(source, dest, 0, originalSize,-                                  endOnOutputSize, decode_full_block, withPrefix64k,-                                  (BYTE*)dest - 64 KB, NULL, 0);+    DEBUGLOG(5, "LZ4_decompress_fast");+    return LZ4_decompress_unsafe_generic(+                (const BYTE*)source, (BYTE*)dest, originalSize,+                0, NULL, 0); }  /*===== Instantiate a few more decoding cases, used more than once. =====*/@@ -2198,16 +2373,25 @@ int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) {     return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,-                                  endOnInputSize, decode_full_block, withPrefix64k,+                                  decode_full_block, withPrefix64k,                                   (BYTE*)dest - 64 KB, NULL, 0); } +LZ4_FORCE_O2+static int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity)+{+    dstCapacity = MIN(targetOutputSize, dstCapacity);+    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,+                                  partial_decode, withPrefix64k,+                                  (BYTE*)dest - 64 KB, NULL, 0);+}+ /* Another obsolete API function, paired with the previous one. */ int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) {-    /* LZ4_decompress_fast doesn't validate match offsets,-     * and thus serves well with any prefixed dictionary. */-    return LZ4_decompress_fast(source, dest, originalSize);+    return LZ4_decompress_unsafe_generic(+                (const BYTE*)source, (BYTE*)dest, originalSize,+                64 KB, NULL, 0); }  LZ4_FORCE_O2@@ -2215,27 +2399,48 @@                                                size_t prefixSize) {     return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,-                                  endOnInputSize, decode_full_block, noDict,+                                  decode_full_block, noDict,                                   (BYTE*)dest-prefixSize, NULL, 0); }  LZ4_FORCE_O2+static int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity,+                                               size_t prefixSize)+{+    dstCapacity = MIN(targetOutputSize, dstCapacity);+    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,+                                  partial_decode, noDict,+                                  (BYTE*)dest-prefixSize, NULL, 0);+}++LZ4_FORCE_O2 int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,                                      int compressedSize, int maxOutputSize,                                      const void* dictStart, size_t dictSize) {     return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,-                                  endOnInputSize, decode_full_block, usingExtDict,+                                  decode_full_block, usingExtDict,                                   (BYTE*)dest, (const BYTE*)dictStart, dictSize); }  LZ4_FORCE_O2+int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,+                                     int compressedSize, int targetOutputSize, int dstCapacity,+                                     const void* dictStart, size_t dictSize)+{+    dstCapacity = MIN(targetOutputSize, dstCapacity);+    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,+                                  partial_decode, usingExtDict,+                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);+}++LZ4_FORCE_O2 static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize,                                        const void* dictStart, size_t dictSize) {-    return LZ4_decompress_generic(source, dest, 0, originalSize,-                                  endOnOutputSize, decode_full_block, usingExtDict,-                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);+    return LZ4_decompress_unsafe_generic(+                (const BYTE*)source, (BYTE*)dest, originalSize,+                0, (const BYTE*)dictStart, dictSize); }  /* The "double dictionary" mode, for use with e.g. ring buffers: the first part@@ -2247,26 +2452,17 @@                                    size_t prefixSize, const void* dictStart, size_t dictSize) {     return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,-                                  endOnInputSize, decode_full_block, usingExtDict,-                                  (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);-}--LZ4_FORCE_INLINE-int LZ4_decompress_fast_doubleDict(const char* source, char* dest, int originalSize,-                                   size_t prefixSize, const void* dictStart, size_t dictSize)-{-    return LZ4_decompress_generic(source, dest, 0, originalSize,-                                  endOnOutputSize, decode_full_block, usingExtDict,+                                  decode_full_block, usingExtDict,                                   (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); }  /*===== streaming decompression functions =====*/ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4_streamDecode_t* LZ4_createStreamDecode(void) {-    LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t));-    LZ4_STATIC_ASSERT(LZ4_STREAMDECODESIZE >= sizeof(LZ4_streamDecode_t_internal));    /* A compilation error here means LZ4_STREAMDECODESIZE is not large enough */-    return lz4s;+    LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal));+    return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); }  int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)@@ -2275,6 +2471,7 @@     FREEMEM(LZ4_stream);     return 0; }+#endif  /*! LZ4_setStreamDecode() :  *  Use this function to instruct where to find the dictionary.@@ -2285,8 +2482,13 @@ int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) {     LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;-    lz4sd->prefixSize = (size_t) dictSize;-    lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;+    lz4sd->prefixSize = (size_t)dictSize;+    if (dictSize) {+        assert(dictionary != NULL);+        lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;+    } else {+        lz4sd->prefixEnd = (const BYTE*) dictionary;+    }     lz4sd->externalDict = NULL;     lz4sd->extDictSize  = 0;     return 1;@@ -2358,29 +2560,35 @@     return result; } -LZ4_FORCE_O2-int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize)+LZ4_FORCE_O2 int+LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode,+                        const char* source, char* dest, int originalSize) {-    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;+    LZ4_streamDecode_t_internal* const lz4sd =+        (assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse);     int result;++    DEBUGLOG(5, "LZ4_decompress_fast_continue (toDecodeSize=%i)", originalSize);     assert(originalSize >= 0);      if (lz4sd->prefixSize == 0) {+        DEBUGLOG(5, "first invocation : no prefix nor extDict");         assert(lz4sd->extDictSize == 0);         result = LZ4_decompress_fast(source, dest, originalSize);         if (result <= 0) return result;         lz4sd->prefixSize = (size_t)originalSize;         lz4sd->prefixEnd = (BYTE*)dest + originalSize;     } else if (lz4sd->prefixEnd == (BYTE*)dest) {-        if (lz4sd->prefixSize >= 64 KB - 1 || lz4sd->extDictSize == 0)-            result = LZ4_decompress_fast(source, dest, originalSize);-        else-            result = LZ4_decompress_fast_doubleDict(source, dest, originalSize,-                                                    lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);+        DEBUGLOG(5, "continue using existing prefix");+        result = LZ4_decompress_unsafe_generic(+                        (const BYTE*)source, (BYTE*)dest, originalSize,+                        lz4sd->prefixSize,+                        lz4sd->externalDict, lz4sd->extDictSize);         if (result <= 0) return result;         lz4sd->prefixSize += (size_t)originalSize;         lz4sd->prefixEnd  += originalSize;     } else {+        DEBUGLOG(5, "prefix becomes extDict");         lz4sd->extDictSize = lz4sd->prefixSize;         lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;         result = LZ4_decompress_fast_extDict(source, dest, originalSize,@@ -2416,10 +2624,27 @@     return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize); } +int LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize)+{+    if (dictSize==0)+        return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity);+    if (dictStart+dictSize == dest) {+        if (dictSize >= 64 KB - 1) {+            return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity);+        }+        assert(dictSize >= 0);+        return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize);+    }+    assert(dictSize >= 0);+    return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize);+}+ int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) {     if (dictSize==0 || dictStart+dictSize == dest)-        return LZ4_decompress_fast(source, dest, originalSize);+        return LZ4_decompress_unsafe_generic(+                        (const BYTE*)source, (BYTE*)dest, originalSize,+                        (size_t)dictSize, NULL, 0);     assert(dictSize >= 0);     return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize); }@@ -2471,7 +2696,7 @@  /* Obsolete Streaming functions */ -int LZ4_sizeofStreamState(void) { return LZ4_STREAMSIZE; }+int LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); }  int LZ4_resetStreamState(void* state, char* inputBuffer) {@@ -2480,11 +2705,13 @@     return 0; } +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) void* LZ4_create (char* inputBuffer) {     (void)inputBuffer;     return LZ4_createStream(); }+#endif  char* LZ4_slideInputBuffer (void* state) {
cbits/lz4.h view
@@ -1,7 +1,7 @@ /*  *  LZ4 - Fast LZ compression algorithm  *  Header File- *  Copyright (C) 2011-present, Yann Collet.+ *  Copyright (C) 2011-2020, Yann Collet.     BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) @@ -97,37 +97,78 @@ #  define LZ4LIB_API LZ4LIB_VISIBILITY #endif +/*! LZ4_FREESTANDING :+ *  When this macro is set to 1, it enables "freestanding mode" that is+ *  suitable for typical freestanding environment which doesn't support+ *  standard C library.+ *+ *  - LZ4_FREESTANDING is a compile-time switch.+ *  - It requires the following macros to be defined:+ *    LZ4_memcpy, LZ4_memmove, LZ4_memset.+ *  - It only enables LZ4/HC functions which don't use heap.+ *    All LZ4F_* functions are not supported.+ *  - See tests/freestanding.c to check its basic setup.+ */+#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1)+#  define LZ4_HEAPMODE 0+#  define LZ4HC_HEAPMODE 0+#  define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1+#  if !defined(LZ4_memcpy)+#    error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'."+#  endif+#  if !defined(LZ4_memset)+#    error "LZ4_FREESTANDING requires macro 'LZ4_memset'."+#  endif+#  if !defined(LZ4_memmove)+#    error "LZ4_FREESTANDING requires macro 'LZ4_memmove'."+#  endif+#elif ! defined(LZ4_FREESTANDING)+#  define LZ4_FREESTANDING 0+#endif++ /*------   Version   ------*/ #define LZ4_VERSION_MAJOR    1    /* for breaking interface changes  */ #define LZ4_VERSION_MINOR    9    /* for new (non-breaking) interface capabilities */-#define LZ4_VERSION_RELEASE  3    /* for tweaks, bug-fixes, or development */+#define LZ4_VERSION_RELEASE  4    /* for tweaks, bug-fixes, or development */  #define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)  #define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE #define LZ4_QUOTE(str) #str #define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)-#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)+#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)  /* requires v1.7.3+ */ -LZ4LIB_API int LZ4_versionNumber (void);  /**< library version number; useful to check dll version */-LZ4LIB_API const char* LZ4_versionString (void);   /**< library version string; useful to check dll version */+LZ4LIB_API int LZ4_versionNumber (void);  /**< library version number; useful to check dll version; requires v1.3.0+ */+LZ4LIB_API const char* LZ4_versionString (void);   /**< library version string; useful to check dll version; requires v1.7.5+ */   /*-************************************ *  Tuning parameter **************************************/+#define LZ4_MEMORY_USAGE_MIN 10+#define LZ4_MEMORY_USAGE_DEFAULT 14+#define LZ4_MEMORY_USAGE_MAX 20+ /*!  * LZ4_MEMORY_USAGE :- * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)- * Increasing memory usage improves compression ratio.- * Reduced memory usage may improve speed, thanks to better cache locality.+ * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; )+ * Increasing memory usage improves compression ratio, at the cost of speed.+ * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.  * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache  */ #ifndef LZ4_MEMORY_USAGE-# define LZ4_MEMORY_USAGE 14+# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT #endif +#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)+#  error "LZ4_MEMORY_USAGE is too small !"+#endif +#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX)+#  error "LZ4_MEMORY_USAGE is too large !"+#endif+ /*-************************************ *  Simple Functions **************************************/@@ -270,8 +311,25 @@ ***********************************************/ typedef union LZ4_stream_u LZ4_stream_t;  /* incomplete type (defined later) */ +/**+ Note about RC_INVOKED++ - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).+   https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros++ - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)+   and reports warning "RC4011: identifier truncated".++ - To eliminate the warning, we surround long preprocessor symbol with+   "#if !defined(RC_INVOKED) ... #endif" block that means+   "skip this block when rc.exe is trying to read it".+*/+#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); LZ4LIB_API int           LZ4_freeStream (LZ4_stream_t* streamPtr);+#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */+#endif  /*! LZ4_resetStream_fast() : v1.9.0+  *  Use this to prepare an LZ4_stream_t for a new chain of dependent blocks@@ -355,8 +413,12 @@  *  creation / destruction of streaming decompression tracking context.  *  A tracking context can be re-used multiple times.  */+#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); LZ4LIB_API int                 LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);+#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */+#endif  /*! LZ4_setStreamDecode() :  *  An LZ4_streamDecode_t context can be allocated once and re-used multiple times.@@ -406,7 +468,10 @@  *  save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,  *  then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block. */-LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity);+LZ4LIB_API int+LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,+                        const char* src, char* dst,+                        int srcSize, int dstCapacity);   /*! LZ4_decompress_*_usingDict() :@@ -417,8 +482,17 @@  *  Performance tip : Decompression speed can be substantially increased  *                    when dst == dictStart + dictSize.  */-LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize);+LZ4LIB_API int+LZ4_decompress_safe_usingDict(const char* src, char* dst,+                              int srcSize, int dstCapacity,+                              const char* dictStart, int dictSize); +LZ4LIB_API int+LZ4_decompress_safe_partial_usingDict(const char* src, char* dst,+                                      int compressedSize,+                                      int targetOutputSize, int maxOutputSize,+                                      const char* dictStart, int dictSize);+ #endif /* LZ4_H_2983827168210 */  @@ -496,13 +570,15 @@  *  stream (and source buffer) must remain in-place / accessible / unchanged  *  through the completion of the first compression call on the stream.  */-LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream);+LZ4LIB_STATIC_API void+LZ4_attach_dictionary(LZ4_stream_t* workingStream,+                const LZ4_stream_t* dictionaryStream);   /*! In-place compression and decompression  *  * It's possible to have input and output sharing the same buffer,- * for highly contrained memory environments.+ * for highly constrained memory environments.  * In both cases, it requires input to lay at the end of the buffer,  * and decompression to start at beginning of the buffer.  * Buffer size must feature some margin, hence be larger than final size.@@ -592,38 +668,26 @@   typedef unsigned int   LZ4_u32; #endif +/*! LZ4_stream_t :+ *  Never ever use below internal definitions directly !+ *  These definitions are not API/ABI safe, and may change in future versions.+ *  If you need static allocation, declare or allocate an LZ4_stream_t object.+**/+ typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; struct LZ4_stream_t_internal {     LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];-    LZ4_u32 currentOffset;-    LZ4_u32 tableType;     const LZ4_byte* dictionary;     const LZ4_stream_t_internal* dictCtx;+    LZ4_u32 currentOffset;+    LZ4_u32 tableType;     LZ4_u32 dictSize;+    /* Implicit padding to ensure structure is aligned */ }; -typedef struct {-    const LZ4_byte* externalDict;-    size_t extDictSize;-    const LZ4_byte* prefixEnd;-    size_t prefixSize;-} LZ4_streamDecode_t_internal;---/*! LZ4_stream_t :- *  Do not use below internal definitions directly !- *  Declare or allocate an LZ4_stream_t instead.- *  LZ4_stream_t can also be created using LZ4_createStream(), which is recommended.- *  The structure definition can be convenient for static allocation- *  (on stack, or as part of larger structure).- *  Init this structure with LZ4_initStream() before first use.- *  note : only use this definition in association with static linking !- *  this definition is not API/ABI safe, and may change in future versions.- */-#define LZ4_STREAMSIZE       16416  /* static size, for inter-version compatibility */-#define LZ4_STREAMSIZE_VOIDP (LZ4_STREAMSIZE / sizeof(void*))+#define LZ4_STREAM_MINSIZE  ((1UL << LZ4_MEMORY_USAGE) + 32)  /* static size, for inter-version compatibility */ union LZ4_stream_u {-    void* table[LZ4_STREAMSIZE_VOIDP];+    char minStateSize[LZ4_STREAM_MINSIZE];     LZ4_stream_t_internal internal_donotuse; }; /* previously typedef'd to LZ4_stream_t */ @@ -641,21 +705,25 @@  *         In which case, the function will @return NULL.  *  Note2: An LZ4_stream_t structure guarantees correct alignment and size.  *  Note3: Before v1.9.0, use LZ4_resetStream() instead- */+**/ LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size);   /*! LZ4_streamDecode_t :- *  information structure to track an LZ4 stream during decompression.- *  init this structure  using LZ4_setStreamDecode() before first use.- *  note : only use in association with static linking !- *         this definition is not API/ABI safe,- *         and may change in a future version !- */-#define LZ4_STREAMDECODESIZE_U64 (4 + ((sizeof(void*)==16) ? 2 : 0) /*AS-400*/ )-#define LZ4_STREAMDECODESIZE     (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))+ *  Never ever use below internal definitions directly !+ *  These definitions are not API/ABI safe, and may change in future versions.+ *  If you need static allocation, declare or allocate an LZ4_streamDecode_t object.+**/+typedef struct {+    const LZ4_byte* externalDict;+    const LZ4_byte* prefixEnd;+    size_t extDictSize;+    size_t prefixSize;+} LZ4_streamDecode_t_internal;++#define LZ4_STREAMDECODE_MINSIZE 32 union LZ4_streamDecode_u {-    unsigned long long table[LZ4_STREAMDECODESIZE_U64];+    char minStateSize[LZ4_STREAMDECODE_MINSIZE];     LZ4_streamDecode_t_internal internal_donotuse; } ;   /* previously typedef'd to LZ4_streamDecode_t */ 
cbits/lz4frame.c view
@@ -45,7 +45,7 @@ *  Compiler Options **************************************/ #ifdef _MSC_VER    /* Visual Studio */-#  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */+#  pragma warning(disable : 4127)   /* disable: C4127: conditional expression is constant */ #endif  @@ -63,6 +63,19 @@   /*-************************************+*  Library declarations+**************************************/+#define LZ4F_STATIC_LINKING_ONLY+#include "lz4frame.h"+#define LZ4_STATIC_LINKING_ONLY+#include "lz4.h"+#define LZ4_HC_STATIC_LINKING_ONLY+#include "lz4hc.h"+#define XXH_STATIC_LINKING_ONLY+#include "xxhash.h"+++/*-************************************ *  Memory routines **************************************/ /*@@ -70,7 +83,13 @@  * malloc(), calloc() and free()  * towards another library or solution of their choice  * by modifying below section.- */+**/++#include <string.h>   /* memset, memcpy, memmove */+#ifndef LZ4_SRC_INCLUDED  /* avoid redefinition when sources are coalesced */+#  define MEM_INIT(p,v,s)   memset((p),(v),(s))+#endif+ #ifndef LZ4_SRC_INCLUDED   /* avoid redefinition when sources are coalesced */ #  include <stdlib.h>   /* malloc, calloc, free */ #  define ALLOC(s)          malloc(s)@@ -78,23 +97,42 @@ #  define FREEMEM(p)        free(p) #endif -#include <string.h>   /* memset, memcpy, memmove */-#ifndef LZ4_SRC_INCLUDED  /* avoid redefinition when sources are coalesced */-#  define MEM_INIT(p,v,s)   memset((p),(v),(s))-#endif+static void* LZ4F_calloc(size_t s, LZ4F_CustomMem cmem)+{+    /* custom calloc defined : use it */+    if (cmem.customCalloc != NULL) {+        return cmem.customCalloc(cmem.opaqueState, s);+    }+    /* nothing defined : use default <stdlib.h>'s calloc() */+    if (cmem.customAlloc == NULL) {+        return ALLOC_AND_ZERO(s);+    }+    /* only custom alloc defined : use it, and combine it with memset() */+    {   void* const p = cmem.customAlloc(cmem.opaqueState, s);+        if (p != NULL) MEM_INIT(p, 0, s);+        return p;+}   } +static void* LZ4F_malloc(size_t s, LZ4F_CustomMem cmem)+{+    /* custom malloc defined : use it */+    if (cmem.customAlloc != NULL) {+        return cmem.customAlloc(cmem.opaqueState, s);+    }+    /* nothing defined : use default <stdlib.h>'s malloc() */+    return ALLOC(s);+} -/*-************************************-*  Library declarations-**************************************/-#define LZ4F_STATIC_LINKING_ONLY-#include "lz4frame.h"-#define LZ4_STATIC_LINKING_ONLY-#include "lz4.h"-#define LZ4_HC_STATIC_LINKING_ONLY-#include "lz4hc.h"-#define XXH_STATIC_LINKING_ONLY-#include "xxhash.h"+static void LZ4F_free(void* p, LZ4F_CustomMem cmem)+{+    /* custom malloc defined : use it */+    if (cmem.customFree != NULL) {+        cmem.customFree(cmem.opaqueState, p);+        return;+    }+    /* nothing defined : use default <stdlib.h>'s free() */+    FREEMEM(p);+}   /*-************************************@@ -143,7 +181,7 @@ #endif  -/* unoptimized version; solves endianess & alignment issues */+/* unoptimized version; solves endianness & alignment issues */ static U32 LZ4F_readLE32 (const void* src) {     const BYTE* const srcPtr = (const BYTE*)src;@@ -206,8 +244,6 @@ #define _4BITS 0x0F #define _8BITS 0xFF -#define LZ4F_MAGIC_SKIPPABLE_START 0x184D2A50U-#define LZ4F_MAGICNUMBER 0x184D2204U #define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U #define LZ4F_BLOCKSIZEID_DEFAULT LZ4F_max64KB @@ -220,22 +256,27 @@ /*-************************************ *  Structures and local types **************************************/++typedef enum { LZ4B_COMPRESSED, LZ4B_UNCOMPRESSED} LZ4F_blockCompression_t;+ typedef struct LZ4F_cctx_s {+    LZ4F_CustomMem cmem;     LZ4F_preferences_t prefs;     U32    version;     U32    cStage;     const LZ4F_CDict* cdict;     size_t maxBlockSize;     size_t maxBufferSize;-    BYTE*  tmpBuff;-    BYTE*  tmpIn;-    size_t tmpInSize;+    BYTE*  tmpBuff;    /* internal buffer, for streaming */+    BYTE*  tmpIn;      /* starting position of data compress within internal buffer (>= tmpBuff) */+    size_t tmpInSize;  /* amount of data to compress after tmpIn */     U64    totalInSize;     XXH32_state_t xxh;     void*  lz4CtxPtr;     U16    lz4CtxAlloc; /* sized for: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */     U16    lz4CtxState; /* in use as: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */+    LZ4F_blockCompression_t  blockCompression; } LZ4F_cctx_t;  @@ -264,27 +305,33 @@     return (LZ4F_errorCodes)(-(ptrdiff_t)functionResult); } -static LZ4F_errorCode_t err0r(LZ4F_errorCodes code)+static LZ4F_errorCode_t LZ4F_returnErrorCode(LZ4F_errorCodes code) {     /* A compilation error here means sizeof(ptrdiff_t) is not large enough */     LZ4F_STATIC_ASSERT(sizeof(ptrdiff_t) >= sizeof(size_t));     return (LZ4F_errorCode_t)-(ptrdiff_t)code; } +#define RETURN_ERROR(e) return LZ4F_returnErrorCode(LZ4F_ERROR_ ## e)++#define RETURN_ERROR_IF(c,e) if (c) RETURN_ERROR(e)++#define FORWARD_IF_ERROR(r)  if (LZ4F_isError(r)) return (r)+ unsigned LZ4F_getVersion(void) { return LZ4F_VERSION; }  int LZ4F_compressionLevel_max(void) { return LZ4HC_CLEVEL_MAX; } -size_t LZ4F_getBlockSize(unsigned blockSizeID)+size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID) {     static const size_t blockSizes[4] = { 64 KB, 256 KB, 1 MB, 4 MB };      if (blockSizeID == 0) blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;     if (blockSizeID < LZ4F_max64KB || blockSizeID > LZ4F_max4MB)-        return err0r(LZ4F_ERROR_maxBlockSize_invalid);-    blockSizeID -= LZ4F_max64KB;-    return blockSizes[blockSizeID];-}+        RETURN_ERROR(maxBlockSize_invalid);+    {   int const blockSizeIdx = (int)blockSizeID - (int)LZ4F_max64KB;+        return blockSizes[blockSizeIdx];+}   }  /*-************************************ *  Private functions@@ -397,21 +444,20 @@     MEM_INIT(&options, 0, sizeof(options));     options.stableSrc = 1; -    if (dstCapacity < LZ4F_compressFrameBound(srcSize, &prefs))  /* condition to guarantee success */-        return err0r(LZ4F_ERROR_dstMaxSize_tooSmall);+    RETURN_ERROR_IF(dstCapacity < LZ4F_compressFrameBound(srcSize, &prefs), dstMaxSize_tooSmall);      { size_t const headerSize = LZ4F_compressBegin_usingCDict(cctx, dstBuffer, dstCapacity, cdict, &prefs);  /* write header */-      if (LZ4F_isError(headerSize)) return headerSize;+      FORWARD_IF_ERROR(headerSize);       dstPtr += headerSize;   /* header size */ }      assert(dstEnd >= dstPtr);     { size_t const cSize = LZ4F_compressUpdate(cctx, dstPtr, (size_t)(dstEnd-dstPtr), srcBuffer, srcSize, &options);-      if (LZ4F_isError(cSize)) return cSize;+      FORWARD_IF_ERROR(cSize);       dstPtr += cSize; }      assert(dstEnd >= dstPtr);     { size_t const tailSize = LZ4F_compressEnd(cctx, dstPtr, (size_t)(dstEnd-dstPtr), &options);   /* flush last block, and generate suffix */-      if (LZ4F_isError(tailSize)) return tailSize;+      FORWARD_IF_ERROR(tailSize);       dstPtr += tailSize; }      assert(dstEnd >= dstStart);@@ -432,27 +478,26 @@ {     size_t result; #if (LZ4F_HEAPMODE)-    LZ4F_cctx_t *cctxPtr;+    LZ4F_cctx_t* cctxPtr;     result = LZ4F_createCompressionContext(&cctxPtr, LZ4F_VERSION);-    if (LZ4F_isError(result)) return result;+    FORWARD_IF_ERROR(result); #else     LZ4F_cctx_t cctx;     LZ4_stream_t lz4ctx;-    LZ4F_cctx_t *cctxPtr = &cctx;+    LZ4F_cctx_t* const cctxPtr = &cctx; -    DEBUGLOG(4, "LZ4F_compressFrame");     MEM_INIT(&cctx, 0, sizeof(cctx));     cctx.version = LZ4F_VERSION;     cctx.maxBufferSize = 5 MB;   /* mess with real buffer size to prevent dynamic allocation; works only because autoflush==1 & stableSrc==1 */-    if (preferencesPtr == NULL ||-        preferencesPtr->compressionLevel < LZ4HC_CLEVEL_MIN)-    {+    if ( preferencesPtr == NULL+      || preferencesPtr->compressionLevel < LZ4HC_CLEVEL_MIN ) {         LZ4_initStream(&lz4ctx, sizeof(lz4ctx));         cctxPtr->lz4CtxPtr = &lz4ctx;         cctxPtr->lz4CtxAlloc = 1;         cctxPtr->lz4CtxState = 1;     } #endif+    DEBUGLOG(4, "LZ4F_compressFrame");      result = LZ4F_compressFrame_usingCDict(cctxPtr, dstBuffer, dstCapacity,                                            srcBuffer, srcSize,@@ -461,10 +506,9 @@ #if (LZ4F_HEAPMODE)     LZ4F_freeCompressionContext(cctxPtr); #else-    if (preferencesPtr != NULL &&-        preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN)-    {-        FREEMEM(cctxPtr->lz4CtxPtr);+    if ( preferencesPtr != NULL+      && preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN ) {+        LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);     } #endif     return result;@@ -476,30 +520,31 @@ *****************************************************/  struct LZ4F_CDict_s {+    LZ4F_CustomMem cmem;     void* dictContent;     LZ4_stream_t* fastCtx;     LZ4_streamHC_t* HCCtx; }; /* typedef'd to LZ4F_CDict within lz4frame_static.h */ -/*! LZ4F_createCDict() :- *  When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once.- *  LZ4F_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay.- *  LZ4F_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.- * `dictBuffer` can be released after LZ4F_CDict creation, since its content is copied within CDict- * @return : digested dictionary for compression, or NULL if failed */-LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize)+LZ4F_CDict*+LZ4F_createCDict_advanced(LZ4F_CustomMem cmem, const void* dictBuffer, size_t dictSize) {     const char* dictStart = (const char*)dictBuffer;-    LZ4F_CDict* cdict = (LZ4F_CDict*) ALLOC(sizeof(*cdict));-    DEBUGLOG(4, "LZ4F_createCDict");+    LZ4F_CDict* const cdict = (LZ4F_CDict*)LZ4F_malloc(sizeof(*cdict), cmem);+    DEBUGLOG(4, "LZ4F_createCDict_advanced");     if (!cdict) return NULL;+    cdict->cmem = cmem;     if (dictSize > 64 KB) {         dictStart += dictSize - 64 KB;         dictSize = 64 KB;     }-    cdict->dictContent = ALLOC(dictSize);-    cdict->fastCtx = LZ4_createStream();-    cdict->HCCtx = LZ4_createStreamHC();+    cdict->dictContent = LZ4F_malloc(dictSize, cmem);+    cdict->fastCtx = (LZ4_stream_t*)LZ4F_malloc(sizeof(LZ4_stream_t), cmem);+    if (cdict->fastCtx)+        LZ4_initStream(cdict->fastCtx, sizeof(LZ4_stream_t));+    cdict->HCCtx = (LZ4_streamHC_t*)LZ4F_malloc(sizeof(LZ4_streamHC_t), cmem);+    if (cdict->HCCtx)+        LZ4_initStream(cdict->HCCtx, sizeof(LZ4_streamHC_t));     if (!cdict->dictContent || !cdict->fastCtx || !cdict->HCCtx) {         LZ4F_freeCDict(cdict);         return NULL;@@ -511,13 +556,25 @@     return cdict; } +/*! LZ4F_createCDict() :+ *  When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once.+ *  LZ4F_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay.+ *  LZ4F_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.+ * @dictBuffer can be released after LZ4F_CDict creation, since its content is copied within CDict+ * @return : digested dictionary for compression, or NULL if failed */+LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize)+{+    DEBUGLOG(4, "LZ4F_createCDict");+    return LZ4F_createCDict_advanced(LZ4F_defaultCMem, dictBuffer, dictSize);+}+ void LZ4F_freeCDict(LZ4F_CDict* cdict) {     if (cdict==NULL) return;  /* support free on NULL */-    FREEMEM(cdict->dictContent);-    LZ4_freeStream(cdict->fastCtx);-    LZ4_freeStreamHC(cdict->HCCtx);-    FREEMEM(cdict);+    LZ4F_free(cdict->dictContent, cdict->cmem);+    LZ4F_free(cdict->fastCtx, cdict->cmem);+    LZ4F_free(cdict->HCCtx, cdict->cmem);+    LZ4F_free(cdict, cdict->cmem); }  @@ -525,6 +582,20 @@ *  Advanced compression functions ***********************************/ +LZ4F_cctx*+LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version)+{+    LZ4F_cctx* const cctxPtr =+        (LZ4F_cctx*)LZ4F_calloc(sizeof(LZ4F_cctx), customMem);+    if (cctxPtr==NULL) return NULL;++    cctxPtr->cmem = customMem;+    cctxPtr->version = version;+    cctxPtr->cStage = 0;   /* Uninitialized. Next stage : init cctx */++    return cctxPtr;+}+ /*! LZ4F_createCompressionContext() :  *  The first thing to do is to create a compressionContext object, which will be used in all compression operations.  *  This is achieved using LZ4F_createCompressionContext(), which takes as argument a version and an LZ4F_preferences_t structure.@@ -532,17 +603,16 @@  *  The function will provide a pointer to an allocated LZ4F_compressionContext_t object.  *  If the result LZ4F_errorCode_t is not OK_NoError, there was an error during context creation.  *  Object can release its memory using LZ4F_freeCompressionContext();- */-LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_cctx** LZ4F_compressionContextPtr, unsigned version)+**/+LZ4F_errorCode_t+LZ4F_createCompressionContext(LZ4F_cctx** LZ4F_compressionContextPtr, unsigned version) {-    LZ4F_cctx_t* const cctxPtr = (LZ4F_cctx_t*)ALLOC_AND_ZERO(sizeof(LZ4F_cctx_t));-    if (cctxPtr==NULL) return err0r(LZ4F_ERROR_allocation_failed);--    cctxPtr->version = version;-    cctxPtr->cStage = 0;   /* Next stage : init stream */--    *LZ4F_compressionContextPtr = cctxPtr;+    assert(LZ4F_compressionContextPtr != NULL); /* considered a violation of narrow contract */+    /* in case it nonetheless happen in production */+    RETURN_ERROR_IF(LZ4F_compressionContextPtr == NULL, parameter_null); +    *LZ4F_compressionContextPtr = LZ4F_createCompressionContext_advanced(LZ4F_defaultCMem, version);+    RETURN_ERROR_IF(*LZ4F_compressionContextPtr==NULL, allocation_failed);     return LZ4F_OK_NoError; } @@ -550,11 +620,10 @@ LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctxPtr) {     if (cctxPtr != NULL) {  /* support free on NULL */-       FREEMEM(cctxPtr->lz4CtxPtr);  /* note: LZ4_streamHC_t and LZ4_stream_t are simple POD types */-       FREEMEM(cctxPtr->tmpBuff);-       FREEMEM(cctxPtr);+       LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);  /* note: LZ4_streamHC_t and LZ4_stream_t are simple POD types */+       LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem);+       LZ4F_free(cctxPtr, cctxPtr->cmem);     }-     return LZ4F_OK_NoError; } @@ -588,11 +657,21 @@     } } +static int ctxTypeID_to_size(int ctxTypeID) {+    switch(ctxTypeID) {+    case 1:+        return LZ4_sizeofState();+    case 2:+        return LZ4_sizeofStateHC();+    default:+        return 0;+    }+}  /*! LZ4F_compressBegin_usingCDict() :- *  init streaming compression and writes frame header into dstBuffer.- *  dstBuffer must be >= LZ4F_HEADER_SIZE_MAX bytes.- * @return : number of bytes written into dstBuffer for the header+ *  init streaming compression AND writes frame header into @dstBuffer.+ * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.+ * @return : number of bytes written into @dstBuffer for the header  *           or an error code (can be tested using LZ4F_isError())  */ size_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctxPtr,@@ -600,41 +679,46 @@                           const LZ4F_CDict* cdict,                           const LZ4F_preferences_t* preferencesPtr) {-    LZ4F_preferences_t prefNull;+    LZ4F_preferences_t const prefNull = LZ4F_INIT_PREFERENCES;     BYTE* const dstStart = (BYTE*)dstBuffer;     BYTE* dstPtr = dstStart;-    BYTE* headerStart; -    if (dstCapacity < maxFHSize) return err0r(LZ4F_ERROR_dstMaxSize_tooSmall);-    MEM_INIT(&prefNull, 0, sizeof(prefNull));+    RETURN_ERROR_IF(dstCapacity < maxFHSize, dstMaxSize_tooSmall);     if (preferencesPtr == NULL) preferencesPtr = &prefNull;     cctxPtr->prefs = *preferencesPtr; -    /* Ctx Management */+    /* cctx Management */     {   U16 const ctxTypeID = (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) ? 1 : 2;-        if (cctxPtr->lz4CtxAlloc < ctxTypeID) {-            FREEMEM(cctxPtr->lz4CtxPtr);+        int requiredSize = ctxTypeID_to_size(ctxTypeID);+        int allocatedSize = ctxTypeID_to_size(cctxPtr->lz4CtxAlloc);+        if (allocatedSize < requiredSize) {+            /* not enough space allocated */+            LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);             if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) {-                cctxPtr->lz4CtxPtr = LZ4_createStream();+                /* must take ownership of memory allocation,+                 * in order to respect custom allocator contract */+                cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_stream_t), cctxPtr->cmem);+                if (cctxPtr->lz4CtxPtr)+                    LZ4_initStream(cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t));             } else {-                cctxPtr->lz4CtxPtr = LZ4_createStreamHC();+                cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_streamHC_t), cctxPtr->cmem);+                if (cctxPtr->lz4CtxPtr)+                    LZ4_initStreamHC(cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t));             }-            if (cctxPtr->lz4CtxPtr == NULL)-                return err0r(LZ4F_ERROR_allocation_failed);+            RETURN_ERROR_IF(cctxPtr->lz4CtxPtr == NULL, allocation_failed);             cctxPtr->lz4CtxAlloc = ctxTypeID;             cctxPtr->lz4CtxState = ctxTypeID;         } else if (cctxPtr->lz4CtxState != ctxTypeID) {-            /* otherwise, a sufficient buffer is allocated, but we need to-             * reset it to the correct context type */+            /* otherwise, a sufficient buffer is already allocated,+             * but we need to reset it to the correct context type */             if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) {-                LZ4_initStream((LZ4_stream_t *) cctxPtr->lz4CtxPtr, sizeof (LZ4_stream_t));+                LZ4_initStream((LZ4_stream_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t));             } else {-                LZ4_initStreamHC((LZ4_streamHC_t *) cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t));-                LZ4_setCompressionLevel((LZ4_streamHC_t *) cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);+                LZ4_initStreamHC((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t));+                LZ4_setCompressionLevel((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);             }             cctxPtr->lz4CtxState = ctxTypeID;-        }-    }+    }   }      /* Buffer Management */     if (cctxPtr->prefs.frameInfo.blockSizeID == 0)@@ -647,9 +731,9 @@          if (cctxPtr->maxBufferSize < requiredBuffSize) {             cctxPtr->maxBufferSize = 0;-            FREEMEM(cctxPtr->tmpBuff);-            cctxPtr->tmpBuff = (BYTE*)ALLOC_AND_ZERO(requiredBuffSize);-            if (cctxPtr->tmpBuff == NULL) return err0r(LZ4F_ERROR_allocation_failed);+            LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem);+            cctxPtr->tmpBuff = (BYTE*)LZ4F_calloc(requiredBuffSize, cctxPtr->cmem);+            RETURN_ERROR_IF(cctxPtr->tmpBuff == NULL, allocation_failed);             cctxPtr->maxBufferSize = requiredBuffSize;     }   }     cctxPtr->tmpIn = cctxPtr->tmpBuff;@@ -669,31 +753,32 @@     /* Magic Number */     LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER);     dstPtr += 4;-    headerStart = dstPtr;+    {   BYTE* const headerStart = dstPtr; -    /* FLG Byte */-    *dstPtr++ = (BYTE)(((1 & _2BITS) << 6)    /* Version('01') */-        + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5)-        + ((cctxPtr->prefs.frameInfo.blockChecksumFlag & _1BIT ) << 4)-        + ((unsigned)(cctxPtr->prefs.frameInfo.contentSize > 0) << 3)-        + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2)-        +  (cctxPtr->prefs.frameInfo.dictID > 0) );-    /* BD Byte */-    *dstPtr++ = (BYTE)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4);-    /* Optional Frame content size field */-    if (cctxPtr->prefs.frameInfo.contentSize) {-        LZ4F_writeLE64(dstPtr, cctxPtr->prefs.frameInfo.contentSize);-        dstPtr += 8;-        cctxPtr->totalInSize = 0;-    }-    /* Optional dictionary ID field */-    if (cctxPtr->prefs.frameInfo.dictID) {-        LZ4F_writeLE32(dstPtr, cctxPtr->prefs.frameInfo.dictID);-        dstPtr += 4;+        /* FLG Byte */+        *dstPtr++ = (BYTE)(((1 & _2BITS) << 6)    /* Version('01') */+            + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5)+            + ((cctxPtr->prefs.frameInfo.blockChecksumFlag & _1BIT ) << 4)+            + ((unsigned)(cctxPtr->prefs.frameInfo.contentSize > 0) << 3)+            + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2)+            +  (cctxPtr->prefs.frameInfo.dictID > 0) );+        /* BD Byte */+        *dstPtr++ = (BYTE)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4);+        /* Optional Frame content size field */+        if (cctxPtr->prefs.frameInfo.contentSize) {+            LZ4F_writeLE64(dstPtr, cctxPtr->prefs.frameInfo.contentSize);+            dstPtr += 8;+            cctxPtr->totalInSize = 0;+        }+        /* Optional dictionary ID field */+        if (cctxPtr->prefs.frameInfo.dictID) {+            LZ4F_writeLE32(dstPtr, cctxPtr->prefs.frameInfo.dictID);+            dstPtr += 4;+        }+        /* Header CRC Byte */+        *dstPtr = LZ4F_headerChecksum(headerStart, (size_t)(dstPtr - headerStart));+        dstPtr++;     }-    /* Header CRC Byte */-    *dstPtr = LZ4F_headerChecksum(headerStart, (size_t)(dstPtr - headerStart));-    dstPtr++;      cctxPtr->cStage = 1;   /* header written, now request input data block */     return (size_t)(dstPtr - dstStart);@@ -701,9 +786,9 @@   /*! LZ4F_compressBegin() :- *  init streaming compression and writes frame header into dstBuffer.- *  dstBuffer must be >= LZ4F_HEADER_SIZE_MAX bytes.- *  preferencesPtr can be NULL, in which case default parameters are selected.+ *  init streaming compression AND writes frame header into @dstBuffer.+ * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.+ * @preferencesPtr can be NULL, in which case default parameters are selected.  * @return : number of bytes written into dstBuffer for the header  *        or an error code (can be tested using LZ4F_isError())  */@@ -744,11 +829,13 @@                              LZ4F_blockChecksum_t crcFlag) {     BYTE* const cSizePtr = (BYTE*)dst;-    U32 cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+BHSize),-                                      (int)(srcSize), (int)(srcSize-1),-                                      level, cdict);-    if (cSize == 0) {  /* compression failed */-        DEBUGLOG(5, "LZ4F_makeBlock: compression failed, creating a raw block (size %u)", (U32)srcSize);+    U32 cSize;+    assert(compress != NULL);+    cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+BHSize),+                          (int)(srcSize), (int)(srcSize-1),+                          level, cdict);++    if (cSize == 0 || cSize >= srcSize) {         cSize = (U32)srcSize;         LZ4F_writeLE32(cSizePtr, cSize | LZ4F_BLOCKUNCOMPRESSED_FLAG);         memcpy(cSizePtr+BHSize, src, srcSize);@@ -766,6 +853,7 @@ static int LZ4F_compressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) {     int const acceleration = (level < 0) ? -level + 1 : 1;+    DEBUGLOG(5, "LZ4F_compressBlock (srcSize=%i)", srcSize);     LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent);     if (cdict) {         return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration);@@ -778,6 +866,7 @@ {     int const acceleration = (level < 0) ? -level + 1 : 1;     (void)cdict; /* init once at beginning of frame */+    DEBUGLOG(5, "LZ4F_compressBlock_continue (srcSize=%i)", srcSize);     return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration); } @@ -796,8 +885,15 @@     return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity); } -static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int level)+static int LZ4F_doNotCompressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) {+    (void)ctx; (void)src; (void)dst; (void)srcSize; (void)dstCapacity; (void)level; (void)cdict;+    return 0;+}++static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int level, LZ4F_blockCompression_t  compressMode)+{+    if (compressMode == LZ4B_UNCOMPRESSED) return LZ4F_doNotCompressBlock;     if (level < LZ4HC_CLEVEL_MIN) {         if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlock;         return LZ4F_compressBlock_continue;@@ -806,6 +902,7 @@     return LZ4F_compressBlockHC_continue; } +/* Save history (up to 64KB) into @tmpBuff */ static int LZ4F_localSaveDict(LZ4F_cctx_t* cctxPtr) {     if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN)@@ -815,38 +912,57 @@  typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus; -/*! LZ4F_compressUpdate() :+static const LZ4F_compressOptions_t k_cOptionsNull = { 0, { 0, 0, 0 } };+++ /*! LZ4F_compressUpdateImpl() :  *  LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.- *  dstBuffer MUST be >= LZ4F_compressBound(srcSize, preferencesPtr).- *  LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.+ *  When successful, the function always entirely consumes @srcBuffer.+ *  src data is either buffered or compressed into @dstBuffer.+ *  If the block compression does not match the compression of the previous block, the old data is flushed+ *  and operations continue with the new compression mode.+ * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr) when block compression is turned on.+ * @compressOptionsPtr is optional : provide NULL to mean "default".  * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.  *           or an error code if it fails (which can be tested using LZ4F_isError())+ *  After an error, the state is left in a UB state, and must be re-initialized.  */-size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr,-                           void* dstBuffer, size_t dstCapacity,+static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr,+                     void* dstBuffer, size_t dstCapacity,                      const void* srcBuffer, size_t srcSize,-                     const LZ4F_compressOptions_t* compressOptionsPtr)-{-    LZ4F_compressOptions_t cOptionsNull;+                     const LZ4F_compressOptions_t* compressOptionsPtr,+                     LZ4F_blockCompression_t blockCompression)+  {     size_t const blockSize = cctxPtr->maxBlockSize;     const BYTE* srcPtr = (const BYTE*)srcBuffer;     const BYTE* const srcEnd = srcPtr + srcSize;     BYTE* const dstStart = (BYTE*)dstBuffer;     BYTE* dstPtr = dstStart;     LZ4F_lastBlockStatus lastBlockCompressed = notDone;-    compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel);-+    compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, blockCompression);+    size_t bytesWritten;     DEBUGLOG(4, "LZ4F_compressUpdate (srcSize=%zu)", srcSize); -    if (cctxPtr->cStage != 1) return err0r(LZ4F_ERROR_GENERIC);+    RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized);   /* state must be initialized and waiting for next block */     if (dstCapacity < LZ4F_compressBound_internal(srcSize, &(cctxPtr->prefs), cctxPtr->tmpInSize))-        return err0r(LZ4F_ERROR_dstMaxSize_tooSmall);-    MEM_INIT(&cOptionsNull, 0, sizeof(cOptionsNull));-    if (compressOptionsPtr == NULL) compressOptionsPtr = &cOptionsNull;+        RETURN_ERROR(dstMaxSize_tooSmall); +    if (blockCompression == LZ4B_UNCOMPRESSED && dstCapacity < srcSize)+        RETURN_ERROR(dstMaxSize_tooSmall);++    /* flush currently written block, to continue with new block compression */+    if (cctxPtr->blockCompression != blockCompression) {+        bytesWritten = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr);+        dstPtr += bytesWritten;+        cctxPtr->blockCompression = blockCompression;+    }++    if (compressOptionsPtr == NULL) compressOptionsPtr = &k_cOptionsNull;+     /* complete tmp buffer */     if (cctxPtr->tmpInSize > 0) {   /* some data already within tmp buffer */         size_t const sizeToCopy = blockSize - cctxPtr->tmpInSize;+        assert(blockSize > cctxPtr->tmpInSize);         if (sizeToCopy > srcSize) {             /* add src to tmpIn buffer */             memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize);@@ -864,11 +980,9 @@                                      compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,                                      cctxPtr->cdict,                                      cctxPtr->prefs.frameInfo.blockChecksumFlag);-             if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += blockSize;             cctxPtr->tmpInSize = 0;-        }-    }+    }   }      while ((size_t)(srcEnd - srcPtr) >= blockSize) {         /* compress full blocks */@@ -882,33 +996,38 @@     }      if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd)) {-        /* compress remaining input < blockSize */+        /* autoFlush : remaining input (< blockSize) is compressed */         lastBlockCompressed = fromSrcBuffer;         dstPtr += LZ4F_makeBlock(dstPtr,                                  srcPtr, (size_t)(srcEnd - srcPtr),                                  compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,                                  cctxPtr->cdict,                                  cctxPtr->prefs.frameInfo.blockChecksumFlag);-        srcPtr  = srcEnd;+        srcPtr = srcEnd;     } -    /* preserve dictionary if necessary */+    /* preserve dictionary within @tmpBuff whenever necessary */     if ((cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) && (lastBlockCompressed==fromSrcBuffer)) {+        /* linked blocks are only supported in compressed mode, see LZ4F_uncompressedUpdate */+        assert(blockCompression == LZ4B_COMPRESSED);         if (compressOptionsPtr->stableSrc) {-            cctxPtr->tmpIn = cctxPtr->tmpBuff;+            cctxPtr->tmpIn = cctxPtr->tmpBuff;  /* src is stable : dictionary remains in src across invocations */         } else {             int const realDictSize = LZ4F_localSaveDict(cctxPtr);-            if (realDictSize==0) return err0r(LZ4F_ERROR_GENERIC);+            assert(0 <= realDictSize && realDictSize <= 64 KB);             cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;         }     }      /* keep tmpIn within limits */-    if ((cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)   /* necessarily LZ4F_blockLinked && lastBlockCompressed==fromTmpBuffer */-        && !(cctxPtr->prefs.autoFlush))+    if (!(cctxPtr->prefs.autoFlush)  /* no autoflush : there may be some data left within internal buffer */+      && (cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize) )  /* not enough room to store next block */     {+        /* only preserve 64KB within internal buffer. Ensures there is enough room for next block.+         * note: this situation necessarily implies lastBlockCompressed==fromTmpBuffer */         int const realDictSize = LZ4F_localSaveDict(cctxPtr);         cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;+        assert((cctxPtr->tmpIn + blockSize) <= (cctxPtr->tmpBuff + cctxPtr->maxBufferSize));     }      /* some input data left, necessarily < blockSize */@@ -926,7 +1045,54 @@     return (size_t)(dstPtr - dstStart); } +/*! LZ4F_compressUpdate() :+ *  LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.+ *  When successful, the function always entirely consumes @srcBuffer.+ *  src data is either buffered or compressed into @dstBuffer.+ *  If previously an uncompressed block was written, buffered data is flushed+ *  before appending compressed data is continued.+ * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr).+ * @compressOptionsPtr is optional : provide NULL to mean "default".+ * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.+ *           or an error code if it fails (which can be tested using LZ4F_isError())+ *  After an error, the state is left in a UB state, and must be re-initialized.+ */+size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr,+                           void* dstBuffer, size_t dstCapacity,+                     const void* srcBuffer, size_t srcSize,+                     const LZ4F_compressOptions_t* compressOptionsPtr)+{+     return LZ4F_compressUpdateImpl(cctxPtr,+                                   dstBuffer, dstCapacity,+                                   srcBuffer, srcSize,+                                   compressOptionsPtr, LZ4B_COMPRESSED);+} +/*! LZ4F_compressUpdate() :+ *  LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.+ *  When successful, the function always entirely consumes @srcBuffer.+ *  src data is either buffered or compressed into @dstBuffer.+ *  If previously an uncompressed block was written, buffered data is flushed+ *  before appending compressed data is continued.+ *  This is only supported when LZ4F_blockIndependent is used+ * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr).+ * @compressOptionsPtr is optional : provide NULL to mean "default".+ * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.+ *           or an error code if it fails (which can be tested using LZ4F_isError())+ *  After an error, the state is left in a UB state, and must be re-initialized.+ */+size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr,+                               void* dstBuffer, size_t dstCapacity,+                         const void* srcBuffer, size_t srcSize,+                         const LZ4F_compressOptions_t* compressOptionsPtr) {+    RETURN_ERROR_IF(cctxPtr->prefs.frameInfo.blockMode != LZ4F_blockIndependent, blockMode_invalid);+    return LZ4F_compressUpdateImpl(cctxPtr,+                                   dstBuffer, dstCapacity,+                                   srcBuffer, srcSize,+                                   compressOptionsPtr, LZ4B_UNCOMPRESSED);+}++ /*! LZ4F_flush() :  *  When compressed data must be sent immediately, without waiting for a block to be filled,  *  invoke LZ4_flush(), which will immediately compress any remaining data stored within LZ4F_cctx.@@ -944,13 +1110,12 @@     compressFunc_t compress;      if (cctxPtr->tmpInSize == 0) return 0;   /* nothing to flush */-    if (cctxPtr->cStage != 1) return err0r(LZ4F_ERROR_GENERIC);-    if (dstCapacity < (cctxPtr->tmpInSize + BHSize + BFSize))-        return err0r(LZ4F_ERROR_dstMaxSize_tooSmall);-    (void)compressOptionsPtr;   /* not yet useful */+    RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized);+    RETURN_ERROR_IF(dstCapacity < (cctxPtr->tmpInSize + BHSize + BFSize), dstMaxSize_tooSmall);+    (void)compressOptionsPtr;   /* not useful (yet) */      /* select compression function */-    compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel);+    compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, cctxPtr->blockCompression);      /* compress tmp buffer */     dstPtr += LZ4F_makeBlock(dstPtr,@@ -992,19 +1157,19 @@      size_t const flushSize = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr);     DEBUGLOG(5,"LZ4F_compressEnd: dstCapacity=%u", (unsigned)dstCapacity);-    if (LZ4F_isError(flushSize)) return flushSize;+    FORWARD_IF_ERROR(flushSize);     dstPtr += flushSize;      assert(flushSize <= dstCapacity);     dstCapacity -= flushSize; -    if (dstCapacity < 4) return err0r(LZ4F_ERROR_dstMaxSize_tooSmall);+    RETURN_ERROR_IF(dstCapacity < 4, dstMaxSize_tooSmall);     LZ4F_writeLE32(dstPtr, 0);     dstPtr += 4;   /* endMark */      if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled) {         U32 const xxh = XXH32_digest(&(cctxPtr->xxh));-        if (dstCapacity < 8) return err0r(LZ4F_ERROR_dstMaxSize_tooSmall);+        RETURN_ERROR_IF(dstCapacity < 8, dstMaxSize_tooSmall);         DEBUGLOG(5,"Writing 32-bit content checksum");         LZ4F_writeLE32(dstPtr, xxh);         dstPtr+=4;   /* content Checksum */@@ -1015,7 +1180,7 @@      if (cctxPtr->prefs.frameInfo.contentSize) {         if (cctxPtr->prefs.frameInfo.contentSize != cctxPtr->totalInSize)-            return err0r(LZ4F_ERROR_frameSize_wrong);+            RETURN_ERROR(frameSize_wrong);     }      return (size_t)(dstPtr - dstStart);@@ -1039,6 +1204,7 @@ } dStage_t;  struct LZ4F_dctx_s {+    LZ4F_CustomMem cmem;     LZ4F_frameInfo_t frameInfo;     U32    version;     dStage_t dStage;@@ -1056,26 +1222,37 @@     size_t tmpOutStart;     XXH32_state_t xxh;     XXH32_state_t blockChecksum;+    int    skipChecksum;     BYTE   header[LZ4F_HEADER_SIZE_MAX]; };  /* typedef'd to LZ4F_dctx in lz4frame.h */  +LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version)+{+    LZ4F_dctx* const dctx = (LZ4F_dctx*)LZ4F_calloc(sizeof(LZ4F_dctx), customMem);+    if (dctx == NULL) return NULL;++    dctx->cmem = customMem;+    dctx->version = version;+    return dctx;+}+ /*! LZ4F_createDecompressionContext() :  *  Create a decompressionContext object, which will track all decompression operations.  *  Provides a pointer to a fully allocated and initialized LZ4F_decompressionContext object.  *  Object can later be released using LZ4F_freeDecompressionContext().  * @return : if != 0, there was an error during context creation.  */-LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_dctx** LZ4F_decompressionContextPtr, unsigned versionNumber)+LZ4F_errorCode_t+LZ4F_createDecompressionContext(LZ4F_dctx** LZ4F_decompressionContextPtr, unsigned versionNumber) {-    LZ4F_dctx* const dctx = (LZ4F_dctx*)ALLOC_AND_ZERO(sizeof(LZ4F_dctx));-    if (dctx == NULL) {  /* failed allocation */-        *LZ4F_decompressionContextPtr = NULL;-        return err0r(LZ4F_ERROR_allocation_failed);-    }+    assert(LZ4F_decompressionContextPtr != NULL);  /* violation of narrow contract */+    RETURN_ERROR_IF(LZ4F_decompressionContextPtr == NULL, parameter_null);  /* in case it nonetheless happen in production */ -    dctx->version = versionNumber;-    *LZ4F_decompressionContextPtr = dctx;+    *LZ4F_decompressionContextPtr = LZ4F_createDecompressionContext_advanced(LZ4F_defaultCMem, versionNumber);+    if (*LZ4F_decompressionContextPtr == NULL) {  /* failed allocation */+        RETURN_ERROR(allocation_failed);+    }     return LZ4F_OK_NoError; } @@ -1084,9 +1261,9 @@     LZ4F_errorCode_t result = LZ4F_OK_NoError;     if (dctx != NULL) {   /* can accept NULL input, like free() */       result = (LZ4F_errorCode_t)dctx->dStage;-      FREEMEM(dctx->tmpIn);-      FREEMEM(dctx->tmpOutBuffer);-      FREEMEM(dctx);+      LZ4F_free(dctx->tmpIn, dctx->cmem);+      LZ4F_free(dctx->tmpOutBuffer, dctx->cmem);+      LZ4F_free(dctx, dctx->cmem);     }     return result; }@@ -1099,6 +1276,7 @@     dctx->dStage = dstage_getFrameHeader;     dctx->dict = NULL;     dctx->dictSize = 0;+    dctx->skipChecksum = 0; }  @@ -1118,7 +1296,7 @@      DEBUGLOG(5, "LZ4F_decodeHeader");     /* need to decode header to get frameInfo */-    if (srcSize < minFHSize) return err0r(LZ4F_ERROR_frameHeader_incomplete);   /* minimal frame header size */+    RETURN_ERROR_IF(srcSize < minFHSize, frameHeader_incomplete);   /* minimal frame header size */     MEM_INIT(&(dctx->frameInfo), 0, sizeof(dctx->frameInfo));      /* special case : skippable frames */@@ -1132,14 +1310,13 @@         } else {             dctx->dStage = dstage_getSFrameSize;             return 4;-        }-    }+    }   }      /* control magic number */ #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION     if (LZ4F_readLE32(srcPtr) != LZ4F_MAGICNUMBER) {         DEBUGLOG(4, "frame header error : unknown magic number");-        return err0r(LZ4F_ERROR_frameType_unknown);+        RETURN_ERROR(frameType_unknown);     } #endif     dctx->frameInfo.frameType = LZ4F_frame;@@ -1153,8 +1330,8 @@         contentChecksumFlag = (FLG>>2) & _1BIT;         dictIDFlag = FLG & _1BIT;         /* validate */-        if (((FLG>>1)&_1BIT) != 0) return err0r(LZ4F_ERROR_reservedFlag_set); /* Reserved bit */-        if (version != 1) return err0r(LZ4F_ERROR_headerVersion_wrong);        /* Version Number, only supported value */+        if (((FLG>>1)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bit */+        if (version != 1) RETURN_ERROR(headerVersion_wrong);       /* Version Number, only supported value */     }      /* Frame Header Size */@@ -1173,17 +1350,16 @@     {   U32 const BD = srcPtr[5];         blockSizeID = (BD>>4) & _3BITS;         /* validate */-        if (((BD>>7)&_1BIT) != 0) return err0r(LZ4F_ERROR_reservedFlag_set);   /* Reserved bit */-        if (blockSizeID < 4) return err0r(LZ4F_ERROR_maxBlockSize_invalid);    /* 4-7 only supported values for the time being */-        if (((BD>>0)&_4BITS) != 0) return err0r(LZ4F_ERROR_reservedFlag_set);  /* Reserved bits */+        if (((BD>>7)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set);   /* Reserved bit */+        if (blockSizeID < 4) RETURN_ERROR(maxBlockSize_invalid);    /* 4-7 only supported values for the time being */+        if (((BD>>0)&_4BITS) != 0) RETURN_ERROR(reservedFlag_set);  /* Reserved bits */     }      /* check header */     assert(frameHeaderSize > 5); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION     {   BYTE const HC = LZ4F_headerChecksum(srcPtr+4, frameHeaderSize-5);-        if (HC != srcPtr[frameHeaderSize-1])-            return err0r(LZ4F_ERROR_headerChecksum_invalid);+        RETURN_ERROR_IF(HC != srcPtr[frameHeaderSize-1], headerChecksum_invalid);     } #endif @@ -1192,10 +1368,9 @@     dctx->frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)blockChecksumFlag;     dctx->frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)contentChecksumFlag;     dctx->frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID;-    dctx->maxBlockSize = LZ4F_getBlockSize(blockSizeID);+    dctx->maxBlockSize = LZ4F_getBlockSize((LZ4F_blockSizeID_t)blockSizeID);     if (contentSizeFlag)-        dctx->frameRemainingSize =-            dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6);+        dctx->frameRemainingSize = dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6);     if (dictIDFlag)         dctx->frameInfo.dictID = LZ4F_readLE32(srcPtr + frameHeaderSize - 5); @@ -1211,11 +1386,11 @@  */ size_t LZ4F_headerSize(const void* src, size_t srcSize) {-    if (src == NULL) return err0r(LZ4F_ERROR_srcPtr_wrong);+    RETURN_ERROR_IF(src == NULL, srcPtr_wrong);      /* minimal srcSize to determine header size */     if (srcSize < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH)-        return err0r(LZ4F_ERROR_frameHeader_incomplete);+        RETURN_ERROR(frameHeader_incomplete);      /* special case : skippable frames */     if ((LZ4F_readLE32(src) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START)@@ -1224,7 +1399,7 @@     /* control magic number */ #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION     if (LZ4F_readLE32(src) != LZ4F_MAGICNUMBER)-        return err0r(LZ4F_ERROR_frameType_unknown);+        RETURN_ERROR(frameType_unknown); #endif      /* Frame Header Size */@@ -1266,13 +1441,13 @@         if (dctx->dStage == dstage_storeFrameHeader) {             /* frame decoding already started, in the middle of header => automatic fail */             *srcSizePtr = 0;-            return err0r(LZ4F_ERROR_frameDecoding_alreadyStarted);+            RETURN_ERROR(frameDecoding_alreadyStarted);         } else {             size_t const hSize = LZ4F_headerSize(srcBuffer, *srcSizePtr);             if (LZ4F_isError(hSize)) { *srcSizePtr=0; return hSize; }             if (*srcSizePtr < hSize) {                 *srcSizePtr=0;-                return err0r(LZ4F_ERROR_frameHeader_incomplete);+                RETURN_ERROR(frameHeader_incomplete);             }              {   size_t decodeResult = LZ4F_decodeHeader(dctx, srcBuffer, hSize);@@ -1290,16 +1465,14 @@  /* LZ4F_updateDict() :  * only used for LZ4F_blockLinked mode- * Condition : dstPtr != NULL+ * Condition : @dstPtr != NULL  */ static void LZ4F_updateDict(LZ4F_dctx* dctx,                       const BYTE* dstPtr, size_t dstSize, const BYTE* dstBufferStart,                       unsigned withinTmp) {     assert(dstPtr != NULL);-    if (dctx->dictSize==0) {-        dctx->dict = (const BYTE*)dstPtr;   /* priority to prefix mode */-    }+    if (dctx->dictSize==0) dctx->dict = (const BYTE*)dstPtr;  /* will lead to prefix mode */     assert(dctx->dict != NULL);      if (dctx->dict + dctx->dictSize == dstPtr) {  /* prefix mode, everything within dstBuffer */@@ -1362,7 +1535,6 @@ }  - /*! LZ4F_decompress() :  *  Call this function repetitively to regenerate compressed data in srcBuffer.  *  The function will attempt to decode up to *srcSizePtr bytes from srcBuffer@@ -1406,6 +1578,7 @@     *srcSizePtr = 0;     *dstSizePtr = 0;     assert(dctx != NULL);+    dctx->skipChecksum |= (decompressOptionsPtr->skipChecksums != 0); /* once set, disable for the remainder of the frame */      /* behaves as a state machine */ @@ -1418,7 +1591,7 @@             DEBUGLOG(6, "dstage_getFrameHeader");             if ((size_t)(srcEnd-srcPtr) >= maxFHSize) {  /* enough to decode - shortcut */                 size_t const hSize = LZ4F_decodeHeader(dctx, srcPtr, (size_t)(srcEnd-srcPtr));  /* will update dStage appropriately */-                if (LZ4F_isError(hSize)) return hSize;+                FORWARD_IF_ERROR(hSize);                 srcPtr += hSize;                 break;             }@@ -1440,9 +1613,7 @@                 doAnotherStage = 0;   /* not enough src data, ask for some more */                 break;             }-            {   size_t const hSize = LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget);  /* will update dStage appropriately */-                if (LZ4F_isError(hSize)) return hSize;-            }+            FORWARD_IF_ERROR( LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget) ); /* will update dStage appropriately */             break;          case dstage_init:@@ -1453,14 +1624,12 @@                     + ((dctx->frameInfo.blockMode==LZ4F_blockLinked) ? 128 KB : 0);                 if (bufferNeeded > dctx->maxBufferSize) {   /* tmp buffers too small */                     dctx->maxBufferSize = 0;   /* ensure allocation will be re-attempted on next entry*/-                    FREEMEM(dctx->tmpIn);-                    dctx->tmpIn = (BYTE*)ALLOC(dctx->maxBlockSize + BFSize /* block checksum */);-                    if (dctx->tmpIn == NULL)-                        return err0r(LZ4F_ERROR_allocation_failed);-                    FREEMEM(dctx->tmpOutBuffer);-                    dctx->tmpOutBuffer= (BYTE*)ALLOC(bufferNeeded);-                    if (dctx->tmpOutBuffer== NULL)-                        return err0r(LZ4F_ERROR_allocation_failed);+                    LZ4F_free(dctx->tmpIn, dctx->cmem);+                    dctx->tmpIn = (BYTE*)LZ4F_malloc(dctx->maxBlockSize + BFSize /* block checksum */, dctx->cmem);+                    RETURN_ERROR_IF(dctx->tmpIn == NULL, allocation_failed);+                    LZ4F_free(dctx->tmpOutBuffer, dctx->cmem);+                    dctx->tmpOutBuffer= (BYTE*)LZ4F_malloc(bufferNeeded, dctx->cmem);+                    RETURN_ERROR_IF(dctx->tmpOutBuffer== NULL, allocation_failed);                     dctx->maxBufferSize = bufferNeeded;             }   }             dctx->tmpInSize = 0;@@ -1509,7 +1678,7 @@                     break;                 }                 if (nextCBlockSize > dctx->maxBlockSize) {-                    return err0r(LZ4F_ERROR_maxBlockSize_invalid);+                    RETURN_ERROR(maxBlockSize_invalid);                 }                 if (blockHeader & LZ4F_BLOCKUNCOMPRESSED_FLAG) {                     /* next block is uncompressed */@@ -1540,11 +1709,13 @@                     size_t const minBuffSize = MIN((size_t)(srcEnd-srcPtr), (size_t)(dstEnd-dstPtr));                     sizeToCopy = MIN(dctx->tmpInTarget, minBuffSize);                     memcpy(dstPtr, srcPtr, sizeToCopy);-                    if (dctx->frameInfo.blockChecksumFlag) {-                        (void)XXH32_update(&dctx->blockChecksum, srcPtr, sizeToCopy);+                    if (!dctx->skipChecksum) {+                        if (dctx->frameInfo.blockChecksumFlag) {+                            (void)XXH32_update(&dctx->blockChecksum, srcPtr, sizeToCopy);+                        }+                        if (dctx->frameInfo.contentChecksumFlag)+                            (void)XXH32_update(&dctx->xxh, srcPtr, sizeToCopy);                     }-                    if (dctx->frameInfo.contentChecksumFlag)-                        (void)XXH32_update(&dctx->xxh, srcPtr, sizeToCopy);                     if (dctx->frameInfo.contentSize)                         dctx->frameRemainingSize -= sizeToCopy; @@ -1590,14 +1761,15 @@                     }                     crcSrc = dctx->header;                 }-                {   U32 const readCRC = LZ4F_readLE32(crcSrc);+                if (!dctx->skipChecksum) {+                    U32 const readCRC = LZ4F_readLE32(crcSrc);                     U32 const calcCRC = XXH32_digest(&dctx->blockChecksum); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION                     DEBUGLOG(6, "compare block checksum");                     if (readCRC != calcCRC) {                         DEBUGLOG(4, "incorrect block checksum: %08X != %08X",                                 readCRC, calcCRC);-                        return err0r(LZ4F_ERROR_blockChecksum_invalid);+                        RETURN_ERROR(blockChecksum_invalid);                     } #else                     (void)readCRC;@@ -1637,37 +1809,44 @@             }              /* At this stage, input is large enough to decode a block */++            /* First, decode and control block checksum if it exists */             if (dctx->frameInfo.blockChecksumFlag) {+                assert(dctx->tmpInTarget >= 4);                 dctx->tmpInTarget -= 4;                 assert(selectedIn != NULL);  /* selectedIn is defined at this stage (either srcPtr, or dctx->tmpIn) */                 {   U32 const readBlockCrc = LZ4F_readLE32(selectedIn + dctx->tmpInTarget);                     U32 const calcBlockCrc = XXH32(selectedIn, dctx->tmpInTarget, 0); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION-                    if (readBlockCrc != calcBlockCrc)-                        return err0r(LZ4F_ERROR_blockChecksum_invalid);+                    RETURN_ERROR_IF(readBlockCrc != calcBlockCrc, blockChecksum_invalid); #else                     (void)readBlockCrc;                     (void)calcBlockCrc; #endif             }   } -            if ((size_t)(dstEnd-dstPtr) >= dctx->maxBlockSize) {+            /* decode directly into destination buffer if there is enough room */+            if ( ((size_t)(dstEnd-dstPtr) >= dctx->maxBlockSize)+                 /* unless the dictionary is stored in tmpOut:+                  * in which case it's faster to decode within tmpOut+                  * to benefit from prefix speedup */+              && !(dctx->dict!= NULL && (const BYTE*)dctx->dict + dctx->dictSize == dctx->tmpOut) )+            {                 const char* dict = (const char*)dctx->dict;                 size_t dictSize = dctx->dictSize;                 int decodedSize;                 assert(dstPtr != NULL);                 if (dict && dictSize > 1 GB) {-                    /* the dictSize param is an int, avoid truncation / sign issues */+                    /* overflow control : dctx->dictSize is an int, avoid truncation / sign issues */                     dict += dictSize - 64 KB;                     dictSize = 64 KB;                 }-                /* enough capacity in `dst` to decompress directly there */                 decodedSize = LZ4_decompress_safe_usingDict(                         (const char*)selectedIn, (char*)dstPtr,                         (int)dctx->tmpInTarget, (int)dctx->maxBlockSize,                         dict, (int)dictSize);-                if (decodedSize < 0) return err0r(LZ4F_ERROR_GENERIC);   /* decompression failed */-                if (dctx->frameInfo.contentChecksumFlag)+                RETURN_ERROR_IF(decodedSize < 0, decompressionFailed);+                if ((dctx->frameInfo.contentChecksumFlag) && (!dctx->skipChecksum))                     XXH32_update(&(dctx->xxh), dstPtr, (size_t)decodedSize);                 if (dctx->frameInfo.contentSize)                     dctx->frameRemainingSize -= (size_t)decodedSize;@@ -1678,25 +1857,27 @@                 }                  dstPtr += decodedSize;-                dctx->dStage = dstage_getBlockHeader;+                dctx->dStage = dstage_getBlockHeader;  /* end of block, let's get another one */                 break;             }              /* not enough place into dst : decode into tmpOut */-            /* ensure enough place for tmpOut */++            /* manage dictionary */             if (dctx->frameInfo.blockMode == LZ4F_blockLinked) {                 if (dctx->dict == dctx->tmpOutBuffer) {+                    /* truncate dictionary to 64 KB if too big */                     if (dctx->dictSize > 128 KB) {                         memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - 64 KB, 64 KB);                         dctx->dictSize = 64 KB;                     }                     dctx->tmpOut = dctx->tmpOutBuffer + dctx->dictSize;-                } else {  /* dict not within tmp */+                } else {  /* dict not within tmpOut */                     size_t const reservedDictSpace = MIN(dctx->dictSize, 64 KB);                     dctx->tmpOut = dctx->tmpOutBuffer + reservedDictSpace;             }   } -            /* Decode block */+            /* Decode block into tmpOut */             {   const char* dict = (const char*)dctx->dict;                 size_t dictSize = dctx->dictSize;                 int decodedSize;@@ -1709,9 +1890,8 @@                         (const char*)selectedIn, (char*)dctx->tmpOut,                         (int)dctx->tmpInTarget, (int)dctx->maxBlockSize,                         dict, (int)dictSize);-                if (decodedSize < 0)  /* decompression failed */-                    return err0r(LZ4F_ERROR_decompressionFailed);-                if (dctx->frameInfo.contentChecksumFlag)+                RETURN_ERROR_IF(decodedSize < 0, decompressionFailed);+                if (dctx->frameInfo.contentChecksumFlag && !dctx->skipChecksum)                     XXH32_update(&(dctx->xxh), dctx->tmpOut, (size_t)decodedSize);                 if (dctx->frameInfo.contentSize)                     dctx->frameRemainingSize -= (size_t)decodedSize;@@ -1744,8 +1924,7 @@             break;          case dstage_getSuffix:-            if (dctx->frameRemainingSize)-                return err0r(LZ4F_ERROR_frameSize_wrong);   /* incorrect frame size decoded */+            RETURN_ERROR_IF(dctx->frameRemainingSize, frameSize_wrong);   /* incorrect frame size decoded */             if (!dctx->frameInfo.contentChecksumFlag) {  /* no checksum, frame is completed */                 nextSrcSizeHint = 0;                 LZ4F_resetDecompressionContext(dctx);@@ -1777,20 +1956,20 @@             }   /* if (dctx->dStage == dstage_storeSuffix) */          /* case dstage_checkSuffix: */   /* no direct entry, avoid initialization risks */-            {   U32 const readCRC = LZ4F_readLE32(selectedIn);+            if (!dctx->skipChecksum) {+                U32 const readCRC = LZ4F_readLE32(selectedIn);                 U32 const resultCRC = XXH32_digest(&(dctx->xxh)); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION-                if (readCRC != resultCRC)-                    return err0r(LZ4F_ERROR_contentChecksum_invalid);+                RETURN_ERROR_IF(readCRC != resultCRC, contentChecksum_invalid); #else                 (void)readCRC;                 (void)resultCRC; #endif-                nextSrcSizeHint = 0;-                LZ4F_resetDecompressionContext(dctx);-                doAnotherStage = 0;-                break;             }+            nextSrcSizeHint = 0;+            LZ4F_resetDecompressionContext(dctx);+            doAnotherStage = 0;+            break;          case dstage_getSFrameSize:             if ((srcEnd - srcPtr) >= 4) {@@ -1841,7 +2020,7 @@         }   /* switch (dctx->dStage) */     }   /* while (doAnotherStage) */ -    /* preserve history within tmp whenever necessary */+    /* preserve history within tmpOut whenever necessary */     LZ4F_STATIC_ASSERT((unsigned)dstage_init == 2);     if ( (dctx->frameInfo.blockMode==LZ4F_blockLinked)  /* next block will use up to 64KB from previous ones */       && (dctx->dict != dctx->tmpOutBuffer)             /* dictionary is not already within tmp */
cbits/lz4frame.h view
@@ -1,7 +1,7 @@ /*-   LZ4 auto-framing library+   LZ4F - LZ4-Frame library    Header File-   Copyright (C) 2011-2017, Yann Collet.+   Copyright (C) 2011-2020, Yann Collet.    BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)     Redistribution and use in source and binary forms, with or without@@ -39,7 +39,7 @@  * LZ4F also offers streaming capabilities.  *  * lz4.h is not required when using lz4frame.h,- * except to extract common constant such as LZ4_VERSION_NUMBER.+ * except to extract common constants such as LZ4_VERSION_NUMBER.  * */  #ifndef LZ4F_H_09782039843@@ -54,12 +54,12 @@   /**-  Introduction--  lz4frame.h implements LZ4 frame specification (doc/lz4_Frame_format.md).-  lz4frame.h provides frame compression functions that take care-  of encoding standard metadata alongside LZ4-compressed blocks.-*/+ * Introduction+ *+ * lz4frame.h implements LZ4 frame specification: see doc/lz4_Frame_format.md .+ * LZ4 Frames are compatible with `lz4` CLI,+ * and designed to be interoperable with any system.+**/  /*-***************************************************************  *  Compiler specifics@@ -210,7 +210,7 @@  *  Returns the maximum possible compressed size with LZ4F_compressFrame() given srcSize and preferences.  * `preferencesPtr` is optional. It can be replaced by NULL, in which case, the function will assume default preferences.  *  Note : this result is only usable with LZ4F_compressFrame().- *         It may also be used with LZ4F_compressUpdate() _if no flush() operation_ is performed.+ *         It may also be relevant to LZ4F_compressUpdate() _only if_ no flush() operation is ever performed.  */ LZ4FLIB_API size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr); @@ -230,7 +230,7 @@ *  Advanced compression functions *************************************/ typedef struct LZ4F_cctx_s LZ4F_cctx;   /* incomplete type */-typedef LZ4F_cctx* LZ4F_compressionContext_t;   /* for compatibility with previous API version */+typedef LZ4F_cctx* LZ4F_compressionContext_t;  /* for compatibility with older APIs, prefer using LZ4F_cctx */  typedef struct {   unsigned stableSrc;    /* 1 == src content will remain present on future calls to LZ4F_compress(); skip copying src content within tmp buffer */@@ -243,20 +243,27 @@ LZ4FLIB_API unsigned LZ4F_getVersion(void);  /*! LZ4F_createCompressionContext() :- * The first thing to do is to create a compressionContext object, which will be used in all compression operations.- * This is achieved using LZ4F_createCompressionContext(), which takes as argument a version.- * The version provided MUST be LZ4F_VERSION. It is intended to track potential version mismatch, notably when using DLL.- * The function will provide a pointer to a fully allocated LZ4F_cctx object.- * If @return != zero, there was an error during context creation.- * Object can release its memory using LZ4F_freeCompressionContext();- */+ *  The first thing to do is to create a compressionContext object,+ *  which will keep track of operation state during streaming compression.+ *  This is achieved using LZ4F_createCompressionContext(), which takes as argument a version,+ *  and a pointer to LZ4F_cctx*, to write the resulting pointer into.+ *  @version provided MUST be LZ4F_VERSION. It is intended to track potential version mismatch, notably when using DLL.+ *  The function provides a pointer to a fully allocated LZ4F_cctx object.+ *  @cctxPtr MUST be != NULL.+ *  If @return != zero, context creation failed.+ *  A created compression context can be employed multiple times for consecutive streaming operations.+ *  Once all streaming compression jobs are completed,+ *  the state object can be released using LZ4F_freeCompressionContext().+ *  Note1 : LZ4F_freeCompressionContext() is always successful. Its return value can be ignored.+ *  Note2 : LZ4F_freeCompressionContext() works fine with NULL input pointers (do nothing).+**/ LZ4FLIB_API LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_cctx** cctxPtr, unsigned version); LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctx);   /*----    Compression    ----*/ -#define LZ4F_HEADER_SIZE_MIN  7   /* LZ4 Frame header size can vary, depending on selected paramaters */+#define LZ4F_HEADER_SIZE_MIN  7   /* LZ4 Frame header size can vary, depending on selected parameters */ #define LZ4F_HEADER_SIZE_MAX 19  /* Size in bytes of a block header in little-endian format. Highest bit indicates if block data is uncompressed */@@ -301,8 +308,9 @@  *  Important rule: dstCapacity MUST be large enough to ensure operation success even in worst case situations.  *  This value is provided by LZ4F_compressBound().  *  If this condition is not respected, LZ4F_compress() will fail (result is an errorCode).- *  LZ4F_compressUpdate() doesn't guarantee error recovery.- *  When an error occurs, compression context must be freed or resized.+ *  After an error, the state is left in a UB state, and must be re-initialized or freed.+ *  If previously an uncompressed block was written, buffered data is flushed+ *  before appending compressed data is continued.  * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default.  * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered).  *           or an error code if it fails (which can be tested using LZ4F_isError())@@ -347,8 +355,12 @@ typedef LZ4F_dctx* LZ4F_decompressionContext_t;   /* compatibility with previous API versions */  typedef struct {-  unsigned stableDst;    /* pledges that last 64KB decompressed data will remain available unmodified. This optimization skips storage operations in tmp buffers. */-  unsigned reserved[3];  /* must be set to zero for forward compatibility */+  unsigned stableDst;     /* pledges that last 64KB decompressed data will remain available unmodified between invocations.+                           * This optimization skips storage operations in tmp buffers. */+  unsigned skipChecksums; /* disable checksum calculation and verification, even when one is present in frame, to save CPU time.+                           * Setting this option to 1 once disables all checksums for the rest of the frame. */+  unsigned reserved1;     /* must be set to zero for forward compatibility */+  unsigned reserved0;     /* idem */ } LZ4F_decompressOptions_t;  @@ -356,9 +368,10 @@  /*! LZ4F_createDecompressionContext() :  *  Create an LZ4F_dctx object, to track all decompression operations.- *  The version provided MUST be LZ4F_VERSION.- *  The function provides a pointer to an allocated and initialized LZ4F_dctx object.- *  The result is an errorCode, which can be tested using LZ4F_isError().+ *  @version provided MUST be LZ4F_VERSION.+ *  @dctxPtr MUST be valid.+ *  The function fills @dctxPtr with the value of a pointer to an allocated and initialized LZ4F_dctx object.+ *  The @return is an errorCode, which can be tested using LZ4F_isError().  *  dctx memory can be released using LZ4F_freeDecompressionContext();  *  Result of LZ4F_freeDecompressionContext() indicates current state of decompressionContext when being released.  *  That is, it should be == 0 if decompression has been completed fully and correctly.@@ -371,6 +384,8 @@ *  Streaming decompression functions *************************************/ +#define LZ4F_MAGICNUMBER 0x184D2204U+#define LZ4F_MAGIC_SKIPPABLE_START 0x184D2A50U #define LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH 5  /*! LZ4F_headerSize() : v1.9.0+@@ -386,7 +401,7 @@  /*! LZ4F_getFrameInfo() :  *  This function extracts frame parameters (max blockSize, dictID, etc.).- *  Its usage is optional: user can call LZ4F_decompress() directly.+ *  Its usage is optional: user can also invoke LZ4F_decompress() directly.  *  *  Extracted information will fill an existing LZ4F_frameInfo_t structure.  *  This can be useful for allocation and dictionary identification purposes.@@ -427,9 +442,10 @@  *  note 1 : in case of error, dctx is not modified. Decoding operation can resume from beginning safely.  *  note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure.  */-LZ4FLIB_API size_t LZ4F_getFrameInfo(LZ4F_dctx* dctx,-                                     LZ4F_frameInfo_t* frameInfoPtr,-                                     const void* srcBuffer, size_t* srcSizePtr);+LZ4FLIB_API size_t+LZ4F_getFrameInfo(LZ4F_dctx* dctx,+                  LZ4F_frameInfo_t* frameInfoPtr,+            const void* srcBuffer, size_t* srcSizePtr);  /*! LZ4F_decompress() :  *  Call this function repetitively to regenerate data compressed in `srcBuffer`.@@ -462,10 +478,11 @@  *  *  After a frame is fully decoded, dctx can be used again to decompress another frame.  */-LZ4FLIB_API size_t LZ4F_decompress(LZ4F_dctx* dctx,-                                   void* dstBuffer, size_t* dstSizePtr,-                                   const void* srcBuffer, size_t* srcSizePtr,-                                   const LZ4F_decompressOptions_t* dOptPtr);+LZ4FLIB_API size_t+LZ4F_decompress(LZ4F_dctx* dctx,+                void* dstBuffer, size_t* dstSizePtr,+          const void* srcBuffer, size_t* srcSizePtr,+          const LZ4F_decompressOptions_t* dOptPtr);   /*! LZ4F_resetDecompressionContext() : added in v1.8.0@@ -529,6 +546,8 @@         ITEM(ERROR_headerChecksum_invalid) \         ITEM(ERROR_contentChecksum_invalid) \         ITEM(ERROR_frameDecoding_alreadyStarted) \+        ITEM(ERROR_compressionState_uninitialized) \+        ITEM(ERROR_parameter_null) \         ITEM(ERROR_maxCode)  #define LZ4F_GENERATE_ENUM(ENUM) LZ4F_##ENUM,@@ -539,8 +558,32 @@  LZ4FLIB_STATIC_API LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult); -LZ4FLIB_STATIC_API size_t LZ4F_getBlockSize(unsigned); +/*! LZ4F_getBlockSize() :+ *  Return, in scalar format (size_t),+ *  the maximum block size associated with blockSizeID.+**/+LZ4FLIB_STATIC_API size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID);++/*! LZ4F_uncompressedUpdate() :+ *  LZ4F_uncompressedUpdate() can be called repetitively to add as much data uncompressed data as necessary.+ *  Important rule: dstCapacity MUST be large enough to store the entire source buffer as+ *  no compression is done for this operation+ *  If this condition is not respected, LZ4F_uncompressedUpdate() will fail (result is an errorCode).+ *  After an error, the state is left in a UB state, and must be re-initialized or freed.+ *  If previously a compressed block was written, buffered data is flushed+ *  before appending uncompressed data is continued.+ *  This is only supported when LZ4F_blockIndependent is used+ * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default.+ * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered).+ *           or an error code if it fails (which can be tested using LZ4F_isError())+ */+LZ4FLIB_STATIC_API size_t+LZ4F_uncompressedUpdate(LZ4F_cctx* cctx,+                        void* dstBuffer, size_t dstCapacity,+                  const void* srcBuffer, size_t srcSize,+                  const LZ4F_compressOptions_t* cOptPtr);+ /**********************************  *  Bulk processing dictionary API  *********************************/@@ -583,12 +626,12 @@  *  but it's not recommended, as it's the only way to provide dictID in the frame header.  * @return : number of bytes written into dstBuffer.  *           or an error code if it fails (can be tested using LZ4F_isError()) */-LZ4FLIB_STATIC_API size_t LZ4F_compressFrame_usingCDict(-    LZ4F_cctx* cctx,-    void* dst, size_t dstCapacity,-    const void* src, size_t srcSize,-    const LZ4F_CDict* cdict,-    const LZ4F_preferences_t* preferencesPtr);+LZ4FLIB_STATIC_API size_t+LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx,+                              void* dst, size_t dstCapacity,+                        const void* src, size_t srcSize,+                        const LZ4F_CDict* cdict,+                        const LZ4F_preferences_t* preferencesPtr);   /*! LZ4F_compressBegin_usingCDict() :@@ -598,23 +641,49 @@  *  however, it's the only way to provide dictID in the frame header.  * @return : number of bytes written into dstBuffer for the header,  *           or an error code (which can be tested using LZ4F_isError()) */-LZ4FLIB_STATIC_API size_t LZ4F_compressBegin_usingCDict(-    LZ4F_cctx* cctx,-    void* dstBuffer, size_t dstCapacity,-    const LZ4F_CDict* cdict,-    const LZ4F_preferences_t* prefsPtr);+LZ4FLIB_STATIC_API size_t+LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctx,+                              void* dstBuffer, size_t dstCapacity,+                        const LZ4F_CDict* cdict,+                        const LZ4F_preferences_t* prefsPtr);   /*! LZ4F_decompress_usingDict() :  *  Same as LZ4F_decompress(), using a predefined dictionary.  *  Dictionary is used "in place", without any preprocessing.- *  It must remain accessible throughout the entire frame decoding. */-LZ4FLIB_STATIC_API size_t LZ4F_decompress_usingDict(-    LZ4F_dctx* dctxPtr,-    void* dstBuffer, size_t* dstSizePtr,-    const void* srcBuffer, size_t* srcSizePtr,-    const void* dict, size_t dictSize,-    const LZ4F_decompressOptions_t* decompressOptionsPtr);+**  It must remain accessible throughout the entire frame decoding. */+LZ4FLIB_STATIC_API size_t+LZ4F_decompress_usingDict(LZ4F_dctx* dctxPtr,+                          void* dstBuffer, size_t* dstSizePtr,+                    const void* srcBuffer, size_t* srcSizePtr,+                    const void* dict, size_t dictSize,+                    const LZ4F_decompressOptions_t* decompressOptionsPtr);+++/*! Custom memory allocation :+ *  These prototypes make it possible to pass custom allocation/free functions.+ *  LZ4F_customMem is provided at state creation time, using LZ4F_create*_advanced() listed below.+ *  All allocation/free operations will be completed using these custom variants instead of regular <stdlib.h> ones.+ */+typedef void* (*LZ4F_AllocFunction) (void* opaqueState, size_t size);+typedef void* (*LZ4F_CallocFunction) (void* opaqueState, size_t size);+typedef void  (*LZ4F_FreeFunction) (void* opaqueState, void* address);+typedef struct {+    LZ4F_AllocFunction customAlloc;+    LZ4F_CallocFunction customCalloc; /* optional; when not defined, uses customAlloc + memset */+    LZ4F_FreeFunction customFree;+    void* opaqueState;+} LZ4F_CustomMem;+static+#ifdef __GNUC__+__attribute__((__unused__))+#endif+LZ4F_CustomMem const LZ4F_defaultCMem = { NULL, NULL, NULL, NULL };  /**< this constant defers to stdlib's functions */++LZ4FLIB_STATIC_API LZ4F_cctx* LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version);+LZ4FLIB_STATIC_API LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version);+LZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict_advanced(LZ4F_CustomMem customMem, const void* dictBuffer, size_t dictSize);+  #if defined (__cplusplus) }
cbits/lz4hc.c view
@@ -1,6 +1,6 @@ /*     LZ4 HC - High Compression Mode of LZ4-    Copyright (C) 2011-2017, Yann Collet.+    Copyright (C) 2011-2020, Yann Collet.      BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) @@ -42,7 +42,7 @@  *  Select how default compression function will allocate workplace memory,  *  in stack (0:fastest), or in heap (1:requires malloc()).  *  Since workplace is rather large, heap mode is recommended.- */+**/ #ifndef LZ4HC_HEAPMODE #  define LZ4HC_HEAPMODE 1 #endif@@ -99,18 +99,20 @@  static void LZ4HC_init_internal (LZ4HC_CCtx_internal* hc4, const BYTE* start) {-    uptrval startingOffset = (uptrval)(hc4->end - hc4->base);-    if (startingOffset > 1 GB) {+    size_t const bufferSize = (size_t)(hc4->end - hc4->prefixStart);+    size_t newStartingOffset = bufferSize + hc4->dictLimit;+    assert(newStartingOffset >= bufferSize);  /* check overflow */+    if (newStartingOffset > 1 GB) {         LZ4HC_clearTables(hc4);-        startingOffset = 0;+        newStartingOffset = 0;     }-    startingOffset += 64 KB;-    hc4->nextToUpdate = (U32) startingOffset;-    hc4->base = start - startingOffset;+    newStartingOffset += 64 KB;+    hc4->nextToUpdate = (U32)newStartingOffset;+    hc4->prefixStart = start;     hc4->end = start;-    hc4->dictBase = start - startingOffset;-    hc4->dictLimit = (U32) startingOffset;-    hc4->lowLimit = (U32) startingOffset;+    hc4->dictStart = start;+    hc4->dictLimit = (U32)newStartingOffset;+    hc4->lowLimit = (U32)newStartingOffset; }  @@ -119,12 +121,15 @@ {     U16* const chainTable = hc4->chainTable;     U32* const hashTable  = hc4->hashTable;-    const BYTE* const base = hc4->base;-    U32 const target = (U32)(ip - base);+    const BYTE* const prefixPtr = hc4->prefixStart;+    U32 const prefixIdx = hc4->dictLimit;+    U32 const target = (U32)(ip - prefixPtr) + prefixIdx;     U32 idx = hc4->nextToUpdate;+    assert(ip >= prefixPtr);+    assert(target >= prefixIdx);      while (idx < target) {-        U32 const h = LZ4HC_hashPtr(base+idx);+        U32 const h = LZ4HC_hashPtr(prefixPtr+idx-prefixIdx);         size_t delta = idx - hashTable[h];         if (delta>LZ4_DISTANCE_MAX) delta = LZ4_DISTANCE_MAX;         DELTANEXTU16(chainTable, idx) = (U16)delta;@@ -193,15 +198,14 @@             BYTE const byte = (BYTE)(pattern >> bitOffset);             if (*ip != byte) break;             ip ++; bitOffset -= 8;-        }-    }+    }   }      return (unsigned)(ip - iStart); }  /* LZ4HC_reverseCountPattern() :  * pattern must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!)- * read using natural platform endianess */+ * read using natural platform endianness */ static unsigned LZ4HC_reverseCountPattern(const BYTE* ip, const BYTE* const iLow, U32 pattern) {@@ -211,7 +215,7 @@         if (LZ4_read32(ip-4) != pattern) break;         ip -= 4;     }-    {   const BYTE* bytePtr = (const BYTE*)(&pattern) + 3; /* works for any endianess */+    {   const BYTE* bytePtr = (const BYTE*)(&pattern) + 3; /* works for any endianness */         while (likely(ip>iLow)) {             if (ip[-1] != *bytePtr) break;             ip--; bytePtr--;@@ -234,28 +238,28 @@  LZ4_FORCE_INLINE int LZ4HC_InsertAndGetWiderMatch (-    LZ4HC_CCtx_internal* hc4,-    const BYTE* const ip,-    const BYTE* const iLowLimit,-    const BYTE* const iHighLimit,-    int longest,-    const BYTE** matchpos,-    const BYTE** startpos,-    const int maxNbAttempts,-    const int patternAnalysis,-    const int chainSwap,-    const dictCtx_directive dict,-    const HCfavor_e favorDecSpeed)+        LZ4HC_CCtx_internal* const hc4,+        const BYTE* const ip,+        const BYTE* const iLowLimit, const BYTE* const iHighLimit,+        int longest,+        const BYTE** matchpos,+        const BYTE** startpos,+        const int maxNbAttempts,+        const int patternAnalysis, const int chainSwap,+        const dictCtx_directive dict,+        const HCfavor_e favorDecSpeed) {     U16* const chainTable = hc4->chainTable;     U32* const HashTable = hc4->hashTable;     const LZ4HC_CCtx_internal * const dictCtx = hc4->dictCtx;-    const BYTE* const base = hc4->base;-    const U32 dictLimit = hc4->dictLimit;-    const BYTE* const lowPrefixPtr = base + dictLimit;-    const U32 ipIndex = (U32)(ip - base);-    const U32 lowestMatchIndex = (hc4->lowLimit + (LZ4_DISTANCE_MAX + 1) > ipIndex) ? hc4->lowLimit : ipIndex - LZ4_DISTANCE_MAX;-    const BYTE* const dictBase = hc4->dictBase;+    const BYTE* const prefixPtr = hc4->prefixStart;+    const U32 prefixIdx = hc4->dictLimit;+    const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx;+    const int withinStartDistance = (hc4->lowLimit + (LZ4_DISTANCE_MAX + 1) > ipIndex);+    const U32 lowestMatchIndex = (withinStartDistance) ? hc4->lowLimit : ipIndex - LZ4_DISTANCE_MAX;+    const BYTE* const dictStart = hc4->dictStart;+    const U32 dictIdx = hc4->lowLimit;+    const BYTE* const dictEnd = dictStart + prefixIdx - dictIdx;     int const lookBackLength = (int)(ip-iLowLimit);     int nbAttempts = maxNbAttempts;     U32 matchChainPos = 0;@@ -277,14 +281,13 @@         assert(matchIndex < ipIndex);         if (favorDecSpeed && (ipIndex - matchIndex < 8)) {             /* do nothing */-        } else if (matchIndex >= dictLimit) {   /* within current Prefix */-            const BYTE* const matchPtr = base + matchIndex;-            assert(matchPtr >= lowPrefixPtr);+        } else if (matchIndex >= prefixIdx) {   /* within current Prefix */+            const BYTE* const matchPtr = prefixPtr + matchIndex - prefixIdx;             assert(matchPtr < ip);             assert(longest >= 1);             if (LZ4_read16(iLowLimit + longest - 1) == LZ4_read16(matchPtr - lookBackLength + longest - 1)) {                 if (LZ4_read32(matchPtr) == pattern) {-                    int const back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, lowPrefixPtr) : 0;+                    int const back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, prefixPtr) : 0;                     matchLength = MINMATCH + (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit);                     matchLength -= back;                     if (matchLength > longest) {@@ -293,24 +296,25 @@                         *startpos = ip + back;             }   }   }         } else {   /* lowestMatchIndex <= matchIndex < dictLimit */-            const BYTE* const matchPtr = dictBase + matchIndex;-            if (LZ4_read32(matchPtr) == pattern) {-                const BYTE* const dictStart = dictBase + hc4->lowLimit;+            const BYTE* const matchPtr = dictStart + (matchIndex - dictIdx);+            assert(matchIndex >= dictIdx);+            if ( likely(matchIndex <= prefixIdx - 4)+              && (LZ4_read32(matchPtr) == pattern) ) {                 int back = 0;-                const BYTE* vLimit = ip + (dictLimit - matchIndex);+                const BYTE* vLimit = ip + (prefixIdx - matchIndex);                 if (vLimit > iHighLimit) vLimit = iHighLimit;                 matchLength = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH;                 if ((ip+matchLength == vLimit) && (vLimit < iHighLimit))-                    matchLength += LZ4_count(ip+matchLength, lowPrefixPtr, iHighLimit);+                    matchLength += LZ4_count(ip+matchLength, prefixPtr, iHighLimit);                 back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictStart) : 0;                 matchLength -= back;                 if (matchLength > longest) {                     longest = matchLength;-                    *matchpos = base + matchIndex + back;   /* virtual pos, relative to ip, to retrieve offset */+                    *matchpos = prefixPtr - prefixIdx + matchIndex + back;   /* virtual pos, relative to ip, to retrieve offset */                     *startpos = ip + back;         }   }   } -        if (chainSwap && matchLength==longest) {    /* better match => select a better chain */+        if (chainSwap && matchLength==longest) {   /* better match => select a better chain */             assert(lookBackLength==0);   /* search forward only */             if (matchIndex + (U32)longest <= ipIndex) {                 int const kTrigger = 4;@@ -326,8 +330,7 @@                         distanceToNextMatch = candidateDist;                         matchChainPos = (U32)pos;                         accel = 1 << kTrigger;-                    }-                }+                }   }                 if (distanceToNextMatch > 1) {                     if (distanceToNextMatch > matchIndex) break;   /* avoid overflow */                     matchIndex -= distanceToNextMatch;@@ -347,23 +350,24 @@                         repeat = rep_not;                 }   }                 if ( (repeat == rep_confirmed) && (matchCandidateIdx >= lowestMatchIndex)-                  && LZ4HC_protectDictEnd(dictLimit, matchCandidateIdx) ) {-                    const int extDict = matchCandidateIdx < dictLimit;-                    const BYTE* const matchPtr = (extDict ? dictBase : base) + matchCandidateIdx;+                  && LZ4HC_protectDictEnd(prefixIdx, matchCandidateIdx) ) {+                    const int extDict = matchCandidateIdx < prefixIdx;+                    const BYTE* const matchPtr = (extDict ? dictStart - dictIdx : prefixPtr - prefixIdx) + matchCandidateIdx;                     if (LZ4_read32(matchPtr) == pattern) {  /* good candidate */-                        const BYTE* const dictStart = dictBase + hc4->lowLimit;-                        const BYTE* const iLimit = extDict ? dictBase + dictLimit : iHighLimit;+                        const BYTE* const iLimit = extDict ? dictEnd : iHighLimit;                         size_t forwardPatternLength = LZ4HC_countPattern(matchPtr+sizeof(pattern), iLimit, pattern) + sizeof(pattern);                         if (extDict && matchPtr + forwardPatternLength == iLimit) {                             U32 const rotatedPattern = LZ4HC_rotatePattern(forwardPatternLength, pattern);-                            forwardPatternLength += LZ4HC_countPattern(lowPrefixPtr, iHighLimit, rotatedPattern);+                            forwardPatternLength += LZ4HC_countPattern(prefixPtr, iHighLimit, rotatedPattern);                         }-                        {   const BYTE* const lowestMatchPtr = extDict ? dictStart : lowPrefixPtr;+                        {   const BYTE* const lowestMatchPtr = extDict ? dictStart : prefixPtr;                             size_t backLength = LZ4HC_reverseCountPattern(matchPtr, lowestMatchPtr, pattern);                             size_t currentSegmentLength;-                            if (!extDict && matchPtr - backLength == lowPrefixPtr && hc4->lowLimit < dictLimit) {+                            if (!extDict+                              && matchPtr - backLength == prefixPtr+                              && dictIdx < prefixIdx) {                                 U32 const rotatedPattern = LZ4HC_rotatePattern((U32)(-(int)backLength), pattern);-                                backLength += LZ4HC_reverseCountPattern(dictBase + dictLimit, dictStart, rotatedPattern);+                                backLength += LZ4HC_reverseCountPattern(dictEnd, dictStart, rotatedPattern);                             }                             /* Limit backLength not go further than lowestMatchIndex */                             backLength = matchCandidateIdx - MAX(matchCandidateIdx - (U32)backLength, lowestMatchIndex);@@ -373,28 +377,28 @@                             if ( (currentSegmentLength >= srcPatternLength)   /* current pattern segment large enough to contain full srcPatternLength */                               && (forwardPatternLength <= srcPatternLength) ) { /* haven't reached this position yet */                                 U32 const newMatchIndex = matchCandidateIdx + (U32)forwardPatternLength - (U32)srcPatternLength;  /* best position, full pattern, might be followed by more match */-                                if (LZ4HC_protectDictEnd(dictLimit, newMatchIndex))+                                if (LZ4HC_protectDictEnd(prefixIdx, newMatchIndex))                                     matchIndex = newMatchIndex;                                 else {                                     /* Can only happen if started in the prefix */-                                    assert(newMatchIndex >= dictLimit - 3 && newMatchIndex < dictLimit && !extDict);-                                    matchIndex = dictLimit;+                                    assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict);+                                    matchIndex = prefixIdx;                                 }                             } else {                                 U32 const newMatchIndex = matchCandidateIdx - (U32)backLength;   /* farthest position in current segment, will find a match of length currentSegmentLength + maybe some back */-                                if (!LZ4HC_protectDictEnd(dictLimit, newMatchIndex)) {-                                    assert(newMatchIndex >= dictLimit - 3 && newMatchIndex < dictLimit && !extDict);-                                    matchIndex = dictLimit;+                                if (!LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) {+                                    assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict);+                                    matchIndex = prefixIdx;                                 } else {                                     matchIndex = newMatchIndex;                                     if (lookBackLength==0) {  /* no back possible */                                         size_t const maxML = MIN(currentSegmentLength, srcPatternLength);                                         if ((size_t)longest < maxML) {-                                            assert(base + matchIndex != ip);-                                            if ((size_t)(ip - base) - matchIndex > LZ4_DISTANCE_MAX) break;+                                            assert(prefixPtr - prefixIdx + matchIndex != ip);+                                            if ((size_t)(ip - prefixPtr) + prefixIdx - matchIndex > LZ4_DISTANCE_MAX) break;                                             assert(maxML < 2 GB);                                             longest = (int)maxML;-                                            *matchpos = base + matchIndex;   /* virtual pos, relative to ip, to retrieve offset */+                                            *matchpos = prefixPtr - prefixIdx + matchIndex;   /* virtual pos, relative to ip, to retrieve offset */                                             *startpos = ip;                                         }                                         {   U32 const distToNextPattern = DELTANEXTU16(chainTable, matchIndex);@@ -413,12 +417,12 @@     if ( dict == usingDictCtxHc       && nbAttempts > 0       && ipIndex - lowestMatchIndex < LZ4_DISTANCE_MAX) {-        size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->base);+        size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit;         U32 dictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)];         assert(dictEndOffset <= 1 GB);         matchIndex = dictMatchIndex + lowestMatchIndex - (U32)dictEndOffset;         while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX && nbAttempts--) {-            const BYTE* const matchPtr = dictCtx->base + dictMatchIndex;+            const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + dictMatchIndex;              if (LZ4_read32(matchPtr) == pattern) {                 int mlt;@@ -426,11 +430,11 @@                 const BYTE* vLimit = ip + (dictEndOffset - dictMatchIndex);                 if (vLimit > iHighLimit) vLimit = iHighLimit;                 mlt = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH;-                back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->base + dictCtx->dictLimit) : 0;+                back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->prefixStart) : 0;                 mlt -= back;                 if (mlt > longest) {                     longest = mlt;-                    *matchpos = base + matchIndex + back;+                    *matchpos = prefixPtr - prefixIdx + matchIndex + back;                     *startpos = ip + back;             }   } @@ -442,13 +446,13 @@     return longest; } -LZ4_FORCE_INLINE-int LZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4,   /* Index table will be updated */-                                 const BYTE* const ip, const BYTE* const iLimit,-                                 const BYTE** matchpos,-                                 const int maxNbAttempts,-                                 const int patternAnalysis,-                                 const dictCtx_directive dict)+LZ4_FORCE_INLINE int+LZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4,   /* Index table will be updated */+                       const BYTE* const ip, const BYTE* const iLimit,+                       const BYTE** matchpos,+                       const int maxNbAttempts,+                       const int patternAnalysis,+                       const dictCtx_directive dict) {     const BYTE* uselessPtr = ip;     /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos),@@ -751,7 +755,7 @@         } else {             *op++ = (BYTE)(lastRunSize << ML_BITS);         }-        memcpy(op, anchor, lastRunSize);+        LZ4_memcpy(op, anchor, lastRunSize);         op += lastRunSize;     } @@ -884,13 +888,13 @@         limitedOutput_directive limit         ) {-    const size_t position = (size_t)(ctx->end - ctx->base) - ctx->lowLimit;+    const size_t position = (size_t)(ctx->end - ctx->prefixStart) + (ctx->dictLimit - ctx->lowLimit);     assert(ctx->dictCtx != NULL);     if (position >= 64 KB) {         ctx->dictCtx = NULL;         return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit);     } else if (position == 0 && *srcSizePtr > 4 KB) {-        memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal));+        LZ4_memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal));         LZ4HC_setExternalDict(ctx, (const BYTE *)src);         ctx->compressionLevel = (short)cLevel;         return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit);@@ -953,13 +957,15 @@  int LZ4_compress_HC(const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) {+    int cSize; #if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1     LZ4_streamHC_t* const statePtr = (LZ4_streamHC_t*)ALLOC(sizeof(LZ4_streamHC_t));+    if (statePtr==NULL) return 0; #else     LZ4_streamHC_t state;     LZ4_streamHC_t* const statePtr = &state; #endif-    int const cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel);+    cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel); #if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1     FREEMEM(statePtr); #endif@@ -982,6 +988,7 @@ *  Streaming Functions **************************************/ /* allocation */+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4_streamHC_t* LZ4_createStreamHC(void) {     LZ4_streamHC_t* const state =@@ -998,13 +1005,12 @@     FREEMEM(LZ4_streamHCPtr);     return 0; }+#endif   LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size) {     LZ4_streamHC_t* const LZ4_streamHCPtr = (LZ4_streamHC_t*)buffer;-    /* if compilation fails here, LZ4_STREAMHCSIZE must be increased */-    LZ4_STATIC_ASSERT(sizeof(LZ4HC_CCtx_internal) <= LZ4_STREAMHCSIZE);     DEBUGLOG(4, "LZ4_initStreamHC(%p, %u)", buffer, (unsigned)size);     /* check conditions */     if (buffer == NULL) return NULL;@@ -1030,9 +1036,13 @@     if (LZ4_streamHCPtr->internal_donotuse.dirty) {         LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr));     } else {-        /* preserve end - base : can trigger clearTable's threshold */-        LZ4_streamHCPtr->internal_donotuse.end -= (uptrval)LZ4_streamHCPtr->internal_donotuse.base;-        LZ4_streamHCPtr->internal_donotuse.base = NULL;+        /* preserve end - prefixStart : can trigger clearTable's threshold */+        if (LZ4_streamHCPtr->internal_donotuse.end != NULL) {+            LZ4_streamHCPtr->internal_donotuse.end -= (uptrval)LZ4_streamHCPtr->internal_donotuse.prefixStart;+        } else {+            assert(LZ4_streamHCPtr->internal_donotuse.prefixStart == NULL);+        }+        LZ4_streamHCPtr->internal_donotuse.prefixStart = NULL;         LZ4_streamHCPtr->internal_donotuse.dictCtx = NULL;     }     LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel);@@ -1083,14 +1093,14 @@ static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock) {     DEBUGLOG(4, "LZ4HC_setExternalDict(%p, %p)", ctxPtr, newBlock);-    if (ctxPtr->end >= ctxPtr->base + ctxPtr->dictLimit + 4)+    if (ctxPtr->end >= ctxPtr->prefixStart + 4)         LZ4HC_Insert (ctxPtr, ctxPtr->end-3);   /* Referencing remaining dictionary content */      /* Only one memory segment for extDict, so any previous extDict is lost at this stage */     ctxPtr->lowLimit  = ctxPtr->dictLimit;-    ctxPtr->dictLimit = (U32)(ctxPtr->end - ctxPtr->base);-    ctxPtr->dictBase  = ctxPtr->base;-    ctxPtr->base = newBlock - ctxPtr->dictLimit;+    ctxPtr->dictStart  = ctxPtr->prefixStart;+    ctxPtr->dictLimit += (U32)(ctxPtr->end - ctxPtr->prefixStart);+    ctxPtr->prefixStart = newBlock;     ctxPtr->end  = newBlock;     ctxPtr->nextToUpdate = ctxPtr->dictLimit;   /* match referencing will resume from there */ @@ -1109,11 +1119,11 @@                 LZ4_streamHCPtr, src, *srcSizePtr, limit);     assert(ctxPtr != NULL);     /* auto-init if forgotten */-    if (ctxPtr->base == NULL) LZ4HC_init_internal (ctxPtr, (const BYTE*) src);+    if (ctxPtr->prefixStart == NULL) LZ4HC_init_internal (ctxPtr, (const BYTE*) src);      /* Check overflow */-    if ((size_t)(ctxPtr->end - ctxPtr->base) > 2 GB) {-        size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->base) - ctxPtr->dictLimit;+    if ((size_t)(ctxPtr->end - ctxPtr->prefixStart) + ctxPtr->dictLimit > 2 GB) {+        size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->prefixStart);         if (dictSize > 64 KB) dictSize = 64 KB;         LZ4_loadDictHC(LZ4_streamHCPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize);     }@@ -1124,13 +1134,16 @@      /* Check overlapping input/dictionary space */     {   const BYTE* sourceEnd = (const BYTE*) src + *srcSizePtr;-        const BYTE* const dictBegin = ctxPtr->dictBase + ctxPtr->lowLimit;-        const BYTE* const dictEnd   = ctxPtr->dictBase + ctxPtr->dictLimit;+        const BYTE* const dictBegin = ctxPtr->dictStart;+        const BYTE* const dictEnd   = ctxPtr->dictStart + (ctxPtr->dictLimit - ctxPtr->lowLimit);         if ((sourceEnd > dictBegin) && ((const BYTE*)src < dictEnd)) {             if (sourceEnd > dictEnd) sourceEnd = dictEnd;-            ctxPtr->lowLimit = (U32)(sourceEnd - ctxPtr->dictBase);-            if (ctxPtr->dictLimit - ctxPtr->lowLimit < 4) ctxPtr->lowLimit = ctxPtr->dictLimit;-    }   }+            ctxPtr->lowLimit += (U32)(sourceEnd - ctxPtr->dictStart);+            ctxPtr->dictStart += (U32)(sourceEnd - ctxPtr->dictStart);+            if (ctxPtr->dictLimit - ctxPtr->lowLimit < 4) {+                ctxPtr->lowLimit = ctxPtr->dictLimit;+                ctxPtr->dictStart = ctxPtr->prefixStart;+    }   }   }      return LZ4HC_compress_generic (ctxPtr, src, dst, srcSizePtr, dstCapacity, ctxPtr->compressionLevel, limit); }@@ -1158,7 +1171,7 @@ int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize) {     LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse;-    int const prefixSize = (int)(streamPtr->end - (streamPtr->base + streamPtr->dictLimit));+    int const prefixSize = (int)(streamPtr->end - streamPtr->prefixStart);     DEBUGLOG(5, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize);     assert(prefixSize >= 0);     if (dictSize > 64 KB) dictSize = 64 KB;@@ -1166,12 +1179,13 @@     if (dictSize > prefixSize) dictSize = prefixSize;     if (safeBuffer == NULL) assert(dictSize == 0);     if (dictSize > 0)-        memmove(safeBuffer, streamPtr->end - dictSize, dictSize);-    {   U32 const endIndex = (U32)(streamPtr->end - streamPtr->base);+        LZ4_memmove(safeBuffer, streamPtr->end - dictSize, dictSize);+    {   U32 const endIndex = (U32)(streamPtr->end - streamPtr->prefixStart) + streamPtr->dictLimit;         streamPtr->end = (const BYTE*)safeBuffer + dictSize;-        streamPtr->base = streamPtr->end - endIndex;+        streamPtr->prefixStart = streamPtr->end - dictSize;         streamPtr->dictLimit = endIndex - (U32)dictSize;         streamPtr->lowLimit = endIndex - (U32)dictSize;+        streamPtr->dictStart = streamPtr->prefixStart;         if (streamPtr->nextToUpdate < streamPtr->dictLimit)             streamPtr->nextToUpdate = streamPtr->dictLimit;     }@@ -1199,7 +1213,7 @@   /* Deprecated streaming functions */-int LZ4_sizeofStreamStateHC(void) { return LZ4_STREAMHCSIZE; }+int LZ4_sizeofStreamStateHC(void) { return sizeof(LZ4_streamHC_t); }  /* state is presumed correctly sized, aka >= sizeof(LZ4_streamHC_t)  * @return : 0 on success, !=0 if error */@@ -1211,6 +1225,7 @@     return 0; } +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) void* LZ4_createHC (const char* inputBuffer) {     LZ4_streamHC_t* const hc4 = LZ4_createStreamHC();@@ -1225,6 +1240,7 @@     FREEMEM(LZ4HC_Data);     return 0; }+#endif  int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int cLevel) {@@ -1238,11 +1254,11 @@  char* LZ4_slideInputBufferHC(void* LZ4HC_Data) {-    LZ4_streamHC_t *ctx = (LZ4_streamHC_t*)LZ4HC_Data;-    const BYTE *bufferStart = ctx->internal_donotuse.base + ctx->internal_donotuse.lowLimit;+    LZ4_streamHC_t* const ctx = (LZ4_streamHC_t*)LZ4HC_Data;+    const BYTE* bufferStart = ctx->internal_donotuse.prefixStart - ctx->internal_donotuse.dictLimit + ctx->internal_donotuse.lowLimit;     LZ4_resetStreamHC_fast(ctx, ctx->internal_donotuse.compressionLevel);     /* avoid const char * -> char * conversion warning :( */-    return (char *)(uptrval)bufferStart;+    return (char*)(uptrval)bufferStart; }  @@ -1325,7 +1341,7 @@ {     int retval = 0; #define TRAILING_LITERALS 3-#ifdef LZ4HC_HEAPMODE+#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1     LZ4HC_optimal_t* const opt = (LZ4HC_optimal_t*)ALLOC(sizeof(LZ4HC_optimal_t) * (LZ4_OPT_NUM + TRAILING_LITERALS)); #else     LZ4HC_optimal_t opt[LZ4_OPT_NUM + TRAILING_LITERALS];   /* ~64 KB, which is a bit large for stack... */@@ -1343,7 +1359,7 @@     const BYTE* ovref = NULL;      /* init */-#ifdef LZ4HC_HEAPMODE+#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1     if (opt == NULL) goto _return_label; #endif     DEBUGLOG(5, "LZ4HC_compress_optimal(dst=%p, dstCapa=%u)", dst, (unsigned)dstCapacity);@@ -1575,7 +1591,7 @@          } else {              *op++ = (BYTE)(lastRunSize << ML_BITS);          }-         memcpy(op, anchor, lastRunSize);+         LZ4_memcpy(op, anchor, lastRunSize);          op += lastRunSize;      } @@ -1608,7 +1624,7 @@      goto _last_literals; } _return_label:-#ifdef LZ4HC_HEAPMODE+#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1      FREEMEM(opt); #endif      return retval;
cbits/lz4hc.h view
@@ -1,7 +1,7 @@ /*    LZ4 HC - High Compression Mode of LZ4    Header File-   Copyright (C) 2011-2017, Yann Collet.+   Copyright (C) 2011-2020, Yann Collet.    BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)     Redistribution and use in source and binary forms, with or without@@ -198,14 +198,17 @@ #define LZ4HC_HASH_MASK (LZ4HC_HASHTABLESIZE - 1)  +/* Never ever use these definitions directly !+ * Declare or allocate an LZ4_streamHC_t instead.+**/ typedef struct LZ4HC_CCtx_internal LZ4HC_CCtx_internal; struct LZ4HC_CCtx_internal {     LZ4_u32   hashTable[LZ4HC_HASHTABLESIZE];     LZ4_u16   chainTable[LZ4HC_MAXD];     const LZ4_byte* end;       /* next block here to continue on current prefix */-    const LZ4_byte* base;      /* All index relative to this position */-    const LZ4_byte* dictBase;  /* alternate base for extDict */+    const LZ4_byte* prefixStart;  /* Indexes relative to this position */+    const LZ4_byte* dictStart; /* alternate reference for extDict */     LZ4_u32   dictLimit;       /* below that point, need extDict */     LZ4_u32   lowLimit;        /* below that point, no more dict */     LZ4_u32   nextToUpdate;    /* index from which to continue dictionary update */@@ -216,20 +219,15 @@     const LZ4HC_CCtx_internal* dictCtx; }; --/* Do not use these definitions directly !- * Declare or allocate an LZ4_streamHC_t instead.- */-#define LZ4_STREAMHCSIZE       262200  /* static size, for inter-version compatibility */-#define LZ4_STREAMHCSIZE_VOIDP (LZ4_STREAMHCSIZE / sizeof(void*))+#define LZ4_STREAMHC_MINSIZE  262200  /* static size, for inter-version compatibility */ union LZ4_streamHC_u {-    void* table[LZ4_STREAMHCSIZE_VOIDP];+    char minStateSize[LZ4_STREAMHC_MINSIZE];     LZ4HC_CCtx_internal internal_donotuse; }; /* previously typedef'd to LZ4_streamHC_t */  /* LZ4_streamHC_t :  * This structure allows static allocation of LZ4 HC streaming state.- * This can be used to allocate statically, on state, or as part of a larger structure.+ * This can be used to allocate statically on stack, or as part of a larger structure.  *  * Such state **must** be initialized using LZ4_initStreamHC() before first use.  *@@ -244,7 +242,7 @@  * Required before first use of a statically allocated LZ4_streamHC_t.  * Before v1.9.0 : use LZ4_resetStreamHC() instead  */-LZ4LIB_API LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size);+LZ4LIB_API LZ4_streamHC_t* LZ4_initStreamHC(void* buffer, size_t size);   /*-************************************@@ -272,9 +270,11 @@  * LZ4_slideInputBufferHC() will truncate the history of the stream, rather  * than preserve a window-sized chunk of history.  */+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4_DEPRECATED("use LZ4_createStreamHC() instead") LZ4LIB_API void* LZ4_createHC (const char* inputBuffer);-LZ4_DEPRECATED("use LZ4_saveDictHC() instead") LZ4LIB_API     char* LZ4_slideInputBufferHC (void* LZ4HC_Data); LZ4_DEPRECATED("use LZ4_freeStreamHC() instead") LZ4LIB_API   int   LZ4_freeHC (void* LZ4HC_Data);+#endif+LZ4_DEPRECATED("use LZ4_saveDictHC() instead") LZ4LIB_API     char* LZ4_slideInputBufferHC (void* LZ4HC_Data); LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC2_continue               (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel); LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel); LZ4_DEPRECATED("use LZ4_createStreamHC() instead") LZ4LIB_API int   LZ4_sizeofStreamStateHC(void);@@ -305,7 +305,7 @@  * They should not be linked from DLL,  * as there is no guarantee of API stability yet.  * Prototypes will be promoted to "stable" status- * after successfull usage in real-life scenarios.+ * after successful usage in real-life scenarios.  ***************************************************/ #ifdef LZ4_HC_STATIC_LINKING_ONLY   /* protection macro */ #ifndef LZ4_HC_SLO_098092834
lz4-hs.cabal view
@@ -1,6 +1,6 @@ cabal-version:   2.2 name:            lz4-hs-version:         0.1.5.1+version:         0.1.5.2 license:         BSD-3-Clause license-file:    LICENSE copyright:       Copyright: (c) 2020 Vanessa McHale
mem/Mem.hs view
@@ -14,10 +14,8 @@  decompressDump :: IO () decompressDump = withSystemTempDirectory "lz4" $-    \fp -> BSL.writeFile (fp </> "valgrind-3.15.0.tar") =<<-        (decompress <$> BSL.readFile "valgrind-3.15.0.tar.lz4")+    \fp -> BSL.writeFile (fp </> "valgrind-3.15.0.tar") . decompress =<< BSL.readFile "valgrind-3.15.0.tar.lz4"  compressDump :: IO () compressDump = withSystemTempDirectory "lz4" $-    \fp -> BSL.writeFile (fp </> "valgrind-3.15.0.tar.lz4") =<<-        (compress <$> BSL.readFile "valgrind-3.15.0.tar")+    \fp -> BSL.writeFile (fp </> "valgrind-3.15.0.tar.lz4") . compress =<< BSL.readFile "valgrind-3.15.0.tar"