From d36bdf688fd63aa219f789505dca9e403e581da3 Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Sun, 2 Jun 2013 13:59:35 -0400 Subject: [PATCH 1/6] Add LZ4 to the runtime --- configure | 3 +- mk/rt.mk | 2 +- mk/tests.mk | 2 + src/rt/lz4/lz4.c | 609 +++++++++++++++++++++++++++++++++++++ src/rt/lz4/lz4.h | 165 ++++++++++ src/rt/lz4/lz4_decoder.h | 233 ++++++++++++++ src/rt/lz4/lz4_encoder.h | 258 ++++++++++++++++ src/rt/lz4/lz4hc.c | 579 +++++++++++++++++++++++++++++++++++ src/rt/lz4/lz4hc.h | 112 +++++++ src/rt/lz4/lz4hc_encoder.h | 349 +++++++++++++++++++++ src/rt/lz4/xxhash.c | 383 +++++++++++++++++++++++ src/rt/lz4/xxhash.h | 159 ++++++++++ src/rt/rustrt.def.in | 3 + 13 files changed, 2855 insertions(+), 2 deletions(-) create mode 100644 src/rt/lz4/lz4.c create mode 100644 src/rt/lz4/lz4.h create mode 100644 src/rt/lz4/lz4_decoder.h create mode 100644 src/rt/lz4/lz4_encoder.h create mode 100644 src/rt/lz4/lz4hc.c create mode 100644 src/rt/lz4/lz4hc.h create mode 100644 src/rt/lz4/lz4hc_encoder.h create mode 100644 src/rt/lz4/xxhash.c create mode 100644 src/rt/lz4/xxhash.h diff --git a/configure b/configure index 767eb332ac190..0c6310533b54d 100755 --- a/configure +++ b/configure @@ -683,7 +683,8 @@ do for i in \ isaac linenoise sync test \ arch/i386 arch/x86_64 arch/arm arch/mips \ - libuv libuv/src/ares libuv/src/eio libuv/src/ev + libuv libuv/src/ares libuv/src/eio libuv/src/ev \ + lz4 do make_dir rt/$t/stage$s/$i done diff --git a/mk/rt.mk b/mk/rt.mk index d8d74d0d0e3fa..09e3a7a5e81fc 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -88,7 +88,7 @@ RUNTIME_CXXS_$(1)_$(2) := \ rt/rust_android_dummy.cpp \ rt/rust_test_helpers.cpp -RUNTIME_CS_$(1)_$(2) := rt/linenoise/linenoise.c rt/linenoise/utf8.c +RUNTIME_CS_$(1)_$(2) := rt/linenoise/linenoise.c rt/linenoise/utf8.c rt/lz4/lz4.c rt/lz4/lz4hc.c rt/lz4/xxhash.c RUNTIME_S_$(1)_$(2) := rt/arch/$$(HOST_$(1))/_context.S \ rt/arch/$$(HOST_$(1))/ccall.S \ diff --git a/mk/tests.mk b/mk/tests.mk index 3858de3f264df..82d12d8016c55 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -200,6 +200,7 @@ ALL_CS := $(filter-out $(S)src/rt/bigint/bigint_ext.cpp \ $(S)src/rt/miniz.cpp \ $(S)src/rt/linenoise/linenoise.c \ $(S)src/rt/linenoise/utf8.c \ + $(wildcard $(S)src/rt/lz4/*.c) \ ,$(ALL_CS)) ALL_HS := $(wildcard $(S)src/rt/*.h \ $(S)src/rt/*/*.h \ @@ -215,6 +216,7 @@ ALL_HS := $(filter-out $(S)src/rt/vg/valgrind.h \ $(S)src/rt/bigint/bigint.h \ $(S)src/rt/linenoise/linenoise.h \ $(S)src/rt/linenoise/utf8.h \ + $(wildcard $(S)src/rt/lz4/*.h) \ ,$(ALL_HS)) # Run the tidy script in multiple parts to avoid huge 'echo' commands diff --git a/src/rt/lz4/lz4.c b/src/rt/lz4/lz4.c new file mode 100644 index 0000000000000..98b0f15800f2a --- /dev/null +++ b/src/rt/lz4/lz4.c @@ -0,0 +1,609 @@ +/* + 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" and "lz4_decoder.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(__x86_64) || defined(__amd64__) || defined(__amd64) \ + || defined(__ppc64__) || defined(_WIN64) || defined(__LP64__) || defined(_LP64) \ + || defined(__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(__ppc__) || defined(_POWER) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC) || defined(PPC) || defined(__powerpc__) || defined(__powerpc) || defined(powerpc) \ + || 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 +//**************************** + +/* +int LZ4_decompress_safe(const char* source, + char* dest, + int inputSize, + int maxOutputSize); + +LZ4_decompress_safe() guarantees it will never write nor read outside of the provided output buffers. +This function is safe against "buffer overflow" attacks. +A corrupted input will produce an error result, a negative int. +*/ +#define FUNCTION_NAME LZ4_decompress_safe +#define EXITCONDITION_INPUTSIZE +#include "lz4_decoder.h" + + +/* +int LZ4_decompress_safe_withPrefix64k( + const char* source, + char* dest, + int inputSize, + int maxOutputSize); + +Same as LZ4_decompress_safe(), but will also use 64K of memory before the beginning of input buffer. +Typically used to decode streams of inter-dependant blocks. +Note : the 64K of memory before pointer 'source' must be allocated and read-allowed. +*/ +#define FUNCTION_NAME LZ4_decompress_safe_withPrefix64k +#define EXITCONDITION_INPUTSIZE +#define PREFIX_64K +#include "lz4_decoder.h" + + +/* +int LZ4_decompress_safe_partial( + const char* source, + char* dest, + int inputSize, + int targetOutputSize, + int maxOutputSize); + +LZ4_decompress_safe_partial() objective is to decompress only a part of the compressed input block provided. +The decoding process stops as soon as 'targetOutputSize' bytes have been decoded, reducing decoding time. +The result of the function is the number of bytes decoded. +LZ4_decompress_safe_partial() may decode less than 'targetOutputSize' if input doesn't contain enough bytes to decode. +Always verify how many bytes were decoded to ensure there are as many as wanted into the output buffer 'dest'. +A corrupted input will produce an error result, a negative int. +*/ +#define FUNCTION_NAME LZ4_decompress_safe_partial +#define EXITCONDITION_INPUTSIZE +#define PARTIAL_DECODING +#include "lz4_decoder.h" + + +/* +int LZ4_decompress_fast(const char* source, + char* dest, + int outputSize); + +This function is faster than LZ4_decompress_safe(). +LZ4_decompress_fast() guarantees it will never write nor read outside of output buffer. +Since LZ4_decompress_fast() doesn't know the size of input buffer. +it can only guarantee that it will never write into the input buffer, and will never read before its beginning. +To be used preferably in a controlled environment (when the compressed data to be decoded is from a trusted source). +A detected corrupted input will produce an error result, a negative int. +*/ +#define FUNCTION_NAME LZ4_decompress_fast +#include "lz4_decoder.h" + + +/* +int LZ4_decompress_fast_withPrefix64k( + const char* source, + char* dest, + int inputSize + int maxOutputSize); + +Same as LZ4_decompress_fast(), but will use the 64K of memory before the beginning of input buffer. +Typically used to decode streams of dependant inter-blocks. +Note : the 64K of memory before pointer 'source' must be allocated and read-allowed. +*/ +#define FUNCTION_NAME LZ4_decompress_fast_withPrefix64k +#define PREFIX_64K +#include "lz4_decoder.h" + + diff --git a/src/rt/lz4/lz4.h b/src/rt/lz4/lz4.h new file mode 100644 index 0000000000000..353025e25588e --- /dev/null +++ b/src/rt/lz4/lz4.h @@ -0,0 +1,165 @@ +/* + LZ4 - Fast LZ compression algorithm + Header File + 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/ +*/ +#pragma once + +#if defined (__cplusplus) +extern "C" { +#endif + + +//************************************** +// Compiler Options +//************************************** +#if defined(_MSC_VER) && !defined(__cplusplus) // Visual Studio +# define inline __inline // Visual is not C99, but supports some kind of inline +#endif + + +//**************************** +// Simple Functions +//**************************** + +int LZ4_compress (const char* source, char* dest, int inputSize); +int LZ4_decompress_safe (const char* source, char* dest, int inputSize, int maxOutputSize); + +/* +LZ4_compress() : + 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() + inputSize : Max supported value is ~1.9GB + return : the number of bytes written in buffer dest + or 0 if the compression fails + +LZ4_decompress_safe() : + maxOutputSize : is the size of the destination buffer (which must be already allocated) + return : the number of bytes decoded in the destination buffer (necessarily <= maxOutputSize) + If the source stream is malformed, the function will stop decoding and return a negative result. + This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets +*/ + + +//**************************** +// Advanced Functions +//**************************** + +static inline int LZ4_compressBound(int isize) { return ((isize) + ((isize)/255) + 16); } +#define LZ4_COMPRESSBOUND( isize) ((isize) + ((isize)/255) + 16) + +/* +LZ4_compressBound() : + 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. + inline function is recommended for the general case, + macro is also provided when result needs to be evaluated at compile time (such as table size allocation). + + isize : is the input size. Max supported value is ~1.9GB + return : maximum output size in a "worst case" scenario + note : this function is limited by "int" range (2^31-1) +*/ + + +int LZ4_compress_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize); + +/* +LZ4_compress_limitedOutput() : + 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. + This function never writes outside of provided output buffer. + + inputSize : Max supported value is ~1.9GB + 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 +*/ + + +int LZ4_decompress_fast (const char* source, char* dest, int outputSize); + +/* +LZ4_decompress_fast() : + outputSize : is the original (uncompressed) size + return : the number of bytes read from the source buffer (in other words, the compressed size) + If the source stream is malformed, the function will stop decoding and return a negative result. + note : This function is a bit faster than LZ4_decompress_safe() + This function never writes outside of output buffers, and never read before input buffer, but may read beyond input buffer (since it doesn't know its size) in case of malicious data packet. + Use this function preferably into a trusted environment (data to decode comes from a trusted source). + Destination buffer must be already allocated. Its size must be a minimum of 'outputSize' bytes. +*/ + +int LZ4_decompress_safe_partial (const char* source, char* dest, int inputSize, int targetOutputSize, int maxOutputSize); + +/* +LZ4_decompress_safe_partial() : + This function decompress a compressed block of size 'inputSize' at position 'source' + into output buffer 'dest' of size 'maxOutputSize'. + The function stops decompressing operation as soon as 'targetOutputSize' has been reached, + reducing decompression time. + return : the number of bytes decoded in the destination buffer (necessarily <= maxOutputSize) + Note : this number might be < 'targetOutputSize' if the number of bytes to decode into the compressed block is not enough. + Always control how many bytes were decoded. + If the source stream is malformed, the function will stop decoding and return a negative result. + This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets +*/ + + +int LZ4_decompress_safe_withPrefix64k (const char* source, char* dest, int inputSize, int maxOutputSize); +int LZ4_decompress_fast_withPrefix64k (const char* source, char* dest, int outputSize); + +/* +*_withPrefix64k() : + These decoding functions work the same as their "normal name" versions, + but will potentially use up to 64KB of data in front of 'char* dest'. + These functions are used for decoding inter-dependant blocks. +*/ + + +//**************************** +// Obsolete Functions +//**************************** + +static inline int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); } +static inline int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); } + +/* +These functions are deprecated and should no longer be used. +They are provided here for compatibility with existing user programs. +*/ + + + +#if defined (__cplusplus) +} +#endif diff --git a/src/rt/lz4/lz4_decoder.h b/src/rt/lz4/lz4_decoder.h new file mode 100644 index 0000000000000..6dcb30715a245 --- /dev/null +++ b/src/rt/lz4/lz4_decoder.h @@ -0,0 +1,233 @@ +/* + LZ4 Decoder - Part of LZ4 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/ +*/ + +/* lz4_decoder.h must be included into lz4.c + The objective of this file is to create a single LZ4 decoder function source + which will be instanciated multiple times with minor variations + depending on a set of #define. +*/ + + + +//**************************** +// Check required defines +//**************************** + +#ifndef FUNCTION_NAME +# error "FUNTION_NAME is not defined" +#endif + + +//**************************** +// Control tests +//**************************** + +#ifdef EXITCONDITION_INPUTSIZE +# define INPUTBUFFER_CONTROL(ip,iend) likely(ip= oexit) +#else +# define OUTPUTTARGET(cpy,oexit) (0) +#endif + + + + +//**************************** +// Function code +//**************************** + +int FUNCTION_NAME(const char* source, + char* dest, +#ifdef EXITCONDITION_INPUTSIZE + int inputSize, +#endif +#ifdef PARTIAL_DECODING + int targetOutputSize, +#endif + int outputSize + ) +{ + // Local Variables + const BYTE* restrict ip = (const BYTE*) source; + const BYTE* ref; +#ifdef EXITCONDITION_INPUTSIZE + const BYTE* const iend = ip + inputSize; +#endif + + BYTE* op = (BYTE*) dest; + BYTE* const oend = op + outputSize; + BYTE* cpy; +#ifdef PARTIAL_DECODING + BYTE* const oexit = op + targetOutputSize; +#endif + + 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 + + +#ifdef EXITCONDITION_INPUTSIZE + // Special case + if unlikely(!inputSize) goto _output_error; // A correctly formed null-compressed LZ4 must have at least one byte (token=0) +#endif + + // Main Loop + while (1) + { + unsigned token; + size_t length; + + // get runlength + token = *ip++; + if ((length=(token>>ML_BITS)) == RUN_MASK) + { + unsigned s=255; + while (INPUTBUFFER_CONTROL(ip,iend) && (s==255)) + { + s=*ip++; + length += s; + } + } + + // copy literals + cpy = op+length; +#ifdef EXITCONDITION_INPUTSIZE + if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS)) || OUTPUTTARGET(cpy,oexit)) + { + if (cpy > oend) goto _output_error; // Error : write attempt beyond end of output buffer + if ((!OUTPUTTARGET(cpy,oexit)) && (ip+length != iend)) goto _output_error; // Error : Must consume all input at this stage, except if reaching TargetOutputSize +#else + if (cpy>oend-COPYLENGTH) + { + if (cpy != oend) goto _output_error; // Error : not enough place for another match (min 4) + 5 literals +#endif + 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; +#ifndef PREFIX_64K + if unlikely(ref < (BYTE* const)dest) goto _output_error; // Error : offset outside destination buffer +#endif + + // get matchlength + if ((length=(token&ML_MASK)) == ML_MASK) + { + while INPUTBUFFER_CONTROL(ip,iend-(LASTLITERALS+1)) // A minimum nb of input bytes must remain for LASTLITERALS + token + { + unsigned s = *ip++; + length += s; + if (s==255) continue; + break; + } + } + + // copy repeated sequence + if unlikely((op-ref)oend-(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< // calloc, free +#define ALLOCATOR(s) calloc(1,s) +#define FREEMEM free +#include // memset, memcpy +#define MEM_INIT memset + + +//************************************** +// CPU Feature Detection +//************************************** +// 32 or 64 bits ? +#if (defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) || defined(__amd64) \ + || defined(__ppc64__) || defined(_WIN64) || defined(__LP64__) || defined(_LP64) \ + || defined(__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(__ppc__) || defined(_POWER) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC) || defined(PPC) || defined(__powerpc__) || defined(__powerpc) || defined(powerpc) \ + || 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 + +#ifdef _MSC_VER +# define inline __inline // Visual is not C99, but supports some kind of inline +# define forceinline __forceinline +# 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 +# pragma warning(disable : 4701) // disable: C4701: potentially uninitialized local variable used +#else +# ifdef __GNUC__ +# define forceinline inline __attribute__((always_inline)) +# else +# define forceinline inline +# endif +#endif + +#ifdef _MSC_VER // Visual Studio +#define lz4_bswap16(x) _byteswap_ushort(x) +#else +#define lz4_bswap16(x) ((unsigned short int) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))) +#endif + + +//************************************** +// Includes +//************************************** +#include "lz4hc.h" +#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 MINMATCH 4 + +#define DICTIONARY_LOGSIZE 16 +#define MAXD (1<> ((MINMATCH*8)-HASH_LOG)) +#define HASH_VALUE(p) HASH_FUNCTION(A32(p)) +#define HASH_POINTER(p) (HashTable[HASH_VALUE(p)] + base) +#define DELTANEXT(p) chainTable[(size_t)(p) & MAXD_MASK] +#define GETNEXT(p) ((p) - (size_t)DELTANEXT(p)) + + +//************************************** +// Private functions +//************************************** +#if LZ4_ARCH64 + +inline static int LZ4_NbCommonBytes (register U64 val) +{ +#if defined(LZ4_BIG_ENDIAN) +# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanReverse64( &r, val ); + return (int)(r>>3); +# elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 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__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 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 + +inline static int LZ4_NbCommonBytes (register U32 val) +{ +#if defined(LZ4_BIG_ENDIAN) +# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanReverse( &r, val ); + return (int)(r>>3); +# elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 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__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 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 + + +inline int LZ4_InitHC (LZ4HC_Data_Structure* hc4, const BYTE* base) +{ + MEM_INIT((void*)hc4->hashTable, 0, sizeof(hc4->hashTable)); + MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable)); + hc4->nextToUpdate = base + 1; + hc4->base = base; + hc4->inputBuffer = base; + hc4->end = base; + return 1; +} + + +extern inline void* LZ4_createHC (const char* slidingInputBuffer) +{ + void* hc4 = ALLOCATOR(sizeof(LZ4HC_Data_Structure)); + LZ4_InitHC ((LZ4HC_Data_Structure*)hc4, (const BYTE*)slidingInputBuffer); + return hc4; +} + + +extern inline int LZ4_freeHC (void* LZ4HC_Data) +{ + FREEMEM(LZ4HC_Data); + return (0); +} + + +// Update chains up to ip (excluded) +static forceinline void LZ4HC_Insert (LZ4HC_Data_Structure* hc4, const BYTE* ip) +{ + U16* chainTable = hc4->chainTable; + HTYPE* HashTable = hc4->hashTable; + INITBASE(base,hc4->base); + + while(hc4->nextToUpdate < ip) + { + const BYTE* const p = hc4->nextToUpdate; + size_t delta = (p) - HASH_POINTER(p); + if (delta>MAX_DISTANCE) delta = MAX_DISTANCE; + DELTANEXT(p) = (U16)delta; + HashTable[HASH_VALUE(p)] = (HTYPE)((p) - base); + hc4->nextToUpdate++; + } +} + + +char* LZ4_slideInputBufferHC(void* LZ4HC_Data) +{ + LZ4HC_Data_Structure* hc4 = (LZ4HC_Data_Structure*)LZ4HC_Data; + U32 distance = (U32)(hc4->end - hc4->inputBuffer) - 64 KB; + distance = (distance >> 16) << 16; // Must be a multiple of 64 KB + LZ4HC_Insert(hc4, hc4->end - MINMATCH); + memcpy((void*)(hc4->end - 64 KB - distance), (const void*)(hc4->end - 64 KB), 64 KB); + hc4->nextToUpdate -= distance; + hc4->base -= distance; + if ((U32)(hc4->inputBuffer - hc4->base) > 1 GB + 64 KB) // Avoid overflow + { + int i; + hc4->base += 1 GB; + for (i=0; ihashTable[i] -= 1 GB; + } + hc4->end -= distance; + return (char*)(hc4->end); +} + + +static forceinline size_t LZ4HC_CommonLength (const BYTE* p1, const BYTE* p2, const BYTE* const matchlimit) +{ + const BYTE* p1t = p1; + + while (p1tchainTable; + HTYPE* const HashTable = hc4->hashTable; + const BYTE* ref; + INITBASE(base,hc4->base); + int nbAttempts=MAX_NB_ATTEMPTS; + size_t repl=0, ml=0; + U16 delta=0; // useless assignment, to remove an uninitialization warning + + // HC4 match finder + LZ4HC_Insert(hc4, ip); + ref = HASH_POINTER(ip); + +#define REPEAT_OPTIMIZATION +#ifdef REPEAT_OPTIMIZATION + // Detect repetitive sequences of length <= 4 + if ((U32)(ip-ref) <= 4) // potential repetition + { + if (A32(ref) == A32(ip)) // confirmed + { + delta = (U16)(ip-ref); + repl = ml = LZ4HC_CommonLength(ip+MINMATCH, ref+MINMATCH, matchlimit) + MINMATCH; + *matchpos = ref; + } + ref = GETNEXT(ref); + } +#endif + + while (((U32)(ip-ref) <= MAX_DISTANCE) && (nbAttempts)) + { + nbAttempts--; + if (*(ref+ml) == *(ip+ml)) + if (A32(ref) == A32(ip)) + { + size_t mlt = LZ4HC_CommonLength(ip+MINMATCH, ref+MINMATCH, matchlimit) + MINMATCH; + if (mlt > ml) { ml = mlt; *matchpos = ref; } + } + ref = GETNEXT(ref); + } + +#ifdef REPEAT_OPTIMIZATION + // Complete table + if (repl) + { + const BYTE* ptr = ip; + const BYTE* end; + + end = ip + repl - (MINMATCH-1); + while(ptr < end-delta) + { + DELTANEXT(ptr) = delta; // Pre-Load + ptr++; + } + do + { + DELTANEXT(ptr) = delta; + HashTable[HASH_VALUE(ptr)] = (HTYPE)((ptr) - base); // Head of chain + ptr++; + } while(ptr < end); + hc4->nextToUpdate = end; + } +#endif + + return (int)ml; +} + + +static forceinline int LZ4HC_InsertAndGetWiderMatch (LZ4HC_Data_Structure* hc4, const BYTE* ip, const BYTE* startLimit, const BYTE* matchlimit, int longest, const BYTE** matchpos, const BYTE** startpos) +{ + U16* const chainTable = hc4->chainTable; + HTYPE* const HashTable = hc4->hashTable; + INITBASE(base,hc4->base); + const BYTE* ref; + int nbAttempts = MAX_NB_ATTEMPTS; + int delta = (int)(ip-startLimit); + + // First Match + LZ4HC_Insert(hc4, ip); + ref = HASH_POINTER(ip); + + while (((U32)(ip-ref) <= MAX_DISTANCE) && (nbAttempts)) + { + nbAttempts--; + if (*(startLimit + longest) == *(ref - delta + longest)) + if (A32(ref) == A32(ip)) + { +#if 1 + const BYTE* reft = ref+MINMATCH; + const BYTE* ipt = ip+MINMATCH; + const BYTE* startt = ip; + + while (iptstartLimit) && (reft > hc4->inputBuffer) && (startt[-1] == reft[-1])) {startt--; reft--;} + + if ((ipt-startt) > longest) + { + longest = (int)(ipt-startt); + *matchpos = reft; + *startpos = startt; + } + } + ref = GETNEXT(ref); + } + + return longest; +} + + + +//************************************** +// Compression functions +//************************************** + +/* +int LZ4_compressHC( + 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_compressHC +#include "lz4hc_encoder.h" + + +/* +int LZ4_compressHC_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_compressHC_limitedOutput +#define LIMITED_OUTPUT +#include "lz4hc_encoder.h" + diff --git a/src/rt/lz4/lz4hc.h b/src/rt/lz4/lz4hc.h new file mode 100644 index 0000000000000..a201237d48941 --- /dev/null +++ b/src/rt/lz4/lz4hc.h @@ -0,0 +1,112 @@ +/* + LZ4 HC - High Compression Mode of LZ4 + Header File + 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/ +*/ +#pragma once + + +#if defined (__cplusplus) +extern "C" { +#endif + + +int LZ4_compressHC (const char* source, char* dest, int inputSize); +/* +LZ4_compressHC : + return : the number of bytes in compressed buffer dest + or 0 if compression fails. + note : destination buffer must be already allocated. + To avoid any problem, size it to handle worst cases situations (input data not compressible) + Worst case size evaluation is provided by function LZ4_compressBound() (see "lz4.h") +*/ + +int LZ4_compressHC_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize); +/* +LZ4_compress_limitedOutput() : + 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. + This function never writes outside of provided output buffer. + + inputSize : Max supported value is 1 GB + maxOutputSize : is maximum allowed size into the destination buffer (which must be already allocated) + return : the number of output bytes written in buffer 'dest' + or 0 if compression fails. +*/ + + +/* Note : +Decompression functions are provided within LZ4 source code (see "lz4.h") (BSD license) +*/ + + +/* Advanced Functions +*/ + +void* LZ4_createHC (const char* slidingInputBuffer); +int LZ4_compressHC_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize); +int LZ4_compressHC_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize); +char* LZ4_slideInputBufferHC (void* LZ4HC_Data); +int LZ4_freeHC (void* LZ4HC_Data); + +/* +These functions allow the compression of dependent blocks, where each block benefits from prior 64 KB within preceding blocks. +In order to achieve this, it is necessary to start creating the LZ4HC Data Structure, thanks to the function : + +void* LZ4_createHC (const char* slidingInputBuffer); +The result of the function is the (void*) pointer on the LZ4HC Data Structure. +This pointer will be needed in all other functions. +If the pointer returned is NULL, then the allocation has failed, and compression must be aborted. +The only parameter 'const char* slidingInputBuffer' must, obviously, point at the beginning of input buffer. +The input buffer must be already allocated, and size at least 192KB. +'slidingInputBuffer' will also be the 'const char* source' of the first block. + +All blocks are expected to lay next to each other within the input buffer, starting from 'slidingInputBuffer'. +To compress each block, use either LZ4_compressHC_continue() or LZ4_compressHC_limitedOutput_continue(). +Their behavior are identical to LZ4_compressHC() or LZ4_compressHC_limitedOutput(), +but require the LZ4HC Data Structure as their first argument, and check that each block starts right after the previous one. +If next block does not begin immediately after the previous one, the compression will fail (return 0). + +When it's no longer possible to lay the next block after the previous one (not enough space left into input buffer), a call to : +char* LZ4_slideInputBufferHC(void* LZ4HC_Data); +must be performed. It will typically copy the latest 64KB of input at the beginning of input buffer. +Note that, for this function to work properly, minimum size of an input buffer must be 192KB. +==> The memory position where the next input data block must start is provided as the result of the function. + +Compression can then resume, using LZ4_compressHC_continue() or LZ4_compressHC_limitedOutput_continue(), as usual. + +When compression is completed, a call to LZ4_freeHC() will release the memory used by the LZ4HC Data Structure. +*/ + + +#if defined (__cplusplus) +} +#endif diff --git a/src/rt/lz4/lz4hc_encoder.h b/src/rt/lz4/lz4hc_encoder.h new file mode 100644 index 0000000000000..b5e29221a975b --- /dev/null +++ b/src/rt/lz4/lz4hc_encoder.h @@ -0,0 +1,349 @@ +/* + LZ4 HC Encoder - Part of LZ4 HC 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/ +*/ + +/* lz4hc_encoder.h must be included into lz4hc.c + The objective of this file is to create a single LZ4 compression function source + which will be instanciated multiple times with minor variations + depending on a set of #define. +*/ + +//**************************** +// Check required defines +//**************************** + +#ifndef FUNCTION_NAME +# error "FUNTION_NAME is not defined" +#endif + + +//**************************** +// Local definitions +//**************************** +#define COMBINED_NAME_RAW(n1,n2) n1 ## n2 +#define COMBINED_NAME(n1,n2) COMBINED_NAME_RAW(n1,n2) +#define ENCODE_SEQUENCE_NAME COMBINED_NAME(FUNCTION_NAME,_encodeSequence) +#ifdef LIMITED_OUTPUT +# define ENCODE_SEQUENCE(i,o,a,m,r,d) if (ENCODE_SEQUENCE_NAME(i,o,a,m,r,d)) return 0; +#else +# define ENCODE_SEQUENCE(i,o,a,m,r,d) ENCODE_SEQUENCE_NAME(i,o,a,m,r) +#endif + +//**************************** +// Function code +//**************************** + +forceinline static int ENCODE_SEQUENCE_NAME ( + const BYTE** ip, + BYTE** op, + const BYTE** anchor, + int matchLength, + const BYTE* ref +#ifdef LIMITED_OUTPUT + ,BYTE* oend +#endif + ) +{ + int length, len; + BYTE* token; + + // Encode Literal length + length = (int)(*ip - *anchor); + token = (*op)++; +#ifdef LIMITED_OUTPUT + if ((*op + length + (2 + 1 + LASTLITERALS) + (length>>8)) > oend) return 1; // Check output limit +#endif + if (length>=(int)RUN_MASK) { *token=(RUN_MASK< 254 ; len-=255) *(*op)++ = 255; *(*op)++ = (BYTE)len; } + else *token = (BYTE)(length<>8) > oend) return 1; // 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 > 254) { length-=255; *(*op)++ = 255; } *(*op)++ = (BYTE)length; } + else *token += (BYTE)length; + + // Prepare next loop + *ip += matchLength; + *anchor = *ip; + + return 0; +} + + +int COMBINED_NAME(FUNCTION_NAME,_continue) ( + void* ctxvoid, + const char* source, + char* dest, + int inputSize +#ifdef LIMITED_OUTPUT + ,int maxOutputSize +#endif + ) +{ + LZ4HC_Data_Structure* ctx = (LZ4HC_Data_Structure*) ctxvoid; + const BYTE* ip = (const BYTE*) source; + const BYTE* anchor = ip; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = (iend - LASTLITERALS); + + BYTE* op = (BYTE*) dest; +#ifdef LIMITED_OUTPUT + BYTE* const oend = op + maxOutputSize; +#endif + + int ml, ml2, ml3, ml0; + const BYTE* ref=NULL; + const BYTE* start2=NULL; + const BYTE* ref2=NULL; + const BYTE* start3=NULL; + const BYTE* ref3=NULL; + const BYTE* start0; + const BYTE* ref0; + + // Ensure blocks follow each other + if (ip != ctx->end) return 0; + ctx->end += inputSize; + + ip++; + + // Main Loop + while (ip < mflimit) + { + ml = LZ4HC_InsertAndFindBestMatch (ctx, ip, matchlimit, (&ref)); + if (!ml) { ip++; continue; } + + // saved, in case we would skip too much + start0 = ip; + ref0 = ref; + ml0 = ml; + +_Search2: + if (ip+ml < mflimit) + ml2 = LZ4HC_InsertAndGetWiderMatch(ctx, ip + ml - 2, ip + 1, matchlimit, ml, &ref2, &start2); + else ml2 = ml; + + if (ml2 == ml) // No better match + { + ENCODE_SEQUENCE(&ip, &op, &anchor, ml, ref, oend); + continue; + } + + if (start0 < ip) + { + if (start2 < ip + ml0) // empirical + { + ip = start0; + ref = ref0; + ml = ml0; + } + } + + // Here, start0==ip + if ((start2 - ip) < 3) // First Match too small : removed + { + ml = ml2; + ip = start2; + ref =ref2; + goto _Search2; + } + +_Search3: + // Currently we have : + // ml2 > ml1, and + // ip1+3 <= ip2 (usually < ip1+ml1) + if ((start2 - ip) < OPTIMAL_ML) + { + int correction; + int new_ml = ml; + if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML; + if (ip+new_ml > start2 + ml2 - MINMATCH) new_ml = (int)(start2 - ip) + ml2 - MINMATCH; + correction = new_ml - (int)(start2 - ip); + if (correction > 0) + { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } + // Now, we have start2 = ip+new_ml, with new_ml = min(ml, OPTIMAL_ML=18) + + if (start2 + ml2 < mflimit) + ml3 = LZ4HC_InsertAndGetWiderMatch(ctx, start2 + ml2 - 3, start2, matchlimit, ml2, &ref3, &start3); + else ml3 = ml2; + + if (ml3 == ml2) // No better match : 2 sequences to encode + { + // ip & ref are known; Now for ml + if (start2 < ip+ml) ml = (int)(start2 - ip); + // Now, encode 2 sequences + ENCODE_SEQUENCE(&ip, &op, &anchor, ml, ref, oend); + ip = start2; + ENCODE_SEQUENCE(&ip, &op, &anchor, ml2, ref2, oend); + continue; + } + + if (start3 < ip+ml+3) // Not enough space for match 2 : remove it + { + if (start3 >= (ip+ml)) // can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 + { + if (start2 < ip+ml) + { + int correction = (int)(ip+ml - start2); + start2 += correction; + ref2 += correction; + ml2 -= correction; + if (ml2 < MINMATCH) + { + start2 = start3; + ref2 = ref3; + ml2 = ml3; + } + } + + ENCODE_SEQUENCE(&ip, &op, &anchor, ml, ref, oend); + ip = start3; + ref = ref3; + ml = ml3; + + start0 = start2; + ref0 = ref2; + ml0 = ml2; + goto _Search2; + } + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + goto _Search3; + } + + // OK, now we have 3 ascending matches; let's write at least the first one + // ip & ref are known; Now for ml + if (start2 < ip+ml) + { + if ((start2 - ip) < (int)ML_MASK) + { + int correction; + if (ml > OPTIMAL_ML) ml = OPTIMAL_ML; + if (ip + ml > start2 + ml2 - MINMATCH) ml = (int)(start2 - ip) + ml2 - MINMATCH; + correction = ml - (int)(start2 - ip); + if (correction > 0) + { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } + else + { + ml = (int)(start2 - ip); + } + } + ENCODE_SEQUENCE(&ip, &op, &anchor, ml, ref, oend); + + ip = start2; + ref = ref2; + ml = ml2; + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + + goto _Search3; + + } + + // 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< 254 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; } + else *op++ = (BYTE)(lastRun< // for malloc(), free() +#include // for memcpy() +#include "xxhash.h" + + + +//************************************** +// CPU Feature Detection +//************************************** +// Little Endian or Big Endian ? +// You can overwrite the #define below if you know your architecture endianess +#if defined(XXH_FORCE_NATIVE_FORMAT) && (XXH_FORCE_NATIVE_FORMAT==1) +// Force native format. The result will be endian dependant. +# define XXH_BIG_ENDIAN 0 +#elif defined (__GLIBC__) +# include +# if (__BYTE_ORDER == __BIG_ENDIAN) +# define XXH_BIG_ENDIAN 1 +# endif +#elif (defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(__LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN) || defined(_LITTLE_ENDIAN)) +# define XXH_BIG_ENDIAN 1 +#elif defined(__sparc) || defined(__sparc__) \ + || defined(__ppc__) || defined(_POWER) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC) || defined(PPC) || defined(__powerpc__) || defined(__powerpc) || defined(powerpc) \ + || defined(__hpux) || defined(__hppa) \ + || defined(_MIPSEB) || defined(__s390__) +# define XXH_BIG_ENDIAN 1 +#endif + +#if !defined(XXH_BIG_ENDIAN) +// Little Endian assumed. PDP Endian and other very rare endian format are unsupported. +# define XXH_BIG_ENDIAN 0 +#endif + + +//************************************** +// 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 + + +//************************************** +// Compiler-specific Options & Functions +//************************************** +#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +// Note : under GCC, it may sometimes be faster to enable the (2nd) macro definition, instead of using win32 intrinsic +#if defined(_WIN32) +# define XXH_rotl32(x,r) _rotl(x,r) +#else +# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) +#endif + +#if defined(_MSC_VER) // Visual Studio +# define XXH_swap32 _byteswap_ulong +#elif GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static inline U32 XXH_swap32 (U32 x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff );} +#endif + + +//************************************** +// Constants +//************************************** +#define PRIME32_1 2654435761U +#define PRIME32_2 2246822519U +#define PRIME32_3 3266489917U +#define PRIME32_4 668265263U +#define PRIME32_5 374761393U + + +//************************************** +// Macros +//************************************** +#define XXH_LE32(p) (XXH_BIG_ENDIAN ? XXH_swap32(*(U32*)(p)) : *(U32*)(p)) + + + +//**************************** +// Simple Hash Functions +//**************************** + +U32 XXH32(const void* input, int len, U32 seed) +{ +#if 0 + // Simple version, good for code maintenance, but unfortunately slow for small inputs + void* state = XXH32_init(seed); + XXH32_update(state, input, len); + return XXH32_digest(state); +#else + + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + U32 h32; + +#ifdef XXH_ACCEPT_NULL_INPUT_POINTER + if (p==NULL) { len=0; p=(const BYTE*)16; } +#endif + + if (len>=16) + { + const BYTE* const limit = bEnd - 16; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do + { + v1 += XXH_LE32(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; + v2 += XXH_LE32(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; + v3 += XXH_LE32(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; + v4 += XXH_LE32(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; 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; + + while (p<=bEnd-4) + { + h32 += XXH_LE32(p) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + p+=4; + } + + while (p> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; + +#endif +} + + +//**************************** +// Advanced Hash Functions +//**************************** + +struct XXH_state32_t +{ + U32 seed; + U32 v1; + U32 v2; + U32 v3; + U32 v4; + U64 total_len; + char memory[16]; + int memsize; +}; + + +int XXH32_sizeofState() { return sizeof(struct XXH_state32_t); } + + +XXH_errorcode XXH32_resetState(void* state_in, unsigned int seed) +{ + struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; + state->seed = seed; + state->v1 = seed + PRIME32_1 + PRIME32_2; + state->v2 = seed + PRIME32_2; + state->v3 = seed + 0; + state->v4 = seed - PRIME32_1; + state->total_len = 0; + state->memsize = 0; + return OK; +} + + +void* XXH32_init (U32 seed) +{ + struct XXH_state32_t * state = (struct XXH_state32_t *) malloc (sizeof(struct XXH_state32_t)); + XXH32_resetState(state, seed); + return (void*)state; +} + + +XXH_errorcode XXH32_update (void* state_in, const void* input, int len) +{ + struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + +#ifdef XXH_ACCEPT_NULL_INPUT_POINTER + if (input==NULL) return XXH_ERROR; +#endif + + state->total_len += len; + + if (state->memsize + len < 16) // fill in tmp buffer + { + memcpy(state->memory + state->memsize, input, len); + state->memsize += len; + return OK; + } + + if (state->memsize) // some data left from previous update + { + memcpy(state->memory + state->memsize, input, 16-state->memsize); + { + const U32* p32 = (const U32*)state->memory; + state->v1 += XXH_LE32(p32) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++; + state->v2 += XXH_LE32(p32) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++; + state->v3 += XXH_LE32(p32) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++; + state->v4 += XXH_LE32(p32) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++; + } + 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 += XXH_LE32(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; + v2 += XXH_LE32(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; + v3 += XXH_LE32(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; + v4 += XXH_LE32(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) + { + memcpy(state->memory, p, bEnd-p); + state->memsize = (int)(bEnd-p); + } + + return OK; +} + + +U32 XXH32_intermediateDigest (void* state_in) +{ + struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; + BYTE * p = (BYTE*)state->memory; + BYTE* bEnd = (BYTE*)state->memory + state->memsize; + U32 h32; + + + if (state->total_len >= 16) + { + h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18); + } + else + { + h32 = state->seed + PRIME32_5; + } + + h32 += (U32) state->total_len; + + while (p<=bEnd-4) + { + h32 += XXH_LE32(p) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4; + p+=4; + } + + while (p> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; +} + + +U32 XXH32_digest (void* state_in) +{ + U32 h32 = XXH32_intermediateDigest(state_in); + + free(state_in); + + return h32; +} diff --git a/src/rt/lz4/xxhash.h b/src/rt/lz4/xxhash.h new file mode 100644 index 0000000000000..9d163c2ce225b --- /dev/null +++ b/src/rt/lz4/xxhash.h @@ -0,0 +1,159 @@ +/* + xxHash - Fast Hash algorithm + Header File + Copyright (C) 2012-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 : + - xxHash source repository : http://code.google.com/p/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. +*/ + +#pragma once + +#if defined (__cplusplus) +extern "C" { +#endif + + +//**************************** +// Type +//**************************** +typedef enum { OK=0, XXH_ERROR } XXH_errorcode; + + + +//**************************** +// Simple Hash Functions +//**************************** + +unsigned int XXH32 (const void* input, int len, unsigned int seed); + +/* +XXH32() : + Calculate the 32-bits hash of sequence of length "len" stored at memory address "input". + The memory between input & input+len must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + This function successfully passes all SMHasher tests. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s + Note that "len" is type "int", which means it is limited to 2^31-1. + If your data is larger, use the advanced functions below. +*/ + + + +//**************************** +// Advanced Hash Functions +//**************************** + +void* XXH32_init (unsigned int seed); +XXH_errorcode XXH32_update (void* state, const void* input, int len); +unsigned int XXH32_digest (void* state); + +/* +These functions calculate the xxhash of an input provided in several small packets, +as opposed to an input provided as a single block. + +It must be started with : +void* XXH32_init() +The function returns a pointer which holds the state of calculation. + +This pointer must be provided as "void* state" parameter for XXH32_update(). +XXH32_update() can be called as many times as necessary. +The user must provide a valid (allocated) input. +The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. +Note that "len" is type "int", which means it is limited to 2^31-1. +If your data is larger, it is recommended to chunk your data into blocks +of size for example 2^30 (1GB) to avoid any "int" overflow issue. + +Finally, you can end the calculation anytime, by using XXH32_digest(). +This function returns the final 32-bits hash. +You must provide the same "void* state" parameter created by XXH32_init(). +Memory will be freed by XXH32_digest(). +*/ + + +int XXH32_sizeofState(); +XXH_errorcode XXH32_resetState(void* state_in, unsigned int seed); +/* +These functions are the basic elements of XXH32_init(); +The objective is to allow user application to make its own allocation. + +XXH32_sizeofState() is used to know how much space must be allocated by the application. +This space must be referenced by a void* pointer. +This pointer must be provided as 'state_in' into XXH32_resetState(), which initializes the state. +*/ + + +unsigned int XXH32_intermediateDigest (void* state); +/* +This function does the same as XXH32_digest(), generating a 32-bit hash, +but preserve memory context. +This way, it becomes possible to generate intermediate hashes, and then continue feeding data with XXH32_update(). +To free memory context, use XXH32_digest(). +*/ + + + +//**************************** +// Deprecated function names +//**************************** +// The following translations are provided to ease code transition +// You are encouraged to no longer this function names +#define XXH32_feed XXH32_update +#define XXH32_result XXH32_digest +#define XXH32_getIntermediateResult XXH32_intermediateDigest + + + +#if defined (__cplusplus) +} +#endif diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index e3e522aa7ceec..e020fefc57dc8 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -239,3 +239,6 @@ rust_valgrind_stack_deregister rust_take_env_lock rust_drop_env_lock rust_update_log_settings +LZ4_compress +LZ4_compressHC +LZ4_decompress_safe From 1cd9ded5a2a1af5a9737a14d56e35799b94c342e Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Sun, 2 Jun 2013 14:00:30 -0400 Subject: [PATCH 2/6] Add LZ4 wrapper to extra --- src/libextra/lz4.rs | 118 ++++++++++++++++++++++++++++++++++++++++++++ src/libextra/std.rc | 1 + 2 files changed, 119 insertions(+) create mode 100644 src/libextra/lz4.rs diff --git a/src/libextra/lz4.rs b/src/libextra/lz4.rs new file mode 100644 index 0000000000000..f4d48b9f53003 --- /dev/null +++ b/src/libextra/lz4.rs @@ -0,0 +1,118 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + +Simple compression using the very fast LZ4 algorithm + +*/ + +#[allow(missing_doc)]; + +use core::prelude::*; + +use core::libc::{c_void, c_int}; +use core::vec; +use core::vec::raw::to_mut_ptr; + +pub mod rustrt { + use core::libc::{c_int, c_void, size_t}; + + #[link_name = "rustrt"] + pub extern { + unsafe fn LZ4_compress(source: *const c_void, dest: *mut c_void, inputSize: c_int) -> c_int; + unsafe fn LZ4_compressHC(source: *const c_void, dest: *mut c_void, inputSize: c_int) -> c_int; + unsafe fn LZ4_decompress_safe(source: *const c_void, dest: *mut c_void, inputSize: c_int, + maxOutputSize: c_int) -> c_int; + } +} + +// worst case compressed size +fn LZ4_compressBound(size: uint) -> uint { size + size/255 + 16 } + +/// Container for LZ4-compressed data, because LZ4 doesn't define its own container +pub struct LZ4Container { + size: c_int, + buf: ~[u8] +} + +/// Compress a buffer +pub fn compress_bytes(bytes: &const [u8], high_compression: bool) -> LZ4Container { + let size = LZ4_compressBound(bytes.len()); + let mut out: ~[u8] = vec::with_capacity(size); + let mut res = 0; + do vec::as_const_buf(bytes) |b, _len| { + unsafe { + if !high_compression { + res = rustrt::LZ4_compress(b as *c_void, to_mut_ptr(out) as *mut c_void, + size as c_int); + } else { + res = rustrt::LZ4_compressHC(b as *c_void, to_mut_ptr(out) as *mut c_void, + size as c_int); + } + assert!(res as int != 0); + } + } + // FIXME #XXXX: realloc buffer to res bytes + return LZ4Container{ size: res, buf: out } +} + +impl LZ4Container { + /// Decompress LZ4 data. Returns None if the input buffer was malformed or didn't decompress + /// to `size` bytes. + pub fn decompress(&self) -> Option<~[u8]> { + let mut ret: Option<~[u8]> = None; + do vec::as_const_buf(self.buf) |b, len| { + let mut out: ~[u8] = vec::with_capacity(self.size as uint); + unsafe { + let res = rustrt::LZ4_decompress_safe(b as *c_void, + to_mut_ptr(out) as *mut c_void, + len as c_int, self.size); + if res != self.size { + ret = None + } + ret = Some(out) + } + } + ret + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::rand; + use core::rand::RngUtil; + + #[test] + #[allow(non_implicitly_copyable_typarams)] + fn test_flate_round_trip() { + let mut r = rand::rng(); + let mut words = ~[]; + for 20.times { + let range = r.gen_uint_range(1, 10); + words.push(r.gen_bytes(range)); + } + for 20.times { + let mut in = ~[]; + for 2000.times { + in.push_all(r.choose(words)); + } + debug!("de/inflate of %u bytes of random word-sequences", + in.len()); + let cmp = compress_bytes(in); + let out = cmp.decompress(); + debug!("%u bytes deflated to %u (%.1f%% size)", + in.len(), cmp.len(), + 100.0 * ((cmp.len() as float) / (in.len() as float))); + assert_eq!(in, out); + } + } +} diff --git a/src/libextra/std.rc b/src/libextra/std.rc index 4e9a547e14119..edd7f7d1d13bc 100644 --- a/src/libextra/std.rc +++ b/src/libextra/std.rc @@ -114,6 +114,7 @@ pub mod stats; pub mod semver; pub mod fileinput; pub mod flate; +pub mod lz4; #[cfg(unicode)] mod unicode; From 9cac3d15e4db55f4cdbfc4ee8102e4a9c732b09b Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Sun, 2 Jun 2013 17:15:05 -0400 Subject: [PATCH 3/6] Tidy fixes --- src/libextra/lz4.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libextra/lz4.rs b/src/libextra/lz4.rs index f4d48b9f53003..999bd4cfd8648 100644 --- a/src/libextra/lz4.rs +++ b/src/libextra/lz4.rs @@ -23,12 +23,13 @@ use core::vec; use core::vec::raw::to_mut_ptr; pub mod rustrt { - use core::libc::{c_int, c_void, size_t}; + use core::libc::{c_int, c_void}; #[link_name = "rustrt"] pub extern { unsafe fn LZ4_compress(source: *const c_void, dest: *mut c_void, inputSize: c_int) -> c_int; - unsafe fn LZ4_compressHC(source: *const c_void, dest: *mut c_void, inputSize: c_int) -> c_int; + unsafe fn LZ4_compressHC(source: *const c_void, dest: *mut c_void, inputSize: c_int) + -> c_int; unsafe fn LZ4_decompress_safe(source: *const c_void, dest: *mut c_void, inputSize: c_int, maxOutputSize: c_int) -> c_int; } @@ -60,7 +61,7 @@ pub fn compress_bytes(bytes: &const [u8], high_compression: bool) -> LZ4Containe assert!(res as int != 0); } } - // FIXME #XXXX: realloc buffer to res bytes + // FIXME #4960: realloc buffer to res bytes return LZ4Container{ size: res, buf: out } } From 9dce32823522f07ceb762b92065b648cbd6d64f8 Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Sun, 2 Jun 2013 21:45:03 -0400 Subject: [PATCH 4/6] Fix LZ4 correctness --- src/libextra/lz4.rs | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/libextra/lz4.rs b/src/libextra/lz4.rs index 999bd4cfd8648..647a115f64b01 100644 --- a/src/libextra/lz4.rs +++ b/src/libextra/lz4.rs @@ -20,9 +20,10 @@ use core::prelude::*; use core::libc::{c_void, c_int}; use core::vec; +use core::num; use core::vec::raw::to_mut_ptr; -pub mod rustrt { +priv mod rustrt { use core::libc::{c_int, c_void}; #[link_name = "rustrt"] @@ -35,8 +36,9 @@ pub mod rustrt { } } -// worst case compressed size -fn LZ4_compressBound(size: uint) -> uint { size + size/255 + 16 } + +/// Worst case compressed size +pub fn LZ4_compressBound(size: uint) -> uint { size + size/255 + 16 } /// Container for LZ4-compressed data, because LZ4 doesn't define its own container pub struct LZ4Container { @@ -46,23 +48,25 @@ pub struct LZ4Container { /// Compress a buffer pub fn compress_bytes(bytes: &const [u8], high_compression: bool) -> LZ4Container { - let size = LZ4_compressBound(bytes.len()); - let mut out: ~[u8] = vec::with_capacity(size); + let max_cint: c_int = num::Bounded::max_value(); + assert!(bytes.len() <= max_cint as uint, "buffer too long"); + let mut out: ~[u8] = vec::with_capacity(LZ4_compressBound(bytes.len())); let mut res = 0; - do vec::as_const_buf(bytes) |b, _len| { + do vec::as_const_buf(bytes) |b, len| { unsafe { if !high_compression { res = rustrt::LZ4_compress(b as *c_void, to_mut_ptr(out) as *mut c_void, - size as c_int); + len as c_int); } else { res = rustrt::LZ4_compressHC(b as *c_void, to_mut_ptr(out) as *mut c_void, - size as c_int); + len as c_int); } + vec::raw::set_len(&mut out, res as uint); assert!(res as int != 0); } } // FIXME #4960: realloc buffer to res bytes - return LZ4Container{ size: res, buf: out } + return LZ4Container{ size: bytes.len() as c_int, buf: out } } impl LZ4Container { @@ -73,13 +77,15 @@ impl LZ4Container { do vec::as_const_buf(self.buf) |b, len| { let mut out: ~[u8] = vec::with_capacity(self.size as uint); unsafe { - let res = rustrt::LZ4_decompress_safe(b as *c_void, - to_mut_ptr(out) as *mut c_void, - len as c_int, self.size); + let res = rustrt::LZ4_decompress_safe(b as *c_void, to_mut_ptr(out) as *mut c_void, + len as c_int, self.size); if res != self.size { + warn!("LZ4_decompress_safe returned %?", res); ret = None + } else { + vec::raw::set_len(&mut out, res as uint); + ret = Some(out) } - ret = Some(out) } } ret @@ -94,7 +100,7 @@ mod tests { #[test] #[allow(non_implicitly_copyable_typarams)] - fn test_flate_round_trip() { + fn test_round_trip() { let mut r = rand::rng(); let mut words = ~[]; for 20.times { @@ -108,11 +114,12 @@ mod tests { } debug!("de/inflate of %u bytes of random word-sequences", in.len()); - let cmp = compress_bytes(in); - let out = cmp.decompress(); - debug!("%u bytes deflated to %u (%.1f%% size)", - in.len(), cmp.len(), - 100.0 * ((cmp.len() as float) / (in.len() as float))); + let cmp = compress_bytes(in, true); + debug!("compressed size reported as %?", cmp.size); + let out = cmp.decompress().unwrap(); + debug!("%u bytes compressed to %u (%.1f%% size) and was decompressed to %?", + in.len(), cmp.buf.len(), + 100.0 * ((cmp.buf.len() as float) / (in.len() as float)), out.len()); assert_eq!(in, out); } } From 59e21dbe8b83cb8afdfc8ea4f6926f77ed2018c1 Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Mon, 3 Jun 2013 07:02:52 -0400 Subject: [PATCH 5/6] Use lz4 for metadata --- src/libextra/lz4.rs | 37 +++++++++++++++++++++++--------- src/librustc/metadata/encoder.rs | 9 +++++--- src/librustc/metadata/loader.rs | 6 +++--- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/libextra/lz4.rs b/src/libextra/lz4.rs index 647a115f64b01..de098a134a85b 100644 --- a/src/libextra/lz4.rs +++ b/src/libextra/lz4.rs @@ -21,6 +21,8 @@ use core::prelude::*; use core::libc::{c_void, c_int}; use core::vec; use core::num; +use core::io; +use core::sys; use core::vec::raw::to_mut_ptr; priv mod rustrt { @@ -42,7 +44,8 @@ pub fn LZ4_compressBound(size: uint) -> uint { size + size/255 + 16 } /// Container for LZ4-compressed data, because LZ4 doesn't define its own container pub struct LZ4Container { - size: c_int, + uncompressed_size: uint, + buf_size: uint, buf: ~[u8] } @@ -50,23 +53,23 @@ pub struct LZ4Container { pub fn compress_bytes(bytes: &const [u8], high_compression: bool) -> LZ4Container { let max_cint: c_int = num::Bounded::max_value(); assert!(bytes.len() <= max_cint as uint, "buffer too long"); - let mut out: ~[u8] = vec::with_capacity(LZ4_compressBound(bytes.len())); + let mut buf: ~[u8] = vec::with_capacity(LZ4_compressBound(bytes.len())); let mut res = 0; do vec::as_const_buf(bytes) |b, len| { unsafe { if !high_compression { - res = rustrt::LZ4_compress(b as *c_void, to_mut_ptr(out) as *mut c_void, + res = rustrt::LZ4_compress(b as *c_void, to_mut_ptr(buf) as *mut c_void, len as c_int); } else { - res = rustrt::LZ4_compressHC(b as *c_void, to_mut_ptr(out) as *mut c_void, + res = rustrt::LZ4_compressHC(b as *c_void, to_mut_ptr(buf) as *mut c_void, len as c_int); } - vec::raw::set_len(&mut out, res as uint); - assert!(res as int != 0); + vec::raw::set_len(&mut buf, res as uint); + assert!(res as int != 0, "LZ4_compress(HC) failed"); } } // FIXME #4960: realloc buffer to res bytes - return LZ4Container{ size: bytes.len() as c_int, buf: out } + return LZ4Container{ uncompressed_size: bytes.len(), buf_size: buf.len(), buf: buf } } impl LZ4Container { @@ -75,11 +78,11 @@ impl LZ4Container { pub fn decompress(&self) -> Option<~[u8]> { let mut ret: Option<~[u8]> = None; do vec::as_const_buf(self.buf) |b, len| { - let mut out: ~[u8] = vec::with_capacity(self.size as uint); + let mut out: ~[u8] = vec::with_capacity(self.uncompressed_size as uint); unsafe { let res = rustrt::LZ4_decompress_safe(b as *c_void, to_mut_ptr(out) as *mut c_void, - len as c_int, self.size); - if res != self.size { + len as c_int, self.uncompressed_size as c_int); + if res != self.uncompressed_size as c_int { warn!("LZ4_decompress_safe returned %?", res); ret = None } else { @@ -90,6 +93,20 @@ impl LZ4Container { } ret } + /// Create an LZ4Container out of bytes + pub fn from_bytes(bytes: &[u8]) -> LZ4Container { + do io::with_bytes_reader(bytes) |rdr| { + let uncompressed_size = rdr.read_le_uint(); + let buf_size = rdr.read_le_uint(); + let remaining = bytes.len() - rdr.tell(); + assert!(remaining >= buf_size, + fmt!("header wants more bytes than present in buffer (wanted %?, found %?)", + buf_size, remaining)); + let buf = bytes.slice(rdr.tell(), rdr.tell() + buf_size).to_owned(); + assert_eq!(buf_size, buf.len()); + LZ4Container { uncompressed_size: uncompressed_size, buf_size: buf_size, buf: buf } + } + } } #[cfg(test)] diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 6f453a79b680d..f6857a9daee58 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -29,7 +29,8 @@ use core::io; use core::str; use core::uint; use core::vec; -use extra::flate; +use core::to_bytes::ToBytes; +use extra::lz4; use extra::serialize::Encodable; use extra; use syntax::abi::AbiSet; @@ -1516,9 +1517,11 @@ pub fn encode_metadata(parms: EncodeParams, crate: &crate) -> ~[u8] { wr.write(&[0u8, 0u8, 0u8, 0u8]); let writer_bytes: &mut ~[u8] = wr.bytes; - + let compressed = lz4::compress_bytes(*writer_bytes, true); vec::to_owned(metadata_encoding_version) + - flate::deflate_bytes(*writer_bytes) + compressed.uncompressed_size.to_bytes(true) + + compressed.buf_size.to_bytes(true) + + compressed.buf } // Get the encoded string for a type diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 182f1e9078ce5..1bc3cf4ee0556 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -31,7 +31,7 @@ use core::ptr; use core::str; use core::uint; use core::vec; -use extra::flate; +use extra::lz4; pub enum os { os_macos, @@ -226,8 +226,8 @@ fn get_metadata_section(os: os, debug!("inflating %u bytes of compressed metadata", csz - vlen); do vec::raw::buf_as_slice(cvbuf1, csz-vlen) |bytes| { - let inflated = flate::inflate_bytes(bytes); - found = Some(@(inflated)); + let s = lz4::LZ4Container::from_bytes(bytes); + found = Some(@(s.decompress().expect("metadata was corrupt!"))); } if found != None { return found; From d5fbc8a0937b4e78606249480e1c9878729b07fa Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Wed, 5 Jun 2013 12:28:58 -0400 Subject: [PATCH 6/6] Add no-copy decompression --- src/libextra/lz4.rs | 53 ++++++++++++++++++++++---------- src/librustc/metadata/encoder.rs | 2 +- src/librustc/metadata/loader.rs | 4 +-- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/libextra/lz4.rs b/src/libextra/lz4.rs index de098a134a85b..a353c8b4f37cd 100644 --- a/src/libextra/lz4.rs +++ b/src/libextra/lz4.rs @@ -50,7 +50,7 @@ pub struct LZ4Container { } /// Compress a buffer -pub fn compress_bytes(bytes: &const [u8], high_compression: bool) -> LZ4Container { +pub fn compress(bytes: &const [u8], high_compression: bool) -> LZ4Container { let max_cint: c_int = num::Bounded::max_value(); assert!(bytes.len() <= max_cint as uint, "buffer too long"); let mut buf: ~[u8] = vec::with_capacity(LZ4_compressBound(bytes.len())); @@ -72,26 +72,29 @@ pub fn compress_bytes(bytes: &const [u8], high_compression: bool) -> LZ4Containe return LZ4Container{ uncompressed_size: bytes.len(), buf_size: buf.len(), buf: buf } } +/// Decompress LZ4 data. Returns None if the input buffer was malformed or didn't decompress +/// to `size` bytes. +pub fn decompress(bytes: &[u8], uncompressed_size: uint) -> Option<~[u8]> { + do vec::as_const_buf(bytes) |b, len| { + let mut out: ~[u8] = vec::with_capacity(uncompressed_size as uint); + unsafe { + let res = rustrt::LZ4_decompress_safe(b as *c_void, to_mut_ptr(out) as *mut c_void, + len as c_int, uncompressed_size as c_int); + if res != uncompressed_size as c_int { + warn!("LZ4_decompress_safe returned %?", res); + None + } else { + vec::raw::set_len(&mut out, res as uint); + Some(out) + } + } + } +} impl LZ4Container { /// Decompress LZ4 data. Returns None if the input buffer was malformed or didn't decompress /// to `size` bytes. pub fn decompress(&self) -> Option<~[u8]> { - let mut ret: Option<~[u8]> = None; - do vec::as_const_buf(self.buf) |b, len| { - let mut out: ~[u8] = vec::with_capacity(self.uncompressed_size as uint); - unsafe { - let res = rustrt::LZ4_decompress_safe(b as *c_void, to_mut_ptr(out) as *mut c_void, - len as c_int, self.uncompressed_size as c_int); - if res != self.uncompressed_size as c_int { - warn!("LZ4_decompress_safe returned %?", res); - ret = None - } else { - vec::raw::set_len(&mut out, res as uint); - ret = Some(out) - } - } - } - ret + decompress(self.buf, self.uncompressed_size) } /// Create an LZ4Container out of bytes pub fn from_bytes(bytes: &[u8]) -> LZ4Container { @@ -107,6 +110,22 @@ impl LZ4Container { LZ4Container { uncompressed_size: uncompressed_size, buf_size: buf_size, buf: buf } } } + + /// Decompress the contents of an encoded LZ4Container. Like `from_bytes(bytes).decompress()` + /// without an extra copy. + pub fn decompress_bytes(bytes: &[u8]) -> Option<~[u8]> { + do io::with_bytes_reader(bytes) |rdr| { + let uncompressed_size = rdr.read_le_uint(); + let buf_size = rdr.read_le_uint(); + let remaining = bytes.len() - rdr.tell(); + assert!(remaining >= buf_size, + fmt!("header wants more bytes than present in buffer (wanted %?, found %?)", + buf_size, remaining)); + let buf = bytes.slice(rdr.tell(), rdr.tell() + buf_size); + assert_eq!(buf_size, buf.len()); + decompress(buf, uncompressed_size) + } + } } #[cfg(test)] diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index f6857a9daee58..0cdf7c2253ff2 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -1517,7 +1517,7 @@ pub fn encode_metadata(parms: EncodeParams, crate: &crate) -> ~[u8] { wr.write(&[0u8, 0u8, 0u8, 0u8]); let writer_bytes: &mut ~[u8] = wr.bytes; - let compressed = lz4::compress_bytes(*writer_bytes, true); + let compressed = lz4::compress(*writer_bytes, true); vec::to_owned(metadata_encoding_version) + compressed.uncompressed_size.to_bytes(true) + compressed.buf_size.to_bytes(true) + diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 1bc3cf4ee0556..236a49bea1c3d 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -226,8 +226,8 @@ fn get_metadata_section(os: os, debug!("inflating %u bytes of compressed metadata", csz - vlen); do vec::raw::buf_as_slice(cvbuf1, csz-vlen) |bytes| { - let s = lz4::LZ4Container::from_bytes(bytes); - found = Some(@(s.decompress().expect("metadata was corrupt!"))); + let s = lz4::LZ4Container::decompress_bytes(bytes); + found = Some(@(s.expect("metadata was corrupt!"))); } if found != None { return found;