From 3e95f5657a5b70183789496875bf787be5aacc52 Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Fri, 18 Oct 2013 20:25:30 +0200 Subject: [PATCH] Bug 888658 - Add LZ4 compression to mfbt. r=jwalden,vlad --HG-- extra : rebase_source : e92051145948cd3c1e6f95596563d8c13e231ee3 --- mfbt/Compression.cpp | 64 ++++ mfbt/Compression.h | 116 +++++++ mfbt/exported_headers.mk | 1 + mfbt/lz4.c | 690 +++++++++++++++++++++++++++++++++++++++ mfbt/lz4.h | 165 ++++++++++ mfbt/lz4_encoder.h | 258 +++++++++++++++ mfbt/sources.mk | 1 + 7 files changed, 1295 insertions(+) create mode 100644 mfbt/Compression.cpp create mode 100644 mfbt/Compression.h create mode 100644 mfbt/lz4.c create mode 100644 mfbt/lz4.h create mode 100644 mfbt/lz4_encoder.h diff --git a/mfbt/Compression.cpp b/mfbt/Compression.cpp new file mode 100644 index 000000000000..a47eeea7a08f --- /dev/null +++ b/mfbt/Compression.cpp @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Compression.h" +#include "mozilla/CheckedInt.h" +using namespace mozilla::Compression; + +namespace { + +#include "lz4.c" + +}/* anonymous namespace */ + +/* Our wrappers */ + +size_t +LZ4::compress(const char* source, size_t inputSize, char* dest) +{ + CheckedInt inputSizeChecked = inputSize; + MOZ_ASSERT(inputSizeChecked.isValid()); + return LZ4_compress(source, dest, inputSizeChecked.value()); +} + +size_t +LZ4::compressLimitedOutput(const char* source, size_t inputSize, char* dest, size_t maxOutputSize) +{ + CheckedInt inputSizeChecked = inputSize; + MOZ_ASSERT(inputSizeChecked.isValid()); + CheckedInt maxOutputSizeChecked = maxOutputSize; + MOZ_ASSERT(maxOutputSizeChecked.isValid()); + return LZ4_compress_limitedOutput(source, dest, inputSizeChecked.value(), + maxOutputSizeChecked.value()); +} + +bool +LZ4::decompress(const char* source, char* dest, size_t outputSize) +{ + CheckedInt outputSizeChecked = outputSize; + MOZ_ASSERT(outputSizeChecked.isValid()); + int ret = LZ4_decompress_fast(source, dest, outputSizeChecked.value()); + return ret >= 0; +} + +bool +LZ4::decompress(const char* source, size_t inputSize, char* dest, size_t maxOutputSize, + size_t *outputSize) +{ + CheckedInt maxOutputSizeChecked = maxOutputSize; + MOZ_ASSERT(maxOutputSizeChecked.isValid()); + CheckedInt inputSizeChecked = inputSize; + MOZ_ASSERT(inputSizeChecked.isValid()); + + int ret = LZ4_decompress_safe(source, dest, inputSizeChecked.value(), + maxOutputSizeChecked.value()); + if (ret >= 0) { + *outputSize = ret; + return true; + } else { + *outputSize = 0; + return false; + } +} + diff --git a/mfbt/Compression.h b/mfbt/Compression.h new file mode 100644 index 000000000000..da77fa895f67 --- /dev/null +++ b/mfbt/Compression.h @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Various simple compression/decompression functions. */ + +#ifndef mozilla_Compression_h_ +#define mozilla_Compression_h_ + +#include "mozilla/Types.h" +#include "mozilla/Assertions.h" + +namespace mozilla { +namespace Compression { + +/** + * LZ4 is a very fast byte-wise compression algorithm. + * + * Compared to Google's Snappy it is faster to compress and decompress and + * generally produces output of about the same size. + * + * Compared to zlib it compresses at about 10x the speed, decompresses at about + * 4x the speed and produces output of about 1.5x the size. + * + */ + +class LZ4 +{ + +public: + + /** + * Compresses 'inputSize' bytes from 'source' into 'dest'. + * Destination buffer must be already allocated, + * and must be sized to handle worst cases situations (input data not compressible) + * Worst case size evaluation is provided by function LZ4_compressBound() + * + * @param inputSize is the input size. Max supported value is ~1.9GB + * @param return the number of bytes written in buffer dest + */ + static MFBT_API size_t compress(const char* source, size_t inputSize, char* dest); + + /** + * Compress 'inputSize' bytes from 'source' into an output buffer + * 'dest' of maximum size 'maxOutputSize'. If it cannot achieve it, + * compression will stop, and result of the function will be zero, + * 'dest' will still be written to, but since the number of input + * bytes consumed is not returned the result is not usable. + * + * This function never writes outside of provided output buffer. + * + * @param inputSize is the input size. Max supported value is ~1.9GB + * @param maxOutputSize is the size of the destination buffer (which must be already allocated) + * @return the number of bytes written in buffer 'dest' + or 0 if the compression fails + */ + static MFBT_API size_t compressLimitedOutput(const char* source, size_t inputSize, char* dest, + size_t maxOutputSize); + + /** + * If the source stream is malformed, the function will stop decoding + * and return a negative result, indicating the byte position of the + * faulty instruction + * + * This function never writes outside of provided buffers, and never + * modifies input buffer. + * + * note : destination buffer must be already allocated. + * its size must be a minimum of 'outputSize' bytes. + * @param outputSize is the output size, therefore the original size + * @return the number of bytes read in the source buffer + */ + static MFBT_API bool decompress(const char* source, char* dest, size_t outputSize); + + /** + * If the source stream is malformed, the function will stop decoding + * and return false. + * + * This function never writes beyond dest + maxOutputSize, and is + * therefore protected against malicious data packets. + * + * note : Destination buffer must be already allocated. + * This version is slightly slower than the decompress + * without the maxOutputSize + * + * @param inputSize is the length of the input compressed data + * @param maxOutputSize is the size of the destination buffer (which must be already allocated) + * @param outputSize the actual number of bytes decoded in the destination buffer (necessarily <= maxOutputSize) + + */ + static MFBT_API bool decompress(const char* source, size_t inputSize, char* dest, + size_t maxOutputSize, size_t *outputSize); + + /* + Provides the maximum size that LZ4 may output in a "worst case" + scenario (input data not compressible) primarily useful for memory + allocation of output buffer. + note : this function is limited by "int" range (2^31-1) + + @param inputSize is the input size. Max supported value is ~1.9GB + @return maximum output size in a "worst case" scenario + */ + static MFBT_API size_t maxCompressedSize(size_t inputSize) + { + size_t max = ((inputSize) + ((inputSize)/255) + 16); + MOZ_ASSERT(max > inputSize); + return max; + } + +}; + +} /* namespace Compression */ +} /* namespace mozilla */ + +#endif /* mozilla_Compression_h_ */ diff --git a/mfbt/exported_headers.mk b/mfbt/exported_headers.mk index 079e36611ed0..c814a92c36a9 100644 --- a/mfbt/exported_headers.mk +++ b/mfbt/exported_headers.mk @@ -21,6 +21,7 @@ EXPORTS_mozilla_FILES += \ Char16.h \ CheckedInt.h \ Compiler.h \ + Compression.h \ Constants.h \ DebugOnly.h \ decimal/Decimal.h \ diff --git a/mfbt/lz4.c b/mfbt/lz4.c new file mode 100644 index 000000000000..91819addd836 --- /dev/null +++ b/mfbt/lz4.c @@ -0,0 +1,690 @@ +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2013, 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://fastcompression.blogspot.com/p/lz4.html + - LZ4 source repository : http://code.google.com/p/lz4/ +*/ + +/* +Note : this source file requires "lz4_encoder.h" +*/ + +//************************************** +// Tuning parameters +//************************************** +// 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 can improve speed, due to cache effect +// Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache +#define MEMORY_USAGE 14 + +// HEAPMODE : +// Select how default compression function will allocate memory for its hash table, +// in memory stack (0:default, fastest), or in memory heap (1:requires memory allocation (malloc)). +// Default allocation strategy is to use stack (HEAPMODE 0) +// Note : explicit functions *_stack* and *_heap* are unaffected by this setting +#define HEAPMODE 0 + +// BIG_ENDIAN_NATIVE_BUT_INCOMPATIBLE : +// This will provide a small boost to performance for big endian cpu, but the resulting compressed stream will be incompatible with little-endian CPU. +// You can set this option to 1 in situations where data will remain within closed environment +// This option is useless on Little_Endian CPU (such as x86) +//#define BIG_ENDIAN_NATIVE_BUT_INCOMPATIBLE 1 + + + +//************************************** +// CPU Feature Detection +//************************************** +// 32 or 64 bits ? +#if (defined(__x86_64__) || defined(_M_X64) || defined(_WIN64) \ + || defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) \ + || defined(__64BIT__) || defined(_LP64) || defined(__LP64__) \ + || defined(__ia64) || defined(__itanium__) || defined(_M_IA64) ) // Detects 64 bits mode +# define LZ4_ARCH64 1 +#else +# define LZ4_ARCH64 0 +#endif + +// Little Endian or Big Endian ? +// Overwrite the #define below if you know your architecture endianess +#if defined (__GLIBC__) +# include +# if (__BYTE_ORDER == __BIG_ENDIAN) +# define LZ4_BIG_ENDIAN 1 +# endif +#elif (defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(__LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN) || defined(_LITTLE_ENDIAN)) +# define LZ4_BIG_ENDIAN 1 +#elif defined(__sparc) || defined(__sparc__) \ + || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) \ + || defined(__hpux) || defined(__hppa) \ + || defined(_MIPSEB) || defined(__s390__) +# define LZ4_BIG_ENDIAN 1 +#else +// Little Endian assumed. PDP Endian and other very rare endian format are unsupported. +#endif + +// Unaligned memory access is automatically enabled for "common" CPU, such as x86. +// For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected +// If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance +#if defined(__ARM_FEATURE_UNALIGNED) +# define LZ4_FORCE_UNALIGNED_ACCESS 1 +#endif + +// 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 Windows CE does not support Hardware bit count +# define LZ4_FORCE_SW_BITCOUNT +#endif + + +//************************************** +// Compiler Options +//************************************** +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // C99 +/* "restrict" is a known keyword */ +#else +# define restrict // Disable restrict +#endif + +#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +#ifdef _MSC_VER // Visual Studio +# include // For Visual 2005 +# if LZ4_ARCH64 // 64-bit +# pragma intrinsic(_BitScanForward64) // For Visual 2005 +# pragma intrinsic(_BitScanReverse64) // For Visual 2005 +# else +# pragma intrinsic(_BitScanForward) // For Visual 2005 +# pragma intrinsic(_BitScanReverse) // For Visual 2005 +# endif +# pragma warning(disable : 4127) // disable: C4127: conditional expression is constant +#endif + +#ifdef _MSC_VER +# define lz4_bswap16(x) _byteswap_ushort(x) +#else +# define lz4_bswap16(x) ((unsigned short int) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))) +#endif + +#if (GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__) +# define expect(expr,value) (__builtin_expect ((expr),(value)) ) +#else +# define expect(expr,value) (expr) +#endif + +#define likely(expr) expect((expr) != 0, 1) +#define unlikely(expr) expect((expr) != 0, 0) + + +//************************************** +// Includes +//************************************** +#include // for malloc +#include // for memset +#include "lz4.h" + + +//************************************** +// Basic Types +//************************************** +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // C99 +# include + 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 + +#if defined(__GNUC__) && !defined(LZ4_FORCE_UNALIGNED_ACCESS) +# define _PACKED __attribute__ ((packed)) +#else +# define _PACKED +#endif + +#if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__) +# pragma pack(push, 1) +#endif + +typedef struct _U16_S { U16 v; } _PACKED U16_S; +typedef struct _U32_S { U32 v; } _PACKED U32_S; +typedef struct _U64_S { U64 v; } _PACKED U64_S; + +#if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__) +# pragma pack(pop) +#endif + +#define A64(x) (((U64_S *)(x))->v) +#define A32(x) (((U32_S *)(x))->v) +#define A16(x) (((U16_S *)(x))->v) + + +//************************************** +// Constants +//************************************** +#define HASHTABLESIZE (1 << MEMORY_USAGE) + +#define MINMATCH 4 + +#define COPYLENGTH 8 +#define LASTLITERALS 5 +#define MFLIMIT (COPYLENGTH+MINMATCH) +#define MINLENGTH (MFLIMIT+1) + +#define LZ4_64KLIMIT ((1<<16) + (MFLIMIT-1)) +#define SKIPSTRENGTH 6 // Increasing this value will make the compression run slower on incompressible data + +#define MAXD_LOG 16 +#define MAX_DISTANCE ((1 << MAXD_LOG) - 1) + +#define ML_BITS 4 +#define ML_MASK ((1U<>3); + #elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_clzll(val) >> 3); + #else + int r; + if (!(val>>32)) { r=4; } else { r=0; val>>=32; } + if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } + r += (!val); + return r; + #endif +#else + #if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanForward64( &r, val ); + return (int)(r>>3); + #elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_ctzll(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 & -val) * 0x0218A392CDABBD3F)) >> 58]; + #endif +#endif +} + +#else + +static inline int LZ4_NbCommonBytes (register U32 val) +{ +#if defined(LZ4_BIG_ENDIAN) +# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanReverse( &r, val ); + return (int)(r>>3); +# elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_clz(val) >> 3); +# else + int r; + if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } + r += (!val); + return r; +# endif +#else +# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanForward( &r, val ); + return (int)(r>>3); +# elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_ctz(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 +#endif +} + +#endif + + + +//****************************** +// Compression functions +//****************************** + +/* +int LZ4_compress_stack( + const char* source, + char* dest, + int inputSize) + +Compress 'inputSize' bytes from 'source' into an output buffer 'dest'. +Destination buffer must be already allocated, and sized at a minimum of LZ4_compressBound(inputSize). +return : the number of bytes written in buffer 'dest' +*/ +#define FUNCTION_NAME LZ4_compress_stack +#include "lz4_encoder.h" + + +/* +int LZ4_compress_stack_limitedOutput( + const char* source, + char* dest, + int inputSize, + int maxOutputSize) + +Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'. +If it cannot achieve it, compression will stop, and result of the function will be zero. +return : the number of bytes written in buffer 'dest', or 0 if the compression fails +*/ +#define FUNCTION_NAME LZ4_compress_stack_limitedOutput +#define LIMITED_OUTPUT +#include "lz4_encoder.h" + + +/* +int LZ4_compress64k_stack( + const char* source, + char* dest, + int inputSize) + +Compress 'inputSize' bytes from 'source' into an output buffer 'dest'. +This function compresses better than LZ4_compress_stack(), on the condition that +'inputSize' must be < to LZ4_64KLIMIT, or the function will fail. +Destination buffer must be already allocated, and sized at a minimum of LZ4_compressBound(inputSize). +return : the number of bytes written in buffer 'dest', or 0 if compression fails +*/ +#define FUNCTION_NAME LZ4_compress64k_stack +#define COMPRESS_64K +#include "lz4_encoder.h" + + +/* +int LZ4_compress64k_stack_limitedOutput( + const char* source, + char* dest, + int inputSize, + int maxOutputSize) + +Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'. +This function compresses better than LZ4_compress_stack_limitedOutput(), on the condition that +'inputSize' must be < to LZ4_64KLIMIT, or the function will fail. +If it cannot achieve it, compression will stop, and result of the function will be zero. +return : the number of bytes written in buffer 'dest', or 0 if the compression fails +*/ +#define FUNCTION_NAME LZ4_compress64k_stack_limitedOutput +#define COMPRESS_64K +#define LIMITED_OUTPUT +#include "lz4_encoder.h" + + +/* +void* LZ4_createHeapMemory(); +int LZ4_freeHeapMemory(void* ctx); + +Used to allocate and free hashTable memory +to be used by the LZ4_compress_heap* family of functions. +LZ4_createHeapMemory() returns NULL is memory allocation fails. +*/ +void* LZ4_create() { return malloc(HASHTABLESIZE); } +int LZ4_free(void* ctx) { free(ctx); return 0; } + + +/* +int LZ4_compress_heap( + void* ctx, + const char* source, + char* dest, + int inputSize) + +Compress 'inputSize' bytes from 'source' into an output buffer 'dest'. +The memory used for compression must be created by LZ4_createHeapMemory() and provided by pointer 'ctx'. +Destination buffer must be already allocated, and sized at a minimum of LZ4_compressBound(inputSize). +return : the number of bytes written in buffer 'dest' +*/ +#define FUNCTION_NAME LZ4_compress_heap +#define USE_HEAPMEMORY +#include "lz4_encoder.h" + + +/* +int LZ4_compress_heap_limitedOutput( + void* ctx, + const char* source, + char* dest, + int inputSize, + int maxOutputSize) + +Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'. +If it cannot achieve it, compression will stop, and result of the function will be zero. +The memory used for compression must be created by LZ4_createHeapMemory() and provided by pointer 'ctx'. +return : the number of bytes written in buffer 'dest', or 0 if the compression fails +*/ +#define FUNCTION_NAME LZ4_compress_heap_limitedOutput +#define LIMITED_OUTPUT +#define USE_HEAPMEMORY +#include "lz4_encoder.h" + + +/* +int LZ4_compress64k_heap( + void* ctx, + const char* source, + char* dest, + int inputSize) + +Compress 'inputSize' bytes from 'source' into an output buffer 'dest'. +The memory used for compression must be created by LZ4_createHeapMemory() and provided by pointer 'ctx'. +'inputSize' must be < to LZ4_64KLIMIT, or the function will fail. +Destination buffer must be already allocated, and sized at a minimum of LZ4_compressBound(inputSize). +return : the number of bytes written in buffer 'dest' +*/ +#define FUNCTION_NAME LZ4_compress64k_heap +#define COMPRESS_64K +#define USE_HEAPMEMORY +#include "lz4_encoder.h" + + +/* +int LZ4_compress64k_heap_limitedOutput( + void* ctx, + const char* source, + char* dest, + int inputSize, + int maxOutputSize) + +Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'. +If it cannot achieve it, compression will stop, and result of the function will be zero. +The memory used for compression must be created by LZ4_createHeapMemory() and provided by pointer 'ctx'. +'inputSize' must be < to LZ4_64KLIMIT, or the function will fail. +return : the number of bytes written in buffer 'dest', or 0 if the compression fails +*/ +#define FUNCTION_NAME LZ4_compress64k_heap_limitedOutput +#define COMPRESS_64K +#define LIMITED_OUTPUT +#define USE_HEAPMEMORY +#include "lz4_encoder.h" + + +int LZ4_compress(const char* source, char* dest, int inputSize) +{ +#if HEAPMODE + void* ctx = LZ4_create(); + int result; + if (ctx == NULL) return 0; // Failed allocation => compression not done + if (inputSize < LZ4_64KLIMIT) + result = LZ4_compress64k_heap(ctx, source, dest, inputSize); + else result = LZ4_compress_heap(ctx, source, dest, inputSize); + LZ4_free(ctx); + return result; +#else + if (inputSize < (int)LZ4_64KLIMIT) return LZ4_compress64k_stack(source, dest, inputSize); + return LZ4_compress_stack(source, dest, inputSize); +#endif +} + + +int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) +{ +#if HEAPMODE + void* ctx = LZ4_create(); + int result; + if (ctx == NULL) return 0; // Failed allocation => compression not done + if (inputSize < LZ4_64KLIMIT) + result = LZ4_compress64k_heap_limitedOutput(ctx, source, dest, inputSize, maxOutputSize); + else result = LZ4_compress_heap_limitedOutput(ctx, source, dest, inputSize, maxOutputSize); + LZ4_free(ctx); + return result; +#else + if (inputSize < (int)LZ4_64KLIMIT) return LZ4_compress64k_stack_limitedOutput(source, dest, inputSize, maxOutputSize); + return LZ4_compress_stack_limitedOutput(source, dest, inputSize, maxOutputSize); +#endif +} + + +//**************************** +// Decompression functions +//**************************** + +typedef enum { noPrefix = 0, withPrefix = 1 } prefix64k_directive; +typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } end_directive; +typedef enum { full = 0, partial = 1 } exit_directive; + + +// This generic decompression function cover all use cases. +// It shall be instanciated several times, using different sets of directives +// Note that it is essential this generic function is really inlined, +// in order to remove useless branches during compilation optimisation. +static inline int LZ4_decompress_generic( + const char* source, + char* dest, + int inputSize, // + int outputSize, // OutputSize must be != 0; if endOnInput==endOnInputSize, this value is the max size of Output Buffer. + + int endOnInput, // endOnOutputSize, endOnInputSize + int prefix64k, // noPrefix, withPrefix + int partialDecoding, // full, partial + int targetOutputSize // only used if partialDecoding==partial + ) +{ + // Local Variables + const BYTE* restrict ip = (const BYTE*) source; + const BYTE* ref; + const BYTE* const iend = ip + inputSize; + + BYTE* op = (BYTE*) dest; + BYTE* const oend = op + outputSize; + BYTE* cpy; + BYTE* oexit = op + targetOutputSize; + + size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0}; +#if LZ4_ARCH64 + size_t dec64table[] = {0, 0, 0, (size_t)-1, 0, 1, 2, 3}; +#endif + + + // Special case + if ((partialDecoding) && (oexit> oend-MFLIMIT)) oexit = oend-MFLIMIT; // targetOutputSize too large, better decode everything + if unlikely(outputSize==0) goto _output_error; // Empty output buffer + + + // Main Loop + while (1) + { + unsigned token; + size_t length; + + // get runlength + token = *ip++; + if ((length=(token>>ML_BITS)) == RUN_MASK) + { + unsigned s=255; + while (((endOnInput)?ip(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) ) + || ((!endOnInput) && (cpy>oend-COPYLENGTH))) + { + if (partialDecoding) + { + if (cpy > oend) goto _output_error; // Error : write attempt beyond end of output buffer + if ((endOnInput) && (ip+length > iend)) goto _output_error; // Error : read attempt beyond end of input buffer + } + else + { + if ((!endOnInput) && (cpy != oend)) goto _output_error; // Error : block decoding must stop exactly there, due to parsing restrictions + if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; // Error : not enough place for another match (min 4) + 5 literals + } + memcpy(op, ip, length); + ip += length; + op += length; + break; // Necessarily EOF, due to parsing restrictions + } + LZ4_WILDCOPY(ip, op, cpy); ip -= (op-cpy); op = cpy; + + // get offset + LZ4_READ_LITTLEENDIAN_16(ref,cpy,ip); ip+=2; + if ((prefix64k==noPrefix) && unlikely(ref < (BYTE* const)dest)) goto _output_error; // Error : offset outside destination buffer + + // get matchlength + if ((length=(token&ML_MASK)) == ML_MASK) + { + while (endOnInput ? ipoend-(COPYLENGTH)-(STEPSIZE-4)) + { + if (cpy > oend-LASTLITERALS) goto _output_error; // Error : last 5 bytes must be literals + LZ4_SECURECOPY(ref, op, (oend-COPYLENGTH)); + while(op> ((MINMATCH*8)-HASHLOG)) +#define LZ4_HASHVALUE(p) LZ4_HASH(A32(p)) + + + +//**************************** +// Function code +//**************************** + +int FUNCTION_NAME( +#ifdef USE_HEAPMEMORY + void* ctx, +#endif + const char* source, + char* dest, + int inputSize +#ifdef LIMITED_OUTPUT + ,int maxOutputSize +#endif + ) +{ +#ifdef USE_HEAPMEMORY + CURRENT_H_TYPE* HashTable = (CURRENT_H_TYPE*)ctx; +#else + CURRENT_H_TYPE HashTable[HASHTABLE_NBCELLS] = {0}; +#endif + + const BYTE* ip = (BYTE*) source; + CURRENTBASE(base); + const BYTE* anchor = ip; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimit = iend - MFLIMIT; +#define matchlimit (iend - LASTLITERALS) + + BYTE* op = (BYTE*) dest; +#ifdef LIMITED_OUTPUT + BYTE* const oend = op + maxOutputSize; +#endif + + int length; + const int skipStrength = SKIPSTRENGTH; + U32 forwardH; + + + // Init + if (inputSizeLZ4_64KLIMIT) return 0; // Size too large (not within 64K limit) +#endif +#ifdef USE_HEAPMEMORY + memset((void*)HashTable, 0, HASHTABLESIZE); +#endif + + // First Byte + HashTable[LZ4_HASHVALUE(ip)] = (CURRENT_H_TYPE)(ip - base); + ip++; forwardH = LZ4_HASHVALUE(ip); + + // Main Loop + for ( ; ; ) + { + int findMatchAttempts = (1U << skipStrength) + 3; + const BYTE* forwardIp = ip; + const BYTE* ref; + BYTE* token; + + // Find a match + do { + U32 h = forwardH; + int step = findMatchAttempts++ >> skipStrength; + ip = forwardIp; + forwardIp = ip + step; + + if unlikely(forwardIp > mflimit) { goto _last_literals; } + + forwardH = LZ4_HASHVALUE(forwardIp); + ref = base + HashTable[h]; + HashTable[h] = (CURRENT_H_TYPE)(ip - base); + + } while ((ref < ip - MAX_DISTANCE) || (A32(ref) != A32(ip))); + + // Catch up + while ((ip>anchor) && (ref>(BYTE*)source) && unlikely(ip[-1]==ref[-1])) { ip--; ref--; } + + // Encode Literal length + length = (int)(ip - anchor); + token = op++; +#ifdef LIMITED_OUTPUT + if unlikely(op + length + (2 + 1 + LASTLITERALS) + (length>>8) > oend) return 0; // Check output limit +#endif + if (length>=(int)RUN_MASK) + { + int len = length-RUN_MASK; + *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255; + *op++ = (BYTE)len; + } + else *token = (BYTE)(length<>8) > oend) return 0; // Check output limit +#endif + if (length>=(int)ML_MASK) + { + *token += ML_MASK; + length -= ML_MASK; + for (; length > 509 ; length-=510) { *op++ = 255; *op++ = 255; } + if (length >= 255) { length-=255; *op++ = 255; } + *op++ = (BYTE)length; + } + else *token += (BYTE)length; + + // Test end of chunk + if (ip > mflimit) { anchor = ip; break; } + + // Fill table + HashTable[LZ4_HASHVALUE(ip-2)] = (CURRENT_H_TYPE)(ip - 2 - base); + + // Test next position + ref = base + HashTable[LZ4_HASHVALUE(ip)]; + HashTable[LZ4_HASHVALUE(ip)] = (CURRENT_H_TYPE)(ip - base); + if ((ref >= ip - MAX_DISTANCE) && (A32(ref) == A32(ip))) { token = op++; *token=0; goto _next_match; } + + // Prepare next loop + anchor = ip++; + forwardH = LZ4_HASHVALUE(ip); + } + +_last_literals: + // Encode Last Literals + { + int lastRun = (int)(iend - anchor); +#ifdef LIMITED_OUTPUT + if (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) return 0; // Check output limit +#endif + if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK<= 255 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; } + else *op++ = (BYTE)(lastRun<