lz4-bytes 0.1.2.0 → 0.2.0.0
raw patch · 14 files changed
+7635/−2821 lines, 14 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Lz4.Frame: decompressU :: Int -> Bytes -> Maybe ByteArray
+ Lz4.Frame: decompressU :: Int -> Bytes -> Either Int ByteArray
Files
- CHANGELOG.md +5/−0
- cbits/hs_lz4.c +99/−0
- cbits/lz4.c +2712/−2392
- cbits/lz4.h +183/−105
- cbits/lz4frame.c +2078/−0
- cbits/lz4frame.h +692/−0
- cbits/lz4hc.c +309/−216
- cbits/lz4hc.h +24/−49
- cbits/xxhash.c +1030/−0
- cbits/xxhash.h +328/−0
- lz4-bytes.cabal +8/−3
- src/Lz4/Frame.hs +86/−49
- src/Lz4/Internal.hs +30/−2
- test/Main.hs +51/−5
CHANGELOG.md view
@@ -1,5 +1,10 @@ # Revision history for lz4-bytes +## 0.2.0.0 -- 2025-07-07++* Make it possible to compress much larger buffers as a frame+* Correctly handle frames with multiple blocks during decompression+ ## 0.1.2.0 -- 2025-03-26 * Add `Lz4.Frame.decompressU`.
cbits/hs_lz4.c view
@@ -1,6 +1,8 @@ #include "Rts.h" #include "lz4.h"+#include "lz4frame.h" #include "lz4hc.h"+#include <string.h> HsInt hs_compress_fast ( const char* src@@ -42,6 +44,103 @@ r = (HsInt)LZ4_decompress_safe(src + soff, dst + doff, slen, dstCapacity); return r; }++// This implementation was copied adapted from the Python lz4.frame subpackage.+// This returns a zero on success. Here are the main changes made to the Python+// code:+// * This function is only called in contexts where the size of the decompressed+// data is communicated some other way, so we never need to grow the destination.+// * full_frame is removed because it is always true+// * This uses the unsafe FFI, so we can set stableDst to true unconditionally+// * We get rid of return_bytearray and return_bytes_read because we+// only return an indicator of whether or not the exact number of+// expected bytes were decompressed.+HsInt hs_decompress_frame(LZ4F_dctx * context, const char* const source, HsInt const source_off, HsInt const source_size, char* const destination, HsInt const decompressed_length)+{+ size_t source_remain = source_size;+ size_t source_read;+ const char * source_cursor;+ const char * source_end;+ char * destination_cursor;+ size_t destination_written;+ size_t result = 0;+ LZ4F_frameInfo_t frame_info;+ LZ4F_decompressOptions_t options = {0};+ int end_of_frame = 0;+ size_t destination_write = decompressed_length;++ options.stableDst = 1;+ options.skipChecksums = 1;++ source_cursor = source + source_off;+ source_end = source + source_off + source_size;+ source_remain = source_size;+ source_read = source_size;++ result = LZ4F_getFrameInfo (context, &frame_info, source_cursor, &source_read);+ if (LZ4F_isError (result)) { return result; }++ /* Advance the source_cursor pointer past the header - the call to+ getFrameInfo above replaces the passed source_read value with the+ number of bytes read. Also reduce source_remain accordingly. */+ source_cursor += source_read;+ source_remain -= source_read;++ /* If the uncompressed content size is available, we'll use that to size+ the destination buffer. Otherwise, guess at twice the remaining source+ source as a starting point, and adjust if needed. */+ if (frame_info.contentSize > 0)+ {+ if (frame_info.contentSize != decompressed_length) {+ return 20000;+ }+ }++ source_read = source_remain;++ destination_cursor = destination;+ destination_written = 0;++ while (1)+ {+ /* Decompress from the source string and write to the destination+ until there's no more source string to read, or until we've reached the+ frame end.++ On calling LZ4F_decompress, source_read is set to the remaining length+ of source available to read. On return, source_read is set to the+ actual number of bytes read from source, which may be less than+ available. NB: LZ4F_decompress does not explicitly fail on empty input.++ On calling LZ4F_decompress, destination_write is the number of bytes in+ destination available for writing. On exit, destination_write is set to+ the actual number of bytes written to destination. */+ result = LZ4F_decompress (context, destination_cursor, &destination_write, source_cursor, &source_read, &options);++ if (LZ4F_isError (result)) { return result; }++ destination_written += destination_write;+ source_cursor += source_read;+ source_read = source_end - source_cursor;++ if (result == 0) {+ end_of_frame = 1;+ break;+ }+ else if (source_cursor == source_end) { break; }+ else if (destination_written == decompressed_length) { break; }+ destination_cursor = destination + destination_written;+ destination_write = decompressed_length - destination_written;+ }++ if (result > 0) { return 20001; }+ if (end_of_frame != 1) { return 20002; }+ if (LZ4F_isError (result)) { return 20003; }++ return 0;+}++ // Started on streaming compression and then realized it has some // restrictions that make it pretty hard to wrap in Haskell. Read
cbits/lz4.c view
@@ -1,2397 +1,2717 @@ /* LZ4 - Fast LZ compression algorithm- Copyright (C) 2011-present, 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- modification, are permitted provided that the following conditions are- met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.- * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following disclaimer- in the documentation and/or other materials provided with the- distribution.-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.-- You can contact the author at :- - LZ4 homepage : http://www.lz4.org- - LZ4 source repository : https://github.com/lz4/lz4-*/--/*-************************************-* Tuning parameters-**************************************/-/*- * LZ4_HEAPMODE :- * Select how default compression functions will allocate memory for their hash table,- * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).- */-#ifndef LZ4_HEAPMODE-# define LZ4_HEAPMODE 0-#endif--/*- * ACCELERATION_DEFAULT :- * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0- */-#define ACCELERATION_DEFAULT 1---/*-************************************-* CPU Feature Detection-**************************************/-/* LZ4_FORCE_MEMORY_ACCESS- * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.- * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.- * The below switch allow to select different access method for improved performance.- * Method 0 (default) : use `memcpy()`. Safe and portable.- * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).- * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.- * Method 2 : direct access. This method is portable but violate C standard.- * It can generate buggy code on targets which assembly generation depends on alignment.- * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)- * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.- * Prefer these methods in priority order (0 > 1 > 2)- */-#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */-# if defined(__GNUC__) && \- ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \- || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )-# define LZ4_FORCE_MEMORY_ACCESS 2-# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__)-# define LZ4_FORCE_MEMORY_ACCESS 1-# endif-#endif--/*- * LZ4_FORCE_SW_BITCOUNT- * Define this parameter if your target system or compiler does not support hardware bit count- */-#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */-# define LZ4_FORCE_SW_BITCOUNT-#endif----/*-************************************-* Dependency-**************************************/-/*- * LZ4_SRC_INCLUDED:- * Amalgamation flag, whether lz4.c is included- */-#ifndef LZ4_SRC_INCLUDED-# define LZ4_SRC_INCLUDED 1-#endif--#ifndef LZ4_STATIC_LINKING_ONLY-#define LZ4_STATIC_LINKING_ONLY-#endif--#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS-#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */-#endif--#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */-#include "lz4.h"-/* see also "memory routines" below */---/*-************************************-* Compiler Options-**************************************/-#ifdef _MSC_VER /* Visual Studio */-# include <intrin.h>-# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */-# pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */-#endif /* _MSC_VER */--#ifndef LZ4_FORCE_INLINE-# ifdef _MSC_VER /* Visual Studio */-# define LZ4_FORCE_INLINE static __forceinline-# else-# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */-# ifdef __GNUC__-# define LZ4_FORCE_INLINE static inline __attribute__((always_inline))-# else-# define LZ4_FORCE_INLINE static inline-# endif-# else-# define LZ4_FORCE_INLINE static-# endif /* __STDC_VERSION__ */-# endif /* _MSC_VER */-#endif /* LZ4_FORCE_INLINE */--/* LZ4_FORCE_O2_GCC_PPC64LE and LZ4_FORCE_O2_INLINE_GCC_PPC64LE- * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8,- * together with a simple 8-byte copy loop as a fall-back path.- * However, this optimization hurts the decompression speed by >30%,- * because the execution does not go to the optimized loop- * for typical compressible data, and all of the preamble checks- * before going to the fall-back path become useless overhead.- * This optimization happens only with the -O3 flag, and -O2 generates- * a simple 8-byte copy loop.- * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8- * functions are annotated with __attribute__((optimize("O2"))),- * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute- * of LZ4_wildCopy8 does not affect the compression speed.- */-#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__)-# define LZ4_FORCE_O2_GCC_PPC64LE __attribute__((optimize("O2")))-# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE __attribute__((optimize("O2"))) LZ4_FORCE_INLINE-#else-# define LZ4_FORCE_O2_GCC_PPC64LE-# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE static-#endif--#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)-# define expect(expr,value) (__builtin_expect ((expr),(value)) )-#else-# define expect(expr,value) (expr)-#endif--#ifndef likely-#define likely(expr) expect((expr) != 0, 1)-#endif-#ifndef unlikely-#define unlikely(expr) expect((expr) != 0, 0)-#endif---/*-************************************-* Memory routines-**************************************/-#include <stdlib.h> /* malloc, calloc, free */-#define ALLOC(s) malloc(s)-#define ALLOC_AND_ZERO(s) calloc(1,s)-#define FREEMEM(p) free(p)-#include <string.h> /* memset, memcpy */-#define MEM_INIT(p,v,s) memset((p),(v),(s))---/*-************************************-* Common Constants-**************************************/-#define MINMATCH 4--#define WILDCOPYLENGTH 8-#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */-#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */-#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */-#define FASTLOOP_SAFE_DISTANCE 64-static const int LZ4_minLength = (MFLIMIT+1);--#define KB *(1 <<10)-#define MB *(1 <<20)-#define GB *(1U<<30)--#define LZ4_DISTANCE_ABSOLUTE_MAX 65535-#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */-# error "LZ4_DISTANCE_MAX is too big : must be <= 65535"-#endif--#define ML_BITS 4-#define ML_MASK ((1U<<ML_BITS)-1)-#define RUN_BITS (8-ML_BITS)-#define RUN_MASK ((1U<<RUN_BITS)-1)---/*-************************************-* Error detection-**************************************/-#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)-# include <assert.h>-#else-# ifndef assert-# define assert(condition) ((void)0)-# endif-#endif--#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */--#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2)-# include <stdio.h>- static int g_debuglog_enable = 1;-# define DEBUGLOG(l, ...) { \- if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \- fprintf(stderr, __FILE__ ": "); \- fprintf(stderr, __VA_ARGS__); \- fprintf(stderr, " \n"); \- } }-#else-# define DEBUGLOG(l, ...) {} /* disabled */-#endif---/*-************************************-* Types-**************************************/-#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)-# include <stdint.h>- typedef uint8_t BYTE;- typedef uint16_t U16;- typedef uint32_t U32;- typedef int32_t S32;- typedef uint64_t U64;- typedef uintptr_t uptrval;-#else-# include <limits.h>-# if UINT_MAX != 4294967295UL-# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4"-# endif- typedef unsigned char BYTE;- typedef unsigned short U16;- typedef unsigned int U32;- typedef signed int S32;- typedef unsigned long long U64;- typedef size_t uptrval; /* generally true, except OpenVMS-64 */-#endif--#if defined(__x86_64__)- typedef U64 reg_t; /* 64-bits in x32 mode */-#else- typedef size_t reg_t; /* 32-bits in x32 mode */-#endif--typedef enum {- notLimited = 0,- limitedOutput = 1,- fillOutput = 2-} limitedOutput_directive;---/*-************************************-* Reading and writing into memory-**************************************/-static unsigned LZ4_isLittleEndian(void)-{- const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */- return one.c[0];-}---#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)-/* lie to the compiler about data alignment; use with caution */--static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }-static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }-static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }--static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }-static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }--#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)--/* __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;--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 void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; }-static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; }--#else /* safe and portable access using memcpy() */--static U16 LZ4_read16(const void* memPtr)-{- U16 val; memcpy(&val, memPtr, sizeof(val)); return val;-}--static U32 LZ4_read32(const void* memPtr)-{- U32 val; memcpy(&val, memPtr, sizeof(val)); return val;-}--static reg_t LZ4_read_ARCH(const void* memPtr)-{- reg_t val; memcpy(&val, memPtr, sizeof(val)); return val;-}--static void LZ4_write16(void* memPtr, U16 value)-{- memcpy(memPtr, &value, sizeof(value));-}--static void LZ4_write32(void* memPtr, U32 value)-{- memcpy(memPtr, &value, sizeof(value));-}--#endif /* LZ4_FORCE_MEMORY_ACCESS */---static U16 LZ4_readLE16(const void* memPtr)-{- if (LZ4_isLittleEndian()) {- return LZ4_read16(memPtr);- } else {- const BYTE* p = (const BYTE*)memPtr;- return (U16)((U16)p[0] + (p[1]<<8));- }-}--static void LZ4_writeLE16(void* memPtr, U16 value)-{- if (LZ4_isLittleEndian()) {- LZ4_write16(memPtr, value);- } else {- BYTE* p = (BYTE*)memPtr;- p[0] = (BYTE) value;- p[1] = (BYTE)(value>>8);- }-}--/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */-LZ4_FORCE_O2_INLINE_GCC_PPC64LE-void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd)-{- BYTE* d = (BYTE*)dstPtr;- const BYTE* s = (const BYTE*)srcPtr;- BYTE* const e = (BYTE*)dstEnd;-- do { memcpy(d,s,8); d+=8; s+=8; } while (d<e);-}--static const unsigned inc32table[8] = {0, 1, 2, 1, 0, 4, 4, 4};-static const int dec64table[8] = {0, 0, 0, -1, -4, 1, 2, 3};---#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(__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 */-# define LZ4_FAST_DEC_LOOP 1-# else-# define LZ4_FAST_DEC_LOOP 0-# endif-#endif--#if LZ4_FAST_DEC_LOOP--LZ4_FORCE_O2_INLINE_GCC_PPC64LE void-LZ4_memcpy_using_offset_base(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)-{- if (offset < 8) {- dstPtr[0] = srcPtr[0];- dstPtr[1] = srcPtr[1];- dstPtr[2] = srcPtr[2];- dstPtr[3] = srcPtr[3];- srcPtr += inc32table[offset];- memcpy(dstPtr+4, srcPtr, 4);- srcPtr -= dec64table[offset];- dstPtr += 8;- } else {- memcpy(dstPtr, srcPtr, 8);- dstPtr += 8;- srcPtr += 8;- }-- LZ4_wildCopy8(dstPtr, srcPtr, dstEnd);-}--/* customized variant of memcpy, which can overwrite up to 32 bytes beyond dstEnd- * this version copies two times 16 bytes (instead of one time 32 bytes)- * because it must be compatible with offsets >= 16. */-LZ4_FORCE_O2_INLINE_GCC_PPC64LE void-LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd)-{- BYTE* d = (BYTE*)dstPtr;- const BYTE* s = (const BYTE*)srcPtr;- BYTE* const e = (BYTE*)dstEnd;-- do { memcpy(d,s,16); memcpy(d+16,s+16,16); d+=32; s+=32; } while (d<e);-}--/* LZ4_memcpy_using_offset() presumes :- * - dstEnd >= dstPtr + MINMATCH- * - there is at least 8 bytes available to write after dstEnd */-LZ4_FORCE_O2_INLINE_GCC_PPC64LE void-LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)-{- BYTE v[8];-- assert(dstEnd >= dstPtr + MINMATCH);- LZ4_write32(dstPtr, 0); /* silence an msan warning when offset==0 */-- switch(offset) {- case 1:- memset(v, *srcPtr, 8);- break;- case 2:- memcpy(v, srcPtr, 2);- memcpy(&v[2], srcPtr, 2);- memcpy(&v[4], &v[0], 4);- break;- case 4:- memcpy(v, srcPtr, 4);- memcpy(&v[4], srcPtr, 4);- break;- default:- LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset);- return;- }-- memcpy(dstPtr, v, 8);- dstPtr += 8;- while (dstPtr < dstEnd) {- memcpy(dstPtr, v, 8);- dstPtr += 8;- }-}-#endif---/*-************************************-* Common functions-**************************************/-static unsigned LZ4_NbCommonBytes (reg_t val)-{- if (LZ4_isLittleEndian()) {- if (sizeof(val)==8) {-# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)- unsigned long r = 0;- _BitScanForward64( &r, (U64)val );- return (int)(r>>3);-# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)- return (unsigned)__builtin_ctzll((U64)val) >> 3;-# else- static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,- 0, 3, 1, 3, 1, 4, 2, 7,- 0, 2, 3, 6, 1, 5, 3, 5,- 1, 3, 4, 4, 2, 5, 6, 7,- 7, 0, 1, 2, 3, 3, 4, 6,- 2, 6, 5, 5, 3, 4, 5, 6,- 7, 1, 2, 4, 6, 4, 4, 5,- 7, 2, 6, 5, 7, 6, 7, 7 };- return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];-# endif- } else /* 32 bits */ {-# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)- unsigned long r;- _BitScanForward( &r, (U32)val );- return (int)(r>>3);-# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)- return (unsigned)__builtin_ctz((U32)val) >> 3;-# else- static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,- 3, 2, 2, 1, 3, 2, 0, 1,- 3, 3, 1, 2, 2, 2, 2, 0,- 3, 1, 2, 0, 1, 0, 1, 1 };- return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];-# endif- }- } else /* Big Endian CPU */ {- if (sizeof(val)==8) { /* 64-bits */-# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)- unsigned long r = 0;- _BitScanReverse64( &r, val );- return (unsigned)(r>>3);-# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)- return (unsigned)__builtin_clzll((U64)val) >> 3;-# else- static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits.- Just to avoid some static analyzer complaining about shift by 32 on 32-bits target.- Note that this code path is never triggered in 32-bits mode. */- unsigned r;- if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; }- if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }- r += (!val);- return r;-# endif- } else /* 32 bits */ {-# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)- unsigned long r = 0;- _BitScanReverse( &r, (unsigned long)val );- return (unsigned)(r>>3);-# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)- return (unsigned)__builtin_clz((U32)val) >> 3;-# else- unsigned r;- if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }- r += (!val);- return r;-# endif- }- }-}--#define STEPSIZE sizeof(reg_t)-LZ4_FORCE_INLINE-unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)-{- const BYTE* const pStart = pIn;-- if (likely(pIn < pInLimit-(STEPSIZE-1))) {- reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);- if (!diff) {- pIn+=STEPSIZE; pMatch+=STEPSIZE;- } else {- return LZ4_NbCommonBytes(diff);- } }-- while (likely(pIn < pInLimit-(STEPSIZE-1))) {- reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);- if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }- pIn += LZ4_NbCommonBytes(diff);- return (unsigned)(pIn - pStart);- }-- if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }- if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }- if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;- return (unsigned)(pIn - pStart);-}---#ifndef LZ4_COMMONDEFS_ONLY-/*-************************************-* Local Constants-**************************************/-static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));-static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */---/*-************************************-* Local Structures and types-**************************************/-typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;--/**- * This enum distinguishes several different modes of accessing previous- * content in the stream.- *- * - noDict : There is no preceding content.- * - withPrefix64k : Table entries up to ctx->dictSize before the current blob- * blob being compressed are valid and refer to the preceding- * content (of length ctx->dictSize), which is available- * contiguously preceding in memory the content currently- * being compressed.- * - 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- * 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- * content, and matches are found by looking in the ctx- * ->dictCtx->hashTable.- */-typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;-typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;---/*-************************************-* Local Utils-**************************************/-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() { return LZ4_STREAMSIZE; }---/*-************************************-* Internal Definitions used in Tests-**************************************/-#if defined (__cplusplus)-extern "C" {-#endif--int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize);--int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,- int compressedSize, int maxOutputSize,- const void* dictStart, size_t dictSize);--#if defined (__cplusplus)-}-#endif--/*-******************************-* Compression functions-********************************/-static U32 LZ4_hash4(U32 sequence, tableType_t const tableType)-{- if (tableType == byU16)- return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));- else- return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));-}--static U32 LZ4_hash5(U64 sequence, tableType_t const tableType)-{- const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;- if (LZ4_isLittleEndian()) {- const U64 prime5bytes = 889523592379ULL;- return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));- } else {- const U64 prime8bytes = 11400714785074694791ULL;- return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));- }-}--LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)-{- if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);- return LZ4_hash4(LZ4_read32(p), tableType);-}--static void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType)-{- switch (tableType)- {- default: /* fallthrough */- case clearedTable: { /* illegal! */ assert(0); return; }- case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; }- case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; }- case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; }- }-}--static void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)-{- switch (tableType)- {- default: /* fallthrough */- case clearedTable: /* fallthrough */- case byPtr: { /* illegal! */ assert(0); return; }- case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }- case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; }- }-}--static void LZ4_putPositionOnHash(const BYTE* p, U32 h,- void* tableBase, tableType_t const tableType,- const BYTE* srcBase)-{- switch (tableType)- {- case clearedTable: { /* illegal! */ assert(0); return; }- case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }- case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }- case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }- }-}--LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)-{- U32 const h = LZ4_hashPosition(p, tableType);- LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);-}--/* LZ4_getIndexOnHash() :- * Index of match position registered in hash table.- * hash position must be calculated by using base+index, or dictBase+index.- * Assumption 1 : only valid if tableType == byU32 or byU16.- * Assumption 2 : h is presumed valid (within limits of hash table)- */-static U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)-{- LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2);- if (tableType == byU32) {- const U32* const hashTable = (const U32*) tableBase;- assert(h < (1U << (LZ4_MEMORY_USAGE-2)));- return hashTable[h];- }- if (tableType == byU16) {- const U16* const hashTable = (const U16*) tableBase;- assert(h < (1U << (LZ4_MEMORY_USAGE-1)));- return hashTable[h];- }- assert(0); return 0; /* forbidden case */-}--static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase)-{- if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }- if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; }- { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */-}--LZ4_FORCE_INLINE const BYTE*-LZ4_getPosition(const BYTE* p,- const void* tableBase, tableType_t tableType,- const BYTE* srcBase)-{- U32 const h = LZ4_hashPosition(p, tableType);- return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);-}--LZ4_FORCE_INLINE void-LZ4_prepareTable(LZ4_stream_t_internal* const cctx,- const int inputSize,- const tableType_t tableType) {- /* If compression failed during the previous step, then the context- * is marked as dirty, therefore, it has to be fully reset.- */- if (cctx->dirty) {- DEBUGLOG(5, "LZ4_prepareTable: Full reset for %p", cctx);- MEM_INIT(cctx, 0, sizeof(LZ4_stream_t_internal));- return;- }-- /* If the table hasn't been used, it's guaranteed to be zeroed out, and is- * therefore safe to use no matter what mode we're in. Otherwise, we figure- * out if it's safe to leave as is or whether it needs to be reset.- */- if (cctx->tableType != clearedTable) {- assert(inputSize >= 0);- if (cctx->tableType != tableType- || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU)- || ((tableType == byU32) && cctx->currentOffset > 1 GB)- || tableType == byPtr- || inputSize >= 4 KB)- {- DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx);- MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);- cctx->currentOffset = 0;- cctx->tableType = clearedTable;- } else {- DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)");- }- }-- /* 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");- cctx->currentOffset += 64 KB;- }-- /* Finally, clear history */- cctx->dictCtx = NULL;- cctx->dictionary = NULL;- cctx->dictSize = 0;-}--/** LZ4_compress_generic() :- inlined, to ensure branches are decided at compilation time */-LZ4_FORCE_INLINE int LZ4_compress_generic(- LZ4_stream_t_internal* const cctx,- const char* const source,- char* const dest,- const int inputSize,- int *inputConsumed, /* only written when outputDirective == fillOutput */- const int maxOutputSize,- const limitedOutput_directive outputDirective,- const tableType_t tableType,- const dict_directive dictDirective,- const dictIssue_directive dictIssue,- const int acceleration)-{- int result;- const BYTE* ip = (const BYTE*) source;-- U32 const startIndex = cctx->currentOffset;- const BYTE* base = (const BYTE*) source - startIndex;- const BYTE* lowLimit;-- const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;- const BYTE* const dictionary =- dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;- const U32 dictSize =- dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;- const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */-- int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);- U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */- const BYTE* const dictEnd = dictionary + dictSize;- const BYTE* anchor = (const BYTE*) source;- const BYTE* const iend = ip + inputSize;- const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;- const BYTE* const matchlimit = iend - LASTLITERALS;-- /* the dictCtx currentOffset is indexed on the start of the dictionary,- * while a dictionary in the current context precedes the currentOffset */- const BYTE* dictBase = (dictDirective == usingDictCtx) ?- dictionary + dictSize - dictCtx->currentOffset :- dictionary + dictSize - startIndex;-- BYTE* op = (BYTE*) dest;- BYTE* const olimit = op + maxOutputSize;-- U32 offset = 0;- U32 forwardH;-- DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, tableType=%u", inputSize, tableType);- /* If init conditions are not met, we don't have to mark stream- * as having dirty context, since no action was taken yet */- if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */- if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported inputSize, too large (or negative) */- if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */- if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */- assert(acceleration >= 1);-- lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);-- /* Update context state */- if (dictDirective == usingDictCtx) {- /* Subsequent linked blocks can't use the dictionary. */- /* Instead, they use the block we just compressed. */- cctx->dictCtx = NULL;- cctx->dictSize = (U32)inputSize;- } else {- cctx->dictSize += (U32)inputSize;- }- cctx->currentOffset += (U32)inputSize;- cctx->tableType = (U16)tableType;-- if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */-- /* First Byte */- LZ4_putPosition(ip, cctx->hashTable, tableType, base);- ip++; forwardH = LZ4_hashPosition(ip, tableType);-- /* Main Loop */- for ( ; ; ) {- const BYTE* match;- BYTE* token;- const BYTE* filledIp;-- /* Find a match */- if (tableType == byPtr) {- const BYTE* forwardIp = ip;- int step = 1;- int searchMatchNb = acceleration << LZ4_skipTrigger;- do {- U32 const h = forwardH;- ip = forwardIp;- forwardIp += step;- step = (searchMatchNb++ >> LZ4_skipTrigger);-- if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;- assert(ip < mflimitPlusOne);-- match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);- forwardH = LZ4_hashPosition(forwardIp, tableType);- LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);-- } while ( (match+LZ4_DISTANCE_MAX < ip)- || (LZ4_read32(match) != LZ4_read32(ip)) );-- } else { /* byU32, byU16 */-- const BYTE* forwardIp = ip;- int step = 1;- int searchMatchNb = acceleration << LZ4_skipTrigger;- do {- U32 const h = forwardH;- U32 const current = (U32)(forwardIp - base);- U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);- assert(matchIndex <= current);- assert(forwardIp - base < (ptrdiff_t)(2 GB - 1));- ip = forwardIp;- forwardIp += step;- step = (searchMatchNb++ >> LZ4_skipTrigger);-- if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;- assert(ip < mflimitPlusOne);-- if (dictDirective == usingDictCtx) {- if (matchIndex < startIndex) {- /* there was no match, try the dictionary */- assert(tableType == byU32);- matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);- match = dictBase + matchIndex;- matchIndex += dictDelta; /* make dictCtx index comparable with current context */- lowLimit = dictionary;- } else {- match = base + matchIndex;- lowLimit = (const BYTE*)source;- }- } else if (dictDirective==usingExtDict) {- if (matchIndex < startIndex) {- DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex);- assert(startIndex - matchIndex >= MINMATCH);- match = dictBase + matchIndex;- lowLimit = dictionary;- } else {- match = base + matchIndex;- lowLimit = (const BYTE*)source;- }- } else { /* single continuous memory segment */- match = base + matchIndex;- }- forwardH = LZ4_hashPosition(forwardIp, tableType);- LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);-- DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex);- if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */- assert(matchIndex < current);- if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX))- && (matchIndex+LZ4_DISTANCE_MAX < current)) {- continue;- } /* too far */- assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */-- if (LZ4_read32(match) == LZ4_read32(ip)) {- if (maybe_extMem) offset = current - matchIndex;- break; /* match found */- }-- } while(1);- }-- /* Catch up */- filledIp = ip;- while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }-- /* Encode Literals */- { unsigned const litLength = (unsigned)(ip - anchor);- token = op++;- if ((outputDirective == limitedOutput) && /* Check output buffer overflow */- (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) {- return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */- }- if ((outputDirective == fillOutput) &&- (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) {- op--;- goto _last_literals;- }- if (litLength >= RUN_MASK) {- int len = (int)(litLength - RUN_MASK);- *token = (RUN_MASK<<ML_BITS);- for(; len >= 255 ; len-=255) *op++ = 255;- *op++ = (BYTE)len;- }- else *token = (BYTE)(litLength<<ML_BITS);-- /* Copy Literals */- LZ4_wildCopy8(op, anchor, op+litLength);- op+=litLength;- DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i",- (int)(anchor-(const BYTE*)source), litLength, (int)(ip-(const BYTE*)source));- }--_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- * - 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- */-- if ((outputDirective == fillOutput) &&- (op + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit)) {- /* the match was too close to the end, rewind and go to last literals */- op = token;- goto _last_literals;- }-- /* Encode Offset */- if (maybe_extMem) { /* static test */- DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source));- assert(offset <= LZ4_DISTANCE_MAX && offset > 0);- LZ4_writeLE16(op, (U16)offset); op+=2;- } else {- DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match));- assert(ip-match <= LZ4_DISTANCE_MAX);- LZ4_writeLE16(op, (U16)(ip - match)); op+=2;- }-- /* Encode MatchLength */- { unsigned matchCode;-- if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx)- && (lowLimit==dictionary) /* match within extDict */ ) {- const BYTE* limit = ip + (dictEnd-match);- assert(dictEnd > match);- if (limit > matchlimit) limit = matchlimit;- matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);- ip += (size_t)matchCode + MINMATCH;- if (ip==limit) {- unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit);- matchCode += more;- ip += more;- }- DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH);- } else {- matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);- ip += (size_t)matchCode + MINMATCH;- DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH);- }-- if ((outputDirective) && /* Check output buffer overflow */- (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) {- if (outputDirective == fillOutput) {- /* Match description too long : reduce it */- U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255;- ip -= matchCode - newMatchCode;- assert(newMatchCode < matchCode);- matchCode = newMatchCode;- if (unlikely(ip <= filledIp)) {- /* We have already filled up to filledIp so if ip ends up less than filledIp- * we have positions in the hash table beyond the current position. This is- * a problem if we reuse the hash table. So we have to remove these positions- * from the hash table.- */- const BYTE* ptr;- DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip));- for (ptr = ip; ptr <= filledIp; ++ptr) {- U32 const h = LZ4_hashPosition(ptr, tableType);- LZ4_clearHash(h, cctx->hashTable, tableType);- }- }- } else {- assert(outputDirective == limitedOutput);- return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */- }- }- if (matchCode >= ML_MASK) {- *token += ML_MASK;- matchCode -= ML_MASK;- LZ4_write32(op, 0xFFFFFFFF);- while (matchCode >= 4*255) {- op+=4;- LZ4_write32(op, 0xFFFFFFFF);- matchCode -= 4*255;- }- op += matchCode / 255;- *op++ = (BYTE)(matchCode % 255);- } else- *token += (BYTE)(matchCode);- }- /* Ensure we have enough space for the last literals. */- assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit));-- anchor = ip;-- /* Test end of chunk */- if (ip >= mflimitPlusOne) break;-- /* Fill table */- LZ4_putPosition(ip-2, cctx->hashTable, tableType, base);-- /* Test next position */- if (tableType == byPtr) {-- match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);- LZ4_putPosition(ip, cctx->hashTable, tableType, base);- if ( (match+LZ4_DISTANCE_MAX >= ip)- && (LZ4_read32(match) == LZ4_read32(ip)) )- { token=op++; *token=0; goto _next_match; }-- } else { /* byU32, byU16 */-- U32 const h = LZ4_hashPosition(ip, tableType);- U32 const current = (U32)(ip-base);- U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);- assert(matchIndex < current);- if (dictDirective == usingDictCtx) {- if (matchIndex < startIndex) {- /* there was no match, try the dictionary */- matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);- match = dictBase + matchIndex;- lowLimit = dictionary; /* required for match length counter */- matchIndex += dictDelta;- } else {- match = base + matchIndex;- lowLimit = (const BYTE*)source; /* required for match length counter */- }- } else if (dictDirective==usingExtDict) {- if (matchIndex < startIndex) {- match = dictBase + matchIndex;- lowLimit = dictionary; /* required for match length counter */- } else {- match = base + matchIndex;- lowLimit = (const BYTE*)source; /* required for match length counter */- }- } else { /* single memory segment */- match = base + matchIndex;- }- LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);- assert(matchIndex < current);- if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)- && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current))- && (LZ4_read32(match) == LZ4_read32(ip)) ) {- token=op++;- *token=0;- if (maybe_extMem) offset = current - matchIndex;- DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i",- (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));- goto _next_match;- }- }-- /* Prepare next loop */- forwardH = LZ4_hashPosition(++ip, tableType);-- }--_last_literals:- /* Encode Last Literals */- { size_t lastRun = (size_t)(iend - anchor);- if ( (outputDirective) && /* Check output buffer overflow */- (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) {- if (outputDirective == fillOutput) {- /* adapt lastRun to fill 'dst' */- assert(olimit >= op);- lastRun = (size_t)(olimit-op) - 1;- lastRun -= (lastRun+240)/255;- } else {- assert(outputDirective == limitedOutput);- return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */- }- }- if (lastRun >= RUN_MASK) {- size_t accumulator = lastRun - RUN_MASK;- *op++ = RUN_MASK << ML_BITS;- for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;- *op++ = (BYTE) accumulator;- } else {- *op++ = (BYTE)(lastRun<<ML_BITS);- }- memcpy(op, anchor, lastRun);- ip = anchor + lastRun;- op += lastRun;- }-- if (outputDirective == fillOutput) {- *inputConsumed = (int) (((const char*)ip)-source);- }- DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, (int)(((char*)op) - dest));- result = (int)(((char*)op) - dest);- assert(result > 0);- return result;-}---int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)-{- LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse;- assert(ctx != NULL);- if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;- if (maxOutputSize >= LZ4_compressBound(inputSize)) {- if (inputSize < LZ4_64Klimit) {- return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration);- } else {- const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;- return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);- }- } else {- if (inputSize < LZ4_64Klimit) {- return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);- } else {- const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;- return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);- }- }-}--/**- * LZ4_compress_fast_extState_fastReset() :- * A variant of LZ4_compress_fast_extState().- *- * Using this variant avoids an expensive initialization step. It is only safe- * to call if the state buffer is known to be correctly initialized already- * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of- * "correctly initialized").- */-int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)-{- LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse;- if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;-- if (dstCapacity >= LZ4_compressBound(srcSize)) {- if (srcSize < LZ4_64Klimit) {- const tableType_t tableType = byU16;- LZ4_prepareTable(ctx, srcSize, tableType);- if (ctx->currentOffset) {- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration);- } else {- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);- }- } else {- const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;- LZ4_prepareTable(ctx, srcSize, tableType);- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);- }- } else {- if (srcSize < LZ4_64Klimit) {- const tableType_t tableType = byU16;- LZ4_prepareTable(ctx, srcSize, tableType);- if (ctx->currentOffset) {- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);- } else {- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);- }- } else {- const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;- LZ4_prepareTable(ctx, srcSize, tableType);- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);- }- }-}---int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)-{- int result;-#if (LZ4_HEAPMODE)- LZ4_stream_t* ctxPtr = ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */- if (ctxPtr == NULL) return 0;-#else- LZ4_stream_t ctx;- LZ4_stream_t* const ctxPtr = &ctx;-#endif- result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);--#if (LZ4_HEAPMODE)- FREEMEM(ctxPtr);-#endif- return result;-}---int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize)-{- return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1);-}---/* hidden debug function */-/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */-int LZ4_compress_fast_force(const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)-{- LZ4_stream_t ctx;- LZ4_initStream(&ctx, sizeof(ctx));-- if (srcSize < LZ4_64Klimit) {- return LZ4_compress_generic(&ctx.internal_donotuse, src, dst, srcSize, NULL, dstCapacity, limitedOutput, byU16, noDict, noDictIssue, acceleration);- } else {- tableType_t const addrMode = (sizeof(void*) > 4) ? byU32 : byPtr;- return LZ4_compress_generic(&ctx.internal_donotuse, src, dst, srcSize, NULL, dstCapacity, limitedOutput, addrMode, noDict, noDictIssue, acceleration);- }-}---/* Note!: This function leaves the stream in an unclean/broken state!- * It is not safe to subsequently use the same state with a _fastReset() or- * _continue() call without resetting it. */-static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)-{- void* const s = LZ4_initStream(state, sizeof (*state));- assert(s != NULL); (void)s;-- if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */- return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);- } else {- if (*srcSizePtr < LZ4_64Klimit) {- return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1);- } else {- tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;- return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1);- } }-}---int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)-{-#if (LZ4_HEAPMODE)- LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */- if (ctx == NULL) return 0;-#else- LZ4_stream_t ctxBody;- LZ4_stream_t* ctx = &ctxBody;-#endif-- int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);--#if (LZ4_HEAPMODE)- FREEMEM(ctx);-#endif- return result;-}----/*-******************************-* Streaming functions-********************************/--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 */- DEBUGLOG(4, "LZ4_createStream %p", lz4s);- if (lz4s == NULL) return NULL;- LZ4_initStream(lz4s, sizeof(*lz4s));- return lz4s;-}--#ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 :- it reports an aligment of 8-bytes,- while actually aligning LZ4_stream_t on 4 bytes. */-static size_t LZ4_stream_t_alignment(void)-{- struct { char c; LZ4_stream_t t; } t_a;- return sizeof(t_a) - sizeof(t_a.t);-}-#endif--LZ4_stream_t* LZ4_initStream (void* buffer, size_t size)-{- DEBUGLOG(5, "LZ4_initStream");- if (buffer == NULL) { return NULL; }- if (size < sizeof(LZ4_stream_t)) { return NULL; }-#ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 :- it reports an aligment of 8-bytes,- while actually aligning LZ4_stream_t on 4 bytes. */- if (((size_t)buffer) & (LZ4_stream_t_alignment() - 1)) { return NULL; } /* alignment check */-#endif- MEM_INIT(buffer, 0, sizeof(LZ4_stream_t));- return (LZ4_stream_t*)buffer;-}--/* resetStream is now deprecated,- * prefer initStream() which is more general */-void LZ4_resetStream (LZ4_stream_t* LZ4_stream)-{- DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream);- MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t));-}--void LZ4_resetStream_fast(LZ4_stream_t* ctx) {- LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32);-}--int LZ4_freeStream (LZ4_stream_t* LZ4_stream)-{- if (!LZ4_stream) return 0; /* support free on NULL */- DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream);- FREEMEM(LZ4_stream);- return (0);-}---#define HASH_UNIT sizeof(reg_t)-int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)-{- LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse;- const tableType_t tableType = byU32;- const BYTE* p = (const BYTE*)dictionary;- const BYTE* const dictEnd = p + dictSize;- const BYTE* base;-- DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict);-- /* It's necessary to reset the context,- * and not just continue it with prepareTable()- * to avoid any risk of generating overflowing matchIndex- * when compressing using this dictionary */- LZ4_resetStream(LZ4_dict);-- /* We always increment the offset by 64 KB, since, if the dict is longer,- * we truncate it to the last 64k, and if it's shorter, we still want to- * advance by a whole window length so we can provide the guarantee that- * there are only valid offsets in the window, which allows an optimization- * in LZ4_compress_fast_continue() where it uses noDictIssue even when the- * dictionary isn't a full 64k. */- dict->currentOffset += 64 KB;-- if (dictSize < (int)HASH_UNIT) {- return 0;- }-- if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;- base = dictEnd - dict->currentOffset;- dict->dictionary = p;- dict->dictSize = (U32)(dictEnd - p);- dict->tableType = tableType;-- while (p <= dictEnd-HASH_UNIT) {- LZ4_putPosition(p, dict->hashTable, tableType, base);- p+=3;- }-- 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 :- &(dictionaryStream->internal_donotuse);-- DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)",- workingStream, dictionaryStream,- dictCtx != NULL ? dictCtx->dictSize : 0);-- /* Calling LZ4_resetStream_fast() here makes sure that changes will not be- * erased by subsequent calls to LZ4_resetStream_fast() in case stream was- * marked as having dirty context, e.g. requiring full reset.- */- LZ4_resetStream_fast(workingStream);-- if (dictCtx != NULL) {- /* If the current offset is zero, we will never look in the- * external dictionary context, since there is no value a table- * entry can take that indicate a miss. In that case, we need- * to bump the offset to something non-zero.- */- if (workingStream->internal_donotuse.currentOffset == 0) {- workingStream->internal_donotuse.currentOffset = 64 KB;- }-- /* Don't actually attach an empty dictionary.- */- if (dictCtx->dictSize == 0) {- dictCtx = NULL;- }- }- workingStream->internal_donotuse.dictCtx = dictCtx;-}---static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize)-{- assert(nextSize >= 0);- if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */- /* rescale hash table */- U32 const delta = LZ4_dict->currentOffset - 64 KB;- const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;- int i;- DEBUGLOG(4, "LZ4_renormDictT");- for (i=0; i<LZ4_HASH_SIZE_U32; i++) {- if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0;- else LZ4_dict->hashTable[i] -= delta;- }- LZ4_dict->currentOffset = 64 KB;- if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;- LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;- }-}---int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream,- const char* source, char* dest,- int inputSize, int maxOutputSize,- int acceleration)-{- const tableType_t tableType = byU32;- LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse;- const BYTE* dictEnd = streamPtr->dictionary + streamPtr->dictSize;-- DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i)", inputSize);-- if (streamPtr->dirty) { return 0; } /* Uninitialized structure detected */- LZ4_renormDictT(streamPtr, inputSize); /* avoid index overflow */- if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;-- /* invalidate tiny dictionaries */- if ( (streamPtr->dictSize-1 < 4-1) /* intentional underflow */- && (dictEnd != (const BYTE*)source) ) {- DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary);- streamPtr->dictSize = 0;- streamPtr->dictionary = (const BYTE*)source;- dictEnd = (const BYTE*)source;- }-- /* Check overlapping input/dictionary space */- { const BYTE* sourceEnd = (const BYTE*) source + inputSize;- if ((sourceEnd > 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;- }- }-- /* prefix mode : source data follows dictionary */- if (dictEnd == (const BYTE*)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- return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);- }-- /* external dictionary mode */- { int result;- if (streamPtr->dictCtx) {- /* We depend here on the fact that dictCtx'es (produced by- * LZ4_loadDict) guarantee that their tables contain no references- * to offsets between dictCtx->currentOffset - 64 KB and- * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe- * to use noDictIssue even when the dict isn't a full 64 KB.- */- if (inputSize > 4 KB) {- /* For compressing large blobs, it is faster to pay the setup- * cost to copy the dictionary's tables into the active context,- * so that the compression loop is only looking into one table.- */- memcpy(streamPtr, streamPtr->dictCtx, sizeof(LZ4_stream_t));- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);- } else {- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);- }- } else {- 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 {- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);- }- }- streamPtr->dictionary = (const BYTE*)source;- streamPtr->dictSize = (U32)inputSize;- return result;- }-}---/* Hidden debug function, to force-test external dictionary mode */-int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)-{- LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse;- int result;-- LZ4_renormDictT(streamPtr, srcSize);-- if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {- result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1);- } else {- result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);- }-- streamPtr->dictionary = (const BYTE*)source;- streamPtr->dictSize = (U32)srcSize;-- return result;-}---/*! 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.- */-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;-- if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */- if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; }-- memmove(safeBuffer, previousDictEnd - dictSize, dictSize);-- dict->dictionary = (const BYTE*)safeBuffer;- dict->dictSize = (U32)dictSize;-- return dictSize;-}----/*-*******************************- * 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) )--/* 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)-{- U32 length = 0;- U32 s;- if (initial_check && unlikely((*ip) >= lencheck)) { /* overflow detection */- *error = initial_error;- return length;- }- do {- s = **ip;- (*ip)++;- length += s;- if (loop_check && unlikely((*ip) >= lencheck)) { /* overflow detection */- *error = loop_error;- return length;- }- } while (s==255);-- return length;-}--/*! LZ4_decompress_generic() :- * This generic decompression function covers all use cases.- * It shall be instantiated several times, using different sets of directives.- * Note that it is important for performance that this function really get inlined,- * in order to remove useless branches during compilation optimization.- */-LZ4_FORCE_INLINE int-LZ4_decompress_generic(- const char* const src,- char* const dst,- 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 */- const BYTE* const dictStart, /* only if dict==usingExtDict */- const size_t dictSize /* note : = 0 if noDict */- )-{- if (src == NULL) { return -1; }-- { const BYTE* ip = (const BYTE*) src;- const BYTE* const iend = ip + srcSize;-- BYTE* op = (BYTE*) dst;- BYTE* const oend = op + outputSize;- BYTE* cpy;-- const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize;-- const int safeDecode = (endOnInput==endOnInputSize);- const int checkOffset = ((safeDecode) && (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* match;- size_t offset;- unsigned token;- size_t length;--- DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize);-- /* Special cases */- assert(lowPrefix <= op);- if ((endOnInput) && (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; }-- /* Currently the fast loop shows a regression on qualcomm 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 */- while (1) {- /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */- assert(oend - op >= FASTLOOP_SAFE_DISTANCE);- if (endOnInput) { 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, endOnInput, 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 */-- /* 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 */- }- 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 */- 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 */- memcpy(op, ip, 8);- if (length > 8) { memcpy(op+8, ip+8, 8); }- }- ip += length; op = cpy;- }-- /* get offset */- offset = LZ4_readLE16(ip); ip+=2;- match = op - offset;- assert(match <= op);-- /* 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, endOnInput, 0, &error);- if (error != ok) { goto _output_error; }- if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */- length += MINMATCH;- if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {- goto safe_match_copy;- }- } else {- length += MINMATCH;- if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {- goto safe_match_copy;- }-- /* Fastpath check: Avoids a branch in LZ4_wildCopy32 if true */- if ((dict == withPrefix64k) || (match >= lowPrefix)) {- if (offset >= 8) {- assert(match >= lowPrefix);- assert(match <= op);- assert(op + 18 <= oend);-- memcpy(op, match, 8);- memcpy(op+8, match+8, 8);- memcpy(op+16, match+16, 2);- op += length;- continue;- } } }-- if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */- /* match starting within external dictionary */- if ((dict==usingExtDict) && (match < lowPrefix)) {- if (unlikely(op+length > oend-LASTLITERALS)) {- if (partialDecoding) {- length = MIN(length, (size_t)(oend-op)); /* reach end of buffer */- } else {- goto _output_error; /* end-of-block condition violated */- } }-- if (length <= (size_t)(lowPrefix-match)) {- /* match fits entirely within external dictionary : just copy */- memmove(op, dictEnd - (lowPrefix-match), length);- op += length;- } else {- /* match stretches into both external dictionary and current block */- size_t const copySize = (size_t)(lowPrefix - match);- size_t const restSize = length - copySize;- memcpy(op, dictEnd - copySize, copySize);- op += copySize;- if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */- BYTE* const endOfMatch = op + restSize;- const BYTE* copyFrom = lowPrefix;- while (op < endOfMatch) { *op++ = *copyFrom++; }- } else {- memcpy(op, lowPrefix, restSize);- op += restSize;- } }- continue;- }-- /* copy match within block */- cpy = op + length;-- assert((op <= oend) && (oend-op >= 32));- if (unlikely(offset<16)) {- LZ4_memcpy_using_offset(op, match, cpy, offset);- } else {- LZ4_wildCopy32(op, match, cpy);- }-- op = cpy; /* wildcopy correction */- }- safe_decode:-#endif-- /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */- while (1) {- 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- * (in the fast mode, only 8 bytes can be safely copied this way).- * 2) Further if the match length is 4..18, copy 18 bytes in a similar- * manner; but we ensure that there's enough space in the output for- * 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)- /* strictly "less than" on input, to re-enter the loop with at least one byte */- && likely((endOnInput ? ip < shortiend : 1) & (op <= shortoend)) ) {- /* Copy the literals */- memcpy(op, ip, endOnInput ? 16 : 8);- op += length; ip += length;-- /* The second stage: prepare for match copying, decode full info.- * If it doesn't work out, the info won't be wasted. */- length = token & ML_MASK; /* match length */- offset = LZ4_readLE16(ip); ip += 2;- match = op - offset;- assert(match <= op); /* check overflow */-- /* Do not deal with overlapping matches. */- if ( (length != ML_MASK)- && (offset >= 8)- && (dict==withPrefix64k || match >= lowPrefix) ) {- /* Copy the match. */- memcpy(op + 0, match + 0, 8);- memcpy(op + 8, match + 8, 8);- memcpy(op +16, match +16, 2);- op += length + MINMATCH;- /* Both stages worked, load the next token. */- continue;- }-- /* The second stage didn't work out, but the info is ready.- * Propel it right to the point of match copying. */- goto _copy_match;- }-- /* decode literal length */- if (length == RUN_MASK) {- variable_length_error error = ok;- length += read_variable_length(&ip, iend-RUN_MASK, endOnInput, 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 */- }-- /* copy literals */- cpy = op+length;-#if LZ4_FAST_DEC_LOOP- safe_literal_copy:-#endif- LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);- if ( ((endOnInput) && ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) )- || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )- {- /* We've either hit the input parsing restriction or the output parsing restriction.- * If we've hit the input parsing condition then this must be the last sequence.- * If we've hit the output parsing condition then we are either using partialDecoding- * or we've hit the output parsing condition.- */- if (partialDecoding) {- /* 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);- /* If we're in this block because of the input parsing condition, then we must be on the- * last sequence (or invalid), so we must check that we exactly consume the input.- */- if ((ip+length>iend-(2+1+LASTLITERALS)) && (ip+length != iend)) { goto _output_error; }- assert(ip+length <= iend);- /* We are finishing in the middle of a literals segment.- * Break after the copy.- */- if (cpy > oend) {- cpy = oend;- assert(op<=oend);- length = (size_t)(oend-op);- }- assert(ip+length <= iend);- } 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))) { goto _output_error; }- }- memmove(op, ip, length); /* supports overlapping memory regions, which only matters for in-place decompression scenarios */- ip += length;- op += length;- /* Necessarily EOF when !partialDecoding. When partialDecoding- * it is EOF if we've either filled the output buffer or hit- * the input parsing restriction.- */- if (!partialDecoding || (cpy == oend) || (ip == iend)) {- break;- }- } else {- LZ4_wildCopy8(op, ip, cpy); /* may overwrite up to WILDCOPYLENGTH beyond cpy */- ip += length; op = cpy;- }-- /* get offset */- offset = LZ4_readLE16(ip); ip+=2;- match = op - offset;-- /* get matchlength */- length = token & ML_MASK;-- _copy_match:- if (length == ML_MASK) {- variable_length_error error = ok;- length += read_variable_length(&ip, iend - LASTLITERALS + 1, endOnInput, 0, &error);- if (error != ok) goto _output_error;- if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */- }- length += MINMATCH;--#if LZ4_FAST_DEC_LOOP- safe_match_copy:-#endif- if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */- /* match starting within external dictionary */- if ((dict==usingExtDict) && (match < lowPrefix)) {- if (unlikely(op+length > oend-LASTLITERALS)) {- if (partialDecoding) length = MIN(length, (size_t)(oend-op));- else goto _output_error; /* doesn't respect parsing restriction */- }-- if (length <= (size_t)(lowPrefix-match)) {- /* match fits entirely within external dictionary : just copy */- memmove(op, dictEnd - (lowPrefix-match), length);- op += length;- } else {- /* match stretches into both external dictionary and current block */- size_t const copySize = (size_t)(lowPrefix - match);- size_t const restSize = length - copySize;- memcpy(op, dictEnd - copySize, copySize);- op += copySize;- if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */- BYTE* const endOfMatch = op + restSize;- const BYTE* copyFrom = lowPrefix;- while (op < endOfMatch) *op++ = *copyFrom++;- } else {- memcpy(op, lowPrefix, restSize);- op += restSize;- } }- continue;- }- assert(match >= lowPrefix);-- /* copy match within block */- cpy = op + length;-- /* partialDecoding : may end anywhere within the block */- assert(op<=oend);- if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {- size_t const mlen = MIN(length, (size_t)(oend-op));- const BYTE* const matchEnd = match + mlen;- BYTE* const copyEnd = op + mlen;- if (matchEnd > op) { /* overlap copy */- while (op < copyEnd) { *op++ = *match++; }- } else {- memcpy(op, match, mlen);- }- op = copyEnd;- if (op == oend) { break; }- continue;- }-- if (unlikely(offset<8)) {- LZ4_write32(op, 0); /* silence msan warning when offset==0 */- op[0] = match[0];- op[1] = match[1];- op[2] = match[2];- op[3] = match[3];- match += inc32table[offset];- memcpy(op+4, match, 4);- match -= dec64table[offset];- } else {- memcpy(op, match, 8);- match += 8;- }- op += 8;-- if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {- BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1);- if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */- if (op < oCopyLimit) {- LZ4_wildCopy8(op, match, oCopyLimit);- match += oCopyLimit - op;- op = oCopyLimit;- }- while (op < cpy) { *op++ = *match++; }- } else {- memcpy(op, match, 8);- if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); }- }- op = cpy; /* wildcopy correction */- }-- /* end of decoding */- if (endOnInput) {- return (int) (((char*)op)-dst); /* Nb of output bytes decoded */- } else {- return (int) (((const char*)ip)-src); /* Nb of input bytes read */- }-- /* Overflow error detected */- _output_error:- return (int) (-(((const char*)ip)-src))-1;- }-}---/*===== Instantiate the API decoding functions. =====*/--LZ4_FORCE_O2_GCC_PPC64LE-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,- (BYTE*)dest, NULL, 0);-}--LZ4_FORCE_O2_GCC_PPC64LE-int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity)-{- dstCapacity = MIN(targetOutputSize, dstCapacity);- return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity,- endOnInputSize, partial_decode,- noDict, (BYTE*)dst, NULL, 0);-}--LZ4_FORCE_O2_GCC_PPC64LE-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);-}--/*===== Instantiate a few more decoding cases, used more than once. =====*/--LZ4_FORCE_O2_GCC_PPC64LE /* Exported, an obsolete API function. */-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,- (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);-}--LZ4_FORCE_O2_GCC_PPC64LE-static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize,- size_t prefixSize)-{- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,- endOnInputSize, decode_full_block, noDict,- (BYTE*)dest-prefixSize, NULL, 0);-}--LZ4_FORCE_O2_GCC_PPC64LE-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,- (BYTE*)dest, (const BYTE*)dictStart, dictSize);-}--LZ4_FORCE_O2_GCC_PPC64LE-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);-}--/* The "double dictionary" mode, for use with e.g. ring buffers: the first part- * of the dictionary is passed as prefix, and the second via dictStart + dictSize.- * These routines are used only once, in LZ4_decompress_*_continue().- */-LZ4_FORCE_INLINE-int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize,- 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,- (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);-}--/*===== streaming decompression functions =====*/--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;-}--int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)-{- if (LZ4_stream == NULL) { return 0; } /* support free on NULL */- FREEMEM(LZ4_stream);- return 0;-}--/*! LZ4_setStreamDecode() :- * Use this function to instruct where to find the dictionary.- * This function is not necessary if previous data is still available where it was decoded.- * Loading a size of 0 is allowed (same effect as no dictionary).- * @return : 1 if OK, 0 if error- */-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->externalDict = NULL;- lz4sd->extDictSize = 0;- return 1;-}--/*! LZ4_decoderRingBufferSize() :- * when setting a ring buffer for streaming decompression (optional scenario),- * provides the minimum size of this ring buffer- * to be compatible with any source respecting maxBlockSize condition.- * Note : in a ring buffer scenario,- * blocks are presumed decompressed next to each other.- * When not enough space remains for next block (remainingSize < maxBlockSize),- * decoding resumes from beginning of ring buffer.- * @return : minimum ring buffer size,- * or 0 if there is an error (invalid maxBlockSize).- */-int LZ4_decoderRingBufferSize(int maxBlockSize)-{- if (maxBlockSize < 0) return 0;- if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0;- if (maxBlockSize < 16) maxBlockSize = 16;- return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize);-}--/*-*_continue() :- These decoding functions allow decompression of multiple blocks in "streaming" mode.- Previously decoded blocks must still be available at the memory position where they were decoded.- If it's not possible, save the relevant part of decoded data into a safe buffer,- and indicate where it stands using LZ4_setStreamDecode()-*/-LZ4_FORCE_O2_GCC_PPC64LE-int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)-{- LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;- int result;-- if (lz4sd->prefixSize == 0) {- /* The first call, no dictionary yet. */- assert(lz4sd->extDictSize == 0);- result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);- if (result <= 0) return result;- lz4sd->prefixSize = (size_t)result;- lz4sd->prefixEnd = (BYTE*)dest + result;- } else if (lz4sd->prefixEnd == (BYTE*)dest) {- /* They're rolling the current segment. */- if (lz4sd->prefixSize >= 64 KB - 1)- result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);- else if (lz4sd->extDictSize == 0)- result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize,- lz4sd->prefixSize);- else- result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize,- lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);- if (result <= 0) return result;- lz4sd->prefixSize += (size_t)result;- lz4sd->prefixEnd += result;- } else {- /* The buffer wraps around, or they're switching to another buffer. */- lz4sd->extDictSize = lz4sd->prefixSize;- lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;- result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize,- lz4sd->externalDict, lz4sd->extDictSize);- if (result <= 0) return result;- lz4sd->prefixSize = (size_t)result;- lz4sd->prefixEnd = (BYTE*)dest + result;- }-- return result;-}--LZ4_FORCE_O2_GCC_PPC64LE-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;- int result;- assert(originalSize >= 0);-- if (lz4sd->prefixSize == 0) {- 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);- if (result <= 0) return result;- lz4sd->prefixSize += (size_t)originalSize;- lz4sd->prefixEnd += originalSize;- } else {- lz4sd->extDictSize = lz4sd->prefixSize;- lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;- result = LZ4_decompress_fast_extDict(source, dest, originalSize,- lz4sd->externalDict, lz4sd->extDictSize);- if (result <= 0) return result;- lz4sd->prefixSize = (size_t)originalSize;- lz4sd->prefixEnd = (BYTE*)dest + originalSize;- }-- return result;-}---/*-Advanced decoding functions :-*_usingDict() :- These decoding functions work the same as "_continue" ones,- the dictionary must be explicitly provided within parameters-*/--int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)-{- if (dictSize==0)- return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);- if (dictStart+dictSize == dest) {- if (dictSize >= 64 KB - 1) {- return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);- }- assert(dictSize >= 0);- return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize);- }- assert(dictSize >= 0);- return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, 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);- assert(dictSize >= 0);- return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize);-}---/*=*************************************************-* Obsolete Functions-***************************************************/-/* obsolete compression functions */-int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)-{- return LZ4_compress_default(source, dest, inputSize, maxOutputSize);-}-int LZ4_compress(const char* src, char* dest, int srcSize)-{- return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize));-}-int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize)-{- return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1);-}-int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize)-{- return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1);-}-int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity)-{- return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1);-}-int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize)-{- return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1);-}--/*-These decompression functions are deprecated and should no longer be used.-They are only provided here for compatibility with older user programs.-- LZ4_uncompress is totally equivalent to LZ4_decompress_fast-- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe-*/-int LZ4_uncompress (const char* source, char* dest, int outputSize)-{- return LZ4_decompress_fast(source, dest, outputSize);-}-int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize)-{- return LZ4_decompress_safe(source, dest, isize, maxOutputSize);-}--/* Obsolete Streaming functions */--int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; }--int LZ4_resetStreamState(void* state, char* inputBuffer)-{- (void)inputBuffer;- LZ4_resetStream((LZ4_stream_t*)state);- return 0;-}--void* LZ4_create (char* inputBuffer)-{- (void)inputBuffer;- return LZ4_createStream();-}+ 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+ modification, are permitted provided that the following conditions are+ met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following disclaimer+ in the documentation and/or other materials provided with the+ distribution.++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.++ You can contact the author at :+ - LZ4 homepage : http://www.lz4.org+ - LZ4 source repository : https://github.com/lz4/lz4+*/++/*-************************************+* Tuning parameters+**************************************/+/*+ * LZ4_HEAPMODE :+ * Select how default compression functions will allocate memory for their hash table,+ * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).+ */+#ifndef LZ4_HEAPMODE+# define LZ4_HEAPMODE 0+#endif++/*+ * LZ4_ACCELERATION_DEFAULT :+ * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0+ */+#define LZ4_ACCELERATION_DEFAULT 1+/*+ * LZ4_ACCELERATION_MAX :+ * Any "acceleration" value higher than this threshold+ * get treated as LZ4_ACCELERATION_MAX instead (fix #876)+ */+#define LZ4_ACCELERATION_MAX 65537+++/*-************************************+* CPU Feature Detection+**************************************/+/* LZ4_FORCE_MEMORY_ACCESS+ * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.+ * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.+ * The below switch allow to select different access method for improved performance.+ * Method 0 (default) : use `memcpy()`. Safe and portable.+ * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).+ * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.+ * Method 2 : direct access. This method is portable but violate C standard.+ * It can generate buggy code on targets which assembly generation depends on alignment.+ * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)+ * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.+ * Prefer these methods in priority order (0 > 1 > 2)+ */+#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */+# if defined(__GNUC__) && \+ ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \+ || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )+# define LZ4_FORCE_MEMORY_ACCESS 2+# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__)+# define LZ4_FORCE_MEMORY_ACCESS 1+# endif+#endif++/*+ * LZ4_FORCE_SW_BITCOUNT+ * Define this parameter if your target system or compiler does not support hardware bit count+ */+#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */+# undef LZ4_FORCE_SW_BITCOUNT /* avoid double def */+# define LZ4_FORCE_SW_BITCOUNT+#endif++++/*-************************************+* Dependency+**************************************/+/*+ * LZ4_SRC_INCLUDED:+ * Amalgamation flag, whether lz4.c is included+ */+#ifndef LZ4_SRC_INCLUDED+# define LZ4_SRC_INCLUDED 1+#endif++#ifndef LZ4_STATIC_LINKING_ONLY+#define LZ4_STATIC_LINKING_ONLY+#endif++#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS+#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */+#endif++#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */+#include "lz4.h"+/* see also "memory routines" below */+++/*-************************************+* Compiler Options+**************************************/+#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+# ifdef _MSC_VER /* Visual Studio */+# define LZ4_FORCE_INLINE static __forceinline+# else+# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */+# ifdef __GNUC__+# define LZ4_FORCE_INLINE static inline __attribute__((always_inline))+# else+# define LZ4_FORCE_INLINE static inline+# endif+# else+# define LZ4_FORCE_INLINE static+# endif /* __STDC_VERSION__ */+# endif /* _MSC_VER */+#endif /* LZ4_FORCE_INLINE */++/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE+ * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8,+ * together with a simple 8-byte copy loop as a fall-back path.+ * However, this optimization hurts the decompression speed by >30%,+ * because the execution does not go to the optimized loop+ * for typical compressible data, and all of the preamble checks+ * before going to the fall-back path become useless overhead.+ * This optimization happens only with the -O3 flag, and -O2 generates+ * a simple 8-byte copy loop.+ * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8+ * functions are annotated with __attribute__((optimize("O2"))),+ * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute+ * of LZ4_wildCopy8 does not affect the compression speed.+ */+#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__)+# define LZ4_FORCE_O2 __attribute__((optimize("O2")))+# undef LZ4_FORCE_INLINE+# define LZ4_FORCE_INLINE static __inline __attribute__((optimize("O2"),always_inline))+#else+# define LZ4_FORCE_O2+#endif++#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)+# define expect(expr,value) (__builtin_expect ((expr),(value)) )+#else+# define expect(expr,value) (expr)+#endif++#ifndef likely+#define likely(expr) expect((expr) != 0, 1)+#endif+#ifndef unlikely+#define unlikely(expr) expect((expr) != 0, 0)+#endif++/* Should the alignment test prove unreliable, for some reason,+ * it can be disabled by setting LZ4_ALIGN_TEST to 0 */+#ifndef LZ4_ALIGN_TEST /* can be externally provided */+# define LZ4_ALIGN_TEST 1+#endif+++/*-************************************+* Memory routines+**************************************/++/*! 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 */+void* LZ4_malloc(size_t s);+void* LZ4_calloc(size_t n, size_t s);+void LZ4_free(void* p);+# define ALLOC(s) LZ4_malloc(s)+# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s)+# define FREEMEM(p) LZ4_free(p)+#else+# include <stdlib.h> /* malloc, calloc, free */+# define ALLOC(s) malloc(s)+# define ALLOC_AND_ZERO(s) calloc(1,s)+# define FREEMEM(p) free(p)+#endif++#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))+++/*-************************************+* Common Constants+**************************************/+#define MINMATCH 4++#define WILDCOPYLENGTH 8+#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */+#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */+#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */+#define FASTLOOP_SAFE_DISTANCE 64+static const int LZ4_minLength = (MFLIMIT+1);++#define KB *(1 <<10)+#define MB *(1 <<20)+#define GB *(1U<<30)++#define LZ4_DISTANCE_ABSOLUTE_MAX 65535+#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */+# error "LZ4_DISTANCE_MAX is too big : must be <= 65535"+#endif++#define ML_BITS 4+#define ML_MASK ((1U<<ML_BITS)-1)+#define RUN_BITS (8-ML_BITS)+#define RUN_MASK ((1U<<RUN_BITS)-1)+++/*-************************************+* Error detection+**************************************/+#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)+# include <assert.h>+#else+# ifndef assert+# define assert(condition) ((void)0)+# endif+#endif++#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */++#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2)+# include <stdio.h>+ static int g_debuglog_enable = 1;+# define DEBUGLOG(l, ...) { \+ if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \+ fprintf(stderr, __FILE__ ": "); \+ fprintf(stderr, __VA_ARGS__); \+ fprintf(stderr, " \n"); \+ } }+#else+# define DEBUGLOG(l, ...) {} /* disabled */+#endif++static int LZ4_isAligned(const void* ptr, size_t alignment)+{+ return ((size_t)ptr & (alignment -1)) == 0;+}+++/*-************************************+* Types+**************************************/+#include <limits.h>+#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)+# include <stdint.h>+ typedef uint8_t BYTE;+ typedef uint16_t U16;+ typedef uint32_t U32;+ typedef int32_t S32;+ typedef uint64_t U64;+ typedef uintptr_t uptrval;+#else+# if UINT_MAX != 4294967295UL+# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4"+# endif+ typedef unsigned char BYTE;+ typedef unsigned short U16;+ typedef unsigned int U32;+ typedef signed int S32;+ typedef unsigned long long U64;+ typedef size_t uptrval; /* generally true, except OpenVMS-64 */+#endif++#if defined(__x86_64__)+ typedef U64 reg_t; /* 64-bits in x32 mode */+#else+ typedef size_t reg_t; /* 32-bits in x32 mode */+#endif++typedef enum {+ notLimited = 0,+ limitedOutput = 1,+ fillOutput = 2+} limitedOutput_directive;+++/*-************************************+* Reading and writing into memory+**************************************/++/**+ * LZ4 relies on memcpy with a constant size being inlined. In freestanding+ * environments, the compiler can't assume the implementation of memcpy() is+ * standard compliant, so it can't apply its specialized memcpy() inlining+ * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze+ * 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(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 */+ return one.c[0];+}+++#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)+/* lie to the compiler about data alignment; use with caution */++static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }+static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }+static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }++static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }+static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }++#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)++/* __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)) LZ4_unalign;++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) { ((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() */++static U16 LZ4_read16(const void* memPtr)+{+ U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;+}++static U32 LZ4_read32(const void* memPtr)+{+ U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;+}++static reg_t LZ4_read_ARCH(const void* memPtr)+{+ reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;+}++static void LZ4_write16(void* memPtr, U16 value)+{+ LZ4_memcpy(memPtr, &value, sizeof(value));+}++static void LZ4_write32(void* memPtr, U32 value)+{+ LZ4_memcpy(memPtr, &value, sizeof(value));+}++#endif /* LZ4_FORCE_MEMORY_ACCESS */+++static U16 LZ4_readLE16(const void* memPtr)+{+ if (LZ4_isLittleEndian()) {+ return LZ4_read16(memPtr);+ } else {+ const BYTE* p = (const BYTE*)memPtr;+ return (U16)((U16)p[0] + (p[1]<<8));+ }+}++static void LZ4_writeLE16(void* memPtr, U16 value)+{+ if (LZ4_isLittleEndian()) {+ LZ4_write16(memPtr, value);+ } else {+ BYTE* p = (BYTE*)memPtr;+ p[0] = (BYTE) value;+ p[1] = (BYTE)(value>>8);+ }+}++/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */+LZ4_FORCE_INLINE+void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd)+{+ BYTE* d = (BYTE*)dstPtr;+ const BYTE* s = (const BYTE*)srcPtr;+ BYTE* const e = (BYTE*)dstEnd;++ do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d<e);+}++static const unsigned inc32table[8] = {0, 1, 2, 1, 0, 4, 4, 4};+static const int dec64table[8] = {0, 0, 0, -1, -4, 1, 2, 3};+++#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 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+# endif+#endif++#if LZ4_FAST_DEC_LOOP++LZ4_FORCE_INLINE void+LZ4_memcpy_using_offset_base(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)+{+ assert(srcPtr + offset == dstPtr);+ if (offset < 8) {+ LZ4_write32(dstPtr, 0); /* silence an msan warning when offset==0 */+ dstPtr[0] = srcPtr[0];+ dstPtr[1] = srcPtr[1];+ dstPtr[2] = srcPtr[2];+ dstPtr[3] = srcPtr[3];+ srcPtr += inc32table[offset];+ LZ4_memcpy(dstPtr+4, srcPtr, 4);+ srcPtr -= dec64table[offset];+ dstPtr += 8;+ } else {+ LZ4_memcpy(dstPtr, srcPtr, 8);+ dstPtr += 8;+ srcPtr += 8;+ }++ LZ4_wildCopy8(dstPtr, srcPtr, dstEnd);+}++/* customized variant of memcpy, which can overwrite up to 32 bytes beyond dstEnd+ * this version copies two times 16 bytes (instead of one time 32 bytes)+ * because it must be compatible with offsets >= 16. */+LZ4_FORCE_INLINE void+LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd)+{+ BYTE* d = (BYTE*)dstPtr;+ const BYTE* s = (const BYTE*)srcPtr;+ BYTE* const e = (BYTE*)dstEnd;++ do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d<e);+}++/* LZ4_memcpy_using_offset() presumes :+ * - dstEnd >= dstPtr + MINMATCH+ * - there is at least 8 bytes available to write after dstEnd */+LZ4_FORCE_INLINE void+LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)+{+ BYTE v[8];++ assert(dstEnd >= dstPtr + MINMATCH);++ switch(offset) {+ case 1:+ MEM_INIT(v, *srcPtr, 8);+ break;+ 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);+ LZ4_memcpy(&v[4], srcPtr, 4);+ break;+ default:+ LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset);+ return;+ }++ LZ4_memcpy(dstPtr, v, 8);+ dstPtr += 8;+ while (dstPtr < dstEnd) {+ LZ4_memcpy(dstPtr, v, 8);+ dstPtr += 8;+ }+}+#endif+++/*-************************************+* Common functions+**************************************/+static unsigned LZ4_NbCommonBytes (reg_t val)+{+ assert(val != 0);+ if (LZ4_isLittleEndian()) {+ if (sizeof(val) == 8) {+# 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);+ return (unsigned)r >> 3;+# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \+ ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \+ !defined(LZ4_FORCE_SW_BITCOUNT)+ return (unsigned)__builtin_ctzll((U64)val) >> 3;+# else+ const U64 m = 0x0101010101010101ULL;+ val ^= val - 1;+ return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56);+# endif+ } else /* 32 bits */ {+# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT)+ unsigned long r;+ _BitScanForward(&r, (U32)val);+ return (unsigned)r >> 3;+# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \+ ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \+ !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)+ return (unsigned)__builtin_ctz((U32)val) >> 3;+# else+ const U32 m = 0x01010101;+ return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24;+# endif+ }+ } else /* Big Endian CPU */ {+ if (sizeof(val)==8) {+# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \+ ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \+ !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)+ return (unsigned)__builtin_clzll((U64)val) >> 3;+# else+#if 1+ /* this method is probably faster,+ * but adds a 128 bytes lookup table */+ static const unsigned char ctz7_tab[128] = {+ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,+ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,+ };+ U64 const mask = 0x0101010101010101ULL;+ U64 const t = (((val >> 8) - mask) | val) & mask;+ return ctz7_tab[(t * 0x0080402010080402ULL) >> 57];+#else+ /* this method doesn't consume memory space like the previous one,+ * but it contains several branches,+ * that may end up slowing execution */+ static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits.+ Just to avoid some static analyzer complaining about shift by 32 on 32-bits target.+ Note that this code path is never triggered in 32-bits mode. */+ unsigned r;+ if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; }+ if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }+ r += (!val);+ return r;+#endif+# endif+ } else /* 32 bits */ {+# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \+ ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \+ !defined(LZ4_FORCE_SW_BITCOUNT)+ return (unsigned)__builtin_clz((U32)val) >> 3;+# else+ val >>= 8;+ val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) |+ (val + 0x00FF0000)) >> 24;+ return (unsigned)val ^ 3;+# endif+ }+ }+}+++#define STEPSIZE sizeof(reg_t)+LZ4_FORCE_INLINE+unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)+{+ const BYTE* const pStart = pIn;++ if (likely(pIn < pInLimit-(STEPSIZE-1))) {+ reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);+ if (!diff) {+ pIn+=STEPSIZE; pMatch+=STEPSIZE;+ } else {+ return LZ4_NbCommonBytes(diff);+ } }++ while (likely(pIn < pInLimit-(STEPSIZE-1))) {+ reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);+ if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }+ pIn += LZ4_NbCommonBytes(diff);+ return (unsigned)(pIn - pStart);+ }++ if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }+ if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }+ if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;+ return (unsigned)(pIn - pStart);+}+++#ifndef LZ4_COMMONDEFS_ONLY+/*-************************************+* Local Constants+**************************************/+static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));+static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */+++/*-************************************+* Local Structures and types+**************************************/+typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;++/**+ * This enum distinguishes several different modes of accessing previous+ * content in the stream.+ *+ * - noDict : There is no preceding content.+ * - withPrefix64k : Table entries up to ctx->dictSize before the current blob+ * blob being compressed are valid and refer to the preceding+ * content (of length ctx->dictSize), which is available+ * contiguously preceding in memory the content currently+ * being compressed.+ * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere+ * else in memory, starting at ctx->dictionary with length+ * ctx->dictSize.+ * - 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+ * content, and matches are found by looking in the ctx+ * ->dictCtx->hashTable.+ */+typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;+typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;+++/*-************************************+* Local Utils+**************************************/+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 sizeof(LZ4_stream_t); }+++/*-****************************************+* Internal Definitions, used only in Tests+*******************************************/+#if defined (__cplusplus)+extern "C" {+#endif++int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize);++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++/*-******************************+* Compression functions+********************************/+LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType)+{+ if (tableType == byU16)+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));+ else+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));+}++LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType)+{+ const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;+ if (LZ4_isLittleEndian()) {+ const U64 prime5bytes = 889523592379ULL;+ return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));+ } else {+ const U64 prime8bytes = 11400714785074694791ULL;+ return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));+ }+}++LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)+{+ if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);+ return LZ4_hash4(LZ4_read32(p), tableType);+}++LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType)+{+ switch (tableType)+ {+ default: /* fallthrough */+ case clearedTable: { /* illegal! */ assert(0); return; }+ case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; }+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; }+ case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; }+ }+}++LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)+{+ switch (tableType)+ {+ default: /* fallthrough */+ case clearedTable: /* fallthrough */+ case byPtr: { /* illegal! */ assert(0); return; }+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }+ case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; }+ }+}++LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h,+ void* tableBase, tableType_t const tableType,+ const BYTE* srcBase)+{+ switch (tableType)+ {+ case clearedTable: { /* illegal! */ assert(0); return; }+ case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }+ case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }+ }+}++LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)+{+ U32 const h = LZ4_hashPosition(p, tableType);+ LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);+}++/* LZ4_getIndexOnHash() :+ * Index of match position registered in hash table.+ * hash position must be calculated by using base+index, or dictBase+index.+ * Assumption 1 : only valid if tableType == byU32 or byU16.+ * Assumption 2 : h is presumed valid (within limits of hash table)+ */+LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)+{+ LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2);+ if (tableType == byU32) {+ const U32* const hashTable = (const U32*) tableBase;+ assert(h < (1U << (LZ4_MEMORY_USAGE-2)));+ return hashTable[h];+ }+ if (tableType == byU16) {+ const U16* const hashTable = (const U16*) tableBase;+ assert(h < (1U << (LZ4_MEMORY_USAGE-1)));+ return hashTable[h];+ }+ assert(0); return 0; /* forbidden case */+}++static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase)+{+ if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }+ if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; }+ { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */+}++LZ4_FORCE_INLINE const BYTE*+LZ4_getPosition(const BYTE* p,+ const void* tableBase, tableType_t tableType,+ const BYTE* srcBase)+{+ U32 const h = LZ4_hashPosition(p, tableType);+ return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);+}++LZ4_FORCE_INLINE void+LZ4_prepareTable(LZ4_stream_t_internal* const cctx,+ const int inputSize,+ const tableType_t tableType) {+ /* If the table hasn't been used, it's guaranteed to be zeroed out, and is+ * therefore safe to use no matter what mode we're in. Otherwise, we figure+ * out if it's safe to leave as is or whether it needs to be reset.+ */+ if ((tableType_t)cctx->tableType != clearedTable) {+ assert(inputSize >= 0);+ if ((tableType_t)cctx->tableType != tableType+ || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU)+ || ((tableType == byU32) && cctx->currentOffset > 1 GB)+ || tableType == byPtr+ || inputSize >= 4 KB)+ {+ DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx);+ MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);+ cctx->currentOffset = 0;+ cctx->tableType = (U32)clearedTable;+ } else {+ DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)");+ }+ }++ /* 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");+ cctx->currentOffset += 64 KB;+ }++ /* Finally, clear history */+ cctx->dictCtx = NULL;+ cctx->dictionary = NULL;+ cctx->dictSize = 0;+}++/** LZ4_compress_generic() :+ * inlined, to ensure branches are decided at compilation time.+ * Presumed already validated at this stage:+ * - source != NULL+ * - inputSize > 0+ */+LZ4_FORCE_INLINE int LZ4_compress_generic_validated(+ LZ4_stream_t_internal* const cctx,+ const char* const source,+ char* const dest,+ const int inputSize,+ int* inputConsumed, /* only written when outputDirective == fillOutput */+ const int maxOutputSize,+ const limitedOutput_directive outputDirective,+ const tableType_t tableType,+ const dict_directive dictDirective,+ const dictIssue_directive dictIssue,+ const int acceleration)+{+ int result;+ const BYTE* ip = (const BYTE*) source;++ U32 const startIndex = cctx->currentOffset;+ const BYTE* base = (const BYTE*) source - startIndex;+ const BYTE* lowLimit;++ const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;+ const BYTE* const dictionary =+ dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;+ const U32 dictSize =+ dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;+ const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */++ int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);+ U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */+ const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary;+ const BYTE* anchor = (const BYTE*) source;+ const BYTE* const iend = ip + inputSize;+ const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;+ const BYTE* const matchlimit = iend - LASTLITERALS;++ /* 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) ? NULL :+ (dictDirective == usingDictCtx) ?+ dictionary + dictSize - dictCtx->currentOffset :+ dictionary + dictSize - startIndex;++ BYTE* op = (BYTE*) dest;+ BYTE* const olimit = op + maxOutputSize;++ U32 offset = 0;+ U32 forwardH;++ DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType);+ assert(ip != NULL);+ /* If init conditions are not met, we don't have to mark stream+ * as having dirty context, since no action was taken yet */+ if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */+ if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */+ if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */+ assert(acceleration >= 1);++ lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);++ /* Update context state */+ if (dictDirective == usingDictCtx) {+ /* Subsequent linked blocks can't use the dictionary. */+ /* Instead, they use the block we just compressed. */+ cctx->dictCtx = NULL;+ cctx->dictSize = (U32)inputSize;+ } else {+ cctx->dictSize += (U32)inputSize;+ }+ cctx->currentOffset += (U32)inputSize;+ cctx->tableType = (U32)tableType;++ if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */++ /* First Byte */+ LZ4_putPosition(ip, cctx->hashTable, tableType, base);+ ip++; forwardH = LZ4_hashPosition(ip, tableType);++ /* Main Loop */+ for ( ; ; ) {+ const BYTE* match;+ BYTE* token;+ const BYTE* filledIp;++ /* Find a match */+ if (tableType == byPtr) {+ const BYTE* forwardIp = ip;+ int step = 1;+ int searchMatchNb = acceleration << LZ4_skipTrigger;+ do {+ U32 const h = forwardH;+ ip = forwardIp;+ forwardIp += step;+ step = (searchMatchNb++ >> LZ4_skipTrigger);++ if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;+ assert(ip < mflimitPlusOne);++ match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);+ forwardH = LZ4_hashPosition(forwardIp, tableType);+ LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);++ } while ( (match+LZ4_DISTANCE_MAX < ip)+ || (LZ4_read32(match) != LZ4_read32(ip)) );++ } else { /* byU32, byU16 */++ const BYTE* forwardIp = ip;+ int step = 1;+ int searchMatchNb = acceleration << LZ4_skipTrigger;+ do {+ U32 const h = forwardH;+ U32 const current = (U32)(forwardIp - base);+ U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);+ assert(matchIndex <= current);+ assert(forwardIp - base < (ptrdiff_t)(2 GB - 1));+ ip = forwardIp;+ forwardIp += step;+ step = (searchMatchNb++ >> LZ4_skipTrigger);++ if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;+ assert(ip < mflimitPlusOne);++ if (dictDirective == usingDictCtx) {+ if (matchIndex < startIndex) {+ /* there was no match, try the dictionary */+ assert(tableType == byU32);+ matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);+ match = dictBase + matchIndex;+ matchIndex += dictDelta; /* make dictCtx index comparable with current context */+ lowLimit = dictionary;+ } else {+ match = base + matchIndex;+ lowLimit = (const BYTE*)source;+ }+ } 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 {+ match = base + matchIndex;+ lowLimit = (const BYTE*)source;+ }+ } else { /* single continuous memory segment */+ match = base + matchIndex;+ }+ forwardH = LZ4_hashPosition(forwardIp, tableType);+ LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);++ DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex);+ if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */+ assert(matchIndex < current);+ if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX))+ && (matchIndex+LZ4_DISTANCE_MAX < current)) {+ continue;+ } /* too far */+ assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */++ if (LZ4_read32(match) == LZ4_read32(ip)) {+ if (maybe_extMem) offset = current - matchIndex;+ break; /* match found */+ }++ } while(1);+ }++ /* Catch up */+ filledIp = ip;+ while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }++ /* Encode Literals */+ { unsigned const litLength = (unsigned)(ip - anchor);+ token = op++;+ if ((outputDirective == limitedOutput) && /* Check output buffer overflow */+ (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) {+ return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */+ }+ if ((outputDirective == fillOutput) &&+ (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) {+ op--;+ goto _last_literals;+ }+ if (litLength >= RUN_MASK) {+ int len = (int)(litLength - RUN_MASK);+ *token = (RUN_MASK<<ML_BITS);+ for(; len >= 255 ; len-=255) *op++ = 255;+ *op++ = (BYTE)len;+ }+ else *token = (BYTE)(litLength<<ML_BITS);++ /* Copy Literals */+ LZ4_wildCopy8(op, anchor, op+litLength);+ op+=litLength;+ DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i",+ (int)(anchor-(const BYTE*)source), litLength, (int)(ip-(const BYTE*)source));+ }++_next_match:+ /* at this stage, the following variables must be correctly set :+ * - ip : at start of LZ operation+ * - 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+ */++ if ((outputDirective == fillOutput) &&+ (op + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit)) {+ /* the match was too close to the end, rewind and go to last literals */+ op = token;+ goto _last_literals;+ }++ /* Encode Offset */+ if (maybe_extMem) { /* static test */+ DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source));+ assert(offset <= LZ4_DISTANCE_MAX && offset > 0);+ LZ4_writeLE16(op, (U16)offset); op+=2;+ } else {+ DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match));+ assert(ip-match <= LZ4_DISTANCE_MAX);+ LZ4_writeLE16(op, (U16)(ip - match)); op+=2;+ }++ /* Encode MatchLength */+ { unsigned matchCode;++ if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx)+ && (lowLimit==dictionary) /* match within extDict */ ) {+ const BYTE* limit = ip + (dictEnd-match);+ assert(dictEnd > match);+ if (limit > matchlimit) limit = matchlimit;+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);+ ip += (size_t)matchCode + MINMATCH;+ if (ip==limit) {+ unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit);+ matchCode += more;+ ip += more;+ }+ DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH);+ } else {+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);+ ip += (size_t)matchCode + MINMATCH;+ DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH);+ }++ if ((outputDirective) && /* Check output buffer overflow */+ (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) {+ if (outputDirective == fillOutput) {+ /* Match description too long : reduce it */+ U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255;+ ip -= matchCode - newMatchCode;+ assert(newMatchCode < matchCode);+ matchCode = newMatchCode;+ if (unlikely(ip <= filledIp)) {+ /* We have already filled up to filledIp so if ip ends up less than filledIp+ * we have positions in the hash table beyond the current position. This is+ * a problem if we reuse the hash table. So we have to remove these positions+ * from the hash table.+ */+ const BYTE* ptr;+ DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip));+ for (ptr = ip; ptr <= filledIp; ++ptr) {+ U32 const h = LZ4_hashPosition(ptr, tableType);+ LZ4_clearHash(h, cctx->hashTable, tableType);+ }+ }+ } else {+ assert(outputDirective == limitedOutput);+ return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */+ }+ }+ if (matchCode >= ML_MASK) {+ *token += ML_MASK;+ matchCode -= ML_MASK;+ LZ4_write32(op, 0xFFFFFFFF);+ while (matchCode >= 4*255) {+ op+=4;+ LZ4_write32(op, 0xFFFFFFFF);+ matchCode -= 4*255;+ }+ op += matchCode / 255;+ *op++ = (BYTE)(matchCode % 255);+ } else+ *token += (BYTE)(matchCode);+ }+ /* Ensure we have enough space for the last literals. */+ assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit));++ anchor = ip;++ /* Test end of chunk */+ if (ip >= mflimitPlusOne) break;++ /* Fill table */+ LZ4_putPosition(ip-2, cctx->hashTable, tableType, base);++ /* Test next position */+ if (tableType == byPtr) {++ match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);+ LZ4_putPosition(ip, cctx->hashTable, tableType, base);+ if ( (match+LZ4_DISTANCE_MAX >= ip)+ && (LZ4_read32(match) == LZ4_read32(ip)) )+ { token=op++; *token=0; goto _next_match; }++ } else { /* byU32, byU16 */++ U32 const h = LZ4_hashPosition(ip, tableType);+ U32 const current = (U32)(ip-base);+ U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);+ assert(matchIndex < current);+ if (dictDirective == usingDictCtx) {+ if (matchIndex < startIndex) {+ /* there was no match, try the dictionary */+ matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);+ match = dictBase + matchIndex;+ lowLimit = dictionary; /* required for match length counter */+ matchIndex += dictDelta;+ } else {+ match = base + matchIndex;+ lowLimit = (const BYTE*)source; /* required for match length counter */+ }+ } else if (dictDirective==usingExtDict) {+ if (matchIndex < startIndex) {+ assert(dictBase);+ match = dictBase + matchIndex;+ lowLimit = dictionary; /* required for match length counter */+ } else {+ match = base + matchIndex;+ lowLimit = (const BYTE*)source; /* required for match length counter */+ }+ } else { /* single memory segment */+ match = base + matchIndex;+ }+ LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);+ assert(matchIndex < current);+ if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)+ && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current))+ && (LZ4_read32(match) == LZ4_read32(ip)) ) {+ token=op++;+ *token=0;+ if (maybe_extMem) offset = current - matchIndex;+ DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i",+ (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));+ goto _next_match;+ }+ }++ /* Prepare next loop */+ forwardH = LZ4_hashPosition(++ip, tableType);++ }++_last_literals:+ /* Encode Last Literals */+ { size_t lastRun = (size_t)(iend - anchor);+ if ( (outputDirective) && /* Check output buffer overflow */+ (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) {+ if (outputDirective == fillOutput) {+ /* adapt lastRun to fill 'dst' */+ assert(olimit >= op);+ lastRun = (size_t)(olimit-op) - 1/*token*/;+ lastRun -= (lastRun + 256 - RUN_MASK) / 256; /*additional length tokens*/+ } else {+ assert(outputDirective == limitedOutput);+ return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */+ }+ }+ DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun);+ if (lastRun >= RUN_MASK) {+ size_t accumulator = lastRun - RUN_MASK;+ *op++ = RUN_MASK << ML_BITS;+ for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;+ *op++ = (BYTE) accumulator;+ } else {+ *op++ = (BYTE)(lastRun<<ML_BITS);+ }+ LZ4_memcpy(op, anchor, lastRun);+ ip = anchor + lastRun;+ op += lastRun;+ }++ if (outputDirective == fillOutput) {+ *inputConsumed = (int) (((const char*)ip)-source);+ }+ result = (int)(((char*)op) - dest);+ assert(result > 0);+ DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result);+ return result;+}++/** LZ4_compress_generic() :+ * inlined, to ensure branches are decided at compilation time;+ * takes care of src == (NULL, 0)+ * and forward the rest to LZ4_compress_generic_validated */+LZ4_FORCE_INLINE int LZ4_compress_generic(+ LZ4_stream_t_internal* const cctx,+ const char* const src,+ char* const dst,+ const int srcSize,+ int *inputConsumed, /* only written when outputDirective == fillOutput */+ const int dstCapacity,+ const limitedOutput_directive outputDirective,+ const tableType_t tableType,+ const dict_directive dictDirective,+ const dictIssue_directive dictIssue,+ const int acceleration)+{+ DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i",+ srcSize, dstCapacity);++ if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported srcSize, too large (or negative) */+ if (srcSize == 0) { /* src == NULL supported if srcSize == 0 */+ if (outputDirective != notLimited && dstCapacity <= 0) return 0; /* no output, can't write anything */+ DEBUGLOG(5, "Generating an empty block");+ assert(outputDirective == notLimited || dstCapacity >= 1);+ assert(dst != NULL);+ dst[0] = 0;+ if (outputDirective == fillOutput) {+ assert (inputConsumed != NULL);+ *inputConsumed = 0;+ }+ return 1;+ }+ assert(src != NULL);++ return LZ4_compress_generic_validated(cctx, src, dst, srcSize,+ inputConsumed, /* only written into if outputDirective == fillOutput */+ dstCapacity, outputDirective,+ tableType, dictDirective, dictIssue, acceleration);+}+++int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)+{+ LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse;+ assert(ctx != NULL);+ if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;+ if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;+ if (maxOutputSize >= LZ4_compressBound(inputSize)) {+ if (inputSize < LZ4_64Klimit) {+ return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration);+ } else {+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;+ return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);+ }+ } else {+ if (inputSize < LZ4_64Klimit) {+ return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);+ } else {+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;+ return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);+ }+ }+}++/**+ * LZ4_compress_fast_extState_fastReset() :+ * A variant of LZ4_compress_fast_extState().+ *+ * Using this variant avoids an expensive initialization step. It is only safe+ * to call if the state buffer is known to be correctly initialized already+ * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of+ * "correctly initialized").+ */+int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)+{+ LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse;+ if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;+ if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;++ if (dstCapacity >= LZ4_compressBound(srcSize)) {+ if (srcSize < LZ4_64Klimit) {+ const tableType_t tableType = byU16;+ LZ4_prepareTable(ctx, srcSize, tableType);+ if (ctx->currentOffset) {+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration);+ } else {+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);+ }+ } else {+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;+ LZ4_prepareTable(ctx, srcSize, tableType);+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);+ }+ } else {+ if (srcSize < LZ4_64Klimit) {+ const tableType_t tableType = byU16;+ LZ4_prepareTable(ctx, srcSize, tableType);+ if (ctx->currentOffset) {+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);+ } else {+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);+ }+ } else {+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;+ LZ4_prepareTable(ctx, srcSize, tableType);+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);+ }+ }+}+++int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)+{+ int result;+#if (LZ4_HEAPMODE)+ 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;+ LZ4_stream_t* const ctxPtr = &ctx;+#endif+ result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);++#if (LZ4_HEAPMODE)+ FREEMEM(ctxPtr);+#endif+ return result;+}+++int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize)+{+ return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1);+}+++/* Note!: This function leaves the stream in an unclean/broken state!+ * It is not safe to subsequently use the same state with a _fastReset() or+ * _continue() call without resetting it. */+static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)+{+ void* const s = LZ4_initStream(state, sizeof (*state));+ assert(s != NULL); (void)s;++ if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */+ return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);+ } else {+ if (*srcSizePtr < LZ4_64Klimit) {+ return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1);+ } else {+ tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;+ return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1);+ } }+}+++int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)+{+#if (LZ4_HEAPMODE)+ LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */+ if (ctx == NULL) return 0;+#else+ LZ4_stream_t ctxBody;+ LZ4_stream_t* ctx = &ctxBody;+#endif++ int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);++#if (LZ4_HEAPMODE)+ FREEMEM(ctx);+#endif+ return result;+}++++/*-******************************+* 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(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)+{+#if LZ4_ALIGN_TEST+ typedef struct { char c; LZ4_stream_t t; } t_a;+ return sizeof(t_a) - sizeof(LZ4_stream_t);+#else+ return 1; /* effectively disabled */+#endif+}++LZ4_stream_t* LZ4_initStream (void* buffer, size_t size)+{+ DEBUGLOG(5, "LZ4_initStream");+ if (buffer == NULL) { return NULL; }+ if (size < sizeof(LZ4_stream_t)) { return NULL; }+ if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL;+ MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal));+ return (LZ4_stream_t*)buffer;+}++/* resetStream is now deprecated,+ * prefer initStream() which is more general */+void LZ4_resetStream (LZ4_stream_t* LZ4_stream)+{+ DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream);+ MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal));+}++void LZ4_resetStream_fast(LZ4_stream_t* ctx) {+ 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 */+ DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream);+ FREEMEM(LZ4_stream);+ return (0);+}+#endif+++#define HASH_UNIT sizeof(reg_t)+int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)+{+ LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse;+ const tableType_t tableType = byU32;+ const BYTE* p = (const BYTE*)dictionary;+ const BYTE* const dictEnd = p + dictSize;+ const BYTE* base;++ DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict);++ /* It's necessary to reset the context,+ * and not just continue it with prepareTable()+ * to avoid any risk of generating overflowing matchIndex+ * when compressing using this dictionary */+ LZ4_resetStream(LZ4_dict);++ /* We always increment the offset by 64 KB, since, if the dict is longer,+ * we truncate it to the last 64k, and if it's shorter, we still want to+ * advance by a whole window length so we can provide the guarantee that+ * there are only valid offsets in the window, which allows an optimization+ * in LZ4_compress_fast_continue() where it uses noDictIssue even when the+ * dictionary isn't a full 64k. */+ dict->currentOffset += 64 KB;++ if (dictSize < (int)HASH_UNIT) {+ return 0;+ }++ if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;+ base = dictEnd - dict->currentOffset;+ dict->dictionary = p;+ dict->dictSize = (U32)(dictEnd - p);+ dict->tableType = (U32)tableType;++ while (p <= dictEnd-HASH_UNIT) {+ LZ4_putPosition(p, dict->hashTable, tableType, base);+ p+=3;+ }++ 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 :+ &(dictionaryStream->internal_donotuse);++ DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)",+ workingStream, dictionaryStream,+ dictCtx != NULL ? dictCtx->dictSize : 0);++ if (dictCtx != NULL) {+ /* If the current offset is zero, we will never look in the+ * external dictionary context, since there is no value a table+ * entry can take that indicate a miss. In that case, we need+ * to bump the offset to something non-zero.+ */+ if (workingStream->internal_donotuse.currentOffset == 0) {+ workingStream->internal_donotuse.currentOffset = 64 KB;+ }++ /* Don't actually attach an empty dictionary.+ */+ if (dictCtx->dictSize == 0) {+ dictCtx = NULL;+ }+ }+ workingStream->internal_donotuse.dictCtx = dictCtx;+}+++static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize)+{+ assert(nextSize >= 0);+ if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */+ /* rescale hash table */+ U32 const delta = LZ4_dict->currentOffset - 64 KB;+ const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;+ int i;+ DEBUGLOG(4, "LZ4_renormDictT");+ for (i=0; i<LZ4_HASH_SIZE_U32; i++) {+ if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0;+ else LZ4_dict->hashTable[i] -= delta;+ }+ LZ4_dict->currentOffset = 64 KB;+ if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;+ LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;+ }+}+++int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream,+ const char* source, char* dest,+ int inputSize, int maxOutputSize,+ int acceleration)+{+ const tableType_t tableType = byU32;+ 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, dictSize=%u)", inputSize, streamPtr->dictSize);++ 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 < 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 = source;+ }++ /* Check overlapping input/dictionary space */+ { 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 = (const BYTE*)dictEnd - streamPtr->dictSize;+ }+ }++ /* prefix mode : source data follows dictionary */+ 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+ return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);+ }++ /* external dictionary mode */+ { int result;+ if (streamPtr->dictCtx) {+ /* We depend here on the fact that dictCtx'es (produced by+ * LZ4_loadDict) guarantee that their tables contain no references+ * to offsets between dictCtx->currentOffset - 64 KB and+ * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe+ * to use noDictIssue even when the dict isn't a full 64 KB.+ */+ if (inputSize > 4 KB) {+ /* For compressing large blobs, it is faster to pay the setup+ * cost to copy the dictionary's tables into the active context,+ * so that the compression loop is only looking into one table.+ */+ LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr));+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);+ } else {+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);+ }+ } 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 {+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);+ }+ }+ streamPtr->dictionary = (const BYTE*)source;+ streamPtr->dictSize = (U32)inputSize;+ return result;+ }+}+++/* Hidden debug function, to force-test external dictionary mode */+int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)+{+ LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse;+ int result;++ LZ4_renormDictT(streamPtr, srcSize);++ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {+ result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1);+ } else {+ result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);+ }++ streamPtr->dictionary = (const BYTE*)source;+ streamPtr->dictSize = (U32)srcSize;++ return result;+}+++/*! 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 : 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;++ 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) {+ 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;++ return dictSize;+}++++/*-*******************************+ * Decompression functions+ ********************************/++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 : 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)+{+ 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 (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;+}++/*! LZ4_decompress_generic() :+ * This generic decompression function covers all use cases.+ * It shall be instantiated several times, using different sets of directives.+ * Note that it is important for performance that this function really get inlined,+ * in order to remove useless branches during compilation optimization.+ */+LZ4_FORCE_INLINE int+LZ4_decompress_generic(+ const char* const src,+ char* const dst,+ int srcSize,+ int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */++ earlyEnd_directive partialDecoding, /* full, partial */+ dict_directive dict, /* noDict, withPrefix64k, usingExtDict */+ const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */+ const BYTE* const dictStart, /* only if dict==usingExtDict */+ const size_t dictSize /* note : = 0 if noDict */+ )+{+ if ((src == NULL) || (outputSize < 0)) { return -1; }++ { const BYTE* ip = (const BYTE*) src;+ const BYTE* const iend = ip + srcSize;++ BYTE* op = (BYTE*) dst;+ BYTE* const oend = op + outputSize;+ BYTE* cpy;++ const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize;++ const int checkOffset = (dictSize < (int)(64 KB));+++ /* Set up the "end" pointers for the shortcut. */+ const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/;+ const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/;++ const BYTE* match;+ size_t offset;+ unsigned token;+ size_t length;+++ DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize);++ /* Special cases */+ assert(lowPrefix <= op);+ if (unlikely(outputSize==0)) {+ /* Empty output buffer */+ if (partialDecoding) return 0;+ return ((srcSize==1) && (*ip==0)) ? 0 : -1;+ }+ if (unlikely(srcSize==0)) { return -1; }++ /* 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 < oend-FASTLOOP_SAFE_DISTANCE */+ while (1) {+ /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */+ assert(oend - op >= FASTLOOP_SAFE_DISTANCE);+ assert(ip < iend);+ token = *ip++;+ length = token >> ML_BITS; /* literal length */++ /* decode literal length */+ if (length == RUN_MASK) {+ 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 ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }+ LZ4_wildCopy32(op, ip, cpy);+ ip += length; op = cpy;+ } else {+ cpy = op+length;+ 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); /* overflow check */++ /* get matchlength */+ length = token & ML_MASK;++ if (length == ML_MASK) {+ 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;+ }+ } else {+ length += MINMATCH;+ if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {+ goto safe_match_copy;+ }++ /* Fastpath check: skip LZ4_wildCopy32 when true */+ if ((dict == withPrefix64k) || (match >= lowPrefix)) {+ if (offset >= 8) {+ assert(match >= lowPrefix);+ assert(match <= op);+ assert(op + 18 <= oend);++ LZ4_memcpy(op, match, 8);+ LZ4_memcpy(op+8, match+8, 8);+ LZ4_memcpy(op+16, match+16, 2);+ op += length;+ continue;+ } } }++ 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");+ length = MIN(length, (size_t)(oend-op));+ } else {+ goto _output_error; /* end-of-block condition violated */+ } }++ if (length <= (size_t)(lowPrefix-match)) {+ /* match fits entirely within external dictionary : just copy */+ LZ4_memmove(op, dictEnd - (lowPrefix-match), length);+ op += length;+ } else {+ /* match stretches into both external dictionary and current block */+ size_t const copySize = (size_t)(lowPrefix - match);+ size_t const restSize = length - copySize;+ LZ4_memcpy(op, dictEnd - copySize, copySize);+ op += copySize;+ if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */+ BYTE* const endOfMatch = op + restSize;+ const BYTE* copyFrom = lowPrefix;+ while (op < endOfMatch) { *op++ = *copyFrom++; }+ } else {+ LZ4_memcpy(op, lowPrefix, restSize);+ op += restSize;+ } }+ continue;+ }++ /* copy match within block */+ cpy = op + length;++ assert((op <= oend) && (oend-op >= 32));+ if (unlikely(offset<16)) {+ LZ4_memcpy_using_offset(op, match, cpy, offset);+ } else {+ LZ4_wildCopy32(op, match, cpy);+ }++ op = cpy; /* wildcopy correction */+ }+ safe_decode:+#endif++ /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */+ while (1) {+ assert(ip < iend);+ token = *ip++;+ length = token >> ML_BITS; /* literal length */++ /* 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+ * (in the fast mode, only 8 bytes can be safely copied this way).+ * 2) Further if the match length is 4..18, copy 18 bytes in a similar+ * manner; but we ensure that there's enough space in the output for+ * those 18 bytes earlier, upon entering the shortcut (in other words,+ * there is a combined check for both stages).+ */+ if ( (length != RUN_MASK)+ /* strictly "less than" on input, to re-enter the loop with at least one byte */+ && likely((ip < shortiend) & (op <= shortoend)) ) {+ /* Copy the literals */+ LZ4_memcpy(op, ip, 16);+ op += length; ip += length;++ /* The second stage: prepare for match copying, decode full info.+ * If it doesn't work out, the info won't be wasted. */+ length = token & ML_MASK; /* match length */+ offset = LZ4_readLE16(ip); ip += 2;+ match = op - offset;+ assert(match <= op); /* check overflow */++ /* Do not deal with overlapping matches. */+ if ( (length != ML_MASK)+ && (offset >= 8)+ && (dict==withPrefix64k || match >= lowPrefix) ) {+ /* Copy the match. */+ LZ4_memcpy(op + 0, match + 0, 8);+ LZ4_memcpy(op + 8, match + 8, 8);+ LZ4_memcpy(op +16, match +16, 2);+ op += length + MINMATCH;+ /* Both stages worked, load the next token. */+ continue;+ }++ /* The second stage didn't work out, but the info is ready.+ * Propel it right to the point of match copying. */+ goto _copy_match;+ }++ /* decode literal length */+ if (length == RUN_MASK) {+ 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;+#if LZ4_FAST_DEC_LOOP+ safe_literal_copy:+#endif+ LZ4_STATIC_ASSERT(MFLIMIT >= 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).+ * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow.+ */+ if (partialDecoding) {+ /* 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.+ */+ 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));+ DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip));+ /* Finishing in the middle of a literals segment,+ * due to lack of input.+ */+ if (ip+length > iend) {+ length = (size_t)(iend-ip);+ cpy = op + length;+ }+ /* Finishing in the middle of a literals segment,+ * due to lack of output space.+ */+ if (cpy > oend) {+ cpy = oend;+ assert(op<=oend);+ length = (size_t)(oend-op);+ }+ } else {+ /* 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 ((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;+ }+ }+ LZ4_memmove(op, ip, length); /* supports overlapping memory regions, for in-place decompression scenarios */+ ip += length;+ op += length;+ /* Necessarily EOF when !partialDecoding.+ * When partialDecoding, it is EOF if we've either+ * filled the output buffer or+ * can't proceed with reading an offset for following match.+ */+ if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) {+ break;+ }+ } else {+ LZ4_wildCopy8(op, ip, cpy); /* can overwrite up to 8 bytes beyond cpy */+ ip += length; op = cpy;+ }++ /* get offset */+ offset = LZ4_readLE16(ip); ip+=2;+ match = op - offset;++ /* get matchlength */+ length = token & ML_MASK;++ _copy_match:+ if (length == ML_MASK) {+ 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;++#if LZ4_FAST_DEC_LOOP+ safe_match_copy:+#endif+ 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 */+ }++ if (length <= (size_t)(lowPrefix-match)) {+ /* match fits entirely within external dictionary : just copy */+ LZ4_memmove(op, dictEnd - (lowPrefix-match), length);+ op += length;+ } else {+ /* match stretches into both external dictionary and current block */+ size_t const copySize = (size_t)(lowPrefix - match);+ size_t const restSize = length - copySize;+ LZ4_memcpy(op, dictEnd - copySize, copySize);+ op += copySize;+ if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */+ BYTE* const endOfMatch = op + restSize;+ const BYTE* copyFrom = lowPrefix;+ while (op < endOfMatch) *op++ = *copyFrom++;+ } else {+ LZ4_memcpy(op, lowPrefix, restSize);+ op += restSize;+ } }+ continue;+ }+ assert(match >= lowPrefix);++ /* copy match within block */+ cpy = op + length;++ /* partialDecoding : may end anywhere within the block */+ assert(op<=oend);+ if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {+ size_t const mlen = MIN(length, (size_t)(oend-op));+ const BYTE* const matchEnd = match + mlen;+ BYTE* const copyEnd = op + mlen;+ if (matchEnd > op) { /* overlap copy */+ while (op < copyEnd) { *op++ = *match++; }+ } else {+ LZ4_memcpy(op, match, mlen);+ }+ op = copyEnd;+ if (op == oend) { break; }+ continue;+ }++ if (unlikely(offset<8)) {+ LZ4_write32(op, 0); /* silence msan warning when offset==0 */+ op[0] = match[0];+ op[1] = match[1];+ op[2] = match[2];+ op[3] = match[3];+ match += inc32table[offset];+ LZ4_memcpy(op+4, match, 4);+ match -= dec64table[offset];+ } else {+ LZ4_memcpy(op, match, 8);+ match += 8;+ }+ op += 8;++ if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {+ BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1);+ if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */+ if (op < oCopyLimit) {+ LZ4_wildCopy8(op, match, oCopyLimit);+ match += oCopyLimit - op;+ op = oCopyLimit;+ }+ while (op < cpy) { *op++ = *match++; }+ } else {+ LZ4_memcpy(op, match, 8);+ if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); }+ }+ op = cpy; /* wildcopy correction */+ }++ /* end of decoding */+ DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst));+ return (int) (((char*)op)-dst); /* Nb of output bytes decoded */++ /* Overflow error detected */+ _output_error:+ return (int) (-(((const char*)ip)-src))-1;+ }+}+++/*===== Instantiate the API decoding functions. =====*/++LZ4_FORCE_O2+int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)+{+ return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,+ decode_full_block, noDict,+ (BYTE*)dest, NULL, 0);+}++LZ4_FORCE_O2+int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity)+{+ dstCapacity = MIN(targetOutputSize, dstCapacity);+ return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity,+ partial_decode,+ noDict, (BYTE*)dst, NULL, 0);+}++LZ4_FORCE_O2+int LZ4_decompress_fast(const char* source, char* dest, int originalSize)+{+ 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. =====*/++LZ4_FORCE_O2 /* Exported, an obsolete API function. */+int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)+{+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,+ 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)+{+ return LZ4_decompress_unsafe_generic(+ (const BYTE*)source, (BYTE*)dest, originalSize,+ 64 KB, NULL, 0);+}++LZ4_FORCE_O2+static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize,+ size_t prefixSize)+{+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,+ 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,+ 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_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+ * of the dictionary is passed as prefix, and the second via dictStart + dictSize.+ * These routines are used only once, in LZ4_decompress_*_continue().+ */+LZ4_FORCE_INLINE+int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize,+ size_t prefixSize, const void* dictStart, size_t dictSize)+{+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,+ 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_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)+{+ if (LZ4_stream == NULL) { return 0; } /* support free on NULL */+ FREEMEM(LZ4_stream);+ return 0;+}+#endif++/*! LZ4_setStreamDecode() :+ * Use this function to instruct where to find the dictionary.+ * This function is not necessary if previous data is still available where it was decoded.+ * Loading a size of 0 is allowed (same effect as no dictionary).+ * @return : 1 if OK, 0 if error+ */+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;+ 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;+}++/*! LZ4_decoderRingBufferSize() :+ * when setting a ring buffer for streaming decompression (optional scenario),+ * provides the minimum size of this ring buffer+ * to be compatible with any source respecting maxBlockSize condition.+ * Note : in a ring buffer scenario,+ * blocks are presumed decompressed next to each other.+ * When not enough space remains for next block (remainingSize < maxBlockSize),+ * decoding resumes from beginning of ring buffer.+ * @return : minimum ring buffer size,+ * or 0 if there is an error (invalid maxBlockSize).+ */+int LZ4_decoderRingBufferSize(int maxBlockSize)+{+ if (maxBlockSize < 0) return 0;+ if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0;+ if (maxBlockSize < 16) maxBlockSize = 16;+ return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize);+}++/*+*_continue() :+ These decoding functions allow decompression of multiple blocks in "streaming" mode.+ Previously decoded blocks must still be available at the memory position where they were decoded.+ If it's not possible, save the relevant part of decoded data into a safe buffer,+ and indicate where it stands using LZ4_setStreamDecode()+*/+LZ4_FORCE_O2+int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)+{+ LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;+ int result;++ if (lz4sd->prefixSize == 0) {+ /* The first call, no dictionary yet. */+ assert(lz4sd->extDictSize == 0);+ result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);+ if (result <= 0) return result;+ lz4sd->prefixSize = (size_t)result;+ lz4sd->prefixEnd = (BYTE*)dest + result;+ } else if (lz4sd->prefixEnd == (BYTE*)dest) {+ /* They're rolling the current segment. */+ if (lz4sd->prefixSize >= 64 KB - 1)+ result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);+ else if (lz4sd->extDictSize == 0)+ result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize,+ lz4sd->prefixSize);+ else+ result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize,+ lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);+ if (result <= 0) return result;+ lz4sd->prefixSize += (size_t)result;+ lz4sd->prefixEnd += result;+ } else {+ /* The buffer wraps around, or they're switching to another buffer. */+ lz4sd->extDictSize = lz4sd->prefixSize;+ lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;+ result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize,+ lz4sd->externalDict, lz4sd->extDictSize);+ if (result <= 0) return result;+ lz4sd->prefixSize = (size_t)result;+ lz4sd->prefixEnd = (BYTE*)dest + result;+ }++ return result;+}++LZ4_FORCE_O2 int+LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode,+ const char* source, char* dest, int originalSize)+{+ 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) {+ 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,+ lz4sd->externalDict, lz4sd->extDictSize);+ if (result <= 0) return result;+ lz4sd->prefixSize = (size_t)originalSize;+ lz4sd->prefixEnd = (BYTE*)dest + originalSize;+ }++ return result;+}+++/*+Advanced decoding functions :+*_usingDict() :+ These decoding functions work the same as "_continue" ones,+ the dictionary must be explicitly provided within parameters+*/++int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)+{+ if (dictSize==0)+ return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);+ if (dictStart+dictSize == dest) {+ if (dictSize >= 64 KB - 1) {+ return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);+ }+ assert(dictSize >= 0);+ return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize);+ }+ assert(dictSize >= 0);+ 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_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);+}+++/*=*************************************************+* Obsolete Functions+***************************************************/+/* obsolete compression functions */+int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)+{+ return LZ4_compress_default(source, dest, inputSize, maxOutputSize);+}+int LZ4_compress(const char* src, char* dest, int srcSize)+{+ return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize));+}+int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize)+{+ return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1);+}+int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize)+{+ return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1);+}+int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity)+{+ return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1);+}+int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize)+{+ return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1);+}++/*+These decompression functions are deprecated and should no longer be used.+They are only provided here for compatibility with older user programs.+- LZ4_uncompress is totally equivalent to LZ4_decompress_fast+- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe+*/+int LZ4_uncompress (const char* source, char* dest, int outputSize)+{+ return LZ4_decompress_fast(source, dest, outputSize);+}+int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize)+{+ return LZ4_decompress_safe(source, dest, isize, maxOutputSize);+}++/* Obsolete Streaming functions */++int LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); }++int LZ4_resetStreamState(void* state, char* inputBuffer)+{+ (void)inputBuffer;+ LZ4_resetStream((LZ4_stream_t*)state);+ 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 2 /* 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 **************************************/@@ -186,7 +227,8 @@ The larger the acceleration value, the faster the algorithm, but also the lesser the compression. It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. An acceleration value of "1" is the same as regular LZ4_compress_default()- Values <= 0 will be replaced by ACCELERATION_DEFAULT (currently == 1, see lz4.c).+ Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).+ Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c). */ LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); @@ -212,7 +254,18 @@ * New value is necessarily <= input value. * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) * or 0 if compression fails.-*/+ *+ * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+):+ * the produced compressed content could, in specific circumstances,+ * require to be decompressed into a destination buffer larger+ * by at least 1 byte than the content to decompress.+ * If an application uses `LZ4_compress_destSize()`,+ * it's highly recommended to update liblz4 to v1.9.2 or better.+ * If this can't be done or ensured,+ * the receiving decompression function should provide+ * a dstCapacity which is > decompressedSize, by at least 1 byte.+ * See https://github.com/lz4/lz4/issues/859 for details+ */ LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); @@ -220,25 +273,35 @@ * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', * into destination buffer 'dst' of size 'dstCapacity'. * Up to 'targetOutputSize' bytes will be decoded.- * The function stops decoding on reaching this objective,- * which can boost performance when only the beginning of a block is required.+ * The function stops decoding on reaching this objective.+ * This can be useful to boost performance+ * whenever only the beginning of a block is required. *- * @return : the number of bytes decoded in `dst` (necessarily <= dstCapacity)+ * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize) * If source stream is detected malformed, function returns a negative result. *- * Note : @return can be < targetOutputSize, if compressed block contains less data.+ * Note 1 : @return can be < targetOutputSize, if compressed block contains less data. *- * Note 2 : this function features 2 parameters, targetOutputSize and dstCapacity,- * and expects targetOutputSize <= dstCapacity.- * It effectively stops decoding on reaching targetOutputSize,+ * Note 2 : targetOutputSize must be <= dstCapacity+ *+ * Note 3 : this function effectively stops decoding on reaching targetOutputSize, * so dstCapacity is kind of redundant.- * This is because in a previous version of this function,- * decoding operation would not "break" a sequence in the middle.- * As a consequence, there was no guarantee that decoding would stop at exactly targetOutputSize,+ * This is because in older versions of this function,+ * decoding operation would still write complete sequences.+ * Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize, * it could write more bytes, though only up to dstCapacity. * Some "margin" used to be required for this operation to work properly.- * This is no longer necessary.- * The function nonetheless keeps its signature, in an effort to not break API.+ * Thankfully, this is no longer necessary.+ * The function nonetheless keeps the same signature, in an effort to preserve API compatibility.+ *+ * Note 4 : If srcSize is the exact size of the block,+ * then targetOutputSize can be any value,+ * including larger than the block's decompressed size.+ * The function will, at most, generate block's decompressed size.+ *+ * Note 5 : If srcSize is _larger_ than block's compressed size,+ * then targetOutputSize **MUST** be <= block's decompressed size.+ * Otherwise, *silent corruption will occur*. */ LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); @@ -248,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@@ -333,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.@@ -384,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() :@@ -395,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 */ @@ -474,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.@@ -547,75 +645,53 @@ #define LZ4_H_98237428734687 /*-************************************************************- * PRIVATE DEFINITIONS+ * Private Definitions ************************************************************** * Do not use these definitions directly. * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.- * Accessing members will expose code to API and/or ABI break in future versions of the library.+ * Accessing members will expose user code to API and/or ABI break in future versions of the library. **************************************************************/ #define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) #define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) #define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ #if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)-#include <stdint.h>--typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;-struct LZ4_stream_t_internal {- uint32_t hashTable[LZ4_HASH_SIZE_U32];- uint32_t currentOffset;- uint16_t dirty;- uint16_t tableType;- const uint8_t* dictionary;- const LZ4_stream_t_internal* dictCtx;- uint32_t dictSize;-};--typedef struct {- const uint8_t* externalDict;- size_t extDictSize;- const uint8_t* prefixEnd;- size_t prefixSize;-} LZ4_streamDecode_t_internal;-+# include <stdint.h>+ typedef int8_t LZ4_i8;+ typedef uint8_t LZ4_byte;+ typedef uint16_t LZ4_u16;+ typedef uint32_t LZ4_u32; #else+ typedef signed char LZ4_i8;+ typedef unsigned char LZ4_byte;+ typedef unsigned short LZ4_u16;+ 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 {- unsigned int hashTable[LZ4_HASH_SIZE_U32];- unsigned int currentOffset;- unsigned short dirty;- unsigned short tableType;- const unsigned char* dictionary;+ LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];+ const LZ4_byte* dictionary; const LZ4_stream_t_internal* dictCtx;- unsigned int dictSize;+ LZ4_u32 currentOffset;+ LZ4_u32 tableType;+ LZ4_u32 dictSize;+ /* Implicit padding to ensure structure is aligned */ }; -typedef struct {- const unsigned char* externalDict;- const unsigned char* prefixEnd;- size_t extDictSize;- size_t prefixSize;-} LZ4_streamDecode_t_internal;--#endif--/*! LZ4_stream_t :- * information structure to track an LZ4 stream.- * 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 a future version.- */-#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4 + ((sizeof(void*)==16) ? 4 : 0) /*AS-400*/ )-#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))+#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */ union LZ4_stream_u {- unsigned long long table[LZ4_STREAMSIZE_U64];+ char minStateSize[LZ4_STREAM_MINSIZE]; LZ4_stream_t_internal internal_donotuse;-} ; /* previously typedef'd to LZ4_stream_t */+}; /* previously typedef'd to LZ4_stream_t */ + /*! LZ4_initStream() : v1.9.0+ * An LZ4_stream_t structure must be initialized at least once. * This is automatically done when invoking LZ4_createStream(),@@ -629,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 */ @@ -667,22 +747,21 @@ #ifdef LZ4_DISABLE_DEPRECATE_WARNINGS # define LZ4_DEPRECATED(message) /* disable deprecation warnings */ #else-# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ # define LZ4_DEPRECATED(message) [[deprecated(message)]]-# elif (LZ4_GCC_VERSION >= 405) || defined(__clang__)-# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))-# elif (LZ4_GCC_VERSION >= 301)-# define LZ4_DEPRECATED(message) __attribute__((deprecated)) # elif defined(_MSC_VER) # define LZ4_DEPRECATED(message) __declspec(deprecated(message))+# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))+# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))+# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)+# define LZ4_DEPRECATED(message) __attribute__((deprecated)) # else-# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")-# define LZ4_DEPRECATED(message)+# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler")+# define LZ4_DEPRECATED(message) /* disabled */ # endif #endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ -/* Obsolete compression functions */+/*! Obsolete compression functions (since v1.7.3) */ LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize); LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize); LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);@@ -690,11 +769,12 @@ LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); -/* Obsolete decompression functions */+/*! Obsolete decompression functions (since v1.8.0) */ LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); -/* Obsolete streaming functions; degraded functionality; do not use!+/* Obsolete streaming functions (since v1.7.0)+ * degraded functionality; do not use! * * In order to perform streaming compression, these functions depended on data * that is no longer tracked in the state. They have been preserved as well as@@ -708,23 +788,22 @@ LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); -/* Obsolete streaming decoding functions */+/*! Obsolete streaming decoding functions (since v1.7.0) */ LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); -/*! LZ4_decompress_fast() : **unsafe!**+/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) : * These functions used to be faster than LZ4_decompress_safe(),- * but it has changed, and they are now slower than LZ4_decompress_safe().+ * but this is no longer the case. They are now slower. * This is because LZ4_decompress_fast() doesn't know the input size,- * and therefore must progress more cautiously in the input buffer to not read beyond the end of block.+ * and therefore must progress more cautiously into the input buffer to not read beyond the end of block. * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability. * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated. * * The last remaining LZ4_decompress_fast() specificity is that * it can decompress a block without knowing its compressed size.- * Such functionality could be achieved in a more secure manner,- * by also providing the maximum size of input buffer,- * but it would require new prototypes, and adaptation of the implementation to this new use case.+ * Such functionality can be achieved in a more secure manner+ * by employing LZ4_decompress_safe_partial(). * * Parameters: * originalSize : is the uncompressed size to regenerate.@@ -739,7 +818,6 @@ * But they may happen if input data is invalid (error or intentional tampering). * As a consequence, use these functions in trusted environments with trusted data **only**. */- LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead")
+ cbits/lz4frame.c view
@@ -0,0 +1,2078 @@+/*+ * LZ4 auto-framing library+ * Copyright (C) 2011-2016, 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+ * modification, are permitted provided that the following conditions are+ * met:+ *+ * - Redistributions of source code must retain the above copyright+ * notice, this list of conditions and the following disclaimer.+ * - Redistributions in binary form must reproduce the above+ * copyright notice, this list of conditions and the following disclaimer+ * in the documentation and/or other materials provided with the+ * distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * You can contact the author at :+ * - LZ4 homepage : http://www.lz4.org+ * - LZ4 source repository : https://github.com/lz4/lz4+ */++/* LZ4F is a stand-alone API to create LZ4-compressed Frames+ * in full conformance with specification v1.6.1 .+ * This library rely upon memory management capabilities (malloc, free)+ * provided either by <stdlib.h>,+ * or redirected towards another library of user's choice+ * (see Memory Routines below).+ */+++/*-************************************+* Compiler Options+**************************************/+#ifdef _MSC_VER /* Visual Studio */+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */+#endif+++/*-************************************+* Tuning parameters+**************************************/+/*+ * LZ4F_HEAPMODE :+ * Select how default compression functions will allocate memory for their hash table,+ * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).+ */+#ifndef LZ4F_HEAPMODE+# define LZ4F_HEAPMODE 0+#endif+++/*-************************************+* 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+**************************************/+/*+ * User may redirect invocations of+ * 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)+# define ALLOC_AND_ZERO(s) calloc(1,(s))+# define FREEMEM(p) free(p)+#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);+}++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);+}+++/*-************************************+* Debug+**************************************/+#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)+# include <assert.h>+#else+# ifndef assert+# define assert(condition) ((void)0)+# endif+#endif++#define LZ4F_STATIC_ASSERT(c) { enum { LZ4F_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */++#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) && !defined(DEBUGLOG)+# include <stdio.h>+static int g_debuglog_enable = 1;+# define DEBUGLOG(l, ...) { \+ if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \+ fprintf(stderr, __FILE__ ": "); \+ fprintf(stderr, __VA_ARGS__); \+ fprintf(stderr, " \n"); \+ } }+#else+# define DEBUGLOG(l, ...) {} /* disabled */+#endif+++/*-************************************+* Basic Types+**************************************/+#if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )+# include <stdint.h>+ typedef uint8_t BYTE;+ typedef uint16_t U16;+ typedef uint32_t U32;+ typedef int32_t S32;+ typedef uint64_t U64;+#else+ typedef unsigned char BYTE;+ typedef unsigned short U16;+ typedef unsigned int U32;+ typedef signed int S32;+ typedef unsigned long long U64;+#endif+++/* unoptimized version; solves endianness & alignment issues */+static U32 LZ4F_readLE32 (const void* src)+{+ const BYTE* const srcPtr = (const BYTE*)src;+ U32 value32 = srcPtr[0];+ value32 += ((U32)srcPtr[1])<< 8;+ value32 += ((U32)srcPtr[2])<<16;+ value32 += ((U32)srcPtr[3])<<24;+ return value32;+}++static void LZ4F_writeLE32 (void* dst, U32 value32)+{+ BYTE* const dstPtr = (BYTE*)dst;+ dstPtr[0] = (BYTE)value32;+ dstPtr[1] = (BYTE)(value32 >> 8);+ dstPtr[2] = (BYTE)(value32 >> 16);+ dstPtr[3] = (BYTE)(value32 >> 24);+}++static U64 LZ4F_readLE64 (const void* src)+{+ const BYTE* const srcPtr = (const BYTE*)src;+ U64 value64 = srcPtr[0];+ value64 += ((U64)srcPtr[1]<<8);+ value64 += ((U64)srcPtr[2]<<16);+ value64 += ((U64)srcPtr[3]<<24);+ value64 += ((U64)srcPtr[4]<<32);+ value64 += ((U64)srcPtr[5]<<40);+ value64 += ((U64)srcPtr[6]<<48);+ value64 += ((U64)srcPtr[7]<<56);+ return value64;+}++static void LZ4F_writeLE64 (void* dst, U64 value64)+{+ BYTE* const dstPtr = (BYTE*)dst;+ dstPtr[0] = (BYTE)value64;+ dstPtr[1] = (BYTE)(value64 >> 8);+ dstPtr[2] = (BYTE)(value64 >> 16);+ dstPtr[3] = (BYTE)(value64 >> 24);+ dstPtr[4] = (BYTE)(value64 >> 32);+ dstPtr[5] = (BYTE)(value64 >> 40);+ dstPtr[6] = (BYTE)(value64 >> 48);+ dstPtr[7] = (BYTE)(value64 >> 56);+}+++/*-************************************+* Constants+**************************************/+#ifndef LZ4_SRC_INCLUDED /* avoid double definition */+# define KB *(1<<10)+# define MB *(1<<20)+# define GB *(1<<30)+#endif++#define _1BIT 0x01+#define _2BITS 0x03+#define _3BITS 0x07+#define _4BITS 0x0F+#define _8BITS 0xFF++#define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U+#define LZ4F_BLOCKSIZEID_DEFAULT LZ4F_max64KB++static const size_t minFHSize = LZ4F_HEADER_SIZE_MIN; /* 7 */+static const size_t maxFHSize = LZ4F_HEADER_SIZE_MAX; /* 19 */+static const size_t BHSize = LZ4F_BLOCK_HEADER_SIZE; /* block header : size, and compress flag */+static const size_t BFSize = LZ4F_BLOCK_CHECKSUM_SIZE; /* block footer : checksum (optional) */+++/*-************************************+* 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; /* 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;+++/*-************************************+* Error management+**************************************/+#define LZ4F_GENERATE_STRING(STRING) #STRING,+static const char* LZ4F_errorStrings[] = { LZ4F_LIST_ERRORS(LZ4F_GENERATE_STRING) };+++unsigned LZ4F_isError(LZ4F_errorCode_t code)+{+ return (code > (LZ4F_errorCode_t)(-LZ4F_ERROR_maxCode));+}++const char* LZ4F_getErrorName(LZ4F_errorCode_t code)+{+ static const char* codeError = "Unspecified error code";+ if (LZ4F_isError(code)) return LZ4F_errorStrings[-(int)(code)];+ return codeError;+}++LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult)+{+ if (!LZ4F_isError(functionResult)) return LZ4F_OK_NoError;+ return (LZ4F_errorCodes)(-(ptrdiff_t)functionResult);+}++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(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_ERROR(maxBlockSize_invalid);+ { int const blockSizeIdx = (int)blockSizeID - (int)LZ4F_max64KB;+ return blockSizes[blockSizeIdx];+} }++/*-************************************+* Private functions+**************************************/+#define MIN(a,b) ( (a) < (b) ? (a) : (b) )++static BYTE LZ4F_headerChecksum (const void* header, size_t length)+{+ U32 const xxh = XXH32(header, length, 0);+ return (BYTE)(xxh >> 8);+}+++/*-************************************+* Simple-pass compression functions+**************************************/+static LZ4F_blockSizeID_t LZ4F_optimalBSID(const LZ4F_blockSizeID_t requestedBSID,+ const size_t srcSize)+{+ LZ4F_blockSizeID_t proposedBSID = LZ4F_max64KB;+ size_t maxBlockSize = 64 KB;+ while (requestedBSID > proposedBSID) {+ if (srcSize <= maxBlockSize)+ return proposedBSID;+ proposedBSID = (LZ4F_blockSizeID_t)((int)proposedBSID + 1);+ maxBlockSize <<= 2;+ }+ return requestedBSID;+}++/*! LZ4F_compressBound_internal() :+ * Provides dstCapacity given a srcSize to guarantee operation success in worst case situations.+ * prefsPtr is optional : if NULL is provided, preferences will be set to cover worst case scenario.+ * @return is always the same for a srcSize and prefsPtr, so it can be relied upon to size reusable buffers.+ * When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() operations.+ */+static size_t LZ4F_compressBound_internal(size_t srcSize,+ const LZ4F_preferences_t* preferencesPtr,+ size_t alreadyBuffered)+{+ LZ4F_preferences_t prefsNull = LZ4F_INIT_PREFERENCES;+ prefsNull.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled; /* worst case */+ prefsNull.frameInfo.blockChecksumFlag = LZ4F_blockChecksumEnabled; /* worst case */+ { const LZ4F_preferences_t* const prefsPtr = (preferencesPtr==NULL) ? &prefsNull : preferencesPtr;+ U32 const flush = prefsPtr->autoFlush | (srcSize==0);+ LZ4F_blockSizeID_t const blockID = prefsPtr->frameInfo.blockSizeID;+ size_t const blockSize = LZ4F_getBlockSize(blockID);+ size_t const maxBuffered = blockSize - 1;+ size_t const bufferedSize = MIN(alreadyBuffered, maxBuffered);+ size_t const maxSrcSize = srcSize + bufferedSize;+ unsigned const nbFullBlocks = (unsigned)(maxSrcSize / blockSize);+ size_t const partialBlockSize = maxSrcSize & (blockSize-1);+ size_t const lastBlockSize = flush ? partialBlockSize : 0;+ unsigned const nbBlocks = nbFullBlocks + (lastBlockSize>0);++ size_t const blockCRCSize = BFSize * prefsPtr->frameInfo.blockChecksumFlag;+ size_t const frameEnd = BHSize + (prefsPtr->frameInfo.contentChecksumFlag*BFSize);++ return ((BHSize + blockCRCSize) * nbBlocks) ++ (blockSize * nbFullBlocks) + lastBlockSize + frameEnd;+ }+}++size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)+{+ LZ4F_preferences_t prefs;+ size_t const headerSize = maxFHSize; /* max header size, including optional fields */++ if (preferencesPtr!=NULL) prefs = *preferencesPtr;+ else MEM_INIT(&prefs, 0, sizeof(prefs));+ prefs.autoFlush = 1;++ return headerSize + LZ4F_compressBound_internal(srcSize, &prefs, 0);;+}+++/*! LZ4F_compressFrame_usingCDict() :+ * Compress srcBuffer using a dictionary, in a single step.+ * cdict can be NULL, in which case, no dictionary is used.+ * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).+ * The LZ4F_preferences_t structure is optional : you may provide NULL as argument,+ * however, it's the only way to provide a dictID, so it's not recommended.+ * @return : number of bytes written into dstBuffer,+ * or an error code if it fails (can be tested using LZ4F_isError())+ */+size_t LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx,+ void* dstBuffer, size_t dstCapacity,+ const void* srcBuffer, size_t srcSize,+ const LZ4F_CDict* cdict,+ const LZ4F_preferences_t* preferencesPtr)+{+ LZ4F_preferences_t prefs;+ LZ4F_compressOptions_t options;+ BYTE* const dstStart = (BYTE*) dstBuffer;+ BYTE* dstPtr = dstStart;+ BYTE* const dstEnd = dstStart + dstCapacity;++ if (preferencesPtr!=NULL)+ prefs = *preferencesPtr;+ else+ MEM_INIT(&prefs, 0, sizeof(prefs));+ if (prefs.frameInfo.contentSize != 0)+ prefs.frameInfo.contentSize = (U64)srcSize; /* auto-correct content size if selected (!=0) */++ prefs.frameInfo.blockSizeID = LZ4F_optimalBSID(prefs.frameInfo.blockSizeID, srcSize);+ prefs.autoFlush = 1;+ if (srcSize <= LZ4F_getBlockSize(prefs.frameInfo.blockSizeID))+ prefs.frameInfo.blockMode = LZ4F_blockIndependent; /* only one block => no need for inter-block link */++ MEM_INIT(&options, 0, sizeof(options));+ options.stableSrc = 1;++ RETURN_ERROR_IF(dstCapacity < LZ4F_compressFrameBound(srcSize, &prefs), dstMaxSize_tooSmall);++ { size_t const headerSize = LZ4F_compressBegin_usingCDict(cctx, dstBuffer, dstCapacity, cdict, &prefs); /* write header */+ 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);+ 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 */+ FORWARD_IF_ERROR(tailSize);+ dstPtr += tailSize; }++ assert(dstEnd >= dstStart);+ return (size_t)(dstPtr - dstStart);+}+++/*! LZ4F_compressFrame() :+ * Compress an entire srcBuffer into a valid LZ4 frame, in a single step.+ * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).+ * The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default.+ * @return : number of bytes written into dstBuffer.+ * or an error code if it fails (can be tested using LZ4F_isError())+ */+size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity,+ const void* srcBuffer, size_t srcSize,+ const LZ4F_preferences_t* preferencesPtr)+{+ size_t result;+#if (LZ4F_HEAPMODE)+ LZ4F_cctx_t* cctxPtr;+ result = LZ4F_createCompressionContext(&cctxPtr, LZ4F_VERSION);+ FORWARD_IF_ERROR(result);+#else+ LZ4F_cctx_t cctx;+ LZ4_stream_t lz4ctx;+ LZ4F_cctx_t* const cctxPtr = &cctx;++ 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 ) {+ 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,+ NULL, preferencesPtr);++#if (LZ4F_HEAPMODE)+ LZ4F_freeCompressionContext(cctxPtr);+#else+ if ( preferencesPtr != NULL+ && preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN ) {+ LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);+ }+#endif+ return result;+}+++/*-***************************************************+* Dictionary compression+*****************************************************/++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_CDict*+LZ4F_createCDict_advanced(LZ4F_CustomMem cmem, const void* dictBuffer, size_t dictSize)+{+ const char* dictStart = (const char*)dictBuffer;+ 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 = 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;+ }+ memcpy(cdict->dictContent, dictStart, dictSize);+ LZ4_loadDict (cdict->fastCtx, (const char*)cdict->dictContent, (int)dictSize);+ LZ4_setCompressionLevel(cdict->HCCtx, LZ4HC_CLEVEL_DEFAULT);+ LZ4_loadDictHC(cdict->HCCtx, (const char*)cdict->dictContent, (int)dictSize);+ 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 */+ LZ4F_free(cdict->dictContent, cdict->cmem);+ LZ4F_free(cdict->fastCtx, cdict->cmem);+ LZ4F_free(cdict->HCCtx, cdict->cmem);+ LZ4F_free(cdict, cdict->cmem);+}+++/*-*********************************+* 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.+ * The version provided MUST be LZ4F_VERSION. It is intended to track potential incompatible differences between different binaries.+ * 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)+{+ 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;+}+++LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctxPtr)+{+ if (cctxPtr != NULL) { /* support free on NULL */+ 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;+}+++/**+ * This function prepares the internal LZ4(HC) stream for a new compression,+ * resetting the context and attaching the dictionary, if there is one.+ *+ * It needs to be called at the beginning of each independent compression+ * stream (i.e., at the beginning of a frame in blockLinked mode, or at the+ * beginning of each block in blockIndependent mode).+ */+static void LZ4F_initStream(void* ctx,+ const LZ4F_CDict* cdict,+ int level,+ LZ4F_blockMode_t blockMode) {+ if (level < LZ4HC_CLEVEL_MIN) {+ if (cdict != NULL || blockMode == LZ4F_blockLinked) {+ /* In these cases, we will call LZ4_compress_fast_continue(),+ * which needs an already reset context. Otherwise, we'll call a+ * one-shot API. The non-continued APIs internally perform their own+ * resets at the beginning of their calls, where they know what+ * tableType they need the context to be in. So in that case this+ * would be misguided / wasted work. */+ LZ4_resetStream_fast((LZ4_stream_t*)ctx);+ }+ LZ4_attach_dictionary((LZ4_stream_t *)ctx, cdict ? cdict->fastCtx : NULL);+ } else {+ LZ4_resetStreamHC_fast((LZ4_streamHC_t*)ctx, level);+ LZ4_attach_HC_dictionary((LZ4_streamHC_t *)ctx, cdict ? cdict->HCCtx : NULL);+ }+}++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.+ * @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,+ void* dstBuffer, size_t dstCapacity,+ const LZ4F_CDict* cdict,+ const LZ4F_preferences_t* preferencesPtr)+{+ LZ4F_preferences_t const prefNull = LZ4F_INIT_PREFERENCES;+ BYTE* const dstStart = (BYTE*)dstBuffer;+ BYTE* dstPtr = dstStart;++ RETURN_ERROR_IF(dstCapacity < maxFHSize, dstMaxSize_tooSmall);+ if (preferencesPtr == NULL) preferencesPtr = &prefNull;+ cctxPtr->prefs = *preferencesPtr;++ /* cctx Management */+ { U16 const ctxTypeID = (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) ? 1 : 2;+ 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) {+ /* 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 = LZ4F_malloc(sizeof(LZ4_streamHC_t), cctxPtr->cmem);+ if (cctxPtr->lz4CtxPtr)+ LZ4_initStreamHC(cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t));+ }+ RETURN_ERROR_IF(cctxPtr->lz4CtxPtr == NULL, allocation_failed);+ cctxPtr->lz4CtxAlloc = ctxTypeID;+ cctxPtr->lz4CtxState = ctxTypeID;+ } else if (cctxPtr->lz4CtxState != ctxTypeID) {+ /* 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));+ } else {+ 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)+ cctxPtr->prefs.frameInfo.blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;+ cctxPtr->maxBlockSize = LZ4F_getBlockSize(cctxPtr->prefs.frameInfo.blockSizeID);++ { size_t const requiredBuffSize = preferencesPtr->autoFlush ?+ ((cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 64 KB : 0) : /* only needs past data up to window size */+ cctxPtr->maxBlockSize + ((cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 128 KB : 0);++ if (cctxPtr->maxBufferSize < requiredBuffSize) {+ cctxPtr->maxBufferSize = 0;+ 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;+ cctxPtr->tmpInSize = 0;+ (void)XXH32_reset(&(cctxPtr->xxh), 0);++ /* context init */+ cctxPtr->cdict = cdict;+ if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) {+ /* frame init only for blockLinked : blockIndependent will be init at each block */+ LZ4F_initStream(cctxPtr->lz4CtxPtr, cdict, cctxPtr->prefs.compressionLevel, LZ4F_blockLinked);+ }+ if (preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN) {+ LZ4_favorDecompressionSpeed((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, (int)preferencesPtr->favorDecSpeed);+ }++ /* Magic Number */+ LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER);+ dstPtr += 4;+ { 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;+ }+ /* 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);+}+++/*! LZ4F_compressBegin() :+ * 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())+ */+size_t LZ4F_compressBegin(LZ4F_cctx* cctxPtr,+ void* dstBuffer, size_t dstCapacity,+ const LZ4F_preferences_t* preferencesPtr)+{+ return LZ4F_compressBegin_usingCDict(cctxPtr, dstBuffer, dstCapacity,+ NULL, preferencesPtr);+}+++/* LZ4F_compressBound() :+ * @return minimum capacity of dstBuffer for a given srcSize to handle worst case scenario.+ * LZ4F_preferences_t structure is optional : if NULL, preferences will be set to cover worst case scenario.+ * This function cannot fail.+ */+size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)+{+ if (preferencesPtr && preferencesPtr->autoFlush) {+ return LZ4F_compressBound_internal(srcSize, preferencesPtr, 0);+ }+ return LZ4F_compressBound_internal(srcSize, preferencesPtr, (size_t)-1);+}+++typedef int (*compressFunc_t)(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level, const LZ4F_CDict* cdict);+++/*! LZ4F_makeBlock():+ * compress a single block, add header and optional checksum.+ * assumption : dst buffer capacity is >= BHSize + srcSize + crcSize+ */+static size_t LZ4F_makeBlock(void* dst,+ const void* src, size_t srcSize,+ compressFunc_t compress, void* lz4ctx, int level,+ const LZ4F_CDict* cdict,+ LZ4F_blockChecksum_t crcFlag)+{+ BYTE* const cSizePtr = (BYTE*)dst;+ 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);+ } else {+ LZ4F_writeLE32(cSizePtr, cSize);+ }+ if (crcFlag) {+ U32 const crc32 = XXH32(cSizePtr+BHSize, cSize, 0); /* checksum of compressed data */+ LZ4F_writeLE32(cSizePtr+BHSize+cSize, crc32);+ }+ return BHSize + cSize + ((U32)crcFlag)*BFSize;+}+++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);+ } else {+ return LZ4_compress_fast_extState_fastReset(ctx, src, dst, srcSize, dstCapacity, acceleration);+ }+}++static int LZ4F_compressBlock_continue(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;+ (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);+}++static int LZ4F_compressBlockHC(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)+{+ LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent);+ if (cdict) {+ return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity);+ }+ return LZ4_compress_HC_extStateHC_fastReset(ctx, src, dst, srcSize, dstCapacity, level);+}++static int LZ4F_compressBlockHC_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)+{+ (void)level; (void)cdict; /* init once at beginning of frame */+ return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity);+}++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;+ }+ if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlockHC;+ 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)+ return LZ4_saveDict ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);+ return LZ4_saveDictHC ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);+}++typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus;++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.+ * 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.+ */+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_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, blockCompression);+ size_t bytesWritten;+ DEBUGLOG(4, "LZ4F_compressUpdate (srcSize=%zu)", srcSize);++ 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_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);+ srcPtr = srcEnd;+ cctxPtr->tmpInSize += srcSize;+ /* still needs some CRC */+ } else {+ /* complete tmpIn block and then compress it */+ lastBlockCompressed = fromTmpBuffer;+ memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy);+ srcPtr += sizeToCopy;++ dstPtr += LZ4F_makeBlock(dstPtr,+ cctxPtr->tmpIn, blockSize,+ 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 */+ lastBlockCompressed = fromSrcBuffer;+ dstPtr += LZ4F_makeBlock(dstPtr,+ srcPtr, blockSize,+ compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,+ cctxPtr->cdict,+ cctxPtr->prefs.frameInfo.blockChecksumFlag);+ srcPtr += blockSize;+ }++ if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd)) {+ /* 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;+ }++ /* 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; /* src is stable : dictionary remains in src across invocations */+ } else {+ int const realDictSize = LZ4F_localSaveDict(cctxPtr);+ assert(0 <= realDictSize && realDictSize <= 64 KB);+ cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;+ }+ }++ /* keep tmpIn within limits */+ 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 */+ if (srcPtr < srcEnd) {+ /* fill tmp buffer */+ size_t const sizeToCopy = (size_t)(srcEnd - srcPtr);+ memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy);+ cctxPtr->tmpInSize = sizeToCopy;+ }++ if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled)+ (void)XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize);++ cctxPtr->totalInSize += srcSize;+ 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.+ * The result of the function is the number of bytes written into dstBuffer.+ * It can be zero, this means there was no data left within LZ4F_cctx.+ * The function outputs an error code if it fails (can be tested using LZ4F_isError())+ * LZ4F_compressOptions_t* is optional. NULL is a valid argument.+ */+size_t LZ4F_flush(LZ4F_cctx* cctxPtr,+ void* dstBuffer, size_t dstCapacity,+ const LZ4F_compressOptions_t* compressOptionsPtr)+{+ BYTE* const dstStart = (BYTE*)dstBuffer;+ BYTE* dstPtr = dstStart;+ compressFunc_t compress;++ if (cctxPtr->tmpInSize == 0) return 0; /* nothing to flush */+ 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, cctxPtr->blockCompression);++ /* compress tmp buffer */+ dstPtr += LZ4F_makeBlock(dstPtr,+ cctxPtr->tmpIn, cctxPtr->tmpInSize,+ compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,+ cctxPtr->cdict,+ cctxPtr->prefs.frameInfo.blockChecksumFlag);+ assert(((void)"flush overflows dstBuffer!", (size_t)(dstPtr - dstStart) <= dstCapacity));++ if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked)+ cctxPtr->tmpIn += cctxPtr->tmpInSize;+ cctxPtr->tmpInSize = 0;++ /* keep tmpIn within limits */+ if ((cctxPtr->tmpIn + cctxPtr->maxBlockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)) { /* necessarily LZ4F_blockLinked */+ int const realDictSize = LZ4F_localSaveDict(cctxPtr);+ cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;+ }++ return (size_t)(dstPtr - dstStart);+}+++/*! LZ4F_compressEnd() :+ * When you want to properly finish the compressed frame, just call LZ4F_compressEnd().+ * It will flush whatever data remained within compressionContext (like LZ4_flush())+ * but also properly finalize the frame, with an endMark and an (optional) checksum.+ * LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.+ * @return: the number of bytes written into dstBuffer (necessarily >= 4 (endMark size))+ * or an error code if it fails (can be tested using LZ4F_isError())+ * The context can then be used again to compress a new frame, starting with LZ4F_compressBegin().+ */+size_t LZ4F_compressEnd(LZ4F_cctx* cctxPtr,+ void* dstBuffer, size_t dstCapacity,+ const LZ4F_compressOptions_t* compressOptionsPtr)+{+ BYTE* const dstStart = (BYTE*)dstBuffer;+ BYTE* dstPtr = dstStart;++ size_t const flushSize = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr);+ DEBUGLOG(5,"LZ4F_compressEnd: dstCapacity=%u", (unsigned)dstCapacity);+ FORWARD_IF_ERROR(flushSize);+ dstPtr += flushSize;++ assert(flushSize <= dstCapacity);+ dstCapacity -= flushSize;++ 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));+ RETURN_ERROR_IF(dstCapacity < 8, dstMaxSize_tooSmall);+ DEBUGLOG(5,"Writing 32-bit content checksum");+ LZ4F_writeLE32(dstPtr, xxh);+ dstPtr+=4; /* content Checksum */+ }++ cctxPtr->cStage = 0; /* state is now re-usable (with identical preferences) */+ cctxPtr->maxBufferSize = 0; /* reuse HC context */++ if (cctxPtr->prefs.frameInfo.contentSize) {+ if (cctxPtr->prefs.frameInfo.contentSize != cctxPtr->totalInSize)+ RETURN_ERROR(frameSize_wrong);+ }++ return (size_t)(dstPtr - dstStart);+}+++/*-***************************************************+* Frame Decompression+*****************************************************/++typedef enum {+ dstage_getFrameHeader=0, dstage_storeFrameHeader,+ dstage_init,+ dstage_getBlockHeader, dstage_storeBlockHeader,+ dstage_copyDirect, dstage_getBlockChecksum,+ dstage_getCBlock, dstage_storeCBlock,+ dstage_flushOut,+ dstage_getSuffix, dstage_storeSuffix,+ dstage_getSFrameSize, dstage_storeSFrameSize,+ dstage_skipSkippable+} dStage_t;++struct LZ4F_dctx_s {+ LZ4F_CustomMem cmem;+ LZ4F_frameInfo_t frameInfo;+ U32 version;+ dStage_t dStage;+ U64 frameRemainingSize;+ size_t maxBlockSize;+ size_t maxBufferSize;+ BYTE* tmpIn;+ size_t tmpInSize;+ size_t tmpInTarget;+ BYTE* tmpOutBuffer;+ const BYTE* dict;+ size_t dictSize;+ BYTE* tmpOut;+ size_t tmpOutSize;+ 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)+{+ assert(LZ4F_decompressionContextPtr != NULL); /* violation of narrow contract */+ RETURN_ERROR_IF(LZ4F_decompressionContextPtr == NULL, parameter_null); /* in case it nonetheless happen in production */++ *LZ4F_decompressionContextPtr = LZ4F_createDecompressionContext_advanced(LZ4F_defaultCMem, versionNumber);+ if (*LZ4F_decompressionContextPtr == NULL) { /* failed allocation */+ RETURN_ERROR(allocation_failed);+ }+ return LZ4F_OK_NoError;+}++LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx)+{+ LZ4F_errorCode_t result = LZ4F_OK_NoError;+ if (dctx != NULL) { /* can accept NULL input, like free() */+ result = (LZ4F_errorCode_t)dctx->dStage;+ LZ4F_free(dctx->tmpIn, dctx->cmem);+ LZ4F_free(dctx->tmpOutBuffer, dctx->cmem);+ LZ4F_free(dctx, dctx->cmem);+ }+ return result;+}+++/*==--- Streaming Decompression operations ---==*/++void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx)+{+ dctx->dStage = dstage_getFrameHeader;+ dctx->dict = NULL;+ dctx->dictSize = 0;+ dctx->skipChecksum = 0;+}+++/*! LZ4F_decodeHeader() :+ * input : `src` points at the **beginning of the frame**+ * output : set internal values of dctx, such as+ * dctx->frameInfo and dctx->dStage.+ * Also allocates internal buffers.+ * @return : nb Bytes read from src (necessarily <= srcSize)+ * or an error code (testable with LZ4F_isError())+ */+static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize)+{+ unsigned blockMode, blockChecksumFlag, contentSizeFlag, contentChecksumFlag, dictIDFlag, blockSizeID;+ size_t frameHeaderSize;+ const BYTE* srcPtr = (const BYTE*)src;++ DEBUGLOG(5, "LZ4F_decodeHeader");+ /* need to decode header to get frameInfo */+ RETURN_ERROR_IF(srcSize < minFHSize, frameHeader_incomplete); /* minimal frame header size */+ MEM_INIT(&(dctx->frameInfo), 0, sizeof(dctx->frameInfo));++ /* special case : skippable frames */+ if ((LZ4F_readLE32(srcPtr) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START) {+ dctx->frameInfo.frameType = LZ4F_skippableFrame;+ if (src == (void*)(dctx->header)) {+ dctx->tmpInSize = srcSize;+ dctx->tmpInTarget = 8;+ dctx->dStage = dstage_storeSFrameSize;+ return srcSize;+ } 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_ERROR(frameType_unknown);+ }+#endif+ dctx->frameInfo.frameType = LZ4F_frame;++ /* Flags */+ { U32 const FLG = srcPtr[4];+ U32 const version = (FLG>>6) & _2BITS;+ blockChecksumFlag = (FLG>>4) & _1BIT;+ blockMode = (FLG>>5) & _1BIT;+ contentSizeFlag = (FLG>>3) & _1BIT;+ contentChecksumFlag = (FLG>>2) & _1BIT;+ dictIDFlag = FLG & _1BIT;+ /* validate */+ 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 */+ frameHeaderSize = minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0);++ if (srcSize < frameHeaderSize) {+ /* not enough input to fully decode frame header */+ if (srcPtr != dctx->header)+ memcpy(dctx->header, srcPtr, srcSize);+ dctx->tmpInSize = srcSize;+ dctx->tmpInTarget = frameHeaderSize;+ dctx->dStage = dstage_storeFrameHeader;+ return srcSize;+ }++ { U32 const BD = srcPtr[5];+ blockSizeID = (BD>>4) & _3BITS;+ /* validate */+ 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);+ RETURN_ERROR_IF(HC != srcPtr[frameHeaderSize-1], headerChecksum_invalid);+ }+#endif++ /* save */+ dctx->frameInfo.blockMode = (LZ4F_blockMode_t)blockMode;+ 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((LZ4F_blockSizeID_t)blockSizeID);+ if (contentSizeFlag)+ dctx->frameRemainingSize = dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6);+ if (dictIDFlag)+ dctx->frameInfo.dictID = LZ4F_readLE32(srcPtr + frameHeaderSize - 5);++ dctx->dStage = dstage_init;++ return frameHeaderSize;+}+++/*! LZ4F_headerSize() :+ * @return : size of frame header+ * or an error code, which can be tested using LZ4F_isError()+ */+size_t LZ4F_headerSize(const void* src, size_t srcSize)+{+ RETURN_ERROR_IF(src == NULL, srcPtr_wrong);++ /* minimal srcSize to determine header size */+ if (srcSize < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH)+ RETURN_ERROR(frameHeader_incomplete);++ /* special case : skippable frames */+ if ((LZ4F_readLE32(src) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START)+ return 8;++ /* control magic number */+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION+ if (LZ4F_readLE32(src) != LZ4F_MAGICNUMBER)+ RETURN_ERROR(frameType_unknown);+#endif++ /* Frame Header Size */+ { BYTE const FLG = ((const BYTE*)src)[4];+ U32 const contentSizeFlag = (FLG>>3) & _1BIT;+ U32 const dictIDFlag = FLG & _1BIT;+ return minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0);+ }+}++/*! LZ4F_getFrameInfo() :+ * This function extracts frame parameters (max blockSize, frame checksum, etc.).+ * Usage is optional. Objective is to provide relevant information for allocation purposes.+ * This function works in 2 situations :+ * - At the beginning of a new frame, in which case it will decode this information from `srcBuffer`, and start the decoding process.+ * Amount of input data provided must be large enough to successfully decode the frame header.+ * A header size is variable, but is guaranteed to be <= LZ4F_HEADER_SIZE_MAX bytes. It's possible to provide more input data than this minimum.+ * - After decoding has been started. In which case, no input is read, frame parameters are extracted from dctx.+ * The number of bytes consumed from srcBuffer will be updated within *srcSizePtr (necessarily <= original value).+ * Decompression must resume from (srcBuffer + *srcSizePtr).+ * @return : an hint about how many srcSize bytes LZ4F_decompress() expects for next call,+ * or an error code which can be tested using LZ4F_isError()+ * note 1 : in case of error, dctx is not modified. Decoding operations can resume from where they stopped.+ * note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure.+ */+LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctx,+ LZ4F_frameInfo_t* frameInfoPtr,+ const void* srcBuffer, size_t* srcSizePtr)+{+ LZ4F_STATIC_ASSERT(dstage_getFrameHeader < dstage_storeFrameHeader);+ if (dctx->dStage > dstage_storeFrameHeader) {+ /* frameInfo already decoded */+ size_t o=0, i=0;+ *srcSizePtr = 0;+ *frameInfoPtr = dctx->frameInfo;+ /* returns : recommended nb of bytes for LZ4F_decompress() */+ return LZ4F_decompress(dctx, NULL, &o, NULL, &i, NULL);+ } else {+ if (dctx->dStage == dstage_storeFrameHeader) {+ /* frame decoding already started, in the middle of header => automatic fail */+ *srcSizePtr = 0;+ 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_ERROR(frameHeader_incomplete);+ }++ { size_t decodeResult = LZ4F_decodeHeader(dctx, srcBuffer, hSize);+ if (LZ4F_isError(decodeResult)) {+ *srcSizePtr = 0;+ } else {+ *srcSizePtr = decodeResult;+ decodeResult = BHSize; /* block header size */+ }+ *frameInfoPtr = dctx->frameInfo;+ return decodeResult;+ } } }+}+++/* LZ4F_updateDict() :+ * only used for LZ4F_blockLinked mode+ * 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; /* will lead to prefix mode */+ assert(dctx->dict != NULL);++ if (dctx->dict + dctx->dictSize == dstPtr) { /* prefix mode, everything within dstBuffer */+ dctx->dictSize += dstSize;+ return;+ }++ assert(dstPtr >= dstBufferStart);+ if ((size_t)(dstPtr - dstBufferStart) + dstSize >= 64 KB) { /* history in dstBuffer becomes large enough to become dictionary */+ dctx->dict = (const BYTE*)dstBufferStart;+ dctx->dictSize = (size_t)(dstPtr - dstBufferStart) + dstSize;+ return;+ }++ assert(dstSize < 64 KB); /* if dstSize >= 64 KB, dictionary would be set into dstBuffer directly */++ /* dstBuffer does not contain whole useful history (64 KB), so it must be saved within tmpOutBuffer */+ assert(dctx->tmpOutBuffer != NULL);++ if (withinTmp && (dctx->dict == dctx->tmpOutBuffer)) { /* continue history within tmpOutBuffer */+ /* withinTmp expectation : content of [dstPtr,dstSize] is same as [dict+dictSize,dstSize], so we just extend it */+ assert(dctx->dict + dctx->dictSize == dctx->tmpOut + dctx->tmpOutStart);+ dctx->dictSize += dstSize;+ return;+ }++ if (withinTmp) { /* copy relevant dict portion in front of tmpOut within tmpOutBuffer */+ size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer);+ size_t copySize = 64 KB - dctx->tmpOutSize;+ const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart;+ if (dctx->tmpOutSize > 64 KB) copySize = 0;+ if (copySize > preserveSize) copySize = preserveSize;++ memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);++ dctx->dict = dctx->tmpOutBuffer;+ dctx->dictSize = preserveSize + dctx->tmpOutStart + dstSize;+ return;+ }++ if (dctx->dict == dctx->tmpOutBuffer) { /* copy dst into tmp to complete dict */+ if (dctx->dictSize + dstSize > dctx->maxBufferSize) { /* tmp buffer not large enough */+ size_t const preserveSize = 64 KB - dstSize;+ memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);+ dctx->dictSize = preserveSize;+ }+ memcpy(dctx->tmpOutBuffer + dctx->dictSize, dstPtr, dstSize);+ dctx->dictSize += dstSize;+ return;+ }++ /* join dict & dest into tmp */+ { size_t preserveSize = 64 KB - dstSize;+ if (preserveSize > dctx->dictSize) preserveSize = dctx->dictSize;+ memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);+ memcpy(dctx->tmpOutBuffer + preserveSize, dstPtr, dstSize);+ dctx->dict = dctx->tmpOutBuffer;+ dctx->dictSize = preserveSize + dstSize;+ }+}+++/*! LZ4F_decompress() :+ * Call this function repetitively to regenerate compressed data in srcBuffer.+ * The function will attempt to decode up to *srcSizePtr bytes from srcBuffer+ * into dstBuffer of capacity *dstSizePtr.+ *+ * The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr (necessarily <= original value).+ *+ * The number of bytes effectively read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).+ * If number of bytes read is < number of bytes provided, then decompression operation is not complete.+ * Remaining data will have to be presented again in a subsequent invocation.+ *+ * The function result is an hint of the better srcSize to use for next call to LZ4F_decompress.+ * Schematically, it's the size of the current (or remaining) compressed block + header of next block.+ * Respecting the hint provides a small boost to performance, since it allows less buffer shuffling.+ * Note that this is just a hint, and it's always possible to any srcSize value.+ * When a frame is fully decoded, @return will be 0.+ * If decompression failed, @return is an error code which can be tested using LZ4F_isError().+ */+size_t LZ4F_decompress(LZ4F_dctx* dctx,+ void* dstBuffer, size_t* dstSizePtr,+ const void* srcBuffer, size_t* srcSizePtr,+ const LZ4F_decompressOptions_t* decompressOptionsPtr)+{+ LZ4F_decompressOptions_t optionsNull;+ const BYTE* const srcStart = (const BYTE*)srcBuffer;+ const BYTE* const srcEnd = srcStart + *srcSizePtr;+ const BYTE* srcPtr = srcStart;+ BYTE* const dstStart = (BYTE*)dstBuffer;+ BYTE* const dstEnd = dstStart ? dstStart + *dstSizePtr : NULL;+ BYTE* dstPtr = dstStart;+ const BYTE* selectedIn = NULL;+ unsigned doAnotherStage = 1;+ size_t nextSrcSizeHint = 1;+++ DEBUGLOG(5, "LZ4F_decompress : %p,%u => %p,%u",+ srcBuffer, (unsigned)*srcSizePtr, dstBuffer, (unsigned)*dstSizePtr);+ if (dstBuffer == NULL) assert(*dstSizePtr == 0);+ MEM_INIT(&optionsNull, 0, sizeof(optionsNull));+ if (decompressOptionsPtr==NULL) decompressOptionsPtr = &optionsNull;+ *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 */++ while (doAnotherStage) {++ switch(dctx->dStage)+ {++ case dstage_getFrameHeader:+ 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 */+ FORWARD_IF_ERROR(hSize);+ srcPtr += hSize;+ break;+ }+ dctx->tmpInSize = 0;+ if (srcEnd-srcPtr == 0) return minFHSize; /* 0-size input */+ dctx->tmpInTarget = minFHSize; /* minimum size to decode header */+ dctx->dStage = dstage_storeFrameHeader;+ /* fall-through */++ case dstage_storeFrameHeader:+ DEBUGLOG(6, "dstage_storeFrameHeader");+ { size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize, (size_t)(srcEnd - srcPtr));+ memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);+ dctx->tmpInSize += sizeToCopy;+ srcPtr += sizeToCopy;+ }+ if (dctx->tmpInSize < dctx->tmpInTarget) {+ nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) + BHSize; /* rest of header + nextBlockHeader */+ doAnotherStage = 0; /* not enough src data, ask for some more */+ break;+ }+ FORWARD_IF_ERROR( LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget) ); /* will update dStage appropriately */+ break;++ case dstage_init:+ DEBUGLOG(6, "dstage_init");+ if (dctx->frameInfo.contentChecksumFlag) (void)XXH32_reset(&(dctx->xxh), 0);+ /* internal buffers allocation */+ { size_t const bufferNeeded = dctx->maxBlockSize+ + ((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*/+ 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;+ dctx->tmpInTarget = 0;+ dctx->tmpOut = dctx->tmpOutBuffer;+ dctx->tmpOutStart = 0;+ dctx->tmpOutSize = 0;++ dctx->dStage = dstage_getBlockHeader;+ /* fall-through */++ case dstage_getBlockHeader:+ if ((size_t)(srcEnd - srcPtr) >= BHSize) {+ selectedIn = srcPtr;+ srcPtr += BHSize;+ } else {+ /* not enough input to read cBlockSize field */+ dctx->tmpInSize = 0;+ dctx->dStage = dstage_storeBlockHeader;+ }++ if (dctx->dStage == dstage_storeBlockHeader) /* can be skipped */+ case dstage_storeBlockHeader:+ { size_t const remainingInput = (size_t)(srcEnd - srcPtr);+ size_t const wantedData = BHSize - dctx->tmpInSize;+ size_t const sizeToCopy = MIN(wantedData, remainingInput);+ memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);+ srcPtr += sizeToCopy;+ dctx->tmpInSize += sizeToCopy;++ if (dctx->tmpInSize < BHSize) { /* not enough input for cBlockSize */+ nextSrcSizeHint = BHSize - dctx->tmpInSize;+ doAnotherStage = 0;+ break;+ }+ selectedIn = dctx->tmpIn;+ } /* if (dctx->dStage == dstage_storeBlockHeader) */++ /* decode block header */+ { U32 const blockHeader = LZ4F_readLE32(selectedIn);+ size_t const nextCBlockSize = blockHeader & 0x7FFFFFFFU;+ size_t const crcSize = dctx->frameInfo.blockChecksumFlag * BFSize;+ if (blockHeader==0) { /* frameEnd signal, no more block */+ DEBUGLOG(5, "end of frame");+ dctx->dStage = dstage_getSuffix;+ break;+ }+ if (nextCBlockSize > dctx->maxBlockSize) {+ RETURN_ERROR(maxBlockSize_invalid);+ }+ if (blockHeader & LZ4F_BLOCKUNCOMPRESSED_FLAG) {+ /* next block is uncompressed */+ dctx->tmpInTarget = nextCBlockSize;+ DEBUGLOG(5, "next block is uncompressed (size %u)", (U32)nextCBlockSize);+ if (dctx->frameInfo.blockChecksumFlag) {+ (void)XXH32_reset(&dctx->blockChecksum, 0);+ }+ dctx->dStage = dstage_copyDirect;+ break;+ }+ /* next block is a compressed block */+ dctx->tmpInTarget = nextCBlockSize + crcSize;+ dctx->dStage = dstage_getCBlock;+ if (dstPtr==dstEnd || srcPtr==srcEnd) {+ nextSrcSizeHint = BHSize + nextCBlockSize + crcSize;+ doAnotherStage = 0;+ }+ break;+ }++ case dstage_copyDirect: /* uncompressed block */+ DEBUGLOG(6, "dstage_copyDirect");+ { size_t sizeToCopy;+ if (dstPtr == NULL) {+ sizeToCopy = 0;+ } else {+ size_t const minBuffSize = MIN((size_t)(srcEnd-srcPtr), (size_t)(dstEnd-dstPtr));+ sizeToCopy = MIN(dctx->tmpInTarget, minBuffSize);+ memcpy(dstPtr, 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.contentSize)+ dctx->frameRemainingSize -= sizeToCopy;++ /* history management (linked blocks only)*/+ if (dctx->frameInfo.blockMode == LZ4F_blockLinked) {+ LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 0);+ } }++ srcPtr += sizeToCopy;+ dstPtr += sizeToCopy;+ if (sizeToCopy == dctx->tmpInTarget) { /* all done */+ if (dctx->frameInfo.blockChecksumFlag) {+ dctx->tmpInSize = 0;+ dctx->dStage = dstage_getBlockChecksum;+ } else+ dctx->dStage = dstage_getBlockHeader; /* new block */+ break;+ }+ dctx->tmpInTarget -= sizeToCopy; /* need to copy more */+ }+ nextSrcSizeHint = dctx->tmpInTarget ++ +(dctx->frameInfo.blockChecksumFlag ? BFSize : 0)+ + BHSize /* next header size */;+ doAnotherStage = 0;+ break;++ /* check block checksum for recently transferred uncompressed block */+ case dstage_getBlockChecksum:+ DEBUGLOG(6, "dstage_getBlockChecksum");+ { const void* crcSrc;+ if ((srcEnd-srcPtr >= 4) && (dctx->tmpInSize==0)) {+ crcSrc = srcPtr;+ srcPtr += 4;+ } else {+ size_t const stillToCopy = 4 - dctx->tmpInSize;+ size_t const sizeToCopy = MIN(stillToCopy, (size_t)(srcEnd-srcPtr));+ memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);+ dctx->tmpInSize += sizeToCopy;+ srcPtr += sizeToCopy;+ if (dctx->tmpInSize < 4) { /* all input consumed */+ doAnotherStage = 0;+ break;+ }+ crcSrc = dctx->header;+ }+ 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_ERROR(blockChecksum_invalid);+ }+#else+ (void)readCRC;+ (void)calcCRC;+#endif+ } }+ dctx->dStage = dstage_getBlockHeader; /* new block */+ break;++ case dstage_getCBlock:+ DEBUGLOG(6, "dstage_getCBlock");+ if ((size_t)(srcEnd-srcPtr) < dctx->tmpInTarget) {+ dctx->tmpInSize = 0;+ dctx->dStage = dstage_storeCBlock;+ break;+ }+ /* input large enough to read full block directly */+ selectedIn = srcPtr;+ srcPtr += dctx->tmpInTarget;++ if (0) /* always jump over next block */+ case dstage_storeCBlock:+ { size_t const wantedData = dctx->tmpInTarget - dctx->tmpInSize;+ size_t const inputLeft = (size_t)(srcEnd-srcPtr);+ size_t const sizeToCopy = MIN(wantedData, inputLeft);+ memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);+ dctx->tmpInSize += sizeToCopy;+ srcPtr += sizeToCopy;+ if (dctx->tmpInSize < dctx->tmpInTarget) { /* need more input */+ nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize)+ + (dctx->frameInfo.blockChecksumFlag ? BFSize : 0)+ + BHSize /* next header size */;+ doAnotherStage = 0;+ break;+ }+ selectedIn = dctx->tmpIn;+ }++ /* 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+ RETURN_ERROR_IF(readBlockCrc != calcBlockCrc, blockChecksum_invalid);+#else+ (void)readBlockCrc;+ (void)calcBlockCrc;+#endif+ } }++ /* 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) {+ /* overflow control : dctx->dictSize is an int, avoid truncation / sign issues */+ dict += dictSize - 64 KB;+ dictSize = 64 KB;+ }+ decodedSize = LZ4_decompress_safe_usingDict(+ (const char*)selectedIn, (char*)dstPtr,+ (int)dctx->tmpInTarget, (int)dctx->maxBlockSize,+ dict, (int)dictSize);+ 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;++ /* dictionary management */+ if (dctx->frameInfo.blockMode==LZ4F_blockLinked) {+ LZ4F_updateDict(dctx, dstPtr, (size_t)decodedSize, dstStart, 0);+ }++ dstPtr += decodedSize;+ dctx->dStage = dstage_getBlockHeader; /* end of block, let's get another one */+ break;+ }++ /* not enough place into dst : decode into 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 tmpOut */+ size_t const reservedDictSpace = MIN(dctx->dictSize, 64 KB);+ dctx->tmpOut = dctx->tmpOutBuffer + reservedDictSpace;+ } }++ /* Decode block into tmpOut */+ { const char* dict = (const char*)dctx->dict;+ size_t dictSize = dctx->dictSize;+ int decodedSize;+ if (dict && dictSize > 1 GB) {+ /* the dictSize param is an int, avoid truncation / sign issues */+ dict += dictSize - 64 KB;+ dictSize = 64 KB;+ }+ decodedSize = LZ4_decompress_safe_usingDict(+ (const char*)selectedIn, (char*)dctx->tmpOut,+ (int)dctx->tmpInTarget, (int)dctx->maxBlockSize,+ dict, (int)dictSize);+ 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;+ dctx->tmpOutSize = (size_t)decodedSize;+ dctx->tmpOutStart = 0;+ dctx->dStage = dstage_flushOut;+ }+ /* fall-through */++ case dstage_flushOut: /* flush decoded data from tmpOut to dstBuffer */+ DEBUGLOG(6, "dstage_flushOut");+ if (dstPtr != NULL) {+ size_t const sizeToCopy = MIN(dctx->tmpOutSize - dctx->tmpOutStart, (size_t)(dstEnd-dstPtr));+ memcpy(dstPtr, dctx->tmpOut + dctx->tmpOutStart, sizeToCopy);++ /* dictionary management */+ if (dctx->frameInfo.blockMode == LZ4F_blockLinked)+ LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 1 /*withinTmp*/);++ dctx->tmpOutStart += sizeToCopy;+ dstPtr += sizeToCopy;+ }+ if (dctx->tmpOutStart == dctx->tmpOutSize) { /* all flushed */+ dctx->dStage = dstage_getBlockHeader; /* get next block */+ break;+ }+ /* could not flush everything : stop there, just request a block header */+ doAnotherStage = 0;+ nextSrcSizeHint = BHSize;+ break;++ case dstage_getSuffix:+ 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);+ doAnotherStage = 0;+ break;+ }+ if ((srcEnd - srcPtr) < 4) { /* not enough size for entire CRC */+ dctx->tmpInSize = 0;+ dctx->dStage = dstage_storeSuffix;+ } else {+ selectedIn = srcPtr;+ srcPtr += 4;+ }++ if (dctx->dStage == dstage_storeSuffix) /* can be skipped */+ case dstage_storeSuffix:+ { size_t const remainingInput = (size_t)(srcEnd - srcPtr);+ size_t const wantedData = 4 - dctx->tmpInSize;+ size_t const sizeToCopy = MIN(wantedData, remainingInput);+ memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);+ srcPtr += sizeToCopy;+ dctx->tmpInSize += sizeToCopy;+ if (dctx->tmpInSize < 4) { /* not enough input to read complete suffix */+ nextSrcSizeHint = 4 - dctx->tmpInSize;+ doAnotherStage=0;+ break;+ }+ selectedIn = dctx->tmpIn;+ } /* if (dctx->dStage == dstage_storeSuffix) */++ /* case dstage_checkSuffix: */ /* no direct entry, avoid initialization risks */+ if (!dctx->skipChecksum) {+ U32 const readCRC = LZ4F_readLE32(selectedIn);+ U32 const resultCRC = XXH32_digest(&(dctx->xxh));+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION+ RETURN_ERROR_IF(readCRC != resultCRC, contentChecksum_invalid);+#else+ (void)readCRC;+ (void)resultCRC;+#endif+ }+ nextSrcSizeHint = 0;+ LZ4F_resetDecompressionContext(dctx);+ doAnotherStage = 0;+ break;++ case dstage_getSFrameSize:+ if ((srcEnd - srcPtr) >= 4) {+ selectedIn = srcPtr;+ srcPtr += 4;+ } else {+ /* not enough input to read cBlockSize field */+ dctx->tmpInSize = 4;+ dctx->tmpInTarget = 8;+ dctx->dStage = dstage_storeSFrameSize;+ }++ if (dctx->dStage == dstage_storeSFrameSize)+ case dstage_storeSFrameSize:+ { size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize,+ (size_t)(srcEnd - srcPtr) );+ memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);+ srcPtr += sizeToCopy;+ dctx->tmpInSize += sizeToCopy;+ if (dctx->tmpInSize < dctx->tmpInTarget) {+ /* not enough input to get full sBlockSize; wait for more */+ nextSrcSizeHint = dctx->tmpInTarget - dctx->tmpInSize;+ doAnotherStage = 0;+ break;+ }+ selectedIn = dctx->header + 4;+ } /* if (dctx->dStage == dstage_storeSFrameSize) */++ /* case dstage_decodeSFrameSize: */ /* no direct entry */+ { size_t const SFrameSize = LZ4F_readLE32(selectedIn);+ dctx->frameInfo.contentSize = SFrameSize;+ dctx->tmpInTarget = SFrameSize;+ dctx->dStage = dstage_skipSkippable;+ break;+ }++ case dstage_skipSkippable:+ { size_t const skipSize = MIN(dctx->tmpInTarget, (size_t)(srcEnd-srcPtr));+ srcPtr += skipSize;+ dctx->tmpInTarget -= skipSize;+ doAnotherStage = 0;+ nextSrcSizeHint = dctx->tmpInTarget;+ if (nextSrcSizeHint) break; /* still more to skip */+ /* frame fully skipped : prepare context for a new frame */+ LZ4F_resetDecompressionContext(dctx);+ break;+ }+ } /* switch (dctx->dStage) */+ } /* while (doAnotherStage) */++ /* 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 */+ && (dctx->dict != NULL) /* dictionary exists */+ && (!decompressOptionsPtr->stableDst) /* cannot rely on dst data to remain there for next call */+ && ((unsigned)(dctx->dStage)-2 < (unsigned)(dstage_getSuffix)-2) ) /* valid stages : [init ... getSuffix[ */+ {+ if (dctx->dStage == dstage_flushOut) {+ size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer);+ size_t copySize = 64 KB - dctx->tmpOutSize;+ const BYTE* oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart;+ if (dctx->tmpOutSize > 64 KB) copySize = 0;+ if (copySize > preserveSize) copySize = preserveSize;+ assert(dctx->tmpOutBuffer != NULL);++ memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);++ dctx->dict = dctx->tmpOutBuffer;+ dctx->dictSize = preserveSize + dctx->tmpOutStart;+ } else {+ const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize;+ size_t const newDictSize = MIN(dctx->dictSize, 64 KB);++ memcpy(dctx->tmpOutBuffer, oldDictEnd - newDictSize, newDictSize);++ dctx->dict = dctx->tmpOutBuffer;+ dctx->dictSize = newDictSize;+ dctx->tmpOut = dctx->tmpOutBuffer + newDictSize;+ }+ }++ *srcSizePtr = (size_t)(srcPtr - srcStart);+ *dstSizePtr = (size_t)(dstPtr - dstStart);+ return nextSrcSizeHint;+}++/*! 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.+ */+size_t LZ4F_decompress_usingDict(LZ4F_dctx* dctx,+ void* dstBuffer, size_t* dstSizePtr,+ const void* srcBuffer, size_t* srcSizePtr,+ const void* dict, size_t dictSize,+ const LZ4F_decompressOptions_t* decompressOptionsPtr)+{+ if (dctx->dStage <= dstage_init) {+ dctx->dict = (const BYTE*)dict;+ dctx->dictSize = dictSize;+ }+ return LZ4F_decompress(dctx, dstBuffer, dstSizePtr,+ srcBuffer, srcSizePtr,+ decompressOptionsPtr);+}
+ cbits/lz4frame.h view
@@ -0,0 +1,692 @@+/*+ LZ4F - LZ4-Frame library+ Header File+ 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+ modification, are permitted provided that the following conditions are+ met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following disclaimer+ in the documentation and/or other materials provided with the+ distribution.++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.++ You can contact the author at :+ - LZ4 source repository : https://github.com/lz4/lz4+ - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c+*/++/* LZ4F is a stand-alone API able to create and decode LZ4 frames+ * conformant with specification v1.6.1 in doc/lz4_Frame_format.md .+ * Generated frames are compatible with `lz4` CLI.+ *+ * LZ4F also offers streaming capabilities.+ *+ * lz4.h is not required when using lz4frame.h,+ * except to extract common constants such as LZ4_VERSION_NUMBER.+ * */++#ifndef LZ4F_H_09782039843+#define LZ4F_H_09782039843++#if defined (__cplusplus)+extern "C" {+#endif++/* --- Dependency --- */+#include <stddef.h> /* size_t */+++/**+ * 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+ *****************************************************************/+/* LZ4_DLL_EXPORT :+ * Enable exporting of functions when building a Windows DLL+ * LZ4FLIB_VISIBILITY :+ * Control library symbols visibility.+ */+#ifndef LZ4FLIB_VISIBILITY+# if defined(__GNUC__) && (__GNUC__ >= 4)+# define LZ4FLIB_VISIBILITY __attribute__ ((visibility ("default")))+# else+# define LZ4FLIB_VISIBILITY+# endif+#endif+#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)+# define LZ4FLIB_API __declspec(dllexport) LZ4FLIB_VISIBILITY+#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)+# define LZ4FLIB_API __declspec(dllimport) LZ4FLIB_VISIBILITY+#else+# define LZ4FLIB_API LZ4FLIB_VISIBILITY+#endif++#ifdef LZ4F_DISABLE_DEPRECATE_WARNINGS+# define LZ4F_DEPRECATE(x) x+#else+# if defined(_MSC_VER)+# define LZ4F_DEPRECATE(x) x /* __declspec(deprecated) x - only works with C++ */+# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))+# define LZ4F_DEPRECATE(x) x __attribute__((deprecated))+# else+# define LZ4F_DEPRECATE(x) x /* no deprecation warning for this compiler */+# endif+#endif+++/*-************************************+ * Error management+ **************************************/+typedef size_t LZ4F_errorCode_t;++LZ4FLIB_API unsigned LZ4F_isError(LZ4F_errorCode_t code); /**< tells when a function result is an error code */+LZ4FLIB_API const char* LZ4F_getErrorName(LZ4F_errorCode_t code); /**< return error code string; for debugging */+++/*-************************************+ * Frame compression types+ ************************************* */+/* #define LZ4F_ENABLE_OBSOLETE_ENUMS // uncomment to enable obsolete enums */+#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS+# define LZ4F_OBSOLETE_ENUM(x) , LZ4F_DEPRECATE(x) = LZ4F_##x+#else+# define LZ4F_OBSOLETE_ENUM(x)+#endif++/* The larger the block size, the (slightly) better the compression ratio,+ * though there are diminishing returns.+ * Larger blocks also increase memory usage on both compression and decompression sides.+ */+typedef enum {+ LZ4F_default=0,+ LZ4F_max64KB=4,+ LZ4F_max256KB=5,+ LZ4F_max1MB=6,+ LZ4F_max4MB=7+ LZ4F_OBSOLETE_ENUM(max64KB)+ LZ4F_OBSOLETE_ENUM(max256KB)+ LZ4F_OBSOLETE_ENUM(max1MB)+ LZ4F_OBSOLETE_ENUM(max4MB)+} LZ4F_blockSizeID_t;++/* Linked blocks sharply reduce inefficiencies when using small blocks,+ * they compress better.+ * However, some LZ4 decoders are only compatible with independent blocks */+typedef enum {+ LZ4F_blockLinked=0,+ LZ4F_blockIndependent+ LZ4F_OBSOLETE_ENUM(blockLinked)+ LZ4F_OBSOLETE_ENUM(blockIndependent)+} LZ4F_blockMode_t;++typedef enum {+ LZ4F_noContentChecksum=0,+ LZ4F_contentChecksumEnabled+ LZ4F_OBSOLETE_ENUM(noContentChecksum)+ LZ4F_OBSOLETE_ENUM(contentChecksumEnabled)+} LZ4F_contentChecksum_t;++typedef enum {+ LZ4F_noBlockChecksum=0,+ LZ4F_blockChecksumEnabled+} LZ4F_blockChecksum_t;++typedef enum {+ LZ4F_frame=0,+ LZ4F_skippableFrame+ LZ4F_OBSOLETE_ENUM(skippableFrame)+} LZ4F_frameType_t;++#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS+typedef LZ4F_blockSizeID_t blockSizeID_t;+typedef LZ4F_blockMode_t blockMode_t;+typedef LZ4F_frameType_t frameType_t;+typedef LZ4F_contentChecksum_t contentChecksum_t;+#endif++/*! LZ4F_frameInfo_t :+ * makes it possible to set or read frame parameters.+ * Structure must be first init to 0, using memset() or LZ4F_INIT_FRAMEINFO,+ * setting all parameters to default.+ * It's then possible to update selectively some parameters */+typedef struct {+ LZ4F_blockSizeID_t blockSizeID; /* max64KB, max256KB, max1MB, max4MB; 0 == default */+ LZ4F_blockMode_t blockMode; /* LZ4F_blockLinked, LZ4F_blockIndependent; 0 == default */+ LZ4F_contentChecksum_t contentChecksumFlag; /* 1: frame terminated with 32-bit checksum of decompressed data; 0: disabled (default) */+ LZ4F_frameType_t frameType; /* read-only field : LZ4F_frame or LZ4F_skippableFrame */+ unsigned long long contentSize; /* Size of uncompressed content ; 0 == unknown */+ unsigned dictID; /* Dictionary ID, sent by compressor to help decoder select correct dictionary; 0 == no dictID provided */+ LZ4F_blockChecksum_t blockChecksumFlag; /* 1: each block followed by a checksum of block's compressed data; 0: disabled (default) */+} LZ4F_frameInfo_t;++#define LZ4F_INIT_FRAMEINFO { LZ4F_default, LZ4F_blockLinked, LZ4F_noContentChecksum, LZ4F_frame, 0ULL, 0U, LZ4F_noBlockChecksum } /* v1.8.3+ */++/*! LZ4F_preferences_t :+ * makes it possible to supply advanced compression instructions to streaming interface.+ * Structure must be first init to 0, using memset() or LZ4F_INIT_PREFERENCES,+ * setting all parameters to default.+ * All reserved fields must be set to zero. */+typedef struct {+ LZ4F_frameInfo_t frameInfo;+ int compressionLevel; /* 0: default (fast mode); values > LZ4HC_CLEVEL_MAX count as LZ4HC_CLEVEL_MAX; values < 0 trigger "fast acceleration" */+ unsigned autoFlush; /* 1: always flush; reduces usage of internal buffers */+ unsigned favorDecSpeed; /* 1: parser favors decompression speed vs compression ratio. Only works for high compression modes (>= LZ4HC_CLEVEL_OPT_MIN) */ /* v1.8.2+ */+ unsigned reserved[3]; /* must be zero for forward compatibility */+} LZ4F_preferences_t;++#define LZ4F_INIT_PREFERENCES { LZ4F_INIT_FRAMEINFO, 0, 0u, 0u, { 0u, 0u, 0u } } /* v1.8.3+ */+++/*-*********************************+* Simple compression function+***********************************/++LZ4FLIB_API int LZ4F_compressionLevel_max(void); /* v1.8.0+ */++/*! LZ4F_compressFrameBound() :+ * 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 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);++/*! LZ4F_compressFrame() :+ * Compress an entire srcBuffer into a valid LZ4 frame.+ * dstCapacity MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).+ * The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default.+ * @return : number of bytes written into dstBuffer.+ * or an error code if it fails (can be tested using LZ4F_isError())+ */+LZ4FLIB_API size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity,+ const void* srcBuffer, size_t srcSize,+ const LZ4F_preferences_t* preferencesPtr);+++/*-***********************************+* Advanced compression functions+*************************************/+typedef struct LZ4F_cctx_s LZ4F_cctx; /* incomplete type */+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 */+ unsigned reserved[3];+} LZ4F_compressOptions_t;++/*--- Resource Management ---*/++#define LZ4F_VERSION 100 /* This number can be used to check for an incompatible API breaking change */+LZ4FLIB_API unsigned LZ4F_getVersion(void);++/*! LZ4F_createCompressionContext() :+ * 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 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 */+#define LZ4F_BLOCK_HEADER_SIZE 4++/* Size in bytes of a block checksum footer in little-endian format. */+#define LZ4F_BLOCK_CHECKSUM_SIZE 4++/* Size in bytes of the content checksum. */+#define LZ4F_CONTENT_CHECKSUM_SIZE 4++/*! LZ4F_compressBegin() :+ * will write the frame header into dstBuffer.+ * dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.+ * `prefsPtr` is optional : you can provide NULL as argument, all preferences will then be set to default.+ * @return : number of bytes written into dstBuffer for the header+ * or an error code (which can be tested using LZ4F_isError())+ */+LZ4FLIB_API size_t LZ4F_compressBegin(LZ4F_cctx* cctx,+ void* dstBuffer, size_t dstCapacity,+ const LZ4F_preferences_t* prefsPtr);++/*! LZ4F_compressBound() :+ * Provides minimum dstCapacity required to guarantee success of+ * LZ4F_compressUpdate(), given a srcSize and preferences, for a worst case scenario.+ * When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() instead.+ * Note that the result is only valid for a single invocation of LZ4F_compressUpdate().+ * When invoking LZ4F_compressUpdate() multiple times,+ * if the output buffer is gradually filled up instead of emptied and re-used from its start,+ * one must check if there is enough remaining capacity before each invocation, using LZ4F_compressBound().+ * @return is always the same for a srcSize and prefsPtr.+ * prefsPtr is optional : when NULL is provided, preferences will be set to cover worst case scenario.+ * tech details :+ * @return if automatic flushing is not enabled, includes the possibility that internal buffer might already be filled by up to (blockSize-1) bytes.+ * It also includes frame footer (ending + checksum), since it might be generated by LZ4F_compressEnd().+ * @return doesn't include frame header, as it was already generated by LZ4F_compressBegin().+ */+LZ4FLIB_API size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* prefsPtr);++/*! LZ4F_compressUpdate() :+ * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.+ * 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).+ * 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())+ */+LZ4FLIB_API size_t LZ4F_compressUpdate(LZ4F_cctx* cctx,+ void* dstBuffer, size_t dstCapacity,+ const void* srcBuffer, size_t srcSize,+ const LZ4F_compressOptions_t* cOptPtr);++/*! LZ4F_flush() :+ * When data must be generated and sent immediately, without waiting for a block to be completely filled,+ * it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx.+ * `dstCapacity` must be large enough to ensure the operation will be successful.+ * `cOptPtr` is optional : it's possible to provide NULL, all options will be set to default.+ * @return : nb of bytes written into dstBuffer (can be zero, when there is no data stored within cctx)+ * or an error code if it fails (which can be tested using LZ4F_isError())+ * Note : LZ4F_flush() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr).+ */+LZ4FLIB_API size_t LZ4F_flush(LZ4F_cctx* cctx,+ void* dstBuffer, size_t dstCapacity,+ const LZ4F_compressOptions_t* cOptPtr);++/*! LZ4F_compressEnd() :+ * To properly finish an LZ4 frame, invoke LZ4F_compressEnd().+ * It will flush whatever data remained within `cctx` (like LZ4_flush())+ * and properly finalize the frame, with an endMark and a checksum.+ * `cOptPtr` is optional : NULL can be provided, in which case all options will be set to default.+ * @return : nb of bytes written into dstBuffer, necessarily >= 4 (endMark),+ * or an error code if it fails (which can be tested using LZ4F_isError())+ * Note : LZ4F_compressEnd() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr).+ * A successful call to LZ4F_compressEnd() makes `cctx` available again for another compression task.+ */+LZ4FLIB_API size_t LZ4F_compressEnd(LZ4F_cctx* cctx,+ void* dstBuffer, size_t dstCapacity,+ const LZ4F_compressOptions_t* cOptPtr);+++/*-*********************************+* Decompression functions+***********************************/+typedef struct LZ4F_dctx_s LZ4F_dctx; /* incomplete type */+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 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;+++/* Resource management */++/*! LZ4F_createDecompressionContext() :+ * Create an LZ4F_dctx object, to track all decompression operations.+ * @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.+ */+LZ4FLIB_API LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_dctx** dctxPtr, unsigned version);+LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx);+++/*-***********************************+* 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++ * Provide the header size of a frame starting at `src`.+ * `srcSize` must be >= LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH,+ * which is enough to decode the header length.+ * @return : size of frame header+ * or an error code, which can be tested using LZ4F_isError()+ * note : Frame header size is variable, but is guaranteed to be+ * >= LZ4F_HEADER_SIZE_MIN bytes, and <= LZ4F_HEADER_SIZE_MAX bytes.+ */+LZ4FLIB_API size_t LZ4F_headerSize(const void* src, size_t srcSize);++/*! LZ4F_getFrameInfo() :+ * This function extracts frame parameters (max blockSize, dictID, etc.).+ * 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.+ *+ * LZ4F_getFrameInfo() can work in the following situations :+ *+ * 1) At the beginning of a new frame, before any invocation of LZ4F_decompress().+ * It will decode header from `srcBuffer`,+ * consuming the header and starting the decoding process.+ *+ * Input size must be large enough to contain the full frame header.+ * Frame header size can be known beforehand by LZ4F_headerSize().+ * Frame header size is variable, but is guaranteed to be >= LZ4F_HEADER_SIZE_MIN bytes,+ * and not more than <= LZ4F_HEADER_SIZE_MAX bytes.+ * Hence, blindly providing LZ4F_HEADER_SIZE_MAX bytes or more will always work.+ * It's allowed to provide more input data than the header size,+ * LZ4F_getFrameInfo() will only consume the header.+ *+ * If input size is not large enough,+ * aka if it's smaller than header size,+ * function will fail and return an error code.+ *+ * 2) After decoding has been started,+ * it's possible to invoke LZ4F_getFrameInfo() anytime+ * to extract already decoded frame parameters stored within dctx.+ *+ * Note that, if decoding has barely started,+ * and not yet read enough information to decode the header,+ * LZ4F_getFrameInfo() will fail.+ *+ * The number of bytes consumed from srcBuffer will be updated in *srcSizePtr (necessarily <= original value).+ * LZ4F_getFrameInfo() only consumes bytes when decoding has not yet started,+ * and when decoding the header has been successful.+ * Decompression must then resume from (srcBuffer + *srcSizePtr).+ *+ * @return : a hint about how many srcSize bytes LZ4F_decompress() expects for next call,+ * or an error code which can be tested using LZ4F_isError().+ * 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);++/*! LZ4F_decompress() :+ * Call this function repetitively to regenerate data compressed in `srcBuffer`.+ *+ * The function requires a valid dctx state.+ * It will read up to *srcSizePtr bytes from srcBuffer,+ * and decompress data into dstBuffer, of capacity *dstSizePtr.+ *+ * The nb of bytes consumed from srcBuffer will be written into *srcSizePtr (necessarily <= original value).+ * The nb of bytes decompressed into dstBuffer will be written into *dstSizePtr (necessarily <= original value).+ *+ * The function does not necessarily read all input bytes, so always check value in *srcSizePtr.+ * Unconsumed source data must be presented again in subsequent invocations.+ *+ * `dstBuffer` can freely change between each consecutive function invocation.+ * `dstBuffer` content will be overwritten.+ *+ * @return : an hint of how many `srcSize` bytes LZ4F_decompress() expects for next call.+ * Schematically, it's the size of the current (or remaining) compressed block + header of next block.+ * Respecting the hint provides some small speed benefit, because it skips intermediate buffers.+ * This is just a hint though, it's always possible to provide any srcSize.+ *+ * When a frame is fully decoded, @return will be 0 (no more data expected).+ * When provided with more bytes than necessary to decode a frame,+ * LZ4F_decompress() will stop reading exactly at end of current frame, and @return 0.+ *+ * If decompression failed, @return is an error code, which can be tested using LZ4F_isError().+ * After a decompression error, the `dctx` context is not resumable.+ * Use LZ4F_resetDecompressionContext() to return to clean state.+ *+ * 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);+++/*! LZ4F_resetDecompressionContext() : added in v1.8.0+ * In case of an error, the context is left in "undefined" state.+ * In which case, it's necessary to reset it, before re-using it.+ * This method can also be used to abruptly stop any unfinished decompression,+ * and start a new one using same context resources. */+LZ4FLIB_API void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx); /* always successful */++++#if defined (__cplusplus)+}+#endif++#endif /* LZ4F_H_09782039843 */++#if defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843)+#define LZ4F_H_STATIC_09782039843++#if defined (__cplusplus)+extern "C" {+#endif++/* These declarations are not stable and may change in the future.+ * They are therefore only safe to depend on+ * when the caller is statically linked against the library.+ * To access their declarations, define LZ4F_STATIC_LINKING_ONLY.+ *+ * By default, these symbols aren't published into shared/dynamic libraries.+ * You can override this behavior and force them to be published+ * by defining LZ4F_PUBLISH_STATIC_FUNCTIONS.+ * Use at your own risk.+ */+#ifdef LZ4F_PUBLISH_STATIC_FUNCTIONS+# define LZ4FLIB_STATIC_API LZ4FLIB_API+#else+# define LZ4FLIB_STATIC_API+#endif+++/* --- Error List --- */+#define LZ4F_LIST_ERRORS(ITEM) \+ ITEM(OK_NoError) \+ ITEM(ERROR_GENERIC) \+ ITEM(ERROR_maxBlockSize_invalid) \+ ITEM(ERROR_blockMode_invalid) \+ ITEM(ERROR_contentChecksumFlag_invalid) \+ ITEM(ERROR_compressionLevel_invalid) \+ ITEM(ERROR_headerVersion_wrong) \+ ITEM(ERROR_blockChecksum_invalid) \+ ITEM(ERROR_reservedFlag_set) \+ ITEM(ERROR_allocation_failed) \+ ITEM(ERROR_srcSize_tooLarge) \+ ITEM(ERROR_dstMaxSize_tooSmall) \+ ITEM(ERROR_frameHeader_incomplete) \+ ITEM(ERROR_frameType_unknown) \+ ITEM(ERROR_frameSize_wrong) \+ ITEM(ERROR_srcPtr_wrong) \+ ITEM(ERROR_decompressionFailed) \+ 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,++/* enum list is exposed, to handle specific errors */+typedef enum { LZ4F_LIST_ERRORS(LZ4F_GENERATE_ENUM)+ _LZ4F_dummy_error_enum_for_c89_never_used } LZ4F_errorCodes;++LZ4FLIB_STATIC_API LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult);+++/*! 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+ *********************************/++/* A Dictionary is useful for the compression of small messages (KB range).+ * It dramatically improves compression efficiency.+ *+ * LZ4 can ingest any input as dictionary, though only the last 64 KB are useful.+ * Best results are generally achieved by using Zstandard's Dictionary Builder+ * to generate a high-quality dictionary from a set of samples.+ *+ * Loading a dictionary has a cost, since it involves construction of tables.+ * The Bulk processing dictionary API makes it possible to share this cost+ * over an arbitrary number of compression jobs, even concurrently,+ * markedly improving compression latency for these cases.+ *+ * The same dictionary will have to be used on the decompression side+ * for decoding to be successful.+ * To help identify the correct dictionary at decoding stage,+ * the frame header allows optional embedding of a dictID field.+ */+typedef struct LZ4F_CDict_s LZ4F_CDict;++/*! LZ4_createCDict() :+ * When compressing multiple messages / blocks using the same dictionary, it's recommended to load it just once.+ * LZ4_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay.+ * LZ4_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.+ * `dictBuffer` can be released after LZ4_CDict creation, since its content is copied within CDict */+LZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize);+LZ4FLIB_STATIC_API void LZ4F_freeCDict(LZ4F_CDict* CDict);+++/*! LZ4_compressFrame_usingCDict() :+ * Compress an entire srcBuffer into a valid LZ4 frame using a digested Dictionary.+ * cctx must point to a context created by LZ4F_createCompressionContext().+ * If cdict==NULL, compress without a dictionary.+ * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).+ * If this condition is not respected, function will fail (@return an errorCode).+ * The LZ4F_preferences_t structure is optional : you may provide NULL as argument,+ * 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);+++/*! LZ4F_compressBegin_usingCDict() :+ * Inits streaming dictionary compression, and writes the frame header into dstBuffer.+ * dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.+ * `prefsPtr` is optional : you may provide NULL as argument,+ * 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);+++/*! 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);+++/*! 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)+}+#endif++#endif /* defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843) */
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@@ -53,7 +53,7 @@ #include "lz4hc.h" -/*=== Common LZ4 definitions ===*/+/*=== Common definitions ===*/ #if defined(__GNUC__) # pragma GCC diagnostic ignored "-Wunused-function" #endif@@ -61,15 +61,16 @@ # pragma clang diagnostic ignored "-Wunused-function" #endif -/*=== Enums ===*/-typedef enum { noDictCtx, usingDictCtxHc } dictCtx_directive;-- #define LZ4_COMMONDEFS_ONLY #ifndef LZ4_SRC_INCLUDED #include "lz4.c" /* LZ4_count, constants, mem */ #endif ++/*=== Enums ===*/+typedef enum { noDictCtx, usingDictCtxHc } dictCtx_directive;++ /*=== Constants ===*/ #define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH) #define LZ4_OPT_NUM (1<<12)@@ -92,24 +93,26 @@ **************************************/ static void LZ4HC_clearTables (LZ4HC_CCtx_internal* hc4) {- MEM_INIT((void*)hc4->hashTable, 0, sizeof(hc4->hashTable));+ MEM_INIT(hc4->hashTable, 0, sizeof(hc4->hashTable)); MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable)); } 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; } @@ -118,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;@@ -161,8 +167,7 @@ static U32 LZ4HC_rotatePattern(size_t const rotate, U32 const pattern) { size_t const bitsToRotate = (rotate & (sizeof(pattern) - 1)) << 3;- if (bitsToRotate == 0)- return pattern;+ if (bitsToRotate == 0) return pattern; return LZ4HC_rotl32(pattern, (int)bitsToRotate); } @@ -172,7 +177,8 @@ LZ4HC_countPattern(const BYTE* ip, const BYTE* const iEnd, U32 const pattern32) { const BYTE* const iStart = ip;- reg_t const pattern = (sizeof(pattern)==8) ? (reg_t)pattern32 + (((reg_t)pattern32) << 32) : pattern32;+ reg_t const pattern = (sizeof(pattern)==8) ?+ (reg_t)pattern32 + (((reg_t)pattern32) << (sizeof(pattern)*4)) : pattern32; while (likely(ip < iEnd-(sizeof(pattern)-1))) { reg_t const diff = LZ4_read_ARCH(ip) ^ pattern;@@ -192,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) {@@ -210,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--;@@ -233,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;@@ -270,20 +275,19 @@ DEBUGLOG(7, "First match at index %u / %u (lowestMatchIndex)", matchIndex, lowestMatchIndex); - while ((matchIndex>=lowestMatchIndex) && (nbAttempts)) {+ while ((matchIndex>=lowestMatchIndex) && (nbAttempts>0)) { int matchLength=0; nbAttempts--; 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) {@@ -292,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;@@ -325,8 +330,7 @@ distanceToNextMatch = candidateDist; matchChainPos = (U32)pos; accel = 1 << kTrigger;- }- }+ } } if (distanceToNextMatch > 1) { if (distanceToNextMatch > matchIndex) break; /* avoid overflow */ matchIndex -= distanceToNextMatch;@@ -346,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);@@ -372,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);@@ -410,14 +415,14 @@ } /* while ((matchIndex>=lowestMatchIndex) && (nbAttempts)) */ if ( dict == usingDictCtxHc- && nbAttempts+ && 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;@@ -425,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; } } @@ -441,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),@@ -460,74 +465,90 @@ * @return : 0 if ok, * 1 if buffer issue detected */ LZ4_FORCE_INLINE int LZ4HC_encodeSequence (- const BYTE** ip,- BYTE** op,- const BYTE** anchor,+ const BYTE** _ip,+ BYTE** _op,+ const BYTE** _anchor, int matchLength, const BYTE* const match, limitedOutput_directive limit, BYTE* oend) {+#define ip (*_ip)+#define op (*_op)+#define anchor (*_anchor)+ size_t length;- BYTE* const token = (*op)++;+ BYTE* const token = op++; #if defined(LZ4_DEBUG) && (LZ4_DEBUG >= 6) static const BYTE* start = NULL; static U32 totalCost = 0;- U32 const pos = (start==NULL) ? 0 : (U32)(*anchor - start);- U32 const ll = (U32)(*ip - *anchor);+ U32 const pos = (start==NULL) ? 0 : (U32)(anchor - start);+ U32 const ll = (U32)(ip - anchor); U32 const llAdd = (ll>=15) ? ((ll-15) / 255) + 1 : 0; U32 const mlAdd = (matchLength>=19) ? ((matchLength-19) / 255) + 1 : 0; U32 const cost = 1 + llAdd + ll + 2 + mlAdd;- if (start==NULL) start = *anchor; /* only works for single segment */+ if (start==NULL) start = anchor; /* only works for single segment */ /* g_debuglog_enable = (pos >= 2228) & (pos <= 2262); */- DEBUGLOG(6, "pos:%7u -- literals:%3u, match:%4i, offset:%5u, cost:%3u + %u",+ DEBUGLOG(6, "pos:%7u -- literals:%4u, match:%4i, offset:%5u, cost:%4u + %5u", pos,- (U32)(*ip - *anchor), matchLength, (U32)(*ip-match),+ (U32)(ip - anchor), matchLength, (U32)(ip-match), cost, totalCost); totalCost += cost; #endif /* Encode Literal length */- length = (size_t)(*ip - *anchor);- if ((limit) && ((*op + (length / 255) + length + (2 + 1 + LASTLITERALS)) > oend)) return 1; /* Check output limit */+ length = (size_t)(ip - anchor);+ LZ4_STATIC_ASSERT(notLimited == 0);+ /* Check output limit */+ if (limit && ((op + (length / 255) + length + (2 + 1 + LASTLITERALS)) > oend)) {+ DEBUGLOG(6, "Not enough room to write %i literals (%i bytes remaining)",+ (int)length, (int)(oend - op));+ return 1;+ } if (length >= RUN_MASK) { size_t len = length - RUN_MASK; *token = (RUN_MASK << ML_BITS);- for(; len >= 255 ; len -= 255) *(*op)++ = 255;- *(*op)++ = (BYTE)len;+ for(; len >= 255 ; len -= 255) *op++ = 255;+ *op++ = (BYTE)len; } else { *token = (BYTE)(length << ML_BITS); } /* Copy Literals */- LZ4_wildCopy8(*op, *anchor, (*op) + length);- *op += length;+ LZ4_wildCopy8(op, anchor, op + length);+ op += length; /* Encode Offset */- assert( (*ip - match) <= LZ4_DISTANCE_MAX ); /* note : consider providing offset as a value, rather than as a pointer difference */- LZ4_writeLE16(*op, (U16)(*ip-match)); *op += 2;+ assert( (ip - match) <= LZ4_DISTANCE_MAX ); /* note : consider providing offset as a value, rather than as a pointer difference */+ LZ4_writeLE16(op, (U16)(ip - match)); op += 2; /* Encode MatchLength */ assert(matchLength >= MINMATCH); length = (size_t)matchLength - MINMATCH;- if ((limit) && (*op + (length / 255) + (1 + LASTLITERALS) > oend)) return 1; /* Check output limit */+ if (limit && (op + (length / 255) + (1 + LASTLITERALS) > oend)) {+ DEBUGLOG(6, "Not enough room to write match length");+ return 1; /* Check output limit */+ } if (length >= ML_MASK) { *token += ML_MASK; length -= ML_MASK;- for(; length >= 510 ; length -= 510) { *(*op)++ = 255; *(*op)++ = 255; }- if (length >= 255) { length -= 255; *(*op)++ = 255; }- *(*op)++ = (BYTE)length;+ for(; length >= 510 ; length -= 510) { *op++ = 255; *op++ = 255; }+ if (length >= 255) { length -= 255; *op++ = 255; }+ *op++ = (BYTE)length; } else { *token += (BYTE)(length); } /* Prepare next loop */- *ip += matchLength;- *anchor = *ip;+ ip += matchLength;+ anchor = ip; return 0; }+#undef ip+#undef op+#undef anchor LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( LZ4HC_CCtx_internal* const ctx,@@ -535,7 +556,7 @@ char* const dest, int* srcSizePtr, int const maxOutputSize,- unsigned maxNbAttempts,+ int maxNbAttempts, const limitedOutput_directive limit, const dictCtx_directive dict )@@ -565,7 +586,7 @@ /* init */ *srcSizePtr = 0; if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */- if (inputSize < LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */+ if (inputSize < LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */ /* Main Loop */ while (ip <= mflimit) {@@ -637,7 +658,11 @@ if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; ip = start2; optr = op;- if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml2, ref2, limit, oend)) goto _dest_overflow;+ if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml2, ref2, limit, oend)) {+ ml = ml2;+ ref = ref2;+ goto _dest_overflow;+ } continue; } @@ -709,17 +734,18 @@ _last_literals: /* Encode Last Literals */ { size_t lastRunSize = (size_t)(iend - anchor); /* literals */- size_t litLength = (lastRunSize + 255 - RUN_MASK) / 255;- size_t const totalSize = 1 + litLength + lastRunSize;+ size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255;+ size_t const totalSize = 1 + llAdd + lastRunSize; if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ if (limit && (op + totalSize > oend)) {- if (limit == limitedOutput) return 0; /* Check output limit */+ if (limit == limitedOutput) return 0; /* adapt lastRunSize to fill 'dest' */- lastRunSize = (size_t)(oend - op) - 1;- litLength = (lastRunSize + 255 - RUN_MASK) / 255;- lastRunSize -= litLength;+ lastRunSize = (size_t)(oend - op) - 1 /*token*/;+ llAdd = (lastRunSize + 256 - RUN_MASK) / 256;+ lastRunSize -= llAdd; }- ip = anchor + lastRunSize;+ DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize);+ ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */ if (lastRunSize >= RUN_MASK) { size_t accumulator = lastRunSize - RUN_MASK;@@ -729,7 +755,7 @@ } else { *op++ = (BYTE)(lastRunSize << ML_BITS); }- memcpy(op, anchor, lastRunSize);+ LZ4_memcpy(op, anchor, lastRunSize); op += lastRunSize; } @@ -739,9 +765,25 @@ _dest_overflow: if (limit == fillOutput) {+ /* Assumption : ip, anchor, ml and ref must be set correctly */+ size_t const ll = (size_t)(ip - anchor);+ size_t const ll_addbytes = (ll + 240) / 255;+ size_t const ll_totalCost = 1 + ll_addbytes + ll;+ BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */+ DEBUGLOG(6, "Last sequence overflowing"); op = optr; /* restore correct out pointer */+ if (op + ll_totalCost <= maxLitPos) {+ /* ll validated; now adjust match length */+ size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost));+ size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255);+ assert(maxMlSize < INT_MAX); assert(ml >= 0);+ if ((size_t)ml > maxMlSize) ml = (int)maxMlSize;+ if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + ml >= MFLIMIT) {+ LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, notLimited, oend);+ } } goto _last_literals; }+ /* compression failed */ return 0; } @@ -752,7 +794,7 @@ int const nbSearches, size_t sufficient_len, const limitedOutput_directive limit, int const fullUpdate, const dictCtx_directive dict,- HCfavor_e favorDecSpeed);+ const HCfavor_e favorDecSpeed); LZ4_FORCE_INLINE int LZ4HC_compress_generic_internal (@@ -769,7 +811,7 @@ typedef enum { lz4hc, lz4opt } lz4hc_strat_e; typedef struct { lz4hc_strat_e strat;- U32 nbSearches;+ int nbSearches; U32 targetLength; } cParams_t; static const cParams_t clTable[LZ4HC_CLEVEL_MAX+1] = {@@ -788,7 +830,8 @@ { lz4opt,16384,LZ4_OPT_NUM }, /* 12==LZ4HC_CLEVEL_MAX */ }; - DEBUGLOG(4, "LZ4HC_compress_generic(ctx=%p, src=%p, srcSize=%d)", ctx, src, *srcSizePtr);+ DEBUGLOG(4, "LZ4HC_compress_generic(ctx=%p, src=%p, srcSize=%d, limit=%d)",+ ctx, src, *srcSizePtr, limit); if (limit == fillOutput && dstCapacity < 1) return 0; /* Impossible to store anything */ if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size (too large or negative) */@@ -808,7 +851,7 @@ assert(cParam.strat == lz4opt); result = LZ4HC_compress_optimal(ctx, src, dst, srcSizePtr, dstCapacity,- (int)cParam.nbSearches, cParam.targetLength, limit,+ cParam.nbSearches, cParam.targetLength, limit, cLevel == LZ4HC_CLEVEL_MAX, /* ultra mode */ dict, favor); }@@ -845,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);@@ -881,27 +924,22 @@ int LZ4_sizeofStateHC(void) { return (int)sizeof(LZ4_streamHC_t); } -#ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 :- * it reports an aligment of 8-bytes,- * while actually aligning LZ4_streamHC_t on 4 bytes. */ static size_t LZ4_streamHC_t_alignment(void) {- struct { char c; LZ4_streamHC_t t; } t_a;- return sizeof(t_a) - sizeof(t_a.t);-}+#if LZ4_ALIGN_TEST+ typedef struct { char c; LZ4_streamHC_t t; } t_a;+ return sizeof(t_a) - sizeof(LZ4_streamHC_t);+#else+ return 1; /* effectively disabled */ #endif+} /* state is presumed correctly initialized, * in which case its size and alignment have already been validate */ int LZ4_compress_HC_extStateHC_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) { LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)state)->internal_donotuse;-#ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 :- * it reports an aligment of 8-bytes,- * while actually aligning LZ4_streamHC_t on 4 bytes. */- assert(((size_t)state & (LZ4_streamHC_t_alignment() - 1)) == 0); /* check alignment */-#endif- if (((size_t)(state)&(sizeof(void*)-1)) != 0) return 0; /* Error : state is not aligned for pointers (32 or 64 bits) */+ if (!LZ4_isAligned(state, LZ4_streamHC_t_alignment())) return 0; LZ4_resetStreamHC_fast((LZ4_streamHC_t*)state, compressionLevel); LZ4HC_init_internal (ctx, (const BYTE*)src); if (dstCapacity < LZ4_compressBound(srcSize))@@ -919,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@@ -948,12 +988,14 @@ * Streaming Functions **************************************/ /* allocation */+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4_streamHC_t* LZ4_createStreamHC(void) {- LZ4_streamHC_t* const LZ4_streamHCPtr = (LZ4_streamHC_t*)ALLOC(sizeof(LZ4_streamHC_t));- if (LZ4_streamHCPtr==NULL) return NULL;- LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); /* full initialization, malloc'ed buffer can be full of garbage */- return LZ4_streamHCPtr;+ LZ4_streamHC_t* const state =+ (LZ4_streamHC_t*)ALLOC_AND_ZERO(sizeof(LZ4_streamHC_t));+ if (state == NULL) return NULL;+ LZ4_setCompressionLevel(state, LZ4HC_CLEVEL_DEFAULT);+ return state; } int LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr)@@ -963,27 +1005,20 @@ 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;+ DEBUGLOG(4, "LZ4_initStreamHC(%p, %u)", buffer, (unsigned)size);+ /* check conditions */ if (buffer == NULL) return NULL; if (size < sizeof(LZ4_streamHC_t)) return NULL;-#ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 :- * it reports an aligment of 8-bytes,- * while actually aligning LZ4_streamHC_t on 4 bytes. */- if (((size_t)buffer) & (LZ4_streamHC_t_alignment() - 1)) return NULL; /* alignment check */-#endif- /* if compilation fails here, LZ4_STREAMHCSIZE must be increased */- LZ4_STATIC_ASSERT(sizeof(LZ4HC_CCtx_internal) <= LZ4_STREAMHCSIZE);- DEBUGLOG(4, "LZ4_initStreamHC(%p, %u)", LZ4_streamHCPtr, (unsigned)size);- /* end-base will trigger a clearTable on starting compression */- LZ4_streamHCPtr->internal_donotuse.end = (const BYTE *)(ptrdiff_t)-1;- LZ4_streamHCPtr->internal_donotuse.base = NULL;- LZ4_streamHCPtr->internal_donotuse.dictCtx = NULL;- LZ4_streamHCPtr->internal_donotuse.favorDecSpeed = 0;- LZ4_streamHCPtr->internal_donotuse.dirty = 0;+ if (!LZ4_isAligned(buffer, LZ4_streamHC_t_alignment())) return NULL;+ /* init */+ { LZ4HC_CCtx_internal* const hcstate = &(LZ4_streamHCPtr->internal_donotuse);+ MEM_INIT(hcstate, 0, sizeof(*hcstate)); } LZ4_setCompressionLevel(LZ4_streamHCPtr, LZ4HC_CLEVEL_DEFAULT); return LZ4_streamHCPtr; }@@ -1001,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);@@ -1054,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 */ @@ -1069,21 +1108,22 @@ ctxPtr->dictCtx = NULL; } -static int LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr,- const char* src, char* dst,- int* srcSizePtr, int dstCapacity,- limitedOutput_directive limit)+static int+LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr,+ const char* src, char* dst,+ int* srcSizePtr, int dstCapacity,+ limitedOutput_directive limit) { LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse;- DEBUGLOG(4, "LZ4_compressHC_continue_generic(ctx=%p, src=%p, srcSize=%d)",- LZ4_streamHCPtr, src, *srcSizePtr);+ DEBUGLOG(5, "LZ4_compressHC_continue_generic(ctx=%p, src=%p, srcSize=%d, limit=%d)",+ 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); }@@ -1094,14 +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); }@@ -1121,23 +1163,31 @@ -/* dictionary saving */-+/* LZ4_saveDictHC :+ * save history content+ * into a user-provided buffer+ * which is then used to continue compression+ */ 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));- DEBUGLOG(4, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize);+ 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; if (dictSize < 4) dictSize = 0; if (dictSize > prefixSize) dictSize = prefixSize;- memmove(safeBuffer, streamPtr->end - dictSize, dictSize);- { U32 const endIndex = (U32)(streamPtr->end - streamPtr->base);+ if (safeBuffer == NULL) assert(dictSize == 0);+ if (dictSize > 0)+ 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;- if (streamPtr->nextToUpdate < streamPtr->dictLimit) streamPtr->nextToUpdate = streamPtr->dictLimit;+ streamPtr->dictStart = streamPtr->prefixStart;+ if (streamPtr->nextToUpdate < streamPtr->dictLimit)+ streamPtr->nextToUpdate = streamPtr->dictLimit; } return dictSize; }@@ -1163,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 */@@ -1175,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();@@ -1189,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) {@@ -1202,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; } @@ -1287,8 +1339,13 @@ const dictCtx_directive dict, const HCfavor_e favorDecSpeed) {+ int retval = 0; #define TRAILING_LITERALS 3+#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... */+#endif const BYTE* ip = (const BYTE*) source; const BYTE* anchor = ip;@@ -1298,15 +1355,19 @@ BYTE* op = (BYTE*) dst; BYTE* opSaved = (BYTE*) dst; BYTE* oend = op + dstCapacity;+ int ovml = MINMATCH; /* overflow - last sequence */+ const BYTE* ovref = NULL; /* init */+#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); *srcSizePtr = 0; if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */ if (sufficient_len >= LZ4_OPT_NUM) sufficient_len = LZ4_OPT_NUM-1; /* Main Loop */- assert(ip - anchor < LZ4_MAX_INPUT_SIZE); while (ip <= mflimit) { int const llen = (int)(ip - anchor); int best_mlen, best_off;@@ -1320,8 +1381,11 @@ int const firstML = firstMatch.len; const BYTE* const matchPos = ip - firstMatch.off; opSaved = op;- if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), firstML, matchPos, limit, oend) ) /* updates ip, op and anchor */+ if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), firstML, matchPos, limit, oend) ) { /* updates ip, op and anchor */+ ovml = firstML;+ ovref = matchPos; goto _dest_overflow;+ } continue; } @@ -1463,7 +1527,7 @@ best_off = opt[last_match_pos].off; cur = last_match_pos - best_mlen; - encode: /* cur, last_match_pos, best_mlen, best_off must be set */+encode: /* cur, last_match_pos, best_mlen, best_off must be set */ assert(cur < LZ4_OPT_NUM); assert(last_match_pos >= 1); /* == 1 when only one candidate */ DEBUGLOG(6, "reverse traversal, looking for shortest path (last_match_pos=%i)", last_match_pos);@@ -1493,25 +1557,31 @@ assert(ml >= MINMATCH); assert((offset >= 1) && (offset <= LZ4_DISTANCE_MAX)); opSaved = op;- if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ip - offset, limit, oend) ) /* updates ip, op and anchor */+ if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ip - offset, limit, oend) ) { /* updates ip, op and anchor */+ ovml = ml;+ ovref = ip - offset; goto _dest_overflow;- } }+ } } } } /* while (ip <= mflimit) */ - _last_literals:+_last_literals: /* Encode Last Literals */ { size_t lastRunSize = (size_t)(iend - anchor); /* literals */- size_t litLength = (lastRunSize + 255 - RUN_MASK) / 255;- size_t const totalSize = 1 + litLength + lastRunSize;+ size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255;+ size_t const totalSize = 1 + llAdd + lastRunSize; if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ if (limit && (op + totalSize > oend)) {- if (limit == limitedOutput) return 0; /* Check output limit */+ if (limit == limitedOutput) { /* Check output limit */+ retval = 0;+ goto _return_label;+ } /* adapt lastRunSize to fill 'dst' */- lastRunSize = (size_t)(oend - op) - 1;- litLength = (lastRunSize + 255 - RUN_MASK) / 255;- lastRunSize -= litLength;+ lastRunSize = (size_t)(oend - op) - 1 /*token*/;+ llAdd = (lastRunSize + 256 - RUN_MASK) / 256;+ lastRunSize -= llAdd; }- ip = anchor + lastRunSize;+ DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize);+ ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */ if (lastRunSize >= RUN_MASK) { size_t accumulator = lastRunSize - RUN_MASK;@@ -1521,18 +1591,41 @@ } else { *op++ = (BYTE)(lastRunSize << ML_BITS); }- memcpy(op, anchor, lastRunSize);+ LZ4_memcpy(op, anchor, lastRunSize); op += lastRunSize; } /* End */ *srcSizePtr = (int) (((const char*)ip) - source);- return (int) ((char*)op-dst);+ retval = (int) ((char*)op-dst);+ goto _return_label; - _dest_overflow:- if (limit == fillOutput) {- op = opSaved; /* restore correct out pointer */- goto _last_literals;- }- return 0;- }+_dest_overflow:+if (limit == fillOutput) {+ /* Assumption : ip, anchor, ovml and ovref must be set correctly */+ size_t const ll = (size_t)(ip - anchor);+ size_t const ll_addbytes = (ll + 240) / 255;+ size_t const ll_totalCost = 1 + ll_addbytes + ll;+ BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */+ DEBUGLOG(6, "Last sequence overflowing (only %i bytes remaining)", (int)(oend-1-opSaved));+ op = opSaved; /* restore correct out pointer */+ if (op + ll_totalCost <= maxLitPos) {+ /* ll validated; now adjust match length */+ size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost));+ size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255);+ assert(maxMlSize < INT_MAX); assert(ovml >= 0);+ if ((size_t)ovml > maxMlSize) ovml = (int)maxMlSize;+ if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + ovml >= MFLIMIT) {+ DEBUGLOG(6, "Space to end : %i + ml (%i)", (int)((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1), ovml);+ DEBUGLOG(6, "Before : ip = %p, anchor = %p", ip, anchor);+ LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ovml, ovref, notLimited, oend);+ DEBUGLOG(6, "After : ip = %p, anchor = %p", ip, anchor);+ } }+ goto _last_literals;+}+_return_label:+#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,63 +198,36 @@ #define LZ4HC_HASH_MASK (LZ4HC_HASHTABLESIZE - 1) -#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)-#include <stdint.h>--typedef struct LZ4HC_CCtx_internal LZ4HC_CCtx_internal;-struct LZ4HC_CCtx_internal-{- uint32_t hashTable[LZ4HC_HASHTABLESIZE];- uint16_t chainTable[LZ4HC_MAXD];- const uint8_t* end; /* next block here to continue on current prefix */- const uint8_t* base; /* All index relative to this position */- const uint8_t* dictBase; /* alternate base for extDict */- uint32_t dictLimit; /* below that point, need extDict */- uint32_t lowLimit; /* below that point, no more dict */- uint32_t nextToUpdate; /* index from which to continue dictionary update */- short compressionLevel;- int8_t favorDecSpeed; /* favor decompression speed if this flag set,- otherwise, favor compression ratio */- int8_t dirty; /* stream has to be fully reset if this flag is set */- const LZ4HC_CCtx_internal* dictCtx;-};--#else-+/* 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 {- unsigned int hashTable[LZ4HC_HASHTABLESIZE];- unsigned short chainTable[LZ4HC_MAXD];- const unsigned char* end; /* next block here to continue on current prefix */- const unsigned char* base; /* All index relative to this position */- const unsigned char* dictBase; /* alternate base for extDict */- unsigned int dictLimit; /* below that point, need extDict */- unsigned int lowLimit; /* below that point, no more dict */- unsigned int nextToUpdate; /* index from which to continue dictionary update */- short compressionLevel;- char favorDecSpeed; /* favor decompression speed if this flag set,- otherwise, favor compression ratio */- char dirty; /* stream has to be fully reset if this flag is set */+ 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* 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 */+ short compressionLevel;+ LZ4_i8 favorDecSpeed; /* favor decompression speed if this flag set,+ otherwise, favor compression ratio */+ LZ4_i8 dirty; /* stream has to be fully reset if this flag is set */ const LZ4HC_CCtx_internal* dictCtx; }; -#endif---/* Do not use these definitions directly !- * Declare or allocate an LZ4_streamHC_t instead.- */-#define LZ4_STREAMHCSIZE (4*LZ4HC_HASHTABLESIZE + 2*LZ4HC_MAXD + 56 + ((sizeof(void*)==16) ? 56 : 0) /* AS400*/ ) /* 262200 or 262256*/-#define LZ4_STREAMHCSIZE_SIZET (LZ4_STREAMHCSIZE / sizeof(size_t))+#define LZ4_STREAMHC_MINSIZE 262200 /* static size, for inter-version compatibility */ union LZ4_streamHC_u {- size_t table[LZ4_STREAMHCSIZE_SIZET];+ 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. *@@ -269,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); /*-************************************@@ -297,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);@@ -330,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
+ cbits/xxhash.c view
@@ -0,0 +1,1030 @@+/*+* xxHash - Fast Hash algorithm+* Copyright (C) 2012-2016, 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+* modification, are permitted provided that the following conditions are+* met:+*+* * Redistributions of source code must retain the above copyright+* notice, this list of conditions and the following disclaimer.+* * Redistributions in binary form must reproduce the above+* copyright notice, this list of conditions and the following disclaimer+* in the documentation and/or other materials provided with the+* distribution.+*+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*+* You can contact the author at :+* - xxHash homepage: http://www.xxhash.com+* - xxHash source repository : https://github.com/Cyan4973/xxHash+*/+++/* *************************************+* Tuning parameters+***************************************/+/*!XXH_FORCE_MEMORY_ACCESS :+ * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.+ * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.+ * The below switch allow to select different access method for improved performance.+ * Method 0 (default) : use `memcpy()`. Safe and portable.+ * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).+ * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.+ * Method 2 : direct access. This method doesn't depend on compiler but violate C standard.+ * It can generate buggy code on targets which do not support unaligned memory accesses.+ * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)+ * See http://stackoverflow.com/a/32095106/646947 for details.+ * Prefer these methods in priority order (0 > 1 > 2)+ */+#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */+# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \+ || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \+ || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )+# define XXH_FORCE_MEMORY_ACCESS 2+# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \+ (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \+ || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \+ || defined(__ARM_ARCH_7S__) ))+# define XXH_FORCE_MEMORY_ACCESS 1+# endif+#endif++/*!XXH_ACCEPT_NULL_INPUT_POINTER :+ * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault.+ * When this macro is enabled, xxHash actively checks input for null pointer.+ * It it is, result for null input pointers is the same as a null-length input.+ */+#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */+# define XXH_ACCEPT_NULL_INPUT_POINTER 0+#endif++/*!XXH_FORCE_NATIVE_FORMAT :+ * By default, xxHash library provides endian-independent Hash values, based on little-endian convention.+ * Results are therefore identical for little-endian and big-endian CPU.+ * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.+ * Should endian-independence be of no importance for your application, you may set the #define below to 1,+ * to improve speed for Big-endian CPU.+ * This option has no impact on Little_Endian CPU.+ */+#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */+# define XXH_FORCE_NATIVE_FORMAT 0+#endif++/*!XXH_FORCE_ALIGN_CHECK :+ * This is a minor performance trick, only useful with lots of very small keys.+ * It means : check for aligned/unaligned input.+ * The check costs one initial branch per hash;+ * set it to 0 when the input is guaranteed to be aligned,+ * or when alignment doesn't matter for performance.+ */+#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */+# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)+# define XXH_FORCE_ALIGN_CHECK 0+# else+# define XXH_FORCE_ALIGN_CHECK 1+# endif+#endif+++/* *************************************+* Includes & Memory related functions+***************************************/+/*! Modify the local functions below should you wish to use some other memory routines+* for malloc(), free() */+#include <stdlib.h>+static void* XXH_malloc(size_t s) { return malloc(s); }+static void XXH_free (void* p) { free(p); }+/*! and for memcpy() */+#include <string.h>+static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }++#include <assert.h> /* assert */++#define XXH_STATIC_LINKING_ONLY+#include "xxhash.h"+++/* *************************************+* Compiler Specific Options+***************************************/+#ifdef _MSC_VER /* Visual Studio */+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */+# define FORCE_INLINE static __forceinline+#else+# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */+# ifdef __GNUC__+# define FORCE_INLINE static inline __attribute__((always_inline))+# else+# define FORCE_INLINE static inline+# endif+# else+# define FORCE_INLINE static+# endif /* __STDC_VERSION__ */+#endif+++/* *************************************+* Basic Types+***************************************/+#ifndef MEM_MODULE+# if !defined (__VMS) \+ && (defined (__cplusplus) \+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )+# include <stdint.h>+ typedef uint8_t BYTE;+ typedef uint16_t U16;+ typedef uint32_t U32;+# else+ typedef unsigned char BYTE;+ typedef unsigned short U16;+ typedef unsigned int U32;+# endif+#endif++#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))++/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */+static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; }++#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))++/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */+/* currently only defined for gcc and icc */+typedef union { U32 u32; } __attribute__((packed)) unalign;+static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }++#else++/* portable and safe solution. Generally efficient.+ * see : http://stackoverflow.com/a/32095106/646947+ */+static U32 XXH_read32(const void* memPtr)+{+ U32 val;+ memcpy(&val, memPtr, sizeof(val));+ return val;+}++#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */+++/* ****************************************+* Compiler-specific Functions and Macros+******************************************/+#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)++/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */+#if defined(_MSC_VER)+# define XXH_rotl32(x,r) _rotl(x,r)+# define XXH_rotl64(x,r) _rotl64(x,r)+#else+# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))+# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r)))+#endif++#if defined(_MSC_VER) /* Visual Studio */+# define XXH_swap32 _byteswap_ulong+#elif XXH_GCC_VERSION >= 403+# define XXH_swap32 __builtin_bswap32+#else+static U32 XXH_swap32 (U32 x)+{+ return ((x << 24) & 0xff000000 ) |+ ((x << 8) & 0x00ff0000 ) |+ ((x >> 8) & 0x0000ff00 ) |+ ((x >> 24) & 0x000000ff );+}+#endif+++/* *************************************+* Architecture Macros+***************************************/+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;++/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */+#ifndef XXH_CPU_LITTLE_ENDIAN+static int XXH_isLittleEndian(void)+{+ const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */+ return one.c[0];+}+# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian()+#endif+++/* ***************************+* Memory reads+*****************************/+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;++FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)+{+ if (align==XXH_unaligned)+ return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));+ else+ return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);+}++FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)+{+ return XXH_readLE32_align(ptr, endian, XXH_unaligned);+}++static U32 XXH_readBE32(const void* ptr)+{+ return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);+}+++/* *************************************+* Macros+***************************************/+#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; } /* use after variable declarations */+XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }+++/* *******************************************************************+* 32-bit hash functions+*********************************************************************/+static const U32 PRIME32_1 = 2654435761U;+static const U32 PRIME32_2 = 2246822519U;+static const U32 PRIME32_3 = 3266489917U;+static const U32 PRIME32_4 = 668265263U;+static const U32 PRIME32_5 = 374761393U;++static U32 XXH32_round(U32 seed, U32 input)+{+ seed += input * PRIME32_2;+ seed = XXH_rotl32(seed, 13);+ seed *= PRIME32_1;+ return seed;+}++/* mix all bits */+static U32 XXH32_avalanche(U32 h32)+{+ h32 ^= h32 >> 15;+ h32 *= PRIME32_2;+ h32 ^= h32 >> 13;+ h32 *= PRIME32_3;+ h32 ^= h32 >> 16;+ return(h32);+}++#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align)++static U32+XXH32_finalize(U32 h32, const void* ptr, size_t len,+ XXH_endianess endian, XXH_alignment align)++{+ const BYTE* p = (const BYTE*)ptr;++#define PROCESS1 \+ h32 += (*p++) * PRIME32_5; \+ h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;++#define PROCESS4 \+ h32 += XXH_get32bits(p) * PRIME32_3; \+ p+=4; \+ h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;++ switch(len&15) /* or switch(bEnd - p) */+ {+ case 12: PROCESS4;+ /* fallthrough */+ case 8: PROCESS4;+ /* fallthrough */+ case 4: PROCESS4;+ return XXH32_avalanche(h32);++ case 13: PROCESS4;+ /* fallthrough */+ case 9: PROCESS4;+ /* fallthrough */+ case 5: PROCESS4;+ PROCESS1;+ return XXH32_avalanche(h32);++ case 14: PROCESS4;+ /* fallthrough */+ case 10: PROCESS4;+ /* fallthrough */+ case 6: PROCESS4;+ PROCESS1;+ PROCESS1;+ return XXH32_avalanche(h32);++ case 15: PROCESS4;+ /* fallthrough */+ case 11: PROCESS4;+ /* fallthrough */+ case 7: PROCESS4;+ /* fallthrough */+ case 3: PROCESS1;+ /* fallthrough */+ case 2: PROCESS1;+ /* fallthrough */+ case 1: PROCESS1;+ /* fallthrough */+ case 0: return XXH32_avalanche(h32);+ }+ assert(0);+ return h32; /* reaching this point is deemed impossible */+}+++FORCE_INLINE U32+XXH32_endian_align(const void* input, size_t len, U32 seed,+ XXH_endianess endian, XXH_alignment align)+{+ const BYTE* p = (const BYTE*)input;+ const BYTE* bEnd = p + len;+ U32 h32;++#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)+ if (p==NULL) {+ len=0;+ bEnd=p=(const BYTE*)(size_t)16;+ }+#endif++ if (len>=16) {+ const BYTE* const limit = bEnd - 15;+ U32 v1 = seed + PRIME32_1 + PRIME32_2;+ U32 v2 = seed + PRIME32_2;+ U32 v3 = seed + 0;+ U32 v4 = seed - PRIME32_1;++ do {+ v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4;+ v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4;+ v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4;+ v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4;+ } while (p < limit);++ h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7)+ + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);+ } else {+ h32 = seed + PRIME32_5;+ }++ h32 += (U32)len;++ return XXH32_finalize(h32, p, len&15, endian, align);+}+++XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed)+{+#if 0+ /* Simple version, good for code maintenance, but unfortunately slow for small inputs */+ XXH32_state_t state;+ XXH32_reset(&state, seed);+ XXH32_update(&state, input, len);+ return XXH32_digest(&state);+#else+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++ if (XXH_FORCE_ALIGN_CHECK) {+ if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+ return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);+ else+ return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);+ } }++ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+ return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);+ else+ return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);+#endif+}++++/*====== Hash streaming ======*/++XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)+{+ return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));+}+XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)+{+ XXH_free(statePtr);+ return XXH_OK;+}++XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)+{+ memcpy(dstState, srcState, sizeof(*dstState));+}++XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed)+{+ XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */+ memset(&state, 0, sizeof(state));+ state.v1 = seed + PRIME32_1 + PRIME32_2;+ state.v2 = seed + PRIME32_2;+ state.v3 = seed + 0;+ state.v4 = seed - PRIME32_1;+ /* do not write into reserved, planned to be removed in a future version */+ memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved));+ return XXH_OK;+}+++FORCE_INLINE XXH_errorcode+XXH32_update_endian(XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian)+{+ if (input==NULL)+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)+ return XXH_OK;+#else+ return XXH_ERROR;+#endif++ { const BYTE* p = (const BYTE*)input;+ const BYTE* const bEnd = p + len;++ state->total_len_32 += (unsigned)len;+ state->large_len |= (len>=16) | (state->total_len_32>=16);++ if (state->memsize + len < 16) { /* fill in tmp buffer */+ XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len);+ state->memsize += (unsigned)len;+ return XXH_OK;+ }++ if (state->memsize) { /* some data left from previous update */+ XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize);+ { const U32* p32 = state->mem32;+ state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++;+ state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++;+ state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++;+ state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian));+ }+ p += 16-state->memsize;+ state->memsize = 0;+ }++ if (p <= bEnd-16) {+ const BYTE* const limit = bEnd - 16;+ U32 v1 = state->v1;+ U32 v2 = state->v2;+ U32 v3 = state->v3;+ U32 v4 = state->v4;++ do {+ v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4;+ v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4;+ v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4;+ v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4;+ } while (p<=limit);++ state->v1 = v1;+ state->v2 = v2;+ state->v3 = v3;+ state->v4 = v4;+ }++ if (p < bEnd) {+ XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));+ state->memsize = (unsigned)(bEnd-p);+ }+ }++ return XXH_OK;+}+++XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len)+{+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+ return XXH32_update_endian(state_in, input, len, XXH_littleEndian);+ else+ return XXH32_update_endian(state_in, input, len, XXH_bigEndian);+}+++FORCE_INLINE U32+XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian)+{+ U32 h32;++ if (state->large_len) {+ h32 = XXH_rotl32(state->v1, 1)+ + XXH_rotl32(state->v2, 7)+ + XXH_rotl32(state->v3, 12)+ + XXH_rotl32(state->v4, 18);+ } else {+ h32 = state->v3 /* == seed */ + PRIME32_5;+ }++ h32 += state->total_len_32;++ return XXH32_finalize(h32, state->mem32, state->memsize, endian, XXH_aligned);+}+++XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in)+{+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+ return XXH32_digest_endian(state_in, XXH_littleEndian);+ else+ return XXH32_digest_endian(state_in, XXH_bigEndian);+}+++/*====== Canonical representation ======*/++/*! Default XXH result types are basic unsigned 32 and 64 bits.+* The canonical representation follows human-readable write convention, aka big-endian (large digits first).+* These functions allow transformation of hash result into and from its canonical format.+* This way, hash values can be written into a file or buffer, remaining comparable across different systems.+*/++XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)+{+ XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));+ if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);+ memcpy(dst, &hash, sizeof(*dst));+}++XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)+{+ return XXH_readBE32(src);+}+++#ifndef XXH_NO_LONG_LONG++/* *******************************************************************+* 64-bit hash functions+*********************************************************************/++/*====== Memory access ======*/++#ifndef MEM_MODULE+# define MEM_MODULE+# if !defined (__VMS) \+ && (defined (__cplusplus) \+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )+# include <stdint.h>+ typedef uint64_t U64;+# else+ /* if compiler doesn't support unsigned long long, replace by another 64-bit type */+ typedef unsigned long long U64;+# endif+#endif+++#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))++/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */+static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; }++#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))++/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */+/* currently only defined for gcc and icc */+typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64;+static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; }++#else++/* portable and safe solution. Generally efficient.+ * see : http://stackoverflow.com/a/32095106/646947+ */++static U64 XXH_read64(const void* memPtr)+{+ U64 val;+ memcpy(&val, memPtr, sizeof(val));+ return val;+}++#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */++#if defined(_MSC_VER) /* Visual Studio */+# define XXH_swap64 _byteswap_uint64+#elif XXH_GCC_VERSION >= 403+# define XXH_swap64 __builtin_bswap64+#else+static U64 XXH_swap64 (U64 x)+{+ return ((x << 56) & 0xff00000000000000ULL) |+ ((x << 40) & 0x00ff000000000000ULL) |+ ((x << 24) & 0x0000ff0000000000ULL) |+ ((x << 8) & 0x000000ff00000000ULL) |+ ((x >> 8) & 0x00000000ff000000ULL) |+ ((x >> 24) & 0x0000000000ff0000ULL) |+ ((x >> 40) & 0x000000000000ff00ULL) |+ ((x >> 56) & 0x00000000000000ffULL);+}+#endif++FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)+{+ if (align==XXH_unaligned)+ return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));+ else+ return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);+}++FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)+{+ return XXH_readLE64_align(ptr, endian, XXH_unaligned);+}++static U64 XXH_readBE64(const void* ptr)+{+ return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);+}+++/*====== xxh64 ======*/++static const U64 PRIME64_1 = 11400714785074694791ULL;+static const U64 PRIME64_2 = 14029467366897019727ULL;+static const U64 PRIME64_3 = 1609587929392839161ULL;+static const U64 PRIME64_4 = 9650029242287828579ULL;+static const U64 PRIME64_5 = 2870177450012600261ULL;++static U64 XXH64_round(U64 acc, U64 input)+{+ acc += input * PRIME64_2;+ acc = XXH_rotl64(acc, 31);+ acc *= PRIME64_1;+ return acc;+}++static U64 XXH64_mergeRound(U64 acc, U64 val)+{+ val = XXH64_round(0, val);+ acc ^= val;+ acc = acc * PRIME64_1 + PRIME64_4;+ return acc;+}++static U64 XXH64_avalanche(U64 h64)+{+ h64 ^= h64 >> 33;+ h64 *= PRIME64_2;+ h64 ^= h64 >> 29;+ h64 *= PRIME64_3;+ h64 ^= h64 >> 32;+ return h64;+}+++#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align)++static U64+XXH64_finalize(U64 h64, const void* ptr, size_t len,+ XXH_endianess endian, XXH_alignment align)+{+ const BYTE* p = (const BYTE*)ptr;++#define PROCESS1_64 \+ h64 ^= (*p++) * PRIME64_5; \+ h64 = XXH_rotl64(h64, 11) * PRIME64_1;++#define PROCESS4_64 \+ h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; \+ p+=4; \+ h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;++#define PROCESS8_64 { \+ U64 const k1 = XXH64_round(0, XXH_get64bits(p)); \+ p+=8; \+ h64 ^= k1; \+ h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; \+}++ switch(len&31) {+ case 24: PROCESS8_64;+ /* fallthrough */+ case 16: PROCESS8_64;+ /* fallthrough */+ case 8: PROCESS8_64;+ return XXH64_avalanche(h64);++ case 28: PROCESS8_64;+ /* fallthrough */+ case 20: PROCESS8_64;+ /* fallthrough */+ case 12: PROCESS8_64;+ /* fallthrough */+ case 4: PROCESS4_64;+ return XXH64_avalanche(h64);++ case 25: PROCESS8_64;+ /* fallthrough */+ case 17: PROCESS8_64;+ /* fallthrough */+ case 9: PROCESS8_64;+ PROCESS1_64;+ return XXH64_avalanche(h64);++ case 29: PROCESS8_64;+ /* fallthrough */+ case 21: PROCESS8_64;+ /* fallthrough */+ case 13: PROCESS8_64;+ /* fallthrough */+ case 5: PROCESS4_64;+ PROCESS1_64;+ return XXH64_avalanche(h64);++ case 26: PROCESS8_64;+ /* fallthrough */+ case 18: PROCESS8_64;+ /* fallthrough */+ case 10: PROCESS8_64;+ PROCESS1_64;+ PROCESS1_64;+ return XXH64_avalanche(h64);++ case 30: PROCESS8_64;+ /* fallthrough */+ case 22: PROCESS8_64;+ /* fallthrough */+ case 14: PROCESS8_64;+ /* fallthrough */+ case 6: PROCESS4_64;+ PROCESS1_64;+ PROCESS1_64;+ return XXH64_avalanche(h64);++ case 27: PROCESS8_64;+ /* fallthrough */+ case 19: PROCESS8_64;+ /* fallthrough */+ case 11: PROCESS8_64;+ PROCESS1_64;+ PROCESS1_64;+ PROCESS1_64;+ return XXH64_avalanche(h64);++ case 31: PROCESS8_64;+ /* fallthrough */+ case 23: PROCESS8_64;+ /* fallthrough */+ case 15: PROCESS8_64;+ /* fallthrough */+ case 7: PROCESS4_64;+ /* fallthrough */+ case 3: PROCESS1_64;+ /* fallthrough */+ case 2: PROCESS1_64;+ /* fallthrough */+ case 1: PROCESS1_64;+ /* fallthrough */+ case 0: return XXH64_avalanche(h64);+ }++ /* impossible to reach */+ assert(0);+ return 0; /* unreachable, but some compilers complain without it */+}++FORCE_INLINE U64+XXH64_endian_align(const void* input, size_t len, U64 seed,+ XXH_endianess endian, XXH_alignment align)+{+ const BYTE* p = (const BYTE*)input;+ const BYTE* bEnd = p + len;+ U64 h64;++#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)+ if (p==NULL) {+ len=0;+ bEnd=p=(const BYTE*)(size_t)32;+ }+#endif++ if (len>=32) {+ const BYTE* const limit = bEnd - 32;+ U64 v1 = seed + PRIME64_1 + PRIME64_2;+ U64 v2 = seed + PRIME64_2;+ U64 v3 = seed + 0;+ U64 v4 = seed - PRIME64_1;++ do {+ v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8;+ v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8;+ v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8;+ v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8;+ } while (p<=limit);++ h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);+ h64 = XXH64_mergeRound(h64, v1);+ h64 = XXH64_mergeRound(h64, v2);+ h64 = XXH64_mergeRound(h64, v3);+ h64 = XXH64_mergeRound(h64, v4);++ } else {+ h64 = seed + PRIME64_5;+ }++ h64 += (U64) len;++ return XXH64_finalize(h64, p, len, endian, align);+}+++XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed)+{+#if 0+ /* Simple version, good for code maintenance, but unfortunately slow for small inputs */+ XXH64_state_t state;+ XXH64_reset(&state, seed);+ XXH64_update(&state, input, len);+ return XXH64_digest(&state);+#else+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++ if (XXH_FORCE_ALIGN_CHECK) {+ if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+ return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);+ else+ return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);+ } }++ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+ return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);+ else+ return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);+#endif+}++/*====== Hash Streaming ======*/++XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)+{+ return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));+}+XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)+{+ XXH_free(statePtr);+ return XXH_OK;+}++XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState)+{+ memcpy(dstState, srcState, sizeof(*dstState));+}++XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed)+{+ XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */+ memset(&state, 0, sizeof(state));+ state.v1 = seed + PRIME64_1 + PRIME64_2;+ state.v2 = seed + PRIME64_2;+ state.v3 = seed + 0;+ state.v4 = seed - PRIME64_1;+ /* do not write into reserved, planned to be removed in a future version */+ memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved));+ return XXH_OK;+}++FORCE_INLINE XXH_errorcode+XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian)+{+ if (input==NULL)+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)+ return XXH_OK;+#else+ return XXH_ERROR;+#endif++ { const BYTE* p = (const BYTE*)input;+ const BYTE* const bEnd = p + len;++ state->total_len += len;++ if (state->memsize + len < 32) { /* fill in tmp buffer */+ XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);+ state->memsize += (U32)len;+ return XXH_OK;+ }++ if (state->memsize) { /* tmp buffer is full */+ XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize);+ state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian));+ state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian));+ state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian));+ state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian));+ p += 32-state->memsize;+ state->memsize = 0;+ }++ if (p+32 <= bEnd) {+ const BYTE* const limit = bEnd - 32;+ U64 v1 = state->v1;+ U64 v2 = state->v2;+ U64 v3 = state->v3;+ U64 v4 = state->v4;++ do {+ v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8;+ v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8;+ v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8;+ v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8;+ } while (p<=limit);++ state->v1 = v1;+ state->v2 = v2;+ state->v3 = v3;+ state->v4 = v4;+ }++ if (p < bEnd) {+ XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));+ state->memsize = (unsigned)(bEnd-p);+ }+ }++ return XXH_OK;+}++XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len)+{+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+ return XXH64_update_endian(state_in, input, len, XXH_littleEndian);+ else+ return XXH64_update_endian(state_in, input, len, XXH_bigEndian);+}++FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian)+{+ U64 h64;++ if (state->total_len >= 32) {+ U64 const v1 = state->v1;+ U64 const v2 = state->v2;+ U64 const v3 = state->v3;+ U64 const v4 = state->v4;++ h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);+ h64 = XXH64_mergeRound(h64, v1);+ h64 = XXH64_mergeRound(h64, v2);+ h64 = XXH64_mergeRound(h64, v3);+ h64 = XXH64_mergeRound(h64, v4);+ } else {+ h64 = state->v3 /*seed*/ + PRIME64_5;+ }++ h64 += (U64) state->total_len;++ return XXH64_finalize(h64, state->mem64, (size_t)state->total_len, endian, XXH_aligned);+}++XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in)+{+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+ return XXH64_digest_endian(state_in, XXH_littleEndian);+ else+ return XXH64_digest_endian(state_in, XXH_bigEndian);+}+++/*====== Canonical representation ======*/++XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)+{+ XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));+ if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);+ memcpy(dst, &hash, sizeof(*dst));+}++XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)+{+ return XXH_readBE64(src);+}++#endif /* XXH_NO_LONG_LONG */
+ cbits/xxhash.h view
@@ -0,0 +1,328 @@+/*+ xxHash - Extremely Fast Hash algorithm+ Header File+ Copyright (C) 2012-2016, 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+ modification, are permitted provided that the following conditions are+ met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following disclaimer+ in the documentation and/or other materials provided with the+ distribution.++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.++ You can contact the author at :+ - xxHash source repository : https://github.com/Cyan4973/xxHash+*/++/* Notice extracted from xxHash homepage :++xxHash is an extremely fast Hash algorithm, running at RAM speed limits.+It also successfully passes all tests from the SMHasher suite.++Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)++Name Speed Q.Score Author+xxHash 5.4 GB/s 10+CrapWow 3.2 GB/s 2 Andrew+MumurHash 3a 2.7 GB/s 10 Austin Appleby+SpookyHash 2.0 GB/s 10 Bob Jenkins+SBox 1.4 GB/s 9 Bret Mulvey+Lookup3 1.2 GB/s 9 Bob Jenkins+SuperFastHash 1.2 GB/s 1 Paul Hsieh+CityHash64 1.05 GB/s 10 Pike & Alakuijala+FNV 0.55 GB/s 5 Fowler, Noll, Vo+CRC32 0.43 GB/s 9+MD5-32 0.33 GB/s 10 Ronald L. Rivest+SHA1-32 0.28 GB/s 10++Q.Score is a measure of quality of the hash function.+It depends on successfully passing SMHasher test set.+10 is a perfect score.++A 64-bit version, named XXH64, is available since r35.+It offers much better speed, but for 64-bit applications only.+Name Speed on 64 bits Speed on 32 bits+XXH64 13.8 GB/s 1.9 GB/s+XXH32 6.8 GB/s 6.0 GB/s+*/++#ifndef XXHASH_H_5627135585666179+#define XXHASH_H_5627135585666179 1++#if defined (__cplusplus)+extern "C" {+#endif+++/* ****************************+* Definitions+******************************/+#include <stddef.h> /* size_t */+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;+++/* ****************************+ * API modifier+ ******************************/+/** XXH_INLINE_ALL (and XXH_PRIVATE_API)+ * This is useful to include xxhash functions in `static` mode+ * in order to inline them, and remove their symbol from the public list.+ * Inlining can offer dramatic performance improvement on small keys.+ * Methodology :+ * #define XXH_INLINE_ALL+ * #include "xxhash.h"+ * `xxhash.c` is automatically included.+ * It's not useful to compile and link it as a separate module.+ */+#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)+# ifndef XXH_STATIC_LINKING_ONLY+# define XXH_STATIC_LINKING_ONLY+# endif+# if defined(__GNUC__)+# define XXH_PUBLIC_API static __inline __attribute__((unused))+# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)+# define XXH_PUBLIC_API static inline+# elif defined(_MSC_VER)+# define XXH_PUBLIC_API static __inline+# else+ /* this version may generate warnings for unused static functions */+# define XXH_PUBLIC_API static+# endif+#else+# define XXH_PUBLIC_API /* do nothing */+#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */++/*! XXH_NAMESPACE, aka Namespace Emulation :+ *+ * If you want to include _and expose_ xxHash functions from within your own library,+ * but also want to avoid symbol collisions with other libraries which may also include xxHash,+ *+ * you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library+ * with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values).+ *+ * Note that no change is required within the calling program as long as it includes `xxhash.h` :+ * regular symbol name will be automatically translated by this header.+ */+#ifdef XXH_NAMESPACE+# define XXH_CAT(A,B) A##B+# define XXH_NAME2(A,B) XXH_CAT(A,B)+# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber)+# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)+# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)+# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)+# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)+# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)+# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)+# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState)+# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash)+# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical)+# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)+# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)+# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)+# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)+# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)+# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)+# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)+# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)+# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical)+#endif+++/* *************************************+* Version+***************************************/+#define XXH_VERSION_MAJOR 0+#define XXH_VERSION_MINOR 6+#define XXH_VERSION_RELEASE 5+#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)+XXH_PUBLIC_API unsigned XXH_versionNumber (void);+++/*-**********************************************************************+* 32-bit hash+************************************************************************/+typedef unsigned int XXH32_hash_t;++/*! XXH32() :+ Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input".+ The memory between input & input+length must be valid (allocated and read-accessible).+ "seed" can be used to alter the result predictably.+ Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */+XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed);++/*====== Streaming ======*/+typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */+XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void);+XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr);+XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state);++XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed);+XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);+XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr);++/*+ * Streaming functions generate the xxHash of an input provided in multiple segments.+ * Note that, for small input, they are slower than single-call functions, due to state management.+ * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized.+ *+ * XXH state must first be allocated, using XXH*_createState() .+ *+ * Start a new hash by initializing state with a seed, using XXH*_reset().+ *+ * Then, feed the hash state by calling XXH*_update() as many times as necessary.+ * The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.+ *+ * Finally, a hash value can be produced anytime, by using XXH*_digest().+ * This function returns the nn-bits hash as an int or long long.+ *+ * It's still possible to continue inserting input into the hash state after a digest,+ * and generate some new hashes later on, by calling again XXH*_digest().+ *+ * When done, free XXH state space if it was allocated dynamically.+ */++/*====== Canonical representation ======*/++typedef struct { unsigned char digest[4]; } XXH32_canonical_t;+XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);+XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);++/* Default result type for XXH functions are primitive unsigned 32 and 64 bits.+ * The canonical representation uses human-readable write convention, aka big-endian (large digits first).+ * These functions allow transformation of hash result into and from its canonical format.+ * This way, hash values can be written into a file / memory, and remain comparable on different systems and programs.+ */+++#ifndef XXH_NO_LONG_LONG+/*-**********************************************************************+* 64-bit hash+************************************************************************/+typedef unsigned long long XXH64_hash_t;++/*! XXH64() :+ Calculate the 64-bit hash of sequence of length "len" stored at memory address "input".+ "seed" can be used to alter the result predictably.+ This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark).+*/+XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed);++/*====== Streaming ======*/+typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */+XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void);+XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr);+XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state);++XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed);+XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length);+XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr);++/*====== Canonical representation ======*/+typedef struct { unsigned char digest[8]; } XXH64_canonical_t;+XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);+XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src);+#endif /* XXH_NO_LONG_LONG */++++#ifdef XXH_STATIC_LINKING_ONLY++/* ================================================================================================+ This section contains declarations which are not guaranteed to remain stable.+ They may change in future versions, becoming incompatible with a different version of the library.+ These declarations should only be used with static linking.+ Never use them in association with dynamic linking !+=================================================================================================== */++/* These definitions are only present to allow+ * static allocation of XXH state, on stack or in a struct for example.+ * Never **ever** use members directly. */++#if !defined (__VMS) \+ && (defined (__cplusplus) \+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )+# include <stdint.h>++struct XXH32_state_s {+ uint32_t total_len_32;+ uint32_t large_len;+ uint32_t v1;+ uint32_t v2;+ uint32_t v3;+ uint32_t v4;+ uint32_t mem32[4];+ uint32_t memsize;+ uint32_t reserved; /* never read nor write, might be removed in a future version */+}; /* typedef'd to XXH32_state_t */++struct XXH64_state_s {+ uint64_t total_len;+ uint64_t v1;+ uint64_t v2;+ uint64_t v3;+ uint64_t v4;+ uint64_t mem64[4];+ uint32_t memsize;+ uint32_t reserved[2]; /* never read nor write, might be removed in a future version */+}; /* typedef'd to XXH64_state_t */++# else++struct XXH32_state_s {+ unsigned total_len_32;+ unsigned large_len;+ unsigned v1;+ unsigned v2;+ unsigned v3;+ unsigned v4;+ unsigned mem32[4];+ unsigned memsize;+ unsigned reserved; /* never read nor write, might be removed in a future version */+}; /* typedef'd to XXH32_state_t */++# ifndef XXH_NO_LONG_LONG /* remove 64-bit support */+struct XXH64_state_s {+ unsigned long long total_len;+ unsigned long long v1;+ unsigned long long v2;+ unsigned long long v3;+ unsigned long long v4;+ unsigned long long mem64[4];+ unsigned memsize;+ unsigned reserved[2]; /* never read nor write, might be removed in a future version */+}; /* typedef'd to XXH64_state_t */+# endif++# endif+++#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)+# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */+#endif++#endif /* XXH_STATIC_LINKING_ONLY */+++#if defined (__cplusplus)+}+#endif++#endif /* XXHASH_H_5627135585666179 */
lz4-bytes.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: lz4-bytes-version: 0.1.2.0+version: 0.2.0.0 synopsis: Bindings to LZ4 description: This library is similar to the @lz4@ library except that it works@@ -17,6 +17,8 @@ build-type: Simple extra-source-files: cbits/lz4.h+ cbits/lz4frame.h+ cbits/xxhash.h cbits/lz4hc.h extra-doc-files: CHANGELOG.md@@ -41,12 +43,15 @@ , run-st >=0.1.1 && <0.2 hs-source-dirs: src- include-dirs: cbits- ghc-options: -O2+ include-dirs: cbits+ ghc-options: -O2 -Wall+ cc-options: -Wall c-sources: cbits/hs_lz4.c cbits/lz4.c+ cbits/lz4frame.c cbits/lz4hc.c+ cbits/xxhash.c test-suite test import: build-settings
src/Lz4/Frame.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE BinaryLiterals #-} {-# LANGUAGE BlockArguments #-} {-# LANGUAGE DerivingStrategies #-}@@ -17,16 +18,22 @@ , decompressU ) where -import Lz4.Internal (requiredBufferSize,c_hs_compress_HC,c_hs_decompress_safe)+import Lz4.Internal (requiredBufferSize,c_hs_compress_HC,c_hs_decompress_safe,DecompressionContext)+import Lz4.Internal (c_LZ4F_createDecompressionContext, c_hs_decompress_frame)+import Lz4.Internal (c_LZ4F_freeDecompressionContext)+import Foreign.C.Types (CSize) +import Foreign.Ptr (Ptr)+import Foreign.Storable (peek)+import GHC.Exts (RealWorld) import Control.Monad (when) import Control.Monad.ST (runST) import Data.Bits ((.&.)) import Data.Bytes.Types (Bytes (Bytes)) import Data.Int (Int32)-import Data.Primitive (ByteArray (..), MutableByteArray (..))+import Data.Primitive (ByteArray (..), MutableByteArray (..), MutablePrimArray) import Data.Word (Word8, Word32)-import GHC.IO (unsafeIOToST)+import GHC.IO (unsafeIOToST,unsafeDupablePerformIO) import qualified Data.Primitive as PM import qualified Data.Primitive.ByteArray.LittleEndian as LE@@ -39,18 +46,21 @@ -- It is difficult to find examples of lz4 frames that actually use -- any of these. Open a PR with an example of an lz4 frame that fails -- to decode if you find one.+--+-- Error codes from the C library lz4 are returned in the Left data+-- constructor. decompressU :: Int -- ^ The exact size of the decompressed bytes -> Bytes -- ^ Compressed bytes- -> Maybe ByteArray+ -> Either Int ByteArray decompressU !decompressedSize (Bytes arr@(ByteArray arr# ) off len) = do- when (len < 11) Nothing- when (indexWord8 arr off /= 0x04) Nothing- when (indexWord8 arr (off + 1) /= 0x22) Nothing- when (indexWord8 arr (off + 2) /= 0x4D) Nothing- when (indexWord8 arr (off + 3) /= 0x18) Nothing+ when (len < 11) (Left 10000)+ when (indexWord8 arr off /= 0x04) (Left 10001)+ when (indexWord8 arr (off + 1) /= 0x22) (Left 10002)+ when (indexWord8 arr (off + 2) /= 0x4D) (Left 10003)+ when (indexWord8 arr (off + 3) /= 0x18) (Left 10004) let !flag = indexWord8 arr (off + 4)- when (flag /= 0b0110_0000) Nothing+ -- when (flag /= 0b0110_0000) Nothing -- Here is the code that would read the size hint from the bd. However, -- there is no reason to use this since this function takes the actual -- size as an argument. We ignore the checksum at position off+6 as well.@@ -62,32 +72,50 @@ -- 0b0100_0000 -> pure 65536 -- _ -> Nothing -- when (maximumDecompressedSize < decompressedSize) Nothing- let !compressedSize = LE.indexUnalignedByteArray arr (off + 7) :: Word32- let !compressedSizeI = fromIntegral (compressedSize .&. 0x7fff_ffff) :: Int- when (compressedSizeI + (4 + 3 + 4 + 4) /= len) Nothing- let !offPost = off + 11 + compressedSizeI- when (indexWord8 arr offPost /= 0x00) Nothing- when (indexWord8 arr (offPost + 1) /= 0x00) Nothing- when (indexWord8 arr (offPost + 2) /= 0x00) Nothing- when (indexWord8 arr (offPost + 3) /= 0x00) Nothing- case compressedSize .&. 0x8000_0000 of- 0 -> runST $ do- dst@(MutableByteArray dst# ) <- PM.newByteArray decompressedSize- actualSz <- unsafeIOToST (c_hs_decompress_safe arr# (off + 11) dst# 0 compressedSizeI decompressedSize)- -- Note: actualSz will be negative if decompression fails. That's fine.- if actualSz == decompressedSize- then do- dst' <- PM.unsafeFreezeByteArray dst- pure (Just dst')- else pure Nothing- _ -> do- -- When the upper bit of the size is set, it means that the data in- -- the block is uncompressed. This code path is not tested in the test- -- suite, and I cannot find examples of this feature used in the wild.- -- If anyone knows of an example, open a PR.- when (decompressedSize /= compressedSizeI) Nothing- when (decompressedSize + 15 /= len) Nothing- Just $! Bytes.toByteArrayClone (Bytes arr (off + 11) decompressedSize)+ if | flag == 0b0110_0000+ , !compressedSize <- LE.indexUnalignedByteArray arr (off + 7) :: Word32+ , !compressedSizeI <- fromIntegral (compressedSize .&. 0x7fff_ffff) :: Int+ , compressedSizeI + (4 + 3 + 4 + 4) == len -> do+ -- When there is only one block and there aren't any checksums,+ -- we can just decompress the single block and allocating and+ -- freeing a context. This case is pretty common in practice.+ let !offPost = off + 11 + compressedSizeI+ when (indexWord8 arr offPost /= 0x00) (Left 10005)+ when (indexWord8 arr (offPost + 1) /= 0x00) (Left 10006)+ when (indexWord8 arr (offPost + 2) /= 0x00) (Left 10007)+ when (indexWord8 arr (offPost + 3) /= 0x00) (Left 10008)+ case compressedSize .&. 0x8000_0000 of+ 0 -> runST $ do+ dst@(MutableByteArray dst# ) <- PM.newByteArray decompressedSize+ actualSz <- unsafeIOToST (c_hs_decompress_safe arr# (off + 11) dst# 0 compressedSizeI decompressedSize)+ -- Note: actualSz will be negative if decompression fails. That's fine.+ if actualSz == decompressedSize+ then do+ dst' <- PM.unsafeFreezeByteArray dst+ pure (Right dst')+ else pure (Left 10009)+ _ -> do+ -- When the upper bit of the size is set, it means that the data in+ -- the block is uncompressed. This code path is not tested in the test+ -- suite, and I cannot find examples of this feature used in the wild.+ -- If anyone knows of an example, open a PR.+ when (decompressedSize /= compressedSizeI) (Left 10010)+ when (decompressedSize + 15 /= len) (Left 10011)+ Right $! Bytes.toByteArrayClone (Bytes arr (off + 11) decompressedSize)+ | otherwise -> unsafeDupablePerformIO $ do+ dst@(MutableByteArray dst# ) <- PM.newByteArray decompressedSize+ dctxPtrMba :: MutablePrimArray RealWorld (Ptr DecompressionContext) <- PM.newPinnedPrimArray 1+ let !dctxPtrPtr = PM.mutablePrimArrayContents dctxPtrMba+ x <- c_LZ4F_createDecompressionContext dctxPtrPtr 100+ case x of+ 0 -> do+ dctxPtr <- peek dctxPtrPtr+ r <- c_hs_decompress_frame dctxPtr arr# off len dst# decompressedSize+ _ <- c_LZ4F_freeDecompressionContext dctxPtr+ case r of+ 0 -> Right <$> PM.unsafeFreezeByteArray dst+ _ -> pure (Left r)+ _ -> errorWithoutStackTrace "Lz4.Frame.decompressU: failed to create decompression context" {- | Use HC compression to produce a frame with a single block. All optional fields (checksums, content sizes, and dictionary IDs)@@ -105,8 +133,8 @@ -- | Bytes to compress Bytes -> ByteArray-compressHighlyU !lvl (Bytes (ByteArray arr) off len) = runST do- let maxSz = requiredBufferSize len + 15+compressHighlyU !lvl (Bytes (ByteArray arr) off0 len0) = runST do+ let maxSz = requiredBufferSize len0 + 15 dst@(MutableByteArray dst#) <- PM.newByteArray maxSz -- -- First 4 bytes: magic identifier PM.writeByteArray dst 0 (0x04 :: Word8)@@ -116,26 +144,35 @@ -- Next 3 bytes: frame descriptor PM.writeByteArray dst 4 (0b0110_0000 :: Word8) if- | len <= 65_536 -> do+ | len0 <= 65_536 -> do PM.writeByteArray dst 5 (0b0100_0000 :: Word8) PM.writeByteArray dst 6 (0x82 :: Word8)- | len <= 262_144 -> do+ | len0 <= 262_144 -> do PM.writeByteArray dst 5 (0b0101_0000 :: Word8) PM.writeByteArray dst 6 (0xFB :: Word8)- | len <= 1_048_576 -> do+ | len0 <= 1_048_576 -> do PM.writeByteArray dst 5 (0b0110_0000 :: Word8) PM.writeByteArray dst 6 (0x51 :: Word8) | otherwise -> do PM.writeByteArray dst 5 (0b0111_0000 :: Word8) PM.writeByteArray dst 6 (0x73 :: Word8)- actualSz <- unsafeIOToST (c_hs_compress_HC arr off dst# 11 len maxSz lvl)- LE.writeUnalignedByteArray dst 7 (fromIntegral actualSz :: Int32)- PM.writeByteArray dst (actualSz + 11) (0x00 :: Word8)- PM.writeByteArray dst (actualSz + 12) (0x00 :: Word8)- PM.writeByteArray dst (actualSz + 13) (0x00 :: Word8)- PM.writeByteArray dst (actualSz + 14) (0x00 :: Word8)- PM.shrinkMutableByteArray dst (actualSz + 15)- PM.unsafeFreezeByteArray dst+ let handleBlocks !ix !inputOff !inputRemaining = do+ let !inputClipped = min inputRemaining 4_194_304+ actualSz <- unsafeIOToST (c_hs_compress_HC arr inputOff dst# (ix + 4) inputClipped maxSz lvl)+ case actualSz of+ 0 -> errorWithoutStackTrace "Lz4.Frame.compressHighlyU: compression failed"+ _ -> do+ LE.writeUnalignedByteArray dst ix (fromIntegral actualSz :: Int32)+ let inputRemaining' = inputRemaining - inputClipped+ let inputOff' = inputOff + inputClipped+ if inputRemaining' > 0+ then do+ handleBlocks (ix + 4 + actualSz) inputOff' inputRemaining'+ else do+ LE.writeUnalignedByteArray dst (actualSz + ix + 4) (0 :: Word32)+ PM.shrinkMutableByteArray dst (actualSz + ix + 8)+ PM.unsafeFreezeByteArray dst+ handleBlocks 7 off0 len0 indexWord8 :: ByteArray -> Int -> Word8 {-# inline indexWord8 #-}
src/Lz4/Internal.hs view
@@ -7,13 +7,20 @@ These functions do not perform any framing. -} module Lz4.Internal- ( requiredBufferSize+ ( DecompressionContext+ , requiredBufferSize , c_hs_compress_HC , c_hs_decompress_safe+ , c_hs_decompress_frame+ , c_LZ4F_createDecompressionContext+ , c_LZ4F_freeDecompressionContext ) where -import GHC.Exts (ByteArray#, MutableByteArray#)+import GHC.Exts (ByteArray#, MutableByteArray#, Ptr)+import Foreign.C.Types (CUInt(..), CSize(..)) +-- | Phantom type for pointers+data DecompressionContext {- | Copied from the @LZ4_COMPRESSBOUND@ macro lz4.h to avoid using FFI for simple arithmetic. Make sure this stays in sync with the macro.@@ -42,3 +49,24 @@ -> Int -- Input size -> Int -- Destination capacity -> IO Int -- Result length++foreign import ccall unsafe "hs_decompress_frame"+ c_hs_decompress_frame ::+ Ptr DecompressionContext+ -> ByteArray# -- Source+ -> Int -- Source offset+ -> Int -- Source length+ -> MutableByteArray# s -- Destination+ -> Int -- Destination offset+ -> IO Int -- Result (0 means success)++foreign import ccall unsafe "LZ4F_createDecompressionContext"+ c_LZ4F_createDecompressionContext ::+ Ptr (Ptr DecompressionContext)+ -> CUInt -- version+ -> IO CSize++foreign import ccall unsafe "LZ4F_freeDecompressionContext"+ c_LZ4F_freeDecompressionContext ::+ Ptr DecompressionContext+ -> IO CSize
test/Main.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE ScopedTypeVariables #-} import Data.Bytes (Bytes)@@ -33,18 +34,50 @@ , testGroup "frame" [ testProperty "roundtrip-HC" $ forAll genByteArray $ \bs -> let cs = Frame.compressHighlyU 3 (Bytes.fromByteArray bs) in- Just bs+ Right bs === Frame.decompressU (sizeofByteArray bs) (Bytes.fromByteArray cs)+ , testCase "roundtrip-HC-80k" $ testRoundtrip 80_000+ , testCase "roundtrip-HC-800k" $ testRoundtrip 800_000+ , testCase "roundtrip-HC-2M" $ testRoundtrip 2_000_000+ , testCase "roundtrip-HC-5M" $ testRoundtrip 5_000_000+ , testCase "roundtrip-HC-9M" $ testRoundtrip 9_000_000+ , testCase "packing-HC-80k" $ testPacking 80_000+ , testCase "packing-HC-800k" $ testPacking 800_000+ , testCase "packing-HC-2M" $ testPacking 2_000_000+ , testCase "packing-HC-5M" $ testPacking 5_000_000+ , testCase "packing-HC-9M" $ testPacking 9_000_000 , testCase "example-a" $ case Frame.decompressU 20 (Bytes.fromByteArray exampleA) of- Nothing -> fail "decompression failed"- Just _ -> pure ()+ Left{} -> fail "decompression failed"+ Right _ -> pure () , testCase "example-b" $ case Frame.decompressU 10 (Bytes.fromByteArray exampleB) of- Nothing -> fail "decompression failed"- Just x -> x @=? Exts.fromList [0xbb :: Word8, 0x01, 0xbb, 0x01, 0xbb, 0x01, 0xbb, 0x01, 0xbb, 0x01 ]+ Left{} -> fail "decompression failed"+ Right x -> x @=? Exts.fromList [0xbb :: Word8, 0x01, 0xbb, 0x01, 0xbb, 0x01, 0xbb, 0x01, 0xbb, 0x01 ]+ , testCase "example-c" $ case Frame.decompressU 20 (Bytes.fromByteArray exampleC) of+ Left e -> fail ("decompression failed, error code: " ++ show e)+ Right _ -> pure () ] ] +testRoundtrip :: Int -> IO ()+testRoundtrip sz =+ let uncompressed = Bytes.replicate sz 0x01 in+ let compressed = Frame.compressHighlyU 3 uncompressed in+ case Frame.decompressU sz (Bytes.fromByteArray compressed) of+ Left{} -> fail "decompression failed"+ Right result -> if uncompressed == Bytes.fromByteArray result+ then pure ()+ else fail "compression and decompression did not round trip"++testPacking :: Int -> IO ()+testPacking sz =+ let uncompressed = Bytes.replicate sz 0x01 in+ let compressed = Frame.compressHighlyU 3 uncompressed in+ let lenCompressed = sizeofByteArray compressed in+ if lenCompressed * 100 > sz+ then fail "Repetition of same byte has compression factor less than 100"+ else pure ()+ genBytes :: Gen Bytes genBytes = do n <- choose (0, 200)@@ -61,6 +94,19 @@ exampleA = Exts.fromList [ 0x04, 0x22, 0x4d, (0x18 :: Word8) , 0x60, 0x40, 0x82+ , 0x0d, 0x00, 0x00, 0x00 -- little-endian encoding of the number 13+ , 0x47, 0x15, 0x08, 0x01, 0x0a, 0x04 , 0x00, 0x50, 0x0a, 0x15, 0x08, 0x01, 0x0a+ , 0x00, 0x00, 0x00, 0x00+ ]++-- Same as exampleA, but the flag indicates that linked blocks are used.+-- There is only one block, so there aren't actually linked blocks, but+-- this lets us force the decompress function to take the slow path+-- where it sets up a decompression context.+exampleC :: ByteArray+exampleC = Exts.fromList+ [ 0x04, 0x22, 0x4d, (0x18 :: Word8)+ , 0x40, 0x40, 0xc0 , 0x0d, 0x00, 0x00, 0x00 -- little-endian encoding of the number 13 , 0x47, 0x15, 0x08, 0x01, 0x0a, 0x04 , 0x00, 0x50, 0x0a, 0x15, 0x08, 0x01, 0x0a , 0x00, 0x00, 0x00, 0x00