From 74cf4e1ad4b9c71e0da68308573a3753f0dc60d6 Mon Sep 17 00:00:00 2001 From: a Date: Mon, 23 Jun 2025 12:17:38 -0400 Subject: [PATCH] Add aglab2 lz4t --- Makefile | 2 +- asm/decompress.s | 306 ++-- sm64.ld | 5 +- src/buffers/buffers.c | 2 - src/buffers/buffers.h | 2 - src/engine/level_script.c | 2 +- src/game/decompress.h | 3 +- src/game/dma_async.c | 42 + src/game/dma_async.h | 15 + src/game/libgcc.c | 29 + src/game/memory.c | 22 +- tools/Makefile | 5 +- tools/lz4.c | 2839 +++++++++++++++++++++++++++++++++++++ tools/lz4.h | 884 ++++++++++++ tools/lz4hc.c | 2075 +++++++++++++++++++++++++++ tools/lz4hc.h | 413 ++++++ tools/lz4tpack | Bin 0 -> 89128 bytes tools/lz4tpack.c | 780 ++++++++++ 18 files changed, 7303 insertions(+), 123 deletions(-) create mode 100644 src/game/dma_async.c create mode 100644 src/game/dma_async.h create mode 100644 tools/lz4.c create mode 100644 tools/lz4.h create mode 100644 tools/lz4hc.c create mode 100644 tools/lz4hc.h create mode 100755 tools/lz4tpack create mode 100644 tools/lz4tpack.c diff --git a/Makefile b/Makefile index 842271dd..7c4c982e 100644 --- a/Makefile +++ b/Makefile @@ -315,7 +315,7 @@ export LANG := C #==============================================================================# # N64 tools -MIO0TOOL := $(TOOLS_DIR)/sm64tools/mio0 +MIO0TOOL := $(TOOLS_DIR)/lz4tpack N64CKSUM := $(TOOLS_DIR)/sm64tools/n64cksum N64GRAPHICS := $(TOOLS_DIR)/sm64tools/n64graphics N64GRAPHICS_CI := $(TOOLS_DIR)/sm64tools/n64graphics_ci diff --git a/asm/decompress.s b/asm/decompress.s index 7a90c25b..661b0e7a 100644 --- a/asm/decompress.s +++ b/asm/decompress.s @@ -1,104 +1,216 @@ -// assembler directives -.set noat // allow manual use of $at -.set noreorder // don't insert nops after branches +############################################## +# LZ4T - Fast decompressor in assembly +# Written by aglab2 inspired by Rasky LZ4 and devwizard YAZ0 +############################################## -#include "macros.inc" +# NOTE: to optimize for speed, this decompressor can write up to 8 bytes +# after the end of the output buffer. The outut buffer must have been sized +# accordingly to accomodate for this. +#define MINMATCH 4 -.section .text, "ax" +#define inbuf $s0 +#define nibbles $s1 +#define outbuf $s2 +#define match_combo_mask $s3 +#define len $s4 +#define match_lim $s5 +#define match_min $s6 +#define v0_st $s7 -// This file is handwritten. +#define dma_ctx $s8 +#define dma_ptr $v0 -glabel decompress -#if !defined(VERSION_JP) && !defined(VERSION_US) - lw $a3, 8($a0) - lw $t9, 0xc($a0) - lw $t8, 4($a0) - add $a3, $a3, $a0 - add $t9, $t9, $a0 - or $a2, $zero, $zero - addi $a0, $a0, 0x10 - add $t8, $t8, $a1 -.L8026ED80: - bnezl $a2, .L8026ED98 - slt $t1, $t0, $zero - lw $t0, ($a0) - li $a2, 32 - addi $a0, $a0, 4 - slt $t1, $t0, $zero -.L8026ED98: - beql $t1, $zero, .L8026EDB8 - lhu $t2, ($a3) - lb $t2, ($t9) - addi $t9, $t9, 1 - addi $a1, $a1, 1 - b .L8026EDE4 - sb $t2, -1($a1) - lhu $t2, ($a3) -.L8026EDB8: - addi $a3, $a3, 2 - srl $t3, $t2, 0xc - andi $t2, $t2, 0xfff - sub $t1, $a1, $t2 - addi $t3, $t3, 3 -.L8026EDCC: - lb $t2, -1($t1) - addi $t3, $t3, -1 - addi $t1, $t1, 1 - addi $a1, $a1, 1 - bnez $t3, .L8026EDCC - sb $t2, -1($a1) -.L8026EDE4: - sll $t0, $t0, 1 - bne $a1, $t8, .L8026ED80 - addi $a2, $a2, -1 - jr $ra +#define shift $t9 +#define len_add $t8 +#define match_len $t7 +#define match_off $t6 +#define match_combo $t5 +#define off_nibble $t4 + + .section .text.lz4t_unpack_fast + .p2align 5 + .globl lz4t_unpack_fast + .func lz4t_unpack_fast + .set at + .set noreorder + +lz4t_unpack_fast: + addiu $sp, $sp, -0x40 + sw $ra, 0x14($sp) + sw $s0, 0x18($sp) + sw $s1, 0x1c($sp) + sw $s2, 0x20($sp) + sw $s3, 0x24($sp) + sw $s4, 0x28($sp) + sw $s5, 0x2C($sp) + sw $s6, 0x30($sp) + sw $s7, 0x34($sp) + sw $s8, 0x38($sp) + + move $s0, $a0 + lw $s1, 12($a0) + move $s2, $a1 + move dma_ctx, $a2 + lbu match_combo_mask, 8($a0) + sll match_combo_mask, 28 + lbu match_min, 9($a0) + + move dma_ptr, $a0 + addiu $s0, 16 + +.Lloop: + sub $t0, inbuf, dma_ptr # check if we need to wait for dma + bgezal $t0, dma_async_ctx_read # if inbuf >= dma_ptr, wait for dma + move $a0, dma_ctx + + bnez nibbles, .Lprocess_nibbles + li match_lim, 7 + +.Lload_nibbles: + lwl nibbles, 0(inbuf) + lwr nibbles, 3(inbuf) + beqz nibbles, .Lend + add inbuf, 4 + +.Lprocess_nibbles: + bgez nibbles, .Lmatches + srl len, nibbles, 28 + +.Lliterals: + andi len, 7 + beqz len, .Llarge_literals nop -#else - lw $t8, 4($a0) - lw $a3, 8($a0) - lw $t9, 0xc($a0) - move $a2, $zero - add $t8, $t8, $a1 - add $a3, $a3, $a0 - add $t9, $t9, $a0 - addi $a0, $a0, 0x10 -.L8027EF50: - bnez $a2, .L8027EF64 + +.Lsmall_literal: + ldl $t0, 0(inbuf) + ldr $t0, 7(inbuf) + add inbuf, len + sdl $t0, 0(outbuf) + sdr $t0, 7(outbuf) + sll nibbles, 4 + beq len, match_lim, .Lloop + add outbuf, len + +.Lmatches_ex: + sub $t0, inbuf, dma_ptr # check if we need to wait for dma + bgezal $t0, dma_async_ctx_read # if inbuf >= dma_ptr, wait for dma + move $a0, dma_ctx + + bnez nibbles, .Lprocess_ex_match_nibble + li match_lim, 15 + +.Lload_nibbles2: + lwl nibbles, 0(inbuf) + lwr nibbles, 3(inbuf) + beqz nibbles, .Lend + add inbuf, 4 + +.Lprocess_ex_match_nibble: + srl len, nibbles, 28 + +.Lmatches: + lwl match_combo, 0(inbuf) + lwr match_combo, 3(inbuf) + addiu inbuf, 2 + srl match_off, match_combo, 16 + + beqz match_combo_mask, .Lfull_offset + sll nibbles, 4 + srl nibbles, 4 + and off_nibble, match_combo, match_combo_mask + or nibbles, off_nibble + andi match_off, 0xfff +.Lfull_offset: + + bne len, match_lim, .Lmatch + addu match_len, len, match_min + + # len is sign extended match_combo[8:15] + sll match_combo, 16 + sra len, match_combo, 24 + add inbuf, 1 + bltzal len, .Lread_large_amount + andi len, 0x7f + + add match_len, len + +.Lmatch: + ble match_off, match_len, .Lmatch1_loop # check if we can do 8-byte copy + sub v0_st, outbuf, match_off # calculate start of match +.Lmatch8_loop: # 8-byte copy loop + ldl $t0, -1(v0_st) # load 8 bytes + ldr $t0, 6(v0_st) + addiu v0_st, 8 + sdl $t0, 0(outbuf) # store 8 bytes + sdr $t0, 7(outbuf) + addiu match_len, -8 + bgtz match_len, .Lmatch8_loop # check we went past match_len + addiu outbuf, 8 + b .Lloop # jump to main loop + addu outbuf, match_len # adjust pointer remove extra bytes + +.Lmatch1_loop: # 1-byte copy loop + lbu $t0, -1(v0_st) # load 1 byte + addiu v0_st, 1 + sb $t0, 0(outbuf) # store 1 byte + addiu match_len, -1 + bgtz match_len, .Lmatch1_loop # check we went past match_len + addiu outbuf, 1 + b .Lloop # jump to main loop nop - lw $t0, ($a0) - li $a2, 32 - addi $a0, $a0, 4 -.L8027EF64: - slt $t1, $t0, $zero - beqz $t1, .L8027EF88 - nop - lb $t2, ($t9) - addi $t9, $t9, 1 - sb $t2, ($a1) - addi $a1, $a1, 1 - b .L8027EFBC - nop -.L8027EF88: - lhu $t2, ($a3) - addi $a3, $a3, 2 - srl $t3, $t2, 0xc - andi $t2, $t2, 0xfff - sub $t1, $a1, $t2 - addi $t3, $t3, 3 -.L8027EFA0: - lb $t2, -1($t1) - addi $t3, $t3, -1 - addi $t1, $t1, 1 - sb $t2, ($a1) - addi $a1, $a1, 1 - bnez $t3, .L8027EFA0 - nop -.L8027EFBC: - sll $t0, $t0, 1 - addi $a2, $a2, -1 - bne $a1, $t8, .L8027EF50 - nop - jr $ra - nop -#endif + +.Llarge_literals: + lb len, 0(inbuf) + add inbuf, 1 + bltzal len, .Lread_large_amount + andi len, 0x7f + + move v0_st, inbuf # store start of literals into v0_st + addiu len, 22 + add inbuf, len # advance inbuf to end of literals +.Lcopy_lit: + sub $t0, v0_st, dma_ptr # check if all the literals have been DMA'd + bgezal $t0, dma_async_ctx_read # if not, wait for DMA + move $a0, dma_ctx + ldl $t0, 0(v0_st) # load 8 bytes of literals + ldr $t0, 7(v0_st) + addiu v0_st, 8 + sdl $t0, 0(outbuf) # store 8 bytes of literals + sdr $t0, 7(outbuf) + addiu len, -8 + bgez len, .Lcopy_lit # check if we went past the end of literals + addiu outbuf, 8 + addu outbuf, len # adjust outbuf to roll back extra copied bytes + + b .Lmatches_ex + sll nibbles, 4 + +.Lend: + lw $ra, 0x14($sp) + lw $s0, 0x18($sp) + lw $s1, 0x1c($sp) + lw $s2, 0x20($sp) + lw $s3, 0x24($sp) + lw $s4, 0x28($sp) + lw $s5, 0x2C($sp) + lw $s6, 0x30($sp) + lw $s7, 0x34($sp) + lw $s8, 0x38($sp) + jr $ra + addiu $sp, $sp, 0x40 + +.Lread_large_amount: + li shift, 7 +.Lread_large_amount_loop: + lb $t0, 0(inbuf) + add inbuf, 1 + andi $t1, $t0, 0x7f + sllv $t1, $t1, shift + or len, $t1 + bltz $t0, .Lread_large_amount_loop + add shift, 7 + jr $ra + nop + +.endfunc diff --git a/sm64.ld b/sm64.ld index a6b1254a..21c1cf7d 100755 --- a/sm64.ld +++ b/sm64.ld @@ -46,7 +46,8 @@ SECTIONS BEGIN_SEG(main, .) { BUILD_DIR/asm/entry.o(.text); - BUILD_DIR/asm/decompress.o(.text); + BUILD_DIR/asm/decompress.o(.text*); + BUILD_DIR/src/game/dma_async.o(.text); BUILD_DIR/src/game*.o(.text); AUDIO_DIR*.o(.text); ULTRA_BUILD_DIR/libgultra_rom.a:*.o(.text); @@ -55,6 +56,7 @@ SECTIONS BUILD_DIR/asm/entry.o(.data*); BUILD_DIR/asm/decompress.o(.data*); + BUILD_DIR/src/game/dma_async.o(.data); BUILD_DIR/src/game*.o(.data*); AUDIO_DIR*.o(.data*); ULTRA_BUILD_DIR/libgultra_rom.a:*.o(.data*); @@ -63,6 +65,7 @@ SECTIONS BUILD_DIR/asm/entry.o(.rodata*); BUILD_DIR/asm/decompress.o(.rodata*); + BUILD_DIR/src/game/dma_async.o(.rodata); BUILD_DIR/src/game*.o(.rodata*); AUDIO_DIR*.o(.rodata*); ULTRA_BUILD_DIR/libgultra_rom.a:*.o(.rodata*); diff --git a/src/buffers/buffers.c b/src/buffers/buffers.c index 908e9a3d..4c80167b 100644 --- a/src/buffers/buffers.c +++ b/src/buffers/buffers.c @@ -2,8 +2,6 @@ #include "buffers.h" -ALIGNED8 u8 gDecompressionHeap[0xD000]; - #if defined(VERSION_EU) ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(0x31200) - 0x3800]; #elif defined(VERSION_SH) diff --git a/src/buffers/buffers.h b/src/buffers/buffers.h index 9adcc142..d8bdf659 100644 --- a/src/buffers/buffers.h +++ b/src/buffers/buffers.h @@ -7,8 +7,6 @@ #include "engine/game_init.h" #include "config.h" -extern u8 gDecompressionHeap[]; - extern u8 gAudioHeap[]; extern u8 gAudioSPTaskYieldBuffer[]; diff --git a/src/engine/level_script.c b/src/engine/level_script.c index 1d0e7bbd..ad374735 100644 --- a/src/engine/level_script.c +++ b/src/engine/level_script.c @@ -297,7 +297,7 @@ static void level_cmd_load_mario_head(void) { } static void level_cmd_load_mio0_texture(void) { - load_segment_decompress_heap(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8)); + load_segment_decompress(CMD_GET(s16, 2), CMD_GET(void *, 4), CMD_GET(void *, 8)); sCurrentCmd = CMD_NEXT; } diff --git a/src/game/decompress.h b/src/game/decompress.h index 653b1cd1..f3ad54a2 100644 --- a/src/game/decompress.h +++ b/src/game/decompress.h @@ -1,6 +1,7 @@ #ifndef DECOMPRESS_H #define DECOMPRESS_H -void decompress(void *mio0, void *dest); +void lz4t_unpack_fast(const uint8_t* restrict inbuf, uint8_t* restrict dst, DMAAsyncCtx *ctx); +#define DMA_ASYNC_HEADER_SIZE 16 #endif // DECOMPRESS_H diff --git a/src/game/dma_async.c b/src/game/dma_async.c new file mode 100644 index 00000000..00ece2c5 --- /dev/null +++ b/src/game/dma_async.c @@ -0,0 +1,42 @@ +#include "dma_async.h" +#include "game/main.h" + +#define ALIGN16(val) (((val) + 0xF) & ~0xF) + +void dma_async_ctx_init(DMAAsyncCtx* ctx, u8 *dest, u8 *srcStart, u8 *srcEnd) { + u32 size = ALIGN16(srcEnd - srcStart); + osInvalDCache(dest, size); + + u32 copySize = (size >= 0x1000) ? 0x1000 : size; + + osPiStartDma(&gDmaIoMesg, OS_MESG_PRI_NORMAL, OS_READ, (uintptr_t) srcStart, dest, copySize, &gDmaMesgQueue); + + dest += copySize; + srcStart += copySize; + size -= copySize; + + ctx->srcStart = srcStart; + ctx->dest = dest; + ctx->size = size; +} + +void* dma_async_ctx_read(DMAAsyncCtx* ctx) { + // wait for the previous DMA issued + osRecvMesg(&gDmaMesgQueue, &gMainReceivedMesg, OS_MESG_BLOCK); + + // start the new DMA transfer + u32 copySize = (ctx->size >= 0x1000) ? 0x1000 : ctx->size; + if (copySize == 0) { + // we are done, return a dummy address that is so gigantic that we will never be called again + return (void*) 0x80800000; + } + osPiStartDma(&gDmaIoMesg, OS_MESG_PRI_NORMAL, OS_READ, (uintptr_t) ctx->srcStart, ctx->dest, copySize, &gDmaMesgQueue); + + const u32 margin = 16; + void* ret = ctx->dest - margin; + ctx->dest += copySize; + ctx->srcStart += copySize; + ctx->size -= copySize; + + return ret; +} diff --git a/src/game/dma_async.h b/src/game/dma_async.h new file mode 100644 index 00000000..ff71e717 --- /dev/null +++ b/src/game/dma_async.h @@ -0,0 +1,15 @@ +#pragma once + +#include "types.h" + +typedef struct { + u8* srcStart; + u8* dest; + u32 size; +} DMAAsyncCtx; + +// Starts to DMA the first block +void dma_async_ctx_init(DMAAsyncCtx* ctx, u8 *dest, u8 *srcStart, u8 *srcEnd); + +// Starts to DMA the next block and waits for the previous block +void* dma_async_ctx_read(DMAAsyncCtx* ctx); diff --git a/src/game/libgcc.c b/src/game/libgcc.c index 16adaf37..f387f207 100644 --- a/src/game/libgcc.c +++ b/src/game/libgcc.c @@ -183,6 +183,35 @@ int __ucmpdi2(unsigned long long a, unsigned long long b) { return (a < b) ? 0 : 2; } +// Taken from LLVM + +typedef union { + u64 all; + struct { + u32 high; + u32 low; + } s; +} UdWords; + +s64 __lshrdi3(s64 a, s32 b) { + const s32 bits_in_word = (s32)(sizeof(s32) * 8); + UdWords input; + UdWords result; + input.all = a; + if (b & bits_in_word) /* bits_in_word <= b < bits_in_dword */ { + result.s.high = 0; + result.s.low = input.s.high >> (b - bits_in_word); + } else /* 0 <= b < bits_in_word */ { + if (b == 0) { + return a; + } + + result.s.high = input.s.high >> b; + result.s.low = (input.s.high << (bits_in_word - b)) | (input.s.low >> b); + } + return result.all; +} + // Compute division and modulo of 64-bit signed and unsigned integers __asm__(" \n\ diff --git a/src/game/memory.c b/src/game/memory.c index fa12ae0d..a6b2252b 100644 --- a/src/game/memory.c +++ b/src/game/memory.c @@ -6,6 +6,7 @@ #include "buffers/zbuffer.h" #include "buffers/buffers.h" +#include "dma_async.h" #include "decompress.h" #include "engine/game_init.h" #include "main.h" @@ -342,7 +343,10 @@ void *load_segment_decompress(s32 segment, u8 *srcStart, u8 *srcEnd) { dest = main_pool_alloc(*size, MEMORY_POOL_LEFT); if (dest != NULL) { CN_DEBUG_PRINTF(("start decompress\n")); - decompress(compressed, dest); + dma_read(compressed, srcStart, srcStart + DMA_ASYNC_HEADER_SIZE); + DMAAsyncCtx asyncCtx; + dma_async_ctx_init(&asyncCtx, compressed + DMA_ASYNC_HEADER_SIZE, srcStart + DMA_ASYNC_HEADER_SIZE, srcEnd); + lz4t_unpack_fast(compressed, dest, &asyncCtx); CN_DEBUG_PRINTF(("end decompress\n")); set_segment_base_addr(segment, dest); @@ -354,22 +358,6 @@ void *load_segment_decompress(s32 segment, u8 *srcStart, u8 *srcEnd) { return dest; } -void *load_segment_decompress_heap(u32 segment, u8 *srcStart, u8 *srcEnd) { - UNUSED void *dest = NULL; - u32 compSize = ALIGN16(srcEnd - srcStart); - u8 *compressed = main_pool_alloc(compSize, MEMORY_POOL_RIGHT); - UNUSED u32 *pUncSize = (u32 *) (compressed + 4); - - if (compressed != NULL) { - dma_read(compressed, srcStart, srcEnd); - decompress(compressed, gDecompressionHeap); - set_segment_base_addr(segment, gDecompressionHeap); - main_pool_free(compressed); - } else { - } - return gDecompressionHeap; -} - #ifndef LIBDRAGON_IPL3 void load_engine_code_segment(void) { void *startAddr = (void *) _engineSegmentStart; diff --git a/tools/Makefile b/tools/Makefile index 41840771..6cb62633 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -7,7 +7,7 @@ CC := gcc CXX := g++ CFLAGS := -I . -I sm64tools -Wall -Wextra -Wno-unused-parameter -pedantic -O2 -s LDFLAGS := -lm -ALL_PROGRAMS := armips textconv patch_elf_32bit aifc_decode aiff_extract_codebook vadpcm_enc tabledesign extract_data_for_mio skyconv +ALL_PROGRAMS := armips textconv patch_elf_32bit aifc_decode aiff_extract_codebook vadpcm_enc tabledesign extract_data_for_mio skyconv lz4tpack LIBAUDIOFILE := audiofile/libaudiofile.a # Only build armips from tools if it is not found on the system @@ -19,6 +19,9 @@ endif default: all +lz4tpack_SOURCES := lz4tpack.c +lz4tpack_CFLAGS := -DLZ4T -flto -O3 -fwhole-program + textconv_SOURCES := textconv.c utf8.c hashtable.c patch_elf_32bit_SOURCES := patch_elf_32bit.c diff --git a/tools/lz4.c b/tools/lz4.c new file mode 100644 index 00000000..760254c5 --- /dev/null +++ b/tools/lz4.c @@ -0,0 +1,2839 @@ +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2023, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://www.lz4.org + - LZ4 source repository : https://github.com/lz4/lz4 +*/ + +/*-************************************ +* Tuning parameters +**************************************/ +/* + * LZ4_HEAPMODE : + * Select how stateless compression functions like `LZ4_compress_default()` + * allocate memory for their hash table, + * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). + */ +#ifndef LZ4_HEAPMODE +# define LZ4_HEAPMODE 0 +#endif + +/* + * LZ4_ACCELERATION_DEFAULT : + * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 + */ +#define LZ4_ACCELERATION_DEFAULT 1 +/* + * LZ4_ACCELERATION_MAX : + * Any "acceleration" value higher than this threshold + * get treated as LZ4_ACCELERATION_MAX instead (fix #876) + */ +#define LZ4_ACCELERATION_MAX 65537 + + +/*-************************************ +* CPU Feature Detection +**************************************/ +/* LZ4_FORCE_MEMORY_ACCESS + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method is portable but violate C standard. + * It can generate buggy code on targets which assembly generation depends on alignment. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */ +# if defined(__GNUC__) && \ + ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define LZ4_FORCE_MEMORY_ACCESS 2 +# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) || defined(_MSC_VER) +# define LZ4_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/* + * LZ4_FORCE_SW_BITCOUNT + * Define this parameter if your target system or compiler does not support hardware bit count + */ +#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */ +# undef LZ4_FORCE_SW_BITCOUNT /* avoid double def */ +# define LZ4_FORCE_SW_BITCOUNT +#endif + + + +/*-************************************ +* Dependency +**************************************/ +/* + * LZ4_SRC_INCLUDED: + * Amalgamation flag, whether lz4.c is included + */ +#ifndef LZ4_SRC_INCLUDED +# define LZ4_SRC_INCLUDED 1 +#endif + +#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS +# define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ +#endif + +#ifndef LZ4_STATIC_LINKING_ONLY +# define LZ4_STATIC_LINKING_ONLY +#endif +#include "lz4.h" +/* see also "memory routines" below */ + + +/*-************************************ +* Compiler Options +**************************************/ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) /* Visual Studio 2005+ */ +# include /* only present in VS2005+ */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */ +# pragma warning(disable : 6239) /* disable: C6239: ( && ) always evaluates to the result of */ +# pragma warning(disable : 6240) /* disable: C6240: ( && ) always evaluates to the result of */ +# pragma warning(disable : 6326) /* disable: C6326: Potential comparison of a constant with another constant */ +#endif /* _MSC_VER */ + +#ifndef LZ4_FORCE_INLINE +# if defined (_MSC_VER) && !defined (__clang__) /* MSVC */ +# define LZ4_FORCE_INLINE static __forceinline +# else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# if defined (__GNUC__) || defined (__clang__) +# define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define LZ4_FORCE_INLINE static inline +# endif +# else +# define LZ4_FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +# endif /* _MSC_VER */ +#endif /* LZ4_FORCE_INLINE */ + +/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE + * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8, + * together with a simple 8-byte copy loop as a fall-back path. + * However, this optimization hurts the decompression speed by >30%, + * because the execution does not go to the optimized loop + * for typical compressible data, and all of the preamble checks + * before going to the fall-back path become useless overhead. + * This optimization happens only with the -O3 flag, and -O2 generates + * a simple 8-byte copy loop. + * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8 + * functions are annotated with __attribute__((optimize("O2"))), + * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute + * of LZ4_wildCopy8 does not affect the compression speed. + */ +#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__) +# define LZ4_FORCE_O2 __attribute__((optimize("O2"))) +# undef LZ4_FORCE_INLINE +# define LZ4_FORCE_INLINE static __inline __attribute__((optimize("O2"),always_inline)) +#else +# define LZ4_FORCE_O2 +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) +# define expect(expr,value) (__builtin_expect ((expr),(value)) ) +#else +# define expect(expr,value) (expr) +#endif + +#ifndef likely +#define likely(expr) expect((expr) != 0, 1) +#endif +#ifndef unlikely +#define unlikely(expr) expect((expr) != 0, 0) +#endif + +/* Should the alignment test prove unreliable, for some reason, + * it can be disabled by setting LZ4_ALIGN_TEST to 0 */ +#ifndef LZ4_ALIGN_TEST /* can be externally provided */ +# define LZ4_ALIGN_TEST 1 +#endif + + +/*-************************************ +* Memory routines +**************************************/ + +/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION : + * Disable relatively high-level LZ4/HC functions that use dynamic memory + * allocation functions (malloc(), calloc(), free()). + * + * Note that this is a compile-time switch. And since it disables + * public/stable LZ4 v1 API functions, we don't recommend using this + * symbol to generate a library for distribution. + * + * The following public functions are removed when this symbol is defined. + * - lz4 : LZ4_createStream, LZ4_freeStream, + * LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated) + * - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC, + * LZ4_createHC (deprecated), LZ4_freeHC (deprecated) + * - lz4frame, lz4file : All LZ4F_* functions + */ +#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +# define ALLOC(s) lz4_error_memory_allocation_is_disabled +# define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled +# define FREEMEM(p) lz4_error_memory_allocation_is_disabled +#elif defined(LZ4_USER_MEMORY_FUNCTIONS) +/* memory management functions can be customized by user project. + * Below functions must exist somewhere in the Project + * and be available at link time */ +void* LZ4_malloc(size_t s); +void* LZ4_calloc(size_t n, size_t s); +void LZ4_free(void* p); +# define ALLOC(s) LZ4_malloc(s) +# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s) +# define FREEMEM(p) LZ4_free(p) +#else +# include /* malloc, calloc, free */ +# define ALLOC(s) malloc(s) +# define ALLOC_AND_ZERO(s) calloc(1,s) +# define FREEMEM(p) free(p) +#endif + +#if ! LZ4_FREESTANDING +# include /* memset, memcpy */ +#endif +#if !defined(LZ4_memset) +# define LZ4_memset(p,v,s) memset((p),(v),(s)) +#endif +#define MEM_INIT(p,v,s) LZ4_memset((p),(v),(s)) + + +/*-************************************ +* Common Constants +**************************************/ +#ifdef LZ4T +extern int LZ4T_minMatch; +#define MINMATCH LZ4T_minMatch +#else +#define MINMATCH 4 +#endif + +#define WILDCOPYLENGTH 8 +#ifndef LZ4T +#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ +#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ +#else +#define LASTLITERALS 1 +#define MFLIMIT 8 +#endif +#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */ +#define FASTLOOP_SAFE_DISTANCE 64 +static const int LZ4_minLength = (MFLIMIT+1); + +#define KB *(1 <<10) +#define MB *(1 <<20) +#define GB *(1U<<30) + +#define LZ4_DISTANCE_ABSOLUTE_MAX 65535 +#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */ +# error "LZ4_DISTANCE_MAX is too big : must be <= 65535" +#endif + +#define ML_BITS 4 +#define ML_MASK ((1U<=1) +# include +#else +# ifndef assert +# define assert(condition) ((void)0) +# endif +#endif + +#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */ + +#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) +# include + static int g_debuglog_enable = 1; +# define DEBUGLOG(l, ...) { \ + if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ + fprintf(stderr, __FILE__ " %i: ", __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " \n"); \ + } } +#else +# define DEBUGLOG(l, ...) {} /* disabled */ +#endif + +static int LZ4_isAligned(const void* ptr, size_t alignment) +{ + return ((size_t)ptr & (alignment -1)) == 0; +} + + +/*-************************************ +* Types +**************************************/ +#include +#if defined(__cplusplus) || (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; + typedef uintptr_t uptrval; +#else +# if UINT_MAX != 4294967295UL +# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4" +# endif + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; + typedef size_t uptrval; /* generally true, except OpenVMS-64 */ +#endif + +#if defined(__x86_64__) + typedef U64 reg_t; /* 64-bits in x32 mode */ +#else + typedef size_t reg_t; /* 32-bits in x32 mode */ +#endif + +typedef enum { + notLimited = 0, + limitedOutput = 1, + fillOutput = 2 +} limitedOutput_directive; + + +/*-************************************ +* Reading and writing into memory +**************************************/ + +/** + * LZ4 relies on memcpy with a constant size being inlined. In freestanding + * environments, the compiler can't assume the implementation of memcpy() is + * standard compliant, so it can't apply its specialized memcpy() inlining + * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze + * memcpy() as if it were standard compliant, so it can inline it in freestanding + * environments. This is needed when decompressing the Linux Kernel, for example. + */ +#if !defined(LZ4_memcpy) +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size) +# else +# define LZ4_memcpy(dst, src, size) memcpy(dst, src, size) +# endif +#endif + +#if !defined(LZ4_memmove) +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4_memmove __builtin_memmove +# else +# define LZ4_memmove memmove +# endif +#endif + +static unsigned LZ4_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) +#define LZ4_PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) +#elif defined(_MSC_VER) +#define LZ4_PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop)) +#endif + +#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) +/* lie to the compiler about data alignment; use with caution */ + +static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; } +static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; } +static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; } + +static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } +static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } + +#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +LZ4_PACK(typedef struct { U16 u16; }) LZ4_unalign16; +LZ4_PACK(typedef struct { U32 u32; }) LZ4_unalign32; +LZ4_PACK(typedef struct { reg_t uArch; }) LZ4_unalignST; + +static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign16*)ptr)->u16; } +static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign32*)ptr)->u32; } +static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalignST*)ptr)->uArch; } + +static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign16*)memPtr)->u16 = value; } +static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign32*)memPtr)->u32 = value; } + +#else /* safe and portable access using memcpy() */ + +static U16 LZ4_read16(const void* memPtr) +{ + U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static U32 LZ4_read32(const void* memPtr) +{ + U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static reg_t LZ4_read_ARCH(const void* memPtr) +{ + reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static void LZ4_write16(void* memPtr, U16 value) +{ + LZ4_memcpy(memPtr, &value, sizeof(value)); +} + +static void LZ4_write32(void* memPtr, U32 value) +{ + LZ4_memcpy(memPtr, &value, sizeof(value)); +} + +#endif /* LZ4_FORCE_MEMORY_ACCESS */ + + +static U16 LZ4_readLE16(const void* memPtr) +{ + if (LZ4_isLittleEndian()) { + return LZ4_read16(memPtr); + } else { + const BYTE* p = (const BYTE*)memPtr; + return (U16)((U16)p[0] | (p[1]<<8)); + } +} + +#ifdef LZ4_STATIC_LINKING_ONLY_ENDIANNESS_INDEPENDENT_OUTPUT +static U32 LZ4_readLE32(const void* memPtr) +{ + if (LZ4_isLittleEndian()) { + return LZ4_read32(memPtr); + } else { + const BYTE* p = (const BYTE*)memPtr; + return (U32)p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); + } +} +#endif + +static void LZ4_writeLE16(void* memPtr, U16 value) +{ + if (LZ4_isLittleEndian()) { + LZ4_write16(memPtr, value); + } else { + BYTE* p = (BYTE*)memPtr; + p[0] = (BYTE) value; + p[1] = (BYTE)(value>>8); + } +} + +/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ +LZ4_FORCE_INLINE +void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd) +{ + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* const e = (BYTE*)dstEnd; + + do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */ +LZ4_FORCE_INLINE void +LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) +{ + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* const e = (BYTE*)dstEnd; + + do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH + * - there is at least 12 bytes available to write after dstEnd */ +LZ4_FORCE_INLINE void +LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) +{ + BYTE v[8]; + + assert(dstEnd >= dstPtr + MINMATCH); + + switch(offset) { + case 1: + MEM_INIT(v, *srcPtr, 8); + break; + case 2: + LZ4_memcpy(v, srcPtr, 2); + LZ4_memcpy(&v[2], srcPtr, 2); +#if defined(_MSC_VER) && (_MSC_VER <= 1937) /* MSVC 2022 ver 17.7 or earlier */ +# pragma warning(push) +# pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */ +#endif + LZ4_memcpy(&v[4], v, 4); +#if defined(_MSC_VER) && (_MSC_VER <= 1937) /* MSVC 2022 ver 17.7 or earlier */ +# pragma warning(pop) +#endif + break; + case 4: + LZ4_memcpy(v, srcPtr, 4); + LZ4_memcpy(&v[4], srcPtr, 4); + break; + default: + LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset); + return; + } + + LZ4_memcpy(dstPtr, v, 8); + dstPtr += 8; + while (dstPtr < dstEnd) { + LZ4_memcpy(dstPtr, v, 8); + dstPtr += 8; + } +} +#endif + + +/*-************************************ +* Common functions +**************************************/ +static unsigned LZ4_NbCommonBytes (reg_t val) +{ + assert(val != 0); + if (LZ4_isLittleEndian()) { + if (sizeof(val) == 8) { +# if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT) +/*-************************************************************************************************* +* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11. +* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics +* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC. +****************************************************************************************************/ +# if defined(__clang__) && (__clang_major__ < 10) + /* Avoid undefined clang-cl intrinsics issue. + * See https://github.com/lz4/lz4/pull/1017 for details. */ + return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3; +# else + /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */ + return (unsigned)_tzcnt_u64(val) >> 3; +# endif +# elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanForward64(&r, (U64)val); + return (unsigned)r >> 3; +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_ctzll((U64)val) >> 3; +# else + const U64 m = 0x0101010101010101ULL; + val ^= val - 1; + return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56); +# endif + } else /* 32 bits */ { +# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanForward(&r, (U32)val); + return (unsigned)r >> 3; +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_ctz((U32)val) >> 3; +# else + const U32 m = 0x01010101; + return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24; +# endif + } + } else /* Big Endian CPU */ { + if (sizeof(val)==8) { +# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_clzll((U64)val) >> 3; +# else +#if 1 + /* this method is probably faster, + * but adds a 128 bytes lookup table */ + static const unsigned char ctz7_tab[128] = { + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + }; + U64 const mask = 0x0101010101010101ULL; + U64 const t = (((val >> 8) - mask) | val) & mask; + return ctz7_tab[(t * 0x0080402010080402ULL) >> 57]; +#else + /* this method doesn't consume memory space like the previous one, + * but it contains several branches, + * that may end up slowing execution */ + static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits. + Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. + Note that this code path is never triggered in 32-bits mode. */ + unsigned r; + if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; } + if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } + r += (!val); + return r; +#endif +# endif + } else /* 32 bits */ { +# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_clz((U32)val) >> 3; +# else + val >>= 8; + val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) | + (val + 0x00FF0000)) >> 24; + return (unsigned)val ^ 3; +# endif + } + } +} + + +#define STEPSIZE sizeof(reg_t) +LZ4_FORCE_INLINE +unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) +{ + const BYTE* const pStart = pIn; + + if (likely(pIn < pInLimit-(STEPSIZE-1))) { + reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); + if (!diff) { + pIn+=STEPSIZE; pMatch+=STEPSIZE; + } else { + return LZ4_NbCommonBytes(diff); + } } + + while (likely(pIn < pInLimit-(STEPSIZE-1))) { + reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); + if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; } + pIn += LZ4_NbCommonBytes(diff); + return (unsigned)(pIn - pStart); + } + + if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; } + if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; } + if ((pIn compression run slower on incompressible data */ + + +/*-************************************ +* Local Structures and types +**************************************/ +typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t; + +/** + * This enum distinguishes several different modes of accessing previous + * content in the stream. + * + * - noDict : There is no preceding content. + * - withPrefix64k : Table entries up to ctx->dictSize before the current blob + * blob being compressed are valid and refer to the preceding + * content (of length ctx->dictSize), which is available + * contiguously preceding in memory the content currently + * being compressed. + * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere + * else in memory, starting at ctx->dictionary with length + * ctx->dictSize. + * - usingDictCtx : Everything concerning the preceding content is + * in a separate context, pointed to by ctx->dictCtx. + * ctx->dictionary, ctx->dictSize, and table entries + * in the current context that refer to positions + * preceding the beginning of the current compression are + * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx + * ->dictSize describe the location and size of the preceding + * content, and matches are found by looking in the ctx + * ->dictCtx->hashTable. + */ +typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive; +typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; + + +/*-************************************ +* Local Utils +**************************************/ +int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } +const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } +int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } +int LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); } + + +/*-**************************************** +* Internal Definitions, used only in Tests +*******************************************/ +#if defined (__cplusplus) +extern "C" { +#endif + +int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize); + +int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, + int compressedSize, int maxOutputSize, + const void* dictStart, size_t dictSize); +int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, + int compressedSize, int targetOutputSize, int dstCapacity, + const void* dictStart, size_t dictSize); +#if defined (__cplusplus) +} +#endif + +/*-****************************** +* Compression functions +********************************/ +LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType) +{ + if (tableType == byU16) + return ((sequence * 2654435761U) >> ((4*8)-(LZ4_HASHLOG+1))); + else + return ((sequence * 2654435761U) >> ((4*8)-LZ4_HASHLOG)); +} + +LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType) +{ + const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; + if (LZ4_isLittleEndian()) { + const U64 prime5bytes = 889523592379ULL; + return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); + } else { + const U64 prime8bytes = 11400714785074694791ULL; + return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); + } +} + +LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) +{ + if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); + +#ifdef LZ4_STATIC_LINKING_ONLY_ENDIANNESS_INDEPENDENT_OUTPUT + return LZ4_hash4(LZ4_readLE32(p), tableType); +#else + return LZ4_hash4(LZ4_read32(p), tableType); +#endif +} + +LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) +{ + switch (tableType) + { + default: /* fallthrough */ + case clearedTable: { /* illegal! */ assert(0); return; } + case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; } + case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; } + } +} + +LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) +{ + switch (tableType) + { + default: /* fallthrough */ + case clearedTable: /* fallthrough */ + case byPtr: { /* illegal! */ assert(0); return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; } + case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; } + } +} + +/* LZ4_putPosition*() : only used in byPtr mode */ +LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h, + void* tableBase, tableType_t const tableType) +{ + const BYTE** const hashTable = (const BYTE**)tableBase; + assert(tableType == byPtr); (void)tableType; + hashTable[h] = p; +} + +LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType) +{ + U32 const h = LZ4_hashPosition(p, tableType); + LZ4_putPositionOnHash(p, h, tableBase, tableType); +} + +/* LZ4_getIndexOnHash() : + * Index of match position registered in hash table. + * hash position must be calculated by using base+index, or dictBase+index. + * Assumption 1 : only valid if tableType == byU32 or byU16. + * Assumption 2 : h is presumed valid (within limits of hash table) + */ +LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) +{ + LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2); + if (tableType == byU32) { + const U32* const hashTable = (const U32*) tableBase; + assert(h < (1U << (LZ4_MEMORY_USAGE-2))); + return hashTable[h]; + } + if (tableType == byU16) { + const U16* const hashTable = (const U16*) tableBase; + assert(h < (1U << (LZ4_MEMORY_USAGE-1))); + return hashTable[h]; + } + assert(0); return 0; /* forbidden case */ +} + +static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType) +{ + assert(tableType == byPtr); (void)tableType; + { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; } +} + +LZ4_FORCE_INLINE const BYTE* +LZ4_getPosition(const BYTE* p, + const void* tableBase, tableType_t tableType) +{ + U32 const h = LZ4_hashPosition(p, tableType); + return LZ4_getPositionOnHash(h, tableBase, tableType); +} + +LZ4_FORCE_INLINE void +LZ4_prepareTable(LZ4_stream_t_internal* const cctx, + const int inputSize, + const tableType_t tableType) { + /* If the table hasn't been used, it's guaranteed to be zeroed out, and is + * therefore safe to use no matter what mode we're in. Otherwise, we figure + * out if it's safe to leave as is or whether it needs to be reset. + */ + if ((tableType_t)cctx->tableType != clearedTable) { + assert(inputSize >= 0); + if ((tableType_t)cctx->tableType != tableType + || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU) + || ((tableType == byU32) && cctx->currentOffset > 1 GB) + || tableType == byPtr + || inputSize >= 4 KB) + { + DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx); + MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE); + cctx->currentOffset = 0; + cctx->tableType = (U32)clearedTable; + } else { + DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)"); + } + } + + /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, + * is faster than compressing without a gap. + * However, compressing with currentOffset == 0 is faster still, + * so we preserve that case. + */ + if (cctx->currentOffset != 0 && tableType == byU32) { + DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset"); + cctx->currentOffset += 64 KB; + } + + /* Finally, clear history */ + cctx->dictCtx = NULL; + cctx->dictionary = NULL; + cctx->dictSize = 0; +} + +/** LZ4_compress_generic_validated() : + * inlined, to ensure branches are decided at compilation time. + * The following conditions are presumed already validated: + * - source != NULL + * - inputSize > 0 + */ +LZ4_FORCE_INLINE int LZ4_compress_generic_validated( + LZ4_stream_t_internal* const cctx, + const char* const source, + char* const dest, + const int inputSize, + int* inputConsumed, /* only written when outputDirective == fillOutput */ + const int maxOutputSize, + const limitedOutput_directive outputDirective, + const tableType_t tableType, + const dict_directive dictDirective, + const dictIssue_directive dictIssue, + const int acceleration) +{ + int result; + const BYTE* ip = (const BYTE*)source; + + U32 const startIndex = cctx->currentOffset; + const BYTE* base = (const BYTE*)source - startIndex; + const BYTE* lowLimit; + + const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx; + const BYTE* const dictionary = + dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary; + const U32 dictSize = + dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize; + const U32 dictDelta = + (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with indexes in current context */ + + int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); + U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ + const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary; + const BYTE* anchor = (const BYTE*) source; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1; + const BYTE* const matchlimit = iend - LASTLITERALS; + + /* the dictCtx currentOffset is indexed on the start of the dictionary, + * while a dictionary in the current context precedes the currentOffset */ + const BYTE* dictBase = (dictionary == NULL) ? NULL : + (dictDirective == usingDictCtx) ? + dictionary + dictSize - dictCtx->currentOffset : + dictionary + dictSize - startIndex; + + BYTE* op = (BYTE*) dest; + BYTE* const olimit = op + maxOutputSize; + + U32 offset = 0; + U32 forwardH; + + DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType); + assert(ip != NULL); + if (tableType == byU16) assert(inputSize= 1); + + lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0); + + /* Update context state */ + if (dictDirective == usingDictCtx) { + /* Subsequent linked blocks can't use the dictionary. */ + /* Instead, they use the block we just compressed. */ + cctx->dictCtx = NULL; + cctx->dictSize = (U32)inputSize; + } else { + cctx->dictSize += (U32)inputSize; + } + cctx->currentOffset += (U32)inputSize; + cctx->tableType = (U32)tableType; + + if (inputSizehashTable, byPtr); + } else { + LZ4_putIndexOnHash(startIndex, h, cctx->hashTable, tableType); + } } + ip++; forwardH = LZ4_hashPosition(ip, tableType); + + /* Main Loop */ + for ( ; ; ) { + const BYTE* match; + BYTE* token; + const BYTE* filledIp; + + /* Find a match */ + if (tableType == byPtr) { + const BYTE* forwardIp = ip; + int step = 1; + int searchMatchNb = acceleration << LZ4_skipTrigger; + do { + U32 const h = forwardH; + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; + assert(ip < mflimitPlusOne); + + match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType); + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType); + + } while ( (match+LZ4_DISTANCE_MAX < ip) + || (LZ4_read32(match) != LZ4_read32(ip)) ); + + } else { /* byU32, byU16 */ + + const BYTE* forwardIp = ip; + int step = 1; + int searchMatchNb = acceleration << LZ4_skipTrigger; + do { + U32 const h = forwardH; + U32 const current = (U32)(forwardIp - base); + U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); + assert(matchIndex <= current); + assert(forwardIp - base < (ptrdiff_t)(2 GB - 1)); + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; + assert(ip < mflimitPlusOne); + + if (dictDirective == usingDictCtx) { + if (matchIndex < startIndex) { + /* there was no match, try the dictionary */ + assert(tableType == byU32); + matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); + match = dictBase + matchIndex; + matchIndex += dictDelta; /* make dictCtx index comparable with current context */ + lowLimit = dictionary; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; + } + } else if (dictDirective == usingExtDict) { + if (matchIndex < startIndex) { + DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex); + assert(startIndex - matchIndex >= MINMATCH); + assert(dictBase); + match = dictBase + matchIndex; + lowLimit = dictionary; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; + } + } else { /* single continuous memory segment */ + match = base + matchIndex; + } + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); + + DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex); + if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */ + assert(matchIndex < current); + if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX)) + && (matchIndex+LZ4_DISTANCE_MAX < current)) { + continue; + } /* too far */ + assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */ + + if (LZ4_read32(match) == LZ4_read32(ip)) { + if (maybe_extMem) offset = current - matchIndex; + break; /* match found */ + } + + } while(1); + } + + /* Catch up */ + filledIp = ip; + assert(ip > anchor); /* this is always true as ip has been advanced before entering the main loop */ + if ((match > lowLimit) && unlikely(ip[-1] == match[-1])) { + do { ip--; match--; } while (((ip > anchor) & (match > lowLimit)) && (unlikely(ip[-1] == match[-1]))); + } + + /* Encode Literals */ + { unsigned const litLength = (unsigned)(ip - anchor); + token = op++; + if ((outputDirective == limitedOutput) && /* Check output buffer overflow */ + (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) { + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + if ((outputDirective == fillOutput) && + (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) { + op--; + goto _last_literals; + } + if (litLength >= RUN_MASK) { + unsigned len = litLength - RUN_MASK; + *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; + *op++ = (BYTE)len; + } + else *token = (BYTE)(litLength< olimit)) { + /* the match was too close to the end, rewind and go to last literals */ + op = token; + goto _last_literals; + } + + /* Encode Offset */ + if (maybe_extMem) { /* static test */ + DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source)); + assert(offset <= LZ4_DISTANCE_MAX && offset > 0); + LZ4_writeLE16(op, (U16)offset); op+=2; + } else { + DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match)); + assert(ip-match <= LZ4_DISTANCE_MAX); + LZ4_writeLE16(op, (U16)(ip - match)); op+=2; + } + + /* Encode MatchLength */ + { unsigned matchCode; + + if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx) + && (lowLimit==dictionary) /* match within extDict */ ) { + const BYTE* limit = ip + (dictEnd-match); + assert(dictEnd > match); + if (limit > matchlimit) limit = matchlimit; + matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); + ip += (size_t)matchCode + MINMATCH; + if (ip==limit) { + unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit); + matchCode += more; + ip += more; + } + DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH); + } else { + matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); + ip += (size_t)matchCode + MINMATCH; + DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH); + } + + if ((outputDirective) && /* Check output buffer overflow */ + (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) { + if (outputDirective == fillOutput) { + /* Match description too long : reduce it */ + U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255; + ip -= matchCode - newMatchCode; + assert(newMatchCode < matchCode); + matchCode = newMatchCode; + if (unlikely(ip <= filledIp)) { + /* We have already filled up to filledIp so if ip ends up less than filledIp + * we have positions in the hash table beyond the current position. This is + * a problem if we reuse the hash table. So we have to remove these positions + * from the hash table. + */ + const BYTE* ptr; + DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip)); + for (ptr = ip; ptr <= filledIp; ++ptr) { + U32 const h = LZ4_hashPosition(ptr, tableType); + LZ4_clearHash(h, cctx->hashTable, tableType); + } + } + } else { + assert(outputDirective == limitedOutput); + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + } + if (matchCode >= ML_MASK) { + *token += ML_MASK; + matchCode -= ML_MASK; + LZ4_write32(op, 0xFFFFFFFF); + while (matchCode >= 4*255) { + op+=4; + LZ4_write32(op, 0xFFFFFFFF); + matchCode -= 4*255; + } + op += matchCode / 255; + *op++ = (BYTE)(matchCode % 255); + } else + *token += (BYTE)(matchCode); + } + /* Ensure we have enough space for the last literals. */ + assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit)); + + anchor = ip; + + /* Test end of chunk */ + if (ip >= mflimitPlusOne) break; + + /* Fill table */ + { U32 const h = LZ4_hashPosition(ip-2, tableType); + if (tableType == byPtr) { + LZ4_putPositionOnHash(ip-2, h, cctx->hashTable, byPtr); + } else { + U32 const idx = (U32)((ip-2) - base); + LZ4_putIndexOnHash(idx, h, cctx->hashTable, tableType); + } } + + /* Test next position */ + if (tableType == byPtr) { + + match = LZ4_getPosition(ip, cctx->hashTable, tableType); + LZ4_putPosition(ip, cctx->hashTable, tableType); + if ( (match+LZ4_DISTANCE_MAX >= ip) + && (LZ4_read32(match) == LZ4_read32(ip)) ) + { token=op++; *token=0; goto _next_match; } + + } else { /* byU32, byU16 */ + + U32 const h = LZ4_hashPosition(ip, tableType); + U32 const current = (U32)(ip-base); + U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); + assert(matchIndex < current); + if (dictDirective == usingDictCtx) { + if (matchIndex < startIndex) { + /* there was no match, try the dictionary */ + assert(tableType == byU32); + matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); + match = dictBase + matchIndex; + lowLimit = dictionary; /* required for match length counter */ + matchIndex += dictDelta; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; /* required for match length counter */ + } + } else if (dictDirective==usingExtDict) { + if (matchIndex < startIndex) { + assert(dictBase); + match = dictBase + matchIndex; + lowLimit = dictionary; /* required for match length counter */ + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; /* required for match length counter */ + } + } else { /* single memory segment */ + match = base + matchIndex; + } + LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); + assert(matchIndex < current); + if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1) + && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current)) + && (LZ4_read32(match) == LZ4_read32(ip)) ) { + token=op++; + *token=0; + if (maybe_extMem) offset = current - matchIndex; + DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", + (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source)); + goto _next_match; + } + } + + /* Prepare next loop */ + forwardH = LZ4_hashPosition(++ip, tableType); + + } + +_last_literals: + /* Encode Last Literals */ + { size_t lastRun = (size_t)(iend - anchor); + if ( (outputDirective) && /* Check output buffer overflow */ + (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) { + if (outputDirective == fillOutput) { + /* adapt lastRun to fill 'dst' */ + assert(olimit >= op); + lastRun = (size_t)(olimit-op) - 1/*token*/; + lastRun -= (lastRun + 256 - RUN_MASK) / 256; /*additional length tokens*/ + } else { + assert(outputDirective == limitedOutput); + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRun< 0); + DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result); + return result; +} + +/** LZ4_compress_generic() : + * inlined, to ensure branches are decided at compilation time; + * takes care of src == (NULL, 0) + * and forward the rest to LZ4_compress_generic_validated */ +LZ4_FORCE_INLINE int LZ4_compress_generic( + LZ4_stream_t_internal* const cctx, + const char* const src, + char* const dst, + const int srcSize, + int *inputConsumed, /* only written when outputDirective == fillOutput */ + const int dstCapacity, + const limitedOutput_directive outputDirective, + const tableType_t tableType, + const dict_directive dictDirective, + const dictIssue_directive dictIssue, + const int acceleration) +{ + DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i", + srcSize, dstCapacity); + + if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported srcSize, too large (or negative) */ + if (srcSize == 0) { /* src == NULL supported if srcSize == 0 */ + if (outputDirective != notLimited && dstCapacity <= 0) return 0; /* no output, can't write anything */ + DEBUGLOG(5, "Generating an empty block"); + assert(outputDirective == notLimited || dstCapacity >= 1); + assert(dst != NULL); + dst[0] = 0; + if (outputDirective == fillOutput) { + assert (inputConsumed != NULL); + *inputConsumed = 0; + } + return 1; + } + assert(src != NULL); + + return LZ4_compress_generic_validated(cctx, src, dst, srcSize, + inputConsumed, /* only written into if outputDirective == fillOutput */ + dstCapacity, outputDirective, + tableType, dictDirective, dictIssue, acceleration); +} + + +int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse; + assert(ctx != NULL); + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + if (maxOutputSize >= LZ4_compressBound(inputSize)) { + if (inputSize < LZ4_64Klimit) { + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration); + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + if (inputSize < LZ4_64Klimit) { + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } +} + +/** + * LZ4_compress_fast_extState_fastReset() : + * A variant of LZ4_compress_fast_extState(). + * + * Using this variant avoids an expensive initialization step. It is only safe + * to call if the state buffer is known to be correctly initialized already + * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of + * "correctly initialized"). + */ +int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) +{ + LZ4_stream_t_internal* const ctx = &((LZ4_stream_t*)state)->internal_donotuse; + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + assert(ctx != NULL); + + if (dstCapacity >= LZ4_compressBound(srcSize)) { + if (srcSize < LZ4_64Klimit) { + const tableType_t tableType = byU16; + LZ4_prepareTable(ctx, srcSize, tableType); + if (ctx->currentOffset) { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration); + } else { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + LZ4_prepareTable(ctx, srcSize, tableType); + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + if (srcSize < LZ4_64Klimit) { + const tableType_t tableType = byU16; + LZ4_prepareTable(ctx, srcSize, tableType); + if (ctx->currentOffset) { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration); + } else { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + LZ4_prepareTable(ctx, srcSize, tableType); + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } +} + + +int LZ4_compress_fast(const char* src, char* dest, int srcSize, int dstCapacity, int acceleration) +{ + int result; +#if (LZ4_HEAPMODE) + LZ4_stream_t* const ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ + if (ctxPtr == NULL) return 0; +#else + LZ4_stream_t ctx; + LZ4_stream_t* const ctxPtr = &ctx; +#endif + result = LZ4_compress_fast_extState(ctxPtr, src, dest, srcSize, dstCapacity, acceleration); + +#if (LZ4_HEAPMODE) + FREEMEM(ctxPtr); +#endif + return result; +} + + +int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity) +{ + return LZ4_compress_fast(src, dst, srcSize, dstCapacity, 1); +} + + +/* Note!: This function leaves the stream in an unclean/broken state! + * It is not safe to subsequently use the same state with a _fastReset() or + * _continue() call without resetting it. */ +static int LZ4_compress_destSize_extState_internal(LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration) +{ + void* const s = LZ4_initStream(state, sizeof (*state)); + assert(s != NULL); (void)s; + + if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ + return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, acceleration); + } else { + if (*srcSizePtr < LZ4_64Klimit) { + return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, acceleration); + } else { + tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, acceleration); + } } +} + +int LZ4_compress_destSize_extState(void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration) +{ + int const r = LZ4_compress_destSize_extState_internal((LZ4_stream_t*)state, src, dst, srcSizePtr, targetDstSize, acceleration); + /* clean the state on exit */ + LZ4_initStream(state, sizeof (LZ4_stream_t)); + return r; +} + + +int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) +{ +#if (LZ4_HEAPMODE) + LZ4_stream_t* const ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ + if (ctx == NULL) return 0; +#else + LZ4_stream_t ctxBody; + LZ4_stream_t* const ctx = &ctxBody; +#endif + + int result = LZ4_compress_destSize_extState_internal(ctx, src, dst, srcSizePtr, targetDstSize, 1); + +#if (LZ4_HEAPMODE) + FREEMEM(ctx); +#endif + return result; +} + + + +/*-****************************** +* Streaming functions +********************************/ + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4_stream_t* LZ4_createStream(void) +{ + LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); + LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal)); + DEBUGLOG(4, "LZ4_createStream %p", lz4s); + if (lz4s == NULL) return NULL; + LZ4_initStream(lz4s, sizeof(*lz4s)); + return lz4s; +} +#endif + +static size_t LZ4_stream_t_alignment(void) +{ +#if LZ4_ALIGN_TEST + typedef struct { char c; LZ4_stream_t t; } t_a; + return sizeof(t_a) - sizeof(LZ4_stream_t); +#else + return 1; /* effectively disabled */ +#endif +} + +LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) +{ + DEBUGLOG(5, "LZ4_initStream"); + if (buffer == NULL) { return NULL; } + if (size < sizeof(LZ4_stream_t)) { return NULL; } + if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL; + MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal)); + return (LZ4_stream_t*)buffer; +} + +/* resetStream is now deprecated, + * prefer initStream() which is more general */ +void LZ4_resetStream (LZ4_stream_t* LZ4_stream) +{ + DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream); + MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal)); +} + +void LZ4_resetStream_fast(LZ4_stream_t* ctx) { + LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32); +} + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +int LZ4_freeStream (LZ4_stream_t* LZ4_stream) +{ + if (!LZ4_stream) return 0; /* support free on NULL */ + DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream); + FREEMEM(LZ4_stream); + return (0); +} +#endif + + +typedef enum { _ld_fast, _ld_slow } LoadDict_mode_e; +#define HASH_UNIT sizeof(reg_t) +int LZ4_loadDict_internal(LZ4_stream_t* LZ4_dict, + const char* dictionary, int dictSize, + LoadDict_mode_e _ld) +{ + LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; + const tableType_t tableType = byU32; + const BYTE* p = (const BYTE*)dictionary; + const BYTE* const dictEnd = p + dictSize; + U32 idx32; + + DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict); + + /* It's necessary to reset the context, + * and not just continue it with prepareTable() + * to avoid any risk of generating overflowing matchIndex + * when compressing using this dictionary */ + LZ4_resetStream(LZ4_dict); + + /* We always increment the offset by 64 KB, since, if the dict is longer, + * we truncate it to the last 64k, and if it's shorter, we still want to + * advance by a whole window length so we can provide the guarantee that + * there are only valid offsets in the window, which allows an optimization + * in LZ4_compress_fast_continue() where it uses noDictIssue even when the + * dictionary isn't a full 64k. */ + dict->currentOffset += 64 KB; + + if (dictSize < (int)HASH_UNIT) { + return 0; + } + + if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; + dict->dictionary = p; + dict->dictSize = (U32)(dictEnd - p); + dict->tableType = (U32)tableType; + idx32 = dict->currentOffset - dict->dictSize; + + while (p <= dictEnd-HASH_UNIT) { + U32 const h = LZ4_hashPosition(p, tableType); + /* Note: overwriting => favors positions end of dictionary */ + LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType); + p+=3; idx32+=3; + } + + if (_ld == _ld_slow) { + /* Fill hash table with additional references, to improve compression capability */ + p = dict->dictionary; + idx32 = dict->currentOffset - dict->dictSize; + while (p <= dictEnd-HASH_UNIT) { + U32 const h = LZ4_hashPosition(p, tableType); + U32 const limit = dict->currentOffset - 64 KB; + if (LZ4_getIndexOnHash(h, dict->hashTable, tableType) <= limit) { + /* Note: not overwriting => favors positions beginning of dictionary */ + LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType); + } + p++; idx32++; + } + } + + return (int)dict->dictSize; +} + +int LZ4_loadDict(LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +{ + return LZ4_loadDict_internal(LZ4_dict, dictionary, dictSize, _ld_fast); +} + +int LZ4_loadDictSlow(LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +{ + return LZ4_loadDict_internal(LZ4_dict, dictionary, dictSize, _ld_slow); +} + +void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) +{ + const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL : + &(dictionaryStream->internal_donotuse); + + DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)", + workingStream, dictionaryStream, + dictCtx != NULL ? dictCtx->dictSize : 0); + + if (dictCtx != NULL) { + /* If the current offset is zero, we will never look in the + * external dictionary context, since there is no value a table + * entry can take that indicate a miss. In that case, we need + * to bump the offset to something non-zero. + */ + if (workingStream->internal_donotuse.currentOffset == 0) { + workingStream->internal_donotuse.currentOffset = 64 KB; + } + + /* Don't actually attach an empty dictionary. + */ + if (dictCtx->dictSize == 0) { + dictCtx = NULL; + } + } + workingStream->internal_donotuse.dictCtx = dictCtx; +} + + +static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize) +{ + assert(nextSize >= 0); + if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */ + /* rescale hash table */ + U32 const delta = LZ4_dict->currentOffset - 64 KB; + const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; + int i; + DEBUGLOG(4, "LZ4_renormDictT"); + for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; + else LZ4_dict->hashTable[i] -= delta; + } + LZ4_dict->currentOffset = 64 KB; + if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; + LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; + } +} + + +int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, + const char* source, char* dest, + int inputSize, int maxOutputSize, + int acceleration) +{ + const tableType_t tableType = byU32; + LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse; + const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL; + + DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)", inputSize, streamPtr->dictSize); + + LZ4_renormDictT(streamPtr, inputSize); /* fix index overflow */ + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + + /* invalidate tiny dictionaries */ + if ( (streamPtr->dictSize < 4) /* tiny dictionary : not enough for a hash */ + && (dictEnd != source) /* prefix mode */ + && (inputSize > 0) /* tolerance : don't lose history, in case next invocation would use prefix mode */ + && (streamPtr->dictCtx == NULL) /* usingDictCtx */ + ) { + DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary); + /* remove dictionary existence from history, to employ faster prefix mode */ + streamPtr->dictSize = 0; + streamPtr->dictionary = (const BYTE*)source; + dictEnd = source; + } + + /* Check overlapping input/dictionary space */ + { const char* const sourceEnd = source + inputSize; + if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) { + streamPtr->dictSize = (U32)(dictEnd - sourceEnd); + if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; + if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; + streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize; + } + } + + /* prefix mode : source data follows dictionary */ + if (dictEnd == source) { + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) + return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration); + else + return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration); + } + + /* external dictionary mode */ + { int result; + if (streamPtr->dictCtx) { + /* We depend here on the fact that dictCtx'es (produced by + * LZ4_loadDict) guarantee that their tables contain no references + * to offsets between dictCtx->currentOffset - 64 KB and + * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe + * to use noDictIssue even when the dict isn't a full 64 KB. + */ + if (inputSize > 4 KB) { + /* For compressing large blobs, it is faster to pay the setup + * cost to copy the dictionary's tables into the active context, + * so that the compression loop is only looking into one table. + */ + LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr)); + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration); + } + } else { /* small data <= 4 KB */ + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); + } + } + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)inputSize; + return result; + } +} + + +/* Hidden debug function, to force-test external dictionary mode */ +int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize) +{ + LZ4_stream_t_internal* const streamPtr = &LZ4_dict->internal_donotuse; + int result; + + LZ4_renormDictT(streamPtr, srcSize); + + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { + result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); + } + + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)srcSize; + + return result; +} + + +/*! LZ4_saveDict() : + * If previously compressed data block is not guaranteed to remain available at its memory location, + * save it into a safer place (char* safeBuffer). + * Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable, + * one can therefore call LZ4_compress_fast_continue() right after. + * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. + */ +int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) +{ + LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; + + DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer); + + if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ + if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } + + if (safeBuffer == NULL) assert(dictSize == 0); + if (dictSize > 0) { + const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; + assert(dict->dictionary); + LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize); + } + + dict->dictionary = (const BYTE*)safeBuffer; + dict->dictSize = (U32)dictSize; + + return dictSize; +} + + + +/*-******************************* + * Decompression functions + ********************************/ + +typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive; + +#undef MIN +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) + + +/* variant for decompress_unsafe() + * does not know end of input + * presumes input is well formed + * note : will consume at least one byte */ +static size_t read_long_length_no_check(const BYTE** pp) +{ + size_t b, l = 0; + do { b = **pp; (*pp)++; l += b; } while (b==255); + DEBUGLOG(6, "read_long_length_no_check: +length=%zu using %zu input bytes", l, l/255 + 1) + return l; +} + +/* core decoder variant for LZ4_decompress_fast*() + * for legacy support only : these entry points are deprecated. + * - Presumes input is correctly formed (no defense vs malformed inputs) + * - Does not know input size (presume input buffer is "large enough") + * - Decompress a full block (only) + * @return : nb of bytes read from input. + * Note : this variant is not optimized for speed, just for maintenance. + * the goal is to remove support of decompress_fast*() variants by v2.0 +**/ +LZ4_FORCE_INLINE int +LZ4_decompress_unsafe_generic( + const BYTE* const istart, + BYTE* const ostart, + int decompressedSize, + + size_t prefixSize, + const BYTE* const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note: =0 if dictStart==NULL */ + ) +{ + const BYTE* ip = istart; + BYTE* op = (BYTE*)ostart; + BYTE* const oend = ostart + decompressedSize; + const BYTE* const prefixStart = ostart - prefixSize; + + DEBUGLOG(5, "LZ4_decompress_unsafe_generic"); + if (dictStart == NULL) assert(dictSize == 0); + + while (1) { + /* start new sequence */ + unsigned token = *ip++; + + /* literals */ + { size_t ll = token >> ML_BITS; + if (ll==15) { + /* long literal length */ + ll += read_long_length_no_check(&ip); + } + if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */ + LZ4_memmove(op, ip, ll); /* support in-place decompression */ + op += ll; + ip += ll; + if ((size_t)(oend-op) < MFLIMIT) { + if (op==oend) break; /* end of block */ + DEBUGLOG(5, "invalid: literals end at distance %zi from end of block", oend-op); + /* incorrect end of block : + * last match must start at least MFLIMIT==12 bytes before end of output block */ + return -1; + } } + + /* match */ + { size_t ml = token & 15; + size_t const offset = LZ4_readLE16(ip); + ip+=2; + + if (ml==15) { + /* long literal length */ + ml += read_long_length_no_check(&ip); + } + ml += MINMATCH; + + if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */ + + { const BYTE* match = op - offset; + + /* out of range */ + if (offset > (size_t)(op - prefixStart) + dictSize) { + DEBUGLOG(6, "offset out of range"); + return -1; + } + + /* check special case : extDict */ + if (offset > (size_t)(op - prefixStart)) { + /* extDict scenario */ + const BYTE* const dictEnd = dictStart + dictSize; + const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart)); + size_t const extml = (size_t)(dictEnd - extMatch); + if (extml > ml) { + /* match entirely within extDict */ + LZ4_memmove(op, extMatch, ml); + op += ml; + ml = 0; + } else { + /* match split between extDict & prefix */ + LZ4_memmove(op, extMatch, extml); + op += extml; + ml -= extml; + } + match = prefixStart; + } + + /* match copy - slow variant, supporting overlap copy */ + { size_t u; + for (u=0; u= ipmax before start of loop. Returns initial_error if so. + * @error (output) - error code. Must be set to 0 before call. +**/ +typedef size_t Rvl_t; +static const Rvl_t rvl_error = (Rvl_t)(-1); +LZ4_FORCE_INLINE Rvl_t +read_variable_length(const BYTE** ip, const BYTE* ilimit, + int initial_check) +{ + Rvl_t s, length = 0; + assert(ip != NULL); + assert(*ip != NULL); + assert(ilimit != NULL); + if (initial_check && unlikely((*ip) >= ilimit)) { /* read limit reached */ + return rvl_error; + } + s = **ip; + (*ip)++; + length += s; + if (unlikely((*ip) > ilimit)) { /* read limit reached */ + return rvl_error; + } + /* accumulator overflow detection (32-bit mode only) */ + if ((sizeof(length) < 8) && unlikely(length > ((Rvl_t)(-1)/2)) ) { + return rvl_error; + } + if (likely(s != 255)) return length; + do { + s = **ip; + (*ip)++; + length += s; + if (unlikely((*ip) > ilimit)) { /* read limit reached */ + return rvl_error; + } + /* accumulator overflow detection (32-bit mode only) */ + if ((sizeof(length) < 8) && unlikely(length > ((Rvl_t)(-1)/2)) ) { + return rvl_error; + } + } while (s == 255); + + return length; +} + +/*! LZ4_decompress_generic() : + * This generic decompression function covers all use cases. + * It shall be instantiated several times, using different sets of directives. + * Note that it is important for performance that this function really get inlined, + * in order to remove useless branches during compilation optimization. + */ +LZ4_FORCE_INLINE int +LZ4_decompress_generic( + const char* const src, + char* const dst, + int srcSize, + int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ + + earlyEnd_directive partialDecoding, /* full, partial */ + dict_directive dict, /* noDict, withPrefix64k, usingExtDict */ + const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */ + const BYTE* const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note : = 0 if noDict */ + ) +{ + if ((src == NULL) || (outputSize < 0)) { return -1; } + + { const BYTE* ip = (const BYTE*) src; + const BYTE* const iend = ip + srcSize; + + BYTE* op = (BYTE*) dst; + BYTE* const oend = op + outputSize; + BYTE* cpy; + + const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; + + const int checkOffset = (dictSize < (int)(64 KB)); + + + /* Set up the "end" pointers for the shortcut. */ + const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/; + const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/; + + const BYTE* match; + size_t offset; + unsigned token; + size_t length; + + + DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize); + + /* Special cases */ + assert(lowPrefix <= op); + if (unlikely(outputSize==0)) { + /* Empty output buffer */ + if (partialDecoding) return 0; + return ((srcSize==1) && (*ip==0)) ? 0 : -1; + } + if (unlikely(srcSize==0)) { return -1; } + + /* LZ4_FAST_DEC_LOOP: + * designed for modern OoO performance cpus, + * where copying reliably 32-bytes is preferable to an unpredictable branch. + * note : fast loop may show a regression for some client arm chips. */ +#if LZ4_FAST_DEC_LOOP + if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { + DEBUGLOG(6, "move to safe decode loop"); + goto safe_decode; + } + + /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */ + DEBUGLOG(6, "using fast decode loop"); + while (1) { + /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */ + assert(oend - op >= FASTLOOP_SAFE_DISTANCE); + assert(ip < iend); + token = *ip++; + length = token >> ML_BITS; /* literal length */ + DEBUGLOG(7, "blockPos%6u: litLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length); + + /* decode literal length */ + if (length == RUN_MASK) { + size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1); + if (addl == rvl_error) { + DEBUGLOG(6, "error reading long literal length"); + goto _output_error; + } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ + if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ + + /* copy literals */ + LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); + if ((op+length>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } + LZ4_wildCopy32(op, ip, op+length); + ip += length; op += length; + } else if (ip <= iend-(16 + 1/*max lit + offset + nextToken*/)) { + /* We don't need to check oend, since we check it once for each loop below */ + DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); + /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */ + LZ4_memcpy(op, ip, 16); + ip += length; op += length; + } else { + goto safe_literal_copy; + } + + /* get offset */ + offset = LZ4_readLE16(ip); ip+=2; + DEBUGLOG(6, "blockPos%6u: offset = %u", (unsigned)(op-(BYTE*)dst), (unsigned)offset); + match = op - offset; + assert(match <= op); /* overflow check */ + + /* get matchlength */ + length = token & ML_MASK; + DEBUGLOG(7, " match length token = %u (len==%u)", (unsigned)length, (unsigned)length+MINMATCH); + + if (length == ML_MASK) { + size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); + if (addl == rvl_error) { + DEBUGLOG(5, "error reading long match length"); + goto _output_error; + } + length += addl; + length += MINMATCH; + DEBUGLOG(7, " long match length == %u", (unsigned)length); + if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ + if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { + goto safe_match_copy; + } + } else { + length += MINMATCH; + if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { + DEBUGLOG(7, "moving to safe_match_copy (ml==%u)", (unsigned)length); + goto safe_match_copy; + } + + /* Fastpath check: skip LZ4_wildCopy32 when true */ + if ((dict == withPrefix64k) || (match >= lowPrefix)) { + if (offset >= 8) { + assert(match >= lowPrefix); + assert(match <= op); + assert(op + 18 <= oend); + + LZ4_memcpy(op, match, 8); + LZ4_memcpy(op+8, match+8, 8); + LZ4_memcpy(op+16, match+16, 2); + op += length; + continue; + } } } + + if ( checkOffset && (unlikely(match + dictSize < lowPrefix)) ) { + DEBUGLOG(5, "Error : pos=%zi, offset=%zi => outside buffers", op-lowPrefix, op-match); + goto _output_error; + } + /* match starting within external dictionary */ + if ((dict==usingExtDict) && (match < lowPrefix)) { + assert(dictEnd != NULL); + if (unlikely(op+length > oend-LASTLITERALS)) { + if (partialDecoding) { + DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd"); + length = MIN(length, (size_t)(oend-op)); + } else { + DEBUGLOG(6, "end-of-block condition violated") + goto _output_error; + } } + + if (length <= (size_t)(lowPrefix-match)) { + /* match fits entirely within external dictionary : just copy */ + LZ4_memmove(op, dictEnd - (lowPrefix-match), length); + op += length; + } else { + /* match stretches into both external dictionary and current block */ + size_t const copySize = (size_t)(lowPrefix - match); + size_t const restSize = length - copySize; + LZ4_memcpy(op, dictEnd - copySize, copySize); + op += copySize; + if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ + BYTE* const endOfMatch = op + restSize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) { *op++ = *copyFrom++; } + } else { + LZ4_memcpy(op, lowPrefix, restSize); + op += restSize; + } } + continue; + } + + /* copy match within block */ + cpy = op + length; + + assert((op <= oend) && (oend-op >= 32)); + if (unlikely(offset<16)) { + LZ4_memcpy_using_offset(op, match, cpy, offset); + } else { + LZ4_wildCopy32(op, match, cpy); + } + + op = cpy; /* wildcopy correction */ + } + safe_decode: +#endif + + /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ + DEBUGLOG(6, "using safe decode loop"); + while (1) { + assert(ip < iend); + token = *ip++; + length = token >> ML_BITS; /* literal length */ + DEBUGLOG(7, "blockPos%6u: litLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length); + + /* A two-stage shortcut for the most common case: + * 1) If the literal length is 0..14, and there is enough space, + * enter the shortcut and copy 16 bytes on behalf of the literals + * (in the fast mode, only 8 bytes can be safely copied this way). + * 2) Further if the match length is 4..18, copy 18 bytes in a similar + * manner; but we ensure that there's enough space in the output for + * those 18 bytes earlier, upon entering the shortcut (in other words, + * there is a combined check for both stages). + */ + if ( (length != RUN_MASK) + /* strictly "less than" on input, to re-enter the loop with at least one byte */ + && likely((ip < shortiend) & (op <= shortoend)) ) { + /* Copy the literals */ + LZ4_memcpy(op, ip, 16); + op += length; ip += length; + + /* The second stage: prepare for match copying, decode full info. + * If it doesn't work out, the info won't be wasted. */ + length = token & ML_MASK; /* match length */ + DEBUGLOG(7, "blockPos%6u: matchLength token = %u (len=%u)", (unsigned)(op-(BYTE*)dst), (unsigned)length, (unsigned)length + 4); + offset = LZ4_readLE16(ip); ip += 2; + match = op - offset; + assert(match <= op); /* check overflow */ + + /* Do not deal with overlapping matches. */ + if ( (length != ML_MASK) + && (offset >= 8) + && (dict==withPrefix64k || match >= lowPrefix) ) { + /* Copy the match. */ + LZ4_memcpy(op + 0, match + 0, 8); + LZ4_memcpy(op + 8, match + 8, 8); + LZ4_memcpy(op +16, match +16, 2); + op += length + MINMATCH; + /* Both stages worked, load the next token. */ + continue; + } + + /* The second stage didn't work out, but the info is ready. + * Propel it right to the point of match copying. */ + goto _copy_match; + } + + /* decode literal length */ + if (length == RUN_MASK) { + size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1); + if (addl == rvl_error) { goto _output_error; } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ + if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ + } + +#if LZ4_FAST_DEC_LOOP + safe_literal_copy: +#endif + /* copy literals */ + cpy = op+length; + + LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); + if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) { + /* We've either hit the input parsing restriction or the output parsing restriction. + * In the normal scenario, decoding a full block, it must be the last sequence, + * otherwise it's an error (invalid input or dimensions). + * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow. + */ + if (partialDecoding) { + /* Since we are partial decoding we may be in this block because of the output parsing + * restriction, which is not valid since the output buffer is allowed to be undersized. + */ + DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end") + DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length); + DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op)); + DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip)); + /* Finishing in the middle of a literals segment, + * due to lack of input. + */ + if (ip+length > iend) { + length = (size_t)(iend-ip); + cpy = op + length; + } + /* Finishing in the middle of a literals segment, + * due to lack of output space. + */ + if (cpy > oend) { + cpy = oend; + assert(op<=oend); + length = (size_t)(oend-op); + } + } else { + /* We must be on the last sequence (or invalid) because of the parsing limitations + * so check that we exactly consume the input and don't overrun the output buffer. + */ + if ((ip+length != iend) || (cpy > oend)) { + DEBUGLOG(5, "should have been last run of literals") + DEBUGLOG(5, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); + DEBUGLOG(5, "or cpy(%p) > (oend-MFLIMIT)(%p)", cpy, oend-MFLIMIT); + DEBUGLOG(5, "after writing %u bytes / %i bytes available", (unsigned)(op-(BYTE*)dst), outputSize); + goto _output_error; + } + } + LZ4_memmove(op, ip, length); /* supports overlapping memory regions, for in-place decompression scenarios */ + ip += length; + op += length; + /* Necessarily EOF when !partialDecoding. + * When partialDecoding, it is EOF if we've either + * filled the output buffer or + * can't proceed with reading an offset for following match. + */ + if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) { + break; + } + } else { + LZ4_wildCopy8(op, ip, cpy); /* can overwrite up to 8 bytes beyond cpy */ + ip += length; op = cpy; + } + + /* get offset */ + offset = LZ4_readLE16(ip); ip+=2; + match = op - offset; + + /* get matchlength */ + length = token & ML_MASK; + DEBUGLOG(7, "blockPos%6u: matchLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length); + + _copy_match: + if (length == ML_MASK) { + size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); + if (addl == rvl_error) { goto _output_error; } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ + } + length += MINMATCH; + +#if LZ4_FAST_DEC_LOOP + safe_match_copy: +#endif + if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */ + /* match starting within external dictionary */ + if ((dict==usingExtDict) && (match < lowPrefix)) { + assert(dictEnd != NULL); + if (unlikely(op+length > oend-LASTLITERALS)) { + if (partialDecoding) length = MIN(length, (size_t)(oend-op)); + else goto _output_error; /* doesn't respect parsing restriction */ + } + + if (length <= (size_t)(lowPrefix-match)) { + /* match fits entirely within external dictionary : just copy */ + LZ4_memmove(op, dictEnd - (lowPrefix-match), length); + op += length; + } else { + /* match stretches into both external dictionary and current block */ + size_t const copySize = (size_t)(lowPrefix - match); + size_t const restSize = length - copySize; + LZ4_memcpy(op, dictEnd - copySize, copySize); + op += copySize; + if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ + BYTE* const endOfMatch = op + restSize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) *op++ = *copyFrom++; + } else { + LZ4_memcpy(op, lowPrefix, restSize); + op += restSize; + } } + continue; + } + assert(match >= lowPrefix); + + /* copy match within block */ + cpy = op + length; + + /* partialDecoding : may end anywhere within the block */ + assert(op<=oend); + if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { + size_t const mlen = MIN(length, (size_t)(oend-op)); + const BYTE* const matchEnd = match + mlen; + BYTE* const copyEnd = op + mlen; + if (matchEnd > op) { /* overlap copy */ + while (op < copyEnd) { *op++ = *match++; } + } else { + LZ4_memcpy(op, match, mlen); + } + op = copyEnd; + if (op == oend) { break; } + continue; + } + + if (unlikely(offset<8)) { + LZ4_write32(op, 0); /* silence msan warning when offset==0 */ + op[0] = match[0]; + op[1] = match[1]; + op[2] = match[2]; + op[3] = match[3]; + match += inc32table[offset]; + LZ4_memcpy(op+4, match, 4); + match -= dec64table[offset]; + } else { + LZ4_memcpy(op, match, 8); + match += 8; + } + op += 8; + + if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { + BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1); + if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ + if (op < oCopyLimit) { + LZ4_wildCopy8(op, match, oCopyLimit); + match += oCopyLimit - op; + op = oCopyLimit; + } + while (op < cpy) { *op++ = *match++; } + } else { + LZ4_memcpy(op, match, 8); + if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } + } + op = cpy; /* wildcopy correction */ + } + + /* end of decoding */ + DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst)); + return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ + + /* Overflow error detected */ + _output_error: + return (int) (-(((const char*)ip)-src))-1; + } +} + + +/*===== Instantiate the API decoding functions. =====*/ + +LZ4_FORCE_O2 +int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, + decode_full_block, noDict, + (BYTE*)dest, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity, + partial_decode, + noDict, (BYTE*)dst, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_fast(const char* source, char* dest, int originalSize) +{ + DEBUGLOG(5, "LZ4_decompress_fast"); + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 0, NULL, 0); +} + +/*===== Instantiate a few more decoding cases, used more than once. =====*/ + +LZ4_FORCE_O2 /* Exported, an obsolete API function. */ +int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, withPrefix64k, + (BYTE*)dest - 64 KB, NULL, 0); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, withPrefix64k, + (BYTE*)dest - 64 KB, NULL, 0); +} + +/* Another obsolete API function, paired with the previous one. */ +int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) +{ + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 64 KB, NULL, 0); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize, + size_t prefixSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, noDict, + (BYTE*)dest-prefixSize, NULL, 0); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, + size_t prefixSize) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, noDict, + (BYTE*)dest-prefixSize, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, + int compressedSize, int maxOutputSize, + const void* dictStart, size_t dictSize) +{ + DEBUGLOG(5, "LZ4_decompress_safe_forceExtDict"); + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, usingExtDict, + (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, + int compressedSize, int targetOutputSize, int dstCapacity, + const void* dictStart, size_t dictSize) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, usingExtDict, + (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize, + const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 0, (const BYTE*)dictStart, dictSize); +} + +/* The "double dictionary" mode, for use with e.g. ring buffers: the first part + * of the dictionary is passed as prefix, and the second via dictStart + dictSize. + * These routines are used only once, in LZ4_decompress_*_continue(). + */ +LZ4_FORCE_INLINE +int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize, + size_t prefixSize, const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, usingExtDict, + (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); +} + +/*===== streaming decompression functions =====*/ + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4_streamDecode_t* LZ4_createStreamDecode(void) +{ + LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal)); + return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); +} + +int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) +{ + if (LZ4_stream == NULL) { return 0; } /* support free on NULL */ + FREEMEM(LZ4_stream); + return 0; +} +#endif + +/*! LZ4_setStreamDecode() : + * Use this function to instruct where to find the dictionary. + * This function is not necessary if previous data is still available where it was decoded. + * Loading a size of 0 is allowed (same effect as no dictionary). + * @return : 1 if OK, 0 if error + */ +int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + lz4sd->prefixSize = (size_t)dictSize; + if (dictSize) { + assert(dictionary != NULL); + lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; + } else { + lz4sd->prefixEnd = (const BYTE*) dictionary; + } + lz4sd->externalDict = NULL; + lz4sd->extDictSize = 0; + return 1; +} + +/*! LZ4_decoderRingBufferSize() : + * when setting a ring buffer for streaming decompression (optional scenario), + * provides the minimum size of this ring buffer + * to be compatible with any source respecting maxBlockSize condition. + * Note : in a ring buffer scenario, + * blocks are presumed decompressed next to each other. + * When not enough space remains for next block (remainingSize < maxBlockSize), + * decoding resumes from beginning of ring buffer. + * @return : minimum ring buffer size, + * or 0 if there is an error (invalid maxBlockSize). + */ +int LZ4_decoderRingBufferSize(int maxBlockSize) +{ + if (maxBlockSize < 0) return 0; + if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0; + if (maxBlockSize < 16) maxBlockSize = 16; + return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize); +} + +/* +*_continue() : + These decoding functions allow decompression of multiple blocks in "streaming" mode. + Previously decoded blocks must still be available at the memory position where they were decoded. + If it's not possible, save the relevant part of decoded data into a safe buffer, + and indicate where it stands using LZ4_setStreamDecode() +*/ +LZ4_FORCE_O2 +int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + int result; + + if (lz4sd->prefixSize == 0) { + /* The first call, no dictionary yet. */ + assert(lz4sd->extDictSize == 0); + result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)result; + lz4sd->prefixEnd = (BYTE*)dest + result; + } else if (lz4sd->prefixEnd == (BYTE*)dest) { + /* They're rolling the current segment. */ + if (lz4sd->prefixSize >= 64 KB - 1) + result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); + else if (lz4sd->extDictSize == 0) + result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, + lz4sd->prefixSize); + else + result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize, + lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += (size_t)result; + lz4sd->prefixEnd += result; + } else { + /* The buffer wraps around, or they're switching to another buffer. */ + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, + lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)result; + lz4sd->prefixEnd = (BYTE*)dest + result; + } + + return result; +} + +LZ4_FORCE_O2 int +LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, + const char* source, char* dest, int originalSize) +{ + LZ4_streamDecode_t_internal* const lz4sd = + (assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse); + int result; + + DEBUGLOG(5, "LZ4_decompress_fast_continue (toDecodeSize=%i)", originalSize); + assert(originalSize >= 0); + + if (lz4sd->prefixSize == 0) { + DEBUGLOG(5, "first invocation : no prefix nor extDict"); + assert(lz4sd->extDictSize == 0); + result = LZ4_decompress_fast(source, dest, originalSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)originalSize; + lz4sd->prefixEnd = (BYTE*)dest + originalSize; + } else if (lz4sd->prefixEnd == (BYTE*)dest) { + DEBUGLOG(5, "continue using existing prefix"); + result = LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + lz4sd->prefixSize, + lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += (size_t)originalSize; + lz4sd->prefixEnd += originalSize; + } else { + DEBUGLOG(5, "prefix becomes extDict"); + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_fast_extDict(source, dest, originalSize, + lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)originalSize; + lz4sd->prefixEnd = (BYTE*)dest + originalSize; + } + + return result; +} + + +/* +Advanced decoding functions : +*_usingDict() : + These decoding functions work the same as "_continue" ones, + the dictionary must be explicitly provided within parameters +*/ + +int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) +{ + if (dictSize==0) + return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); + if (dictStart+dictSize == dest) { + if (dictSize >= 64 KB - 1) { + return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize); +} + +int LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize) +{ + if (dictSize==0) + return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity); + if (dictStart+dictSize == dest) { + if (dictSize >= 64 KB - 1) { + return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize); +} + +int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) +{ + if (dictSize==0 || dictStart+dictSize == dest) + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + (size_t)dictSize, NULL, 0); + assert(dictSize >= 0); + return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize); +} + + +/*=************************************************* +* Obsolete Functions +***************************************************/ +/* obsolete compression functions */ +int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) +{ + return LZ4_compress_default(source, dest, inputSize, maxOutputSize); +} +int LZ4_compress(const char* src, char* dest, int srcSize) +{ + return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize)); +} +int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) +{ + return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); +} +int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) +{ + return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); +} +int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity) +{ + return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1); +} +int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) +{ + return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); +} + +/* +These decompression functions are deprecated and should no longer be used. +They are only provided here for compatibility with older user programs. +- LZ4_uncompress is totally equivalent to LZ4_decompress_fast +- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe +*/ +int LZ4_uncompress (const char* source, char* dest, int outputSize) +{ + return LZ4_decompress_fast(source, dest, outputSize); +} +int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) +{ + return LZ4_decompress_safe(source, dest, isize, maxOutputSize); +} + +/* Obsolete Streaming functions */ + +int LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); } + +int LZ4_resetStreamState(void* state, char* inputBuffer) +{ + (void)inputBuffer; + LZ4_resetStream((LZ4_stream_t*)state); + return 0; +} + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +void* LZ4_create (char* inputBuffer) +{ + (void)inputBuffer; + return LZ4_createStream(); +} +#endif + +char* LZ4_slideInputBuffer (void* state) +{ + /* avoid const char * -> char * conversion warning */ + return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary; +} + +#endif /* LZ4_COMMONDEFS_ONLY */ diff --git a/tools/lz4.h b/tools/lz4.h new file mode 100644 index 00000000..d4178b22 --- /dev/null +++ b/tools/lz4.h @@ -0,0 +1,884 @@ +/* + * LZ4 - Fast LZ compression algorithm + * Header File + * Copyright (C) 2011-2023, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://www.lz4.org + - LZ4 source repository : https://github.com/lz4/lz4 +*/ +#if defined (__cplusplus) +extern "C" { +#endif + +#ifndef LZ4_H_2983827168210 +#define LZ4_H_2983827168210 + +/* --- Dependency --- */ +#include /* size_t */ + + +/** + Introduction + + LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core, + scalable with multi-cores CPU. It features an extremely fast decoder, with speed in + multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. + + The LZ4 compression library provides in-memory compression and decompression functions. + It gives full buffer control to user. + Compression can be done in: + - a single step (described as Simple Functions) + - a single step, reusing a context (described in Advanced Functions) + - unbounded multiple steps (described as Streaming compression) + + lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md). + Decompressing such a compressed block requires additional metadata. + Exact metadata depends on exact decompression function. + For the typical case of LZ4_decompress_safe(), + metadata includes block's compressed size, and maximum bound of decompressed size. + Each application is free to encode and pass such metadata in whichever way it wants. + + lz4.h only handle blocks, it can not generate Frames. + + Blocks are different from Frames (doc/lz4_Frame_format.md). + Frames bundle both blocks and metadata in a specified manner. + Embedding metadata is required for compressed data to be self-contained and portable. + Frame format is delivered through a companion API, declared in lz4frame.h. + The `lz4` CLI can only manage frames. +*/ + +/*^*************************************************************** +* Export parameters +*****************************************************************/ +/* +* LZ4_DLL_EXPORT : +* Enable exporting of functions when building a Windows DLL +* LZ4LIB_VISIBILITY : +* Control library symbols visibility. +*/ +#ifndef LZ4LIB_VISIBILITY +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default"))) +# else +# define LZ4LIB_VISIBILITY +# endif +#endif +#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) +# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY +#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) +# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +#else +# define LZ4LIB_API LZ4LIB_VISIBILITY +#endif + +/*! LZ4_FREESTANDING : + * When this macro is set to 1, it enables "freestanding mode" that is + * suitable for typical freestanding environment which doesn't support + * standard C library. + * + * - LZ4_FREESTANDING is a compile-time switch. + * - It requires the following macros to be defined: + * LZ4_memcpy, LZ4_memmove, LZ4_memset. + * - It only enables LZ4/HC functions which don't use heap. + * All LZ4F_* functions are not supported. + * - See tests/freestanding.c to check its basic setup. + */ +#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1) +# define LZ4_HEAPMODE 0 +# define LZ4HC_HEAPMODE 0 +# define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1 +# if !defined(LZ4_memcpy) +# error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'." +# endif +# if !defined(LZ4_memset) +# error "LZ4_FREESTANDING requires macro 'LZ4_memset'." +# endif +# if !defined(LZ4_memmove) +# error "LZ4_FREESTANDING requires macro 'LZ4_memmove'." +# endif +#elif ! defined(LZ4_FREESTANDING) +# define LZ4_FREESTANDING 0 +#endif + + +/*------ Version ------*/ +#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ +#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ +#define LZ4_VERSION_RELEASE 5 /* for tweaks, bug-fixes, or development */ + +#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) + +#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE +#define LZ4_QUOTE(str) #str +#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) +#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */ + +LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */ +LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */ + + +/*-************************************ +* Tuning memory usage +**************************************/ +/*! + * LZ4_MEMORY_USAGE : + * Can be selected at compile time, by setting LZ4_MEMORY_USAGE. + * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB) + * Increasing memory usage improves compression ratio, generally at the cost of speed. + * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality. + * Default value is 14, for 16KB, which nicely fits into most L1 caches. + */ +#ifndef LZ4_MEMORY_USAGE +# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT +#endif + +/* These are absolute limits, they should not be changed by users */ +#define LZ4_MEMORY_USAGE_MIN 10 +#define LZ4_MEMORY_USAGE_DEFAULT 20 +#define LZ4_MEMORY_USAGE_MAX 20 + +#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN) +# error "LZ4_MEMORY_USAGE is too small !" +#endif + +#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX) +# error "LZ4_MEMORY_USAGE is too large !" +#endif + +/*-************************************ +* Simple Functions +**************************************/ +/*! LZ4_compress_default() : + * Compresses 'srcSize' bytes from buffer 'src' + * into already allocated 'dst' buffer of size 'dstCapacity'. + * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize). + * It also runs faster, so it's a recommended setting. + * If the function cannot compress 'src' into a more limited 'dst' budget, + * compression stops *immediately*, and the function result is zero. + * In which case, 'dst' content is undefined (invalid). + * srcSize : max supported value is LZ4_MAX_INPUT_SIZE. + * dstCapacity : size of buffer 'dst' (which must be already allocated) + * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity) + * or 0 if compression fails + * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer). + */ +LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); + +/*! LZ4_decompress_safe() : + * @compressedSize : is the exact complete size of the compressed block. + * @dstCapacity : is the size of destination buffer (which must be already allocated), + * presumed an upper bound of decompressed size. + * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) + * If destination buffer is not large enough, decoding will stop and output an error code (negative value). + * If the source stream is detected malformed, the function will stop decoding and return a negative result. + * Note 1 : This function is protected against malicious data packets : + * it will never writes outside 'dst' buffer, nor read outside 'source' buffer, + * even if the compressed block is maliciously modified to order the decoder to do these actions. + * In such case, the decoder stops immediately, and considers the compressed block malformed. + * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them. + * The implementation is free to send / store / derive this information in whichever way is most beneficial. + * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead. + */ +LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity); + + +/*-************************************ +* Advanced Functions +**************************************/ +#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ +#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) + +/*! LZ4_compressBound() : + Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) + This function is primarily useful for memory allocation purposes (destination buffer size). + Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). + Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize) + inputSize : max supported value is LZ4_MAX_INPUT_SIZE + return : maximum output size in a "worst case" scenario + or 0, if input size is incorrect (too large or negative) +*/ +LZ4LIB_API int LZ4_compressBound(int inputSize); + +/*! LZ4_compress_fast() : + Same as LZ4_compress_default(), but allows selection of "acceleration" factor. + The larger the acceleration value, the faster the algorithm, but also the lesser the compression. + It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. + An acceleration value of "1" is the same as regular LZ4_compress_default() + Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c). + Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c). +*/ +LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + + +/*! LZ4_compress_fast_extState() : + * Same as LZ4_compress_fast(), using an externally allocated memory space for its state. + * Use LZ4_sizeofState() to know how much memory must be allocated, + * and allocate it on 8-bytes boundaries (using `malloc()` typically). + * Then, provide this buffer as `void* state` to compression function. + */ +LZ4LIB_API int LZ4_sizeofState(void); +LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + +/*! LZ4_compress_destSize() : + * Reverse the logic : compresses as much data as possible from 'src' buffer + * into already allocated buffer 'dst', of size >= 'dstCapacity'. + * This function either compresses the entire 'src' content into 'dst' if it's large enough, + * or fill 'dst' buffer completely with as much data as possible from 'src'. + * note: acceleration parameter is fixed to "default". + * + * *srcSizePtr : in+out parameter. Initially contains size of input. + * Will be modified to indicate how many bytes where read from 'src' to fill 'dst'. + * New value is necessarily <= input value. + * @return : Nb bytes written into 'dst' (necessarily <= dstCapacity) + * or 0 if compression fails. + * + * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed in v1.9.2+): + * the produced compressed content could, in specific circumstances, + * require to be decompressed into a destination buffer larger + * by at least 1 byte than the content to decompress. + * If an application uses `LZ4_compress_destSize()`, + * it's highly recommended to update liblz4 to v1.9.2 or better. + * If this can't be done or ensured, + * the receiving decompression function should provide + * a dstCapacity which is > decompressedSize, by at least 1 byte. + * See https://github.com/lz4/lz4/issues/859 for details + */ +LZ4LIB_API int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize); + +/*! LZ4_decompress_safe_partial() : + * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', + * into destination buffer 'dst' of size 'dstCapacity'. + * Up to 'targetOutputSize' bytes will be decoded. + * The function stops decoding on reaching this objective. + * This can be useful to boost performance + * whenever only the beginning of a block is required. + * + * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize) + * If source stream is detected malformed, function returns a negative result. + * + * Note 1 : @return can be < targetOutputSize, if compressed block contains less data. + * + * Note 2 : targetOutputSize must be <= dstCapacity + * + * Note 3 : this function effectively stops decoding on reaching targetOutputSize, + * so dstCapacity is kind of redundant. + * This is because in older versions of this function, + * decoding operation would still write complete sequences. + * Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize, + * it could write more bytes, though only up to dstCapacity. + * Some "margin" used to be required for this operation to work properly. + * Thankfully, this is no longer necessary. + * The function nonetheless keeps the same signature, in an effort to preserve API compatibility. + * + * Note 4 : If srcSize is the exact size of the block, + * then targetOutputSize can be any value, + * including larger than the block's decompressed size. + * The function will, at most, generate block's decompressed size. + * + * Note 5 : If srcSize is _larger_ than block's compressed size, + * then targetOutputSize **MUST** be <= block's decompressed size. + * Otherwise, *silent corruption will occur*. + */ +LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); + + +/*-********************************************* +* Streaming Compression Functions +***********************************************/ +typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ + +/*! + Note about RC_INVOKED + + - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio). + https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros + + - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars) + and reports warning "RC4011: identifier truncated". + + - To eliminate the warning, we surround long preprocessor symbol with + "#if !defined(RC_INVOKED) ... #endif" block that means + "skip this block when rc.exe is trying to read it". +*/ +#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); +LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); +#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */ +#endif + +/*! LZ4_resetStream_fast() : v1.9.0+ + * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks + * (e.g., LZ4_compress_fast_continue()). + * + * An LZ4_stream_t must be initialized once before usage. + * This is automatically done when created by LZ4_createStream(). + * However, should the LZ4_stream_t be simply declared on stack (for example), + * it's necessary to initialize it first, using LZ4_initStream(). + * + * After init, start any new stream with LZ4_resetStream_fast(). + * A same LZ4_stream_t can be re-used multiple times consecutively + * and compress multiple streams, + * provided that it starts each new stream with LZ4_resetStream_fast(). + * + * LZ4_resetStream_fast() is much faster than LZ4_initStream(), + * but is not compatible with memory regions containing garbage data. + * + * Note: it's only useful to call LZ4_resetStream_fast() + * in the context of streaming compression. + * The *extState* functions perform their own resets. + * Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive. + */ +LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); + +/*! LZ4_loadDict() : + * Use this function to reference a static dictionary into LZ4_stream_t. + * The dictionary must remain available during compression. + * LZ4_loadDict() triggers a reset, so any previous data will be forgotten. + * The same dictionary will have to be loaded on decompression side for successful decoding. + * Dictionary are useful for better compression of small data (KB range). + * While LZ4 itself accepts any input as dictionary, dictionary efficiency is also a topic. + * When in doubt, employ the Zstandard's Dictionary Builder. + * Loading a size of 0 is allowed, and is the same as reset. + * @return : loaded dictionary size, in bytes (note: only the last 64 KB are loaded) + */ +LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); + +/*! LZ4_loadDictSlow() : v1.9.5+ + * Same as LZ4_loadDict(), + * but uses a bit more cpu to reference the dictionary content more thoroughly. + * This is expected to slightly improve compression ratio. + * The extra-cpu cost is likely worth it if the dictionary is re-used across multiple sessions. + * @return : loaded dictionary size, in bytes (note: only the last 64 KB are loaded) + */ +LZ4LIB_API int LZ4_loadDictSlow(LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); + +/*! LZ4_compress_fast_continue() : + * Compress 'src' content using data from previously compressed blocks, for better compression ratio. + * 'dst' buffer must be already allocated. + * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. + * + * @return : size of compressed block + * or 0 if there is an error (typically, cannot fit into 'dst'). + * + * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block. + * Each block has precise boundaries. + * Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata. + * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together. + * + * Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory ! + * + * Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB. + * Make sure that buffers are separated, by at least one byte. + * This construction ensures that each block only depends on previous block. + * + * Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB. + * + * Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed. + */ +LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + +/*! LZ4_saveDict() : + * If last 64KB data cannot be guaranteed to remain available at its current memory location, + * save it into a safer place (char* safeBuffer). + * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(), + * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables. + * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error. + */ +LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize); + + +/*-********************************************** +* Streaming Decompression Functions +* Bufferless synchronous API +************************************************/ +typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */ + +/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() : + * creation / destruction of streaming decompression tracking context. + * A tracking context can be re-used multiple times. + */ +#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); +LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); +#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */ +#endif + +/*! LZ4_setStreamDecode() : + * An LZ4_streamDecode_t context can be allocated once and re-used multiple times. + * Use this function to start decompression of a new stream of blocks. + * A dictionary can optionally be set. Use NULL or size 0 for a reset order. + * Dictionary is presumed stable : it must remain accessible and unmodified during next decompression. + * @return : 1 if OK, 0 if error + */ +LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); + +/*! LZ4_decoderRingBufferSize() : v1.8.2+ + * Note : in a ring buffer scenario (optional), + * blocks are presumed decompressed next to each other + * up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize), + * at which stage it resumes from beginning of ring buffer. + * When setting such a ring buffer for streaming decompression, + * provides the minimum size of this ring buffer + * to be compatible with any source respecting maxBlockSize condition. + * @return : minimum ring buffer size, + * or 0 if there is an error (invalid maxBlockSize). + */ +LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize); +#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */ + +/*! LZ4_decompress_safe_continue() : + * This decoding function allows decompression of consecutive blocks in "streaming" mode. + * The difference with the usual independent blocks is that + * new blocks are allowed to find references into former blocks. + * A block is an unsplittable entity, and must be presented entirely to the decompression function. + * LZ4_decompress_safe_continue() only accepts one block at a time. + * It's modeled after `LZ4_decompress_safe()` and behaves similarly. + * + * @LZ4_streamDecode : decompression state, tracking the position in memory of past data + * @compressedSize : exact complete size of one compressed block. + * @dstCapacity : size of destination buffer (which must be already allocated), + * must be an upper bound of decompressed size. + * @return : number of bytes decompressed into destination buffer (necessarily <= dstCapacity) + * If destination buffer is not large enough, decoding will stop and output an error code (negative value). + * If the source stream is detected malformed, the function will stop decoding and return a negative result. + * + * The last 64KB of previously decoded data *must* remain available and unmodified + * at the memory position where they were previously decoded. + * If less than 64KB of data has been decoded, all the data must be present. + * + * Special : if decompression side sets a ring buffer, it must respect one of the following conditions : + * - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize). + * maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes. + * In which case, encoding and decoding buffers do not need to be synchronized. + * Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize. + * - Synchronized mode : + * Decompression buffer size is _exactly_ the same as compression buffer size, + * and follows exactly same update rule (block boundaries at same positions), + * and decoding function is provided with exact decompressed size of each block (exception for last block of the stream), + * _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB). + * - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes. + * In which case, encoding and decoding buffers do not need to be synchronized, + * and encoding ring buffer can have any size, including small ones ( < 64 KB). + * + * Whenever these conditions are not possible, + * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression, + * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block. +*/ +LZ4LIB_API int +LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, + const char* src, char* dst, + int srcSize, int dstCapacity); + + +/*! LZ4_decompress_safe_usingDict() : + * Works the same as + * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_safe_continue() + * However, it's stateless: it doesn't need any LZ4_streamDecode_t state. + * Dictionary is presumed stable : it must remain accessible and unmodified during decompression. + * Performance tip : Decompression speed can be substantially increased + * when dst == dictStart + dictSize. + */ +LZ4LIB_API int +LZ4_decompress_safe_usingDict(const char* src, char* dst, + int srcSize, int dstCapacity, + const char* dictStart, int dictSize); + +/*! LZ4_decompress_safe_partial_usingDict() : + * Behaves the same as LZ4_decompress_safe_partial() + * with the added ability to specify a memory segment for past data. + * Performance tip : Decompression speed can be substantially increased + * when dst == dictStart + dictSize. + */ +LZ4LIB_API int +LZ4_decompress_safe_partial_usingDict(const char* src, char* dst, + int compressedSize, + int targetOutputSize, int maxOutputSize, + const char* dictStart, int dictSize); + +#endif /* LZ4_H_2983827168210 */ + + +/*^************************************* + * !!!!!! STATIC LINKING ONLY !!!!!! + ***************************************/ + +/*-**************************************************************************** + * Experimental section + * + * Symbols declared in this section must be considered unstable. Their + * signatures or semantics may change, or they may be removed altogether in the + * future. They are therefore only safe to depend on when the caller is + * statically linked against the library. + * + * To protect against unsafe usage, not only are the declarations guarded, + * the definitions are hidden by default + * when building LZ4 as a shared/dynamic library. + * + * In order to access these declarations, + * define LZ4_STATIC_LINKING_ONLY in your application + * before including LZ4's headers. + * + * In order to make their implementations accessible dynamically, you must + * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library. + ******************************************************************************/ + +#ifdef LZ4_STATIC_LINKING_ONLY + +#ifndef LZ4_STATIC_3504398509 +#define LZ4_STATIC_3504398509 + +#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS +# define LZ4LIB_STATIC_API LZ4LIB_API +#else +# define LZ4LIB_STATIC_API +#endif + + +/*! LZ4_compress_fast_extState_fastReset() : + * A variant of LZ4_compress_fast_extState(). + * + * Using this variant avoids an expensive initialization step. + * It is only safe to call if the state buffer is known to be correctly initialized already + * (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized"). + * From a high level, the difference is that + * this function initializes the provided state with a call to something like LZ4_resetStream_fast() + * while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream(). + */ +LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + +/*! LZ4_compress_destSize_extState() : + * Same as LZ4_compress_destSize(), but using an externally allocated state. + * Also: exposes @acceleration + */ +int LZ4_compress_destSize_extState(void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration); + +/*! LZ4_attach_dictionary() : + * This is an experimental API that allows + * efficient use of a static dictionary many times. + * + * Rather than re-loading the dictionary buffer into a working context before + * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a + * working LZ4_stream_t, this function introduces a no-copy setup mechanism, + * in which the working stream references the dictionary stream in-place. + * + * Several assumptions are made about the state of the dictionary stream. + * Currently, only streams which have been prepared by LZ4_loadDict() should + * be expected to work. + * + * Alternatively, the provided dictionaryStream may be NULL, + * in which case any existing dictionary stream is unset. + * + * If a dictionary is provided, it replaces any pre-existing stream history. + * The dictionary contents are the only history that can be referenced and + * logically immediately precede the data compressed in the first subsequent + * compression call. + * + * The dictionary will only remain attached to the working stream through the + * first compression call, at the end of which it is cleared. The dictionary + * stream (and source buffer) must remain in-place / accessible / unchanged + * through the completion of the first compression call on the stream. + */ +LZ4LIB_STATIC_API void +LZ4_attach_dictionary(LZ4_stream_t* workingStream, + const LZ4_stream_t* dictionaryStream); + + +/*! In-place compression and decompression + * + * It's possible to have input and output sharing the same buffer, + * for highly constrained memory environments. + * In both cases, it requires input to lay at the end of the buffer, + * and decompression to start at beginning of the buffer. + * Buffer size must feature some margin, hence be larger than final size. + * + * |<------------------------buffer--------------------------------->| + * |<-----------compressed data--------->| + * |<-----------decompressed size------------------>| + * |<----margin---->| + * + * This technique is more useful for decompression, + * since decompressed size is typically larger, + * and margin is short. + * + * In-place decompression will work inside any buffer + * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize). + * This presumes that decompressedSize > compressedSize. + * Otherwise, it means compression actually expanded data, + * and it would be more efficient to store such data with a flag indicating it's not compressed. + * This can happen when data is not compressible (already compressed, or encrypted). + * + * For in-place compression, margin is larger, as it must be able to cope with both + * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX, + * and data expansion, which can happen when input is not compressible. + * As a consequence, buffer size requirements are much higher, + * and memory savings offered by in-place compression are more limited. + * + * There are ways to limit this cost for compression : + * - Reduce history size, by modifying LZ4_DISTANCE_MAX. + * Note that it is a compile-time constant, so all compressions will apply this limit. + * Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX, + * so it's a reasonable trick when inputs are known to be small. + * - Require the compressor to deliver a "maximum compressed size". + * This is the `dstCapacity` parameter in `LZ4_compress*()`. + * When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail, + * in which case, the return code will be 0 (zero). + * The caller must be ready for these cases to happen, + * and typically design a backup scheme to send data uncompressed. + * The combination of both techniques can significantly reduce + * the amount of margin required for in-place compression. + * + * In-place compression can work in any buffer + * which size is >= (maxCompressedSize) + * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success. + * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX, + * so it's possible to reduce memory requirements by playing with them. + */ + +#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32) +#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */ + +#ifdef LZ4T +extern int LZ4T_distanceMax; +#define LZ4_DISTANCE_MAX LZ4T_distanceMax +#endif + +#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */ +# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */ +#endif + +#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */ +#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */ + +#endif /* LZ4_STATIC_3504398509 */ +#endif /* LZ4_STATIC_LINKING_ONLY */ + + + +#ifndef LZ4_H_98237428734687 +#define LZ4_H_98237428734687 + +/*-************************************************************ + * Private Definitions + ************************************************************** + * Do not use these definitions directly. + * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. + * Accessing members will expose user code to API and/or ABI break in future versions of the library. + **************************************************************/ +#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) +#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) +#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ + +#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# include + typedef int8_t LZ4_i8; + typedef uint8_t LZ4_byte; + typedef uint16_t LZ4_u16; + typedef uint32_t LZ4_u32; +#else + typedef signed char LZ4_i8; + typedef unsigned char LZ4_byte; + typedef unsigned short LZ4_u16; + typedef unsigned int LZ4_u32; +#endif + +/*! LZ4_stream_t : + * Never ever use below internal definitions directly ! + * These definitions are not API/ABI safe, and may change in future versions. + * If you need static allocation, declare or allocate an LZ4_stream_t object. +**/ + +typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; +struct LZ4_stream_t_internal { + LZ4_u32 hashTable[LZ4_HASH_SIZE_U32]; + const LZ4_byte* dictionary; + const LZ4_stream_t_internal* dictCtx; + LZ4_u32 currentOffset; + LZ4_u32 tableType; + LZ4_u32 dictSize; + /* Implicit padding to ensure structure is aligned */ +}; + +#define LZ4_STREAM_MINSIZE ((1UL << (LZ4_MEMORY_USAGE)) + 32) /* static size, for inter-version compatibility */ +union LZ4_stream_u { + char minStateSize[LZ4_STREAM_MINSIZE]; + LZ4_stream_t_internal internal_donotuse; +}; /* previously typedef'd to LZ4_stream_t */ + + +/*! LZ4_initStream() : v1.9.0+ + * An LZ4_stream_t structure must be initialized at least once. + * This is automatically done when invoking LZ4_createStream(), + * but it's not when the structure is simply declared on stack (for example). + * + * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t. + * It can also initialize any arbitrary buffer of sufficient size, + * and will @return a pointer of proper type upon initialization. + * + * Note : initialization fails if size and alignment conditions are not respected. + * In which case, the function will @return NULL. + * Note2: An LZ4_stream_t structure guarantees correct alignment and size. + * Note3: Before v1.9.0, use LZ4_resetStream() instead +**/ +LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* stateBuffer, size_t size); + + +/*! LZ4_streamDecode_t : + * Never ever use below internal definitions directly ! + * These definitions are not API/ABI safe, and may change in future versions. + * If you need static allocation, declare or allocate an LZ4_streamDecode_t object. +**/ +typedef struct { + const LZ4_byte* externalDict; + const LZ4_byte* prefixEnd; + size_t extDictSize; + size_t prefixSize; +} LZ4_streamDecode_t_internal; + +#define LZ4_STREAMDECODE_MINSIZE 32 +union LZ4_streamDecode_u { + char minStateSize[LZ4_STREAMDECODE_MINSIZE]; + LZ4_streamDecode_t_internal internal_donotuse; +} ; /* previously typedef'd to LZ4_streamDecode_t */ + + + +/*-************************************ +* Obsolete Functions +**************************************/ + +/*! Deprecation warnings + * + * Deprecated functions make the compiler generate a warning when invoked. + * This is meant to invite users to update their source code. + * Should deprecation warnings be a problem, it is generally possible to disable them, + * typically with -Wno-deprecated-declarations for gcc + * or _CRT_SECURE_NO_WARNINGS in Visual. + * + * Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS + * before including the header file. + */ +#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS +# define LZ4_DEPRECATED(message) /* disable deprecation warnings */ +#else +# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ +# define LZ4_DEPRECATED(message) [[deprecated(message)]] +# elif defined(_MSC_VER) +# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) +# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45)) +# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) +# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31) +# define LZ4_DEPRECATED(message) __attribute__((deprecated)) +# else +# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler") +# define LZ4_DEPRECATED(message) /* disabled */ +# endif +#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ + +/*! Obsolete compression functions (since v1.7.3) */ +LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize); +LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); + +/*! Obsolete decompression functions (since v1.8.0) */ +LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); +LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); + +/* Obsolete streaming functions (since v1.7.0) + * degraded functionality; do not use! + * + * In order to perform streaming compression, these functions depended on data + * that is no longer tracked in the state. They have been preserved as well as + * possible: using them will still produce a correct output. However, they don't + * actually retain any history between compression calls. The compression ratio + * achieved will therefore be no better than compressing each chunk + * independently. + */ +LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer); +LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void); +LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); +LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); + +/*! Obsolete streaming decoding functions (since v1.7.0) */ +LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); +LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); + +/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) : + * These functions used to be faster than LZ4_decompress_safe(), + * but this is no longer the case. They are now slower. + * This is because LZ4_decompress_fast() doesn't know the input size, + * and therefore must progress more cautiously into the input buffer to not read beyond the end of block. + * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability. + * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated. + * + * The last remaining LZ4_decompress_fast() specificity is that + * it can decompress a block without knowing its compressed size. + * Such functionality can be achieved in a more secure manner + * by employing LZ4_decompress_safe_partial(). + * + * Parameters: + * originalSize : is the uncompressed size to regenerate. + * `dst` must be already allocated, its size must be >= 'originalSize' bytes. + * @return : number of bytes read from source buffer (== compressed size). + * The function expects to finish at block's end exactly. + * If the source stream is detected malformed, the function stops decoding and returns a negative result. + * note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer. + * However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds. + * Also, since match offsets are not validated, match reads from 'src' may underflow too. + * These issues never happen if input (compressed) data is correct. + * But they may happen if input data is invalid (error or intentional tampering). + * As a consequence, use these functions in trusted environments with trusted data **only**. + */ +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_partial() instead") +LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider migrating towards LZ4_decompress_safe_continue() instead. " + "Note that the contract will change (requires block's compressed size, instead of decompressed size)") +LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_partial_usingDict() instead") +LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); + +/*! LZ4_resetStream() : + * An LZ4_stream_t structure must be initialized at least once. + * This is done with LZ4_initStream(), or LZ4_resetStream(). + * Consider switching to LZ4_initStream(), + * invoking LZ4_resetStream() will trigger deprecation warnings in the future. + */ +LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr); + + +#endif /* LZ4_H_98237428734687 */ + + +#if defined (__cplusplus) +} +#endif diff --git a/tools/lz4hc.c b/tools/lz4hc.c new file mode 100644 index 00000000..f1605358 --- /dev/null +++ b/tools/lz4hc.c @@ -0,0 +1,2075 @@ +/* + LZ4 HC - High Compression Mode of LZ4 + Copyright (C) 2011-2020, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +/* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */ + + +/* ************************************* +* Tuning Parameter +***************************************/ + +/*! HEAPMODE : + * Select how stateless HC compression functions like `LZ4_compress_HC()` + * allocate memory for their workspace: + * in stack (0:fastest), or in heap (1:default, requires malloc()). + * Since workspace is rather large, heap mode is recommended. +**/ +#ifndef LZ4HC_HEAPMODE +# define LZ4HC_HEAPMODE 1 +#endif + + +/*=== Dependency ===*/ +#define LZ4_HC_STATIC_LINKING_ONLY +#include "lz4hc.h" +#include + + +/*=== Shared lz4.c code ===*/ +#ifndef LZ4_SRC_INCLUDED +# if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +# endif +# if defined (__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +# endif +# define LZ4_COMMONDEFS_ONLY +# include "lz4.c" /* LZ4_count, constants, mem */ +#endif + + +/*=== Enums ===*/ +typedef enum { noDictCtx, usingDictCtxHc } dictCtx_directive; + + +/*=== Constants ===*/ +#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH) +#define LZ4_OPT_NUM (1<<16) + + +/*=== Macros ===*/ +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) +#define MAX(a,b) ( (a) > (b) ? (a) : (b) ) +#define DELTANEXTU16(table, pos) table[(U16)(pos)] /* faster */ +/* Make fields passed to, and updated by LZ4HC_encodeSequence explicit */ +#define UPDATABLE(ip, op, anchor) &ip, &op, &anchor + + +/*=== Hashing ===*/ +#define LZ4HC_HASHSIZE 4 +#ifdef LZ4T +extern uint32_t LZ4T_hashMask; +#define LZ4T_HASH_MASK LZ4T_hashMask +#else +#define LZ4T_HASH_MASK 0xffffffffU +#endif +#define HASH_FUNCTION(i) (((i & 0x00ffffff) * 2654435761U) >> ((4*8)-LZ4HC_HASH_LOG)) +static U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr)); } + +#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) +/* lie to the compiler about data alignment; use with caution */ +static U64 LZ4_read64(const void* memPtr) { return *(const U64*) memPtr; } + +#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) +/* __pack instructions are safer, but compiler specific */ +LZ4_PACK(typedef struct { U64 u64; }) LZ4_unalign64; +static U64 LZ4_read64(const void* ptr) { return ((const LZ4_unalign64*)ptr)->u64; } + +#else /* safe and portable access using memcpy() */ +static U64 LZ4_read64(const void* memPtr) +{ + U64 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +#endif /* LZ4_FORCE_MEMORY_ACCESS */ + +#define LZ4MID_HASHSIZE 8 +#define LZ4MID_HASHLOG (LZ4HC_HASH_LOG-1) +#define LZ4MID_HASHTABLESIZE (1 << LZ4MID_HASHLOG) + +static U32 LZ4MID_hash4(U32 v) { return (v * 2654435761U) >> (32-LZ4MID_HASHLOG); } +static U32 LZ4MID_hash4Ptr(const void* ptr) { return LZ4MID_hash4(LZ4_read32(ptr)); } +/* note: hash7 hashes the lower 56-bits. + * It presumes input was read using little endian.*/ +static U32 LZ4MID_hash7(U64 v) { return (U32)(((v << (64-56)) * 58295818150454627ULL) >> (64-LZ4MID_HASHLOG)) ; } +static U64 LZ4_readLE64(const void* memPtr); +static U32 LZ4MID_hash8Ptr(const void* ptr) { return LZ4MID_hash7(LZ4_readLE64(ptr)); } + +static U64 LZ4_readLE64(const void* memPtr) +{ + if (LZ4_isLittleEndian()) { + return LZ4_read64(memPtr); + } else { + const BYTE* p = (const BYTE*)memPtr; + /* note: relies on the compiler to simplify this expression */ + return (U64)p[0] | ((U64)p[1]<<8) | ((U64)p[2]<<16) | ((U64)p[3]<<24) + | ((U64)p[4]<<32) | ((U64)p[5]<<40) | ((U64)p[6]<<48) | ((U64)p[7]<<56); + } +} + + +/*=== Count match length ===*/ +LZ4_FORCE_INLINE +unsigned LZ4HC_NbCommonBytes32(U32 val) +{ + assert(val != 0); + if (LZ4_isLittleEndian()) { +# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanReverse(&r, val); + return (unsigned)((31 - r) >> 3); +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_clz(val) >> 3; +# else + val >>= 8; + val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) | + (val + 0x00FF0000)) >> 24; + return (unsigned)val ^ 3; +# endif + } else { +# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanForward(&r, val); + return (unsigned)(r >> 3); +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_ctz(val) >> 3; +# else + const U32 m = 0x01010101; + return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24; +# endif + } +} + +/** LZ4HC_countBack() : + * @return : negative value, nb of common bytes before ip/match */ +LZ4_FORCE_INLINE +int LZ4HC_countBack(const BYTE* const ip, const BYTE* const match, + const BYTE* const iMin, const BYTE* const mMin) +{ + int back = 0; + int const min = (int)MAX(iMin - ip, mMin - match); + assert(min <= 0); + assert(ip >= iMin); assert((size_t)(ip-iMin) < (1U<<31)); + assert(match >= mMin); assert((size_t)(match - mMin) < (1U<<31)); + + while ((back - min) > 3) { + U32 const v = LZ4_read32(ip + back - 4) ^ LZ4_read32(match + back - 4); + if (v) { + return (back - (int)LZ4HC_NbCommonBytes32(v)); + } else back -= 4; /* 4-byte step */ + } + /* check remainder if any */ + while ( (back > min) + && (ip[back-1] == match[back-1]) ) + back--; + return back; +} + + +/************************************** +* Init +**************************************/ +static void LZ4HC_clearTables (LZ4HC_CCtx_internal* hc4) +{ + MEM_INIT(hc4->hashTable, 0, sizeof(hc4->hashTable)); + MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable)); +} + +static void LZ4HC_init_internal (LZ4HC_CCtx_internal* hc4, const BYTE* start) +{ + size_t const bufferSize = (size_t)(hc4->end - hc4->prefixStart); + size_t newStartingOffset = bufferSize + hc4->dictLimit; + DEBUGLOG(5, "LZ4HC_init_internal"); + assert(newStartingOffset >= bufferSize); /* check overflow */ + if (newStartingOffset > 1 GB) { + LZ4HC_clearTables(hc4); + newStartingOffset = 0; + } + newStartingOffset += 64 KB; + hc4->nextToUpdate = (U32)newStartingOffset; + hc4->prefixStart = start; + hc4->end = start; + hc4->dictStart = start; + hc4->dictLimit = (U32)newStartingOffset; + hc4->lowLimit = (U32)newStartingOffset; +} + + +/************************************** +* Encode +**************************************/ +/* LZ4HC_encodeSequence() : + * @return : 0 if ok, + * 1 if buffer issue detected */ +#ifndef LZ4T +LZ4_FORCE_INLINE int LZ4HC_encodeSequence ( + const BYTE** _ip, + BYTE** _op, + const BYTE** _anchor, + int matchLength, + int offset, + limitedOutput_directive limit, + BYTE* oend) +{ +#define ip (*_ip) +#define op (*_op) +#define anchor (*_anchor) + + size_t length; + BYTE* const token = op++; + +#if defined(LZ4_DEBUG) && (LZ4_DEBUG >= 6) + static const BYTE* start = NULL; + static U32 totalCost = 0; + U32 const pos = (start==NULL) ? 0 : (U32)(anchor - start); + U32 const ll = (U32)(ip - anchor); + U32 const llAdd = (ll>=15) ? ((ll-15) / 255) + 1 : 0; + U32 const mlAdd = (matchLength>=19) ? ((matchLength-19) / 255) + 1 : 0; + U32 const cost = 1 + llAdd + ll + 2 + mlAdd; + if (start==NULL) start = anchor; /* only works for single segment */ + /* g_debuglog_enable = (pos >= 2228) & (pos <= 2262); */ + DEBUGLOG(6, "pos:%7u -- literals:%4u, match:%4i, offset:%5i, cost:%4u + %5u", + pos, + (U32)(ip - anchor), matchLength, offset, + cost, totalCost); + totalCost += cost; +#endif + + /* Encode Literal length */ + length = (size_t)(ip - anchor); + LZ4_STATIC_ASSERT(notLimited == 0); + /* Check output limit */ + if (limit && ((op + (length / 255) + length + (2 + 1 + LASTLITERALS)) > oend)) { + DEBUGLOG(6, "Not enough room to write %i literals (%i bytes remaining)", + (int)length, (int)(oend - op)); + return 1; + } + if (length >= RUN_MASK) { + size_t len = length - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for(; len >= 255 ; len -= 255) *op++ = 255; + *op++ = (BYTE)len; + } else { + *token = (BYTE)(length << ML_BITS); + } + + /* Copy Literals */ + LZ4_wildCopy8(op, anchor, op + length); + op += length; + + /* Encode Offset */ + assert(offset <= LZ4_DISTANCE_MAX ); + assert(offset > 0); + LZ4_writeLE16(op, (U16)(offset)); op += 2; + + /* Encode MatchLength */ + assert(matchLength >= MINMATCH); + length = (size_t)matchLength - MINMATCH; + if (limit && (op + (length / 255) + (1 + LASTLITERALS) > oend)) { + DEBUGLOG(6, "Not enough room to write match length"); + return 1; /* Check output limit */ + } + if (length >= ML_MASK) { + *token += ML_MASK; + length -= ML_MASK; + for(; length >= 510 ; length -= 510) { *op++ = 255; *op++ = 255; } + if (length >= 255) { length -= 255; *op++ = 255; } + *op++ = (BYTE)length; + } else { + *token += (BYTE)(length); + } + + /* Prepare next loop */ + ip += matchLength; + anchor = ip; + + return 0; + +#undef ip +#undef op +#undef anchor +} +#else +LZ4_FORCE_INLINE int LZ4HC_encodeSequence ( + const BYTE** _ip, + BYTE** _op, + const BYTE** _anchor, + int matchLength, + int offset, + limitedOutput_directive limit, + BYTE* oend); +#endif + + +typedef struct { + int off; + int len; + int back; /* negative value */ +} LZ4HC_match_t; + +LZ4HC_match_t LZ4HC_searchExtDict(const BYTE* ip, U32 ipIndex, + const BYTE* const iLowLimit, const BYTE* const iHighLimit, + const LZ4HC_CCtx_internal* dictCtx, U32 gDictEndIndex, + int currentBestML, int nbAttempts) +{ + size_t const lDictEndIndex = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit; + U32 lDictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)]; + U32 matchIndex = lDictMatchIndex + gDictEndIndex - (U32)lDictEndIndex; + int offset = 0, sBack = 0; + assert(lDictEndIndex <= 1 GB); + if (lDictMatchIndex>0) + DEBUGLOG(7, "lDictEndIndex = %zu, lDictMatchIndex = %u", lDictEndIndex, lDictMatchIndex); + while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX && nbAttempts--) { + const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + lDictMatchIndex; + + if (LZ4_read32(matchPtr) == LZ4_read32(ip)) { + int mlt; + int back = 0; + const BYTE* vLimit = ip + (lDictEndIndex - lDictMatchIndex); + if (vLimit > iHighLimit) vLimit = iHighLimit; + mlt = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; + back = (ip > iLowLimit) ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->prefixStart) : 0; + mlt -= back; + if (mlt > currentBestML) { + currentBestML = mlt; + offset = (int)(ipIndex - matchIndex); + sBack = back; + DEBUGLOG(7, "found match of length %i within extDictCtx", currentBestML); + } } + + { U32 const nextOffset = DELTANEXTU16(dictCtx->chainTable, lDictMatchIndex); + lDictMatchIndex -= nextOffset; + matchIndex -= nextOffset; + } } + + { LZ4HC_match_t md; + md.len = currentBestML; + md.off = offset; + md.back = sBack; + return md; + } +} + +/************************************** +* Mid Compression (level 2) +**************************************/ + +LZ4_FORCE_INLINE void +LZ4MID_addPosition(U32* hTable, U32 hValue, U32 index) +{ + hTable[hValue] = index; +} + +#define ADDPOS8(_p, _idx) LZ4MID_addPosition(hash8Table, LZ4MID_hash8Ptr(_p), _idx) +#define ADDPOS4(_p, _idx) LZ4MID_addPosition(hash4Table, LZ4MID_hash4Ptr(_p), _idx) + +static int LZ4HC_compress_2hashes ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* srcSizePtr, + int const maxOutputSize, + const limitedOutput_directive limit, + const dictCtx_directive dict + ) +{ + U32* const hash4Table = ctx->hashTable; + U32* const hash8Table = hash4Table + LZ4MID_HASHTABLESIZE; + const BYTE* ip = (const BYTE*)src; + const BYTE* anchor = ip; + const BYTE* const iend = ip + *srcSizePtr; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = (iend - LASTLITERALS); + const BYTE* const ilimit = (iend - LZ4MID_HASHSIZE); + BYTE* op = (BYTE*)dst; + BYTE* oend = op + maxOutputSize; + + const BYTE* const prefixPtr = ctx->prefixStart; + const U32 prefixIdx = ctx->dictLimit; + const U32 ilimitIdx = (U32)(ilimit - prefixPtr) + prefixIdx; + const U32 gDictEndIndex = ctx->lowLimit; + unsigned matchLength; + unsigned matchDistance; + + /* input sanitization */ + DEBUGLOG(5, "LZ4HC_compress_2hashes (%i bytes)", *srcSizePtr); + assert(*srcSizePtr >= 0); + if (*srcSizePtr) assert(src != NULL); + if (maxOutputSize) assert(dst != NULL); + if (*srcSizePtr < 0) return 0; /* invalid */ + if (maxOutputSize < 0) return 0; /* invalid */ + if (*srcSizePtr > LZ4_MAX_INPUT_SIZE) { + /* forbidden: no input is allowed to be that large */ + return 0; + } + if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */ + if (*srcSizePtr < LZ4_minLength) + goto _lz4mid_last_literals; /* Input too small, no compression (all literals) */ + + /* main loop */ + while (ip <= mflimit) { + const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx; + /* search long match */ + { U32 h8 = LZ4MID_hash8Ptr(ip); + U32 pos8 = hash8Table[h8]; + assert(h8 < LZ4MID_HASHTABLESIZE); + assert(h8 < ipIndex); + LZ4MID_addPosition(hash8Table, h8, ipIndex); + if ( ipIndex - pos8 <= LZ4_DISTANCE_MAX + && pos8 >= prefixIdx /* note: currently only search within prefix */ + ) { + /* match candidate found */ + const BYTE* matchPtr = prefixPtr + pos8 - prefixIdx; + assert(matchPtr < ip); + matchLength = LZ4_count(ip, matchPtr, matchlimit); + if (matchLength >= MINMATCH) { + DEBUGLOG(7, "found candidate match at pos %u (len=%u)", pos8, matchLength); + matchDistance = ipIndex - pos8; + goto _lz4mid_encode_sequence; + } + } } + /* search short match */ + { U32 h4 = LZ4MID_hash4Ptr(ip); + U32 pos4 = hash4Table[h4]; + assert(h4 < LZ4MID_HASHTABLESIZE); + assert(pos4 < ipIndex); + LZ4MID_addPosition(hash4Table, h4, ipIndex); + if (ipIndex - pos4 <= LZ4_DISTANCE_MAX + && pos4 >= prefixIdx /* only search within prefix */ + ) { + /* match candidate found */ + const BYTE* const matchPtr = prefixPtr + (pos4 - prefixIdx); + assert(matchPtr < ip); + assert(matchPtr >= prefixPtr); + matchLength = LZ4_count(ip, matchPtr, matchlimit); + if (matchLength >= MINMATCH) { + /* short match found, let's just check ip+1 for longer */ + U32 const h8 = LZ4MID_hash8Ptr(ip+1); + U32 const pos8 = hash8Table[h8]; + U32 const m2Distance = ipIndex + 1 - pos8; + matchDistance = ipIndex - pos4; + if ( m2Distance <= LZ4_DISTANCE_MAX + && pos8 >= prefixIdx /* only search within prefix */ + && likely(ip < mflimit) + ) { + const BYTE* const m2Ptr = prefixPtr + (pos8 - prefixIdx); + unsigned ml2 = LZ4_count(ip+1, m2Ptr, matchlimit); + if (ml2 > matchLength) { + LZ4MID_addPosition(hash8Table, h8, ipIndex+1); + ip++; + matchLength = ml2; + matchDistance = m2Distance; + } } + goto _lz4mid_encode_sequence; + } + } } + /* no match found in prefix */ + if ( (dict == usingDictCtxHc) + && (ipIndex - gDictEndIndex < LZ4_DISTANCE_MAX - 8) ) { + /* search a match in dictionary */ + LZ4HC_match_t dMatch = LZ4HC_searchExtDict(ip, ipIndex, + anchor, matchlimit, + ctx->dictCtx, gDictEndIndex, + 0, 2); + if (dMatch.len >= MINMATCH) { + DEBUGLOG(7, "found Dictionary match (offset=%i)", dMatch.off); + ip += dMatch.back; + assert(ip >= anchor); + matchLength = (unsigned)dMatch.len; + matchDistance = (unsigned)dMatch.off; + goto _lz4mid_encode_sequence; + } + } + /* no match found */ + ip += 1 + ((ip-anchor) >> 9); /* skip faster over incompressible data */ + continue; + +_lz4mid_encode_sequence: + /* catch back */ + while (((ip > anchor) & ((U32)(ip-prefixPtr) > matchDistance)) && (unlikely(ip[-1] == ip[-(int)matchDistance-1]))) { + ip--; matchLength++; + }; + + /* fill table with beginning of match */ + ADDPOS8(ip+1, ipIndex+1); + ADDPOS8(ip+2, ipIndex+2); + ADDPOS4(ip+1, ipIndex+1); + + /* encode */ + { BYTE* const saved_op = op; + /* LZ4HC_encodeSequence always updates @op; on success, it updates @ip and @anchor */ + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + (int)matchLength, (int)matchDistance, + limit, oend) ) { + op = saved_op; /* restore @op value before failed LZ4HC_encodeSequence */ + goto _lz4mid_dest_overflow; + } + } + + /* fill table with end of match */ + { U32 endMatchIdx = (U32)(ip-prefixPtr) + prefixIdx; + U32 pos_m2 = endMatchIdx - 2; + if (pos_m2 < ilimitIdx) { + if (likely(ip - prefixPtr > 5)) { + ADDPOS8(ip-5, endMatchIdx - 5); + } + ADDPOS8(ip-3, endMatchIdx - 3); + ADDPOS8(ip-2, endMatchIdx - 2); + ADDPOS4(ip-2, endMatchIdx - 2); + ADDPOS4(ip-1, endMatchIdx - 1); + } + } + } + +_lz4mid_last_literals: + /* Encode Last Literals */ + { size_t lastRunSize = (size_t)(iend - anchor); /* literals */ + size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255; + size_t const totalSize = 1 + llAdd + lastRunSize; + if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ + if (limit && (op + totalSize > oend)) { + if (limit == limitedOutput) return 0; /* not enough space in @dst */ + /* adapt lastRunSize to fill 'dest' */ + lastRunSize = (size_t)(oend - op) - 1 /*token*/; + llAdd = (lastRunSize + 256 - RUN_MASK) / 256; + lastRunSize -= llAdd; + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize); + ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */ + + if (lastRunSize >= RUN_MASK) { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = (RUN_MASK << ML_BITS); + for(; accumulator >= 255 ; accumulator -= 255) + *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRunSize << ML_BITS); + } + assert(lastRunSize <= (size_t)(oend - op)); + LZ4_memcpy(op, anchor, lastRunSize); + op += lastRunSize; + } + + /* End */ + DEBUGLOG(5, "compressed %i bytes into %i bytes", *srcSizePtr, (int)((char*)op - dst)); + assert(ip >= (const BYTE*)src); + assert(ip <= iend); + *srcSizePtr = (int)(ip - (const BYTE*)src); + assert((char*)op >= dst); + assert(op <= oend); + assert((char*)op - dst < INT_MAX); + return (int)((char*)op - dst); + +_lz4mid_dest_overflow: + if (limit == fillOutput) { + /* Assumption : @ip, @anchor, @optr and @matchLength must be set correctly */ + size_t const ll = (size_t)(ip - anchor); + size_t const ll_addbytes = (ll + 240) / 255; + size_t const ll_totalCost = 1 + ll_addbytes + ll; + BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */ + DEBUGLOG(6, "Last sequence is overflowing : %u literals, %u remaining space", + (unsigned)ll, (unsigned)(oend-op)); + if (op + ll_totalCost <= maxLitPos) { + /* ll validated; now adjust match length */ + size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost)); + size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255); + assert(maxMlSize < INT_MAX); + if ((size_t)matchLength > maxMlSize) matchLength= (unsigned)maxMlSize; + if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + matchLength >= MFLIMIT) { + DEBUGLOG(6, "Let's encode a last sequence (ll=%u, ml=%u)", (unsigned)ll, matchLength); + LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + (int)matchLength, (int)matchDistance, + notLimited, oend); + } } + DEBUGLOG(6, "Let's finish with a run of literals (%u bytes left)", (unsigned)(oend-op)); + goto _lz4mid_last_literals; + } + /* compression failed */ + return 0; +} + + +/************************************** +* HC Compression - Search +**************************************/ + +/* Update chains up to ip (excluded) */ +LZ4_FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip) +{ + U16* const chainTable = hc4->chainTable; + U32* const hashTable = hc4->hashTable; + const BYTE* const prefixPtr = hc4->prefixStart; + U32 const prefixIdx = hc4->dictLimit; + U32 const target = (U32)(ip - prefixPtr) + prefixIdx; + U32 idx = hc4->nextToUpdate; + assert(ip >= prefixPtr); + assert(target >= prefixIdx); + + while (idx < target) { + U32 const h = LZ4HC_hashPtr(prefixPtr+idx-prefixIdx); + size_t delta = idx - hashTable[h]; + if (delta>LZ4_DISTANCE_MAX) delta = LZ4_DISTANCE_MAX; + DELTANEXTU16(chainTable, idx) = (U16)delta; + hashTable[h] = idx; + idx++; + } + + hc4->nextToUpdate = target; +} + +#if defined(_MSC_VER) +# define LZ4HC_rotl32(x,r) _rotl(x,r) +#else +# define LZ4HC_rotl32(x,r) ((x << r) | (x >> (32 - r))) +#endif + + +static U32 LZ4HC_rotatePattern(size_t const rotate, U32 const pattern) +{ + size_t const bitsToRotate = (rotate & (sizeof(pattern) - 1)) << 3; + if (bitsToRotate == 0) return pattern; + return LZ4HC_rotl32(pattern, (int)bitsToRotate); +} + +/* LZ4HC_countPattern() : + * pattern32 must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) */ +static unsigned +LZ4HC_countPattern(const BYTE* ip, const BYTE* const iEnd, U32 const pattern32) +{ + const BYTE* const iStart = ip; + reg_t const pattern = (sizeof(pattern)==8) ? + (reg_t)pattern32 + (((reg_t)pattern32) << (sizeof(pattern)*4)) : pattern32; + + while (likely(ip < iEnd-(sizeof(pattern)-1))) { + reg_t const diff = LZ4_read_ARCH(ip) ^ pattern; + if (!diff) { ip+=sizeof(pattern); continue; } + ip += LZ4_NbCommonBytes(diff); + return (unsigned)(ip - iStart); + } + + if (LZ4_isLittleEndian()) { + reg_t patternByte = pattern; + while ((ip>= 8; + } + } else { /* big endian */ + U32 bitOffset = (sizeof(pattern)*8) - 8; + while (ip < iEnd) { + BYTE const byte = (BYTE)(pattern >> bitOffset); + if (*ip != byte) break; + ip ++; bitOffset -= 8; + } } + + return (unsigned)(ip - iStart); +} + +/* LZ4HC_reverseCountPattern() : + * pattern must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) + * read using natural platform endianness */ +static unsigned +LZ4HC_reverseCountPattern(const BYTE* ip, const BYTE* const iLow, U32 pattern) +{ + const BYTE* const iStart = ip; + + while (likely(ip >= iLow+4)) { + if (LZ4_read32(ip-4) != pattern) break; + ip -= 4; + } + { const BYTE* bytePtr = (const BYTE*)(&pattern) + 3; /* works for any endianness */ + while (likely(ip>iLow)) { + if (ip[-1] != *bytePtr) break; + ip--; bytePtr--; + } } + return (unsigned)(iStart - ip); +} + +/* LZ4HC_protectDictEnd() : + * Checks if the match is in the last 3 bytes of the dictionary, so reading the + * 4 byte MINMATCH would overflow. + * @returns true if the match index is okay. + */ +static int LZ4HC_protectDictEnd(U32 const dictLimit, U32 const matchIndex) +{ + return ((U32)((dictLimit - 1) - matchIndex) >= 3); +} + +typedef enum { rep_untested, rep_not, rep_confirmed } repeat_state_e; +typedef enum { favorCompressionRatio=0, favorDecompressionSpeed } HCfavor_e; + + +LZ4_FORCE_INLINE LZ4HC_match_t +LZ4HC_InsertAndGetWiderMatch ( + LZ4HC_CCtx_internal* const hc4, + const BYTE* const ip, + const BYTE* const iLowLimit, const BYTE* const iHighLimit, + int longest, + const int maxNbAttempts, + const int patternAnalysis, const int chainSwap, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed) +{ + U16* const chainTable = hc4->chainTable; + U32* const hashTable = hc4->hashTable; + const LZ4HC_CCtx_internal* const dictCtx = hc4->dictCtx; + const BYTE* const prefixPtr = hc4->prefixStart; + const U32 prefixIdx = hc4->dictLimit; + const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx; + const int withinStartDistance = (hc4->lowLimit + (LZ4_DISTANCE_MAX + 1) > ipIndex); + const U32 lowestMatchIndex = (withinStartDistance) ? hc4->lowLimit : ipIndex - LZ4_DISTANCE_MAX; + const BYTE* const dictStart = hc4->dictStart; + const U32 dictIdx = hc4->lowLimit; + const BYTE* const dictEnd = dictStart + prefixIdx - dictIdx; + int const lookBackLength = (int)(ip-iLowLimit); + int nbAttempts = maxNbAttempts; + U32 matchChainPos = 0; + U32 const pattern = LZ4_read32(ip); + U32 matchIndex; + repeat_state_e repeat = rep_untested; + size_t srcPatternLength = 0; + int offset = 0, sBack = 0; + + DEBUGLOG(7, "LZ4HC_InsertAndGetWiderMatch"); + /* First Match */ + LZ4HC_Insert(hc4, ip); /* insert all prior positions up to ip (excluded) */ + matchIndex = hashTable[LZ4HC_hashPtr(ip)]; + DEBUGLOG(7, "First candidate match for pos %u found at index %u / %u (lowestMatchIndex)", + ipIndex, matchIndex, lowestMatchIndex); + + while ((matchIndex>=lowestMatchIndex) && (nbAttempts>0)) { + int matchLength=0; + nbAttempts--; + assert(matchIndex < ipIndex); + if (favorDecSpeed && (ipIndex - matchIndex < 8)) { + /* do nothing: + * favorDecSpeed intentionally skips matches with offset < 8 */ + } else if (matchIndex >= prefixIdx) { /* within current Prefix */ + const BYTE* const matchPtr = prefixPtr + (matchIndex - prefixIdx); + assert(matchPtr < ip); + assert(longest >= 1); + if (LZ4_read16(iLowLimit + longest - 1) == LZ4_read16(matchPtr - lookBackLength + longest - 1)) { + if ((LZ4_read32(matchPtr) & LZ4T_HASH_MASK) == (pattern & LZ4T_HASH_MASK)) { + int const back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, prefixPtr) : 0; + matchLength = MINMATCH + (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit); + matchLength -= back; + if (matchLength > longest) { + longest = matchLength; + offset = (int)(ipIndex - matchIndex); + sBack = back; + DEBUGLOG(7, "Found match of len=%i within prefix, offset=%i, back=%i", longest, offset, -back); + } } } + } else { /* lowestMatchIndex <= matchIndex < dictLimit : within Ext Dict */ + const BYTE* const matchPtr = dictStart + (matchIndex - dictIdx); + assert(matchIndex >= dictIdx); + if ( likely(matchIndex <= prefixIdx - 4) + && ((LZ4_read32(matchPtr) & LZ4T_HASH_MASK) == (pattern & LZ4T_HASH_MASK)) ) { + int back = 0; + const BYTE* vLimit = ip + (prefixIdx - matchIndex); + if (vLimit > iHighLimit) vLimit = iHighLimit; + matchLength = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; + if ((ip+matchLength == vLimit) && (vLimit < iHighLimit)) + matchLength += LZ4_count(ip+matchLength, prefixPtr, iHighLimit); + back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictStart) : 0; + matchLength -= back; + if (matchLength > longest) { + longest = matchLength; + offset = (int)(ipIndex - matchIndex); + sBack = back; + DEBUGLOG(7, "Found match of len=%i within dict, offset=%i, back=%i", longest, offset, -back); + } } } + + if (chainSwap && matchLength==longest) { /* better match => select a better chain */ + assert(lookBackLength==0); /* search forward only */ + if (matchIndex + (U32)longest <= ipIndex) { + int const kTrigger = 4; + U32 distanceToNextMatch = 1; + int const end = longest - MINMATCH + 1; + int step = 1; + int accel = 1 << kTrigger; + int pos; + for (pos = 0; pos < end; pos += step) { + U32 const candidateDist = DELTANEXTU16(chainTable, matchIndex + (U32)pos); + step = (accel++ >> kTrigger); + if (candidateDist > distanceToNextMatch) { + distanceToNextMatch = candidateDist; + matchChainPos = (U32)pos; + accel = 1 << kTrigger; + } } + if (distanceToNextMatch > 1) { + if (distanceToNextMatch > matchIndex) break; /* avoid overflow */ + matchIndex -= distanceToNextMatch; + continue; + } } } + + { U32 const distNextMatch = DELTANEXTU16(chainTable, matchIndex); + if (patternAnalysis && distNextMatch==1 && matchChainPos==0) { + U32 const matchCandidateIdx = matchIndex-1; + /* may be a repeated pattern */ + if (repeat == rep_untested) { + if ( ((pattern & 0xFFFF) == (pattern >> 16)) + & ((pattern & 0xFF) == (pattern >> 24)) ) { + DEBUGLOG(7, "Repeat pattern detected, char %02X", pattern >> 24); + repeat = rep_confirmed; + srcPatternLength = LZ4HC_countPattern(ip+sizeof(pattern), iHighLimit, pattern) + sizeof(pattern); + } else { + repeat = rep_not; + } } + if ( (repeat == rep_confirmed) && (matchCandidateIdx >= lowestMatchIndex) + && LZ4HC_protectDictEnd(prefixIdx, matchCandidateIdx) ) { + const int extDict = matchCandidateIdx < prefixIdx; + const BYTE* const matchPtr = extDict ? dictStart + (matchCandidateIdx - dictIdx) : prefixPtr + (matchCandidateIdx - prefixIdx); + if (LZ4_read32(matchPtr) == pattern) { /* good candidate */ + const BYTE* const iLimit = extDict ? dictEnd : iHighLimit; + size_t forwardPatternLength = LZ4HC_countPattern(matchPtr+sizeof(pattern), iLimit, pattern) + sizeof(pattern); + if (extDict && matchPtr + forwardPatternLength == iLimit) { + U32 const rotatedPattern = LZ4HC_rotatePattern(forwardPatternLength, pattern); + forwardPatternLength += LZ4HC_countPattern(prefixPtr, iHighLimit, rotatedPattern); + } + { const BYTE* const lowestMatchPtr = extDict ? dictStart : prefixPtr; + size_t backLength = LZ4HC_reverseCountPattern(matchPtr, lowestMatchPtr, pattern); + size_t currentSegmentLength; + if (!extDict + && matchPtr - backLength == prefixPtr + && dictIdx < prefixIdx) { + U32 const rotatedPattern = LZ4HC_rotatePattern((U32)(-(int)backLength), pattern); + backLength += LZ4HC_reverseCountPattern(dictEnd, dictStart, rotatedPattern); + } + /* Limit backLength not go further than lowestMatchIndex */ + backLength = matchCandidateIdx - MAX(matchCandidateIdx - (U32)backLength, lowestMatchIndex); + assert(matchCandidateIdx - backLength >= lowestMatchIndex); + currentSegmentLength = backLength + forwardPatternLength; + /* Adjust to end of pattern if the source pattern fits, otherwise the beginning of the pattern */ + if ( (currentSegmentLength >= srcPatternLength) /* current pattern segment large enough to contain full srcPatternLength */ + && (forwardPatternLength <= srcPatternLength) ) { /* haven't reached this position yet */ + U32 const newMatchIndex = matchCandidateIdx + (U32)forwardPatternLength - (U32)srcPatternLength; /* best position, full pattern, might be followed by more match */ + if (LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) + matchIndex = newMatchIndex; + else { + /* Can only happen if started in the prefix */ + assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict); + matchIndex = prefixIdx; + } + } else { + U32 const newMatchIndex = matchCandidateIdx - (U32)backLength; /* farthest position in current segment, will find a match of length currentSegmentLength + maybe some back */ + if (!LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) { + assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict); + matchIndex = prefixIdx; + } else { + matchIndex = newMatchIndex; + if (lookBackLength==0) { /* no back possible */ + size_t const maxML = MIN(currentSegmentLength, srcPatternLength); + if ((size_t)longest < maxML) { + assert(prefixPtr - prefixIdx + matchIndex != ip); + if ((size_t)(ip - prefixPtr) + prefixIdx - matchIndex > LZ4_DISTANCE_MAX) break; + assert(maxML < 2 GB); + longest = (int)maxML; + offset = (int)(ipIndex - matchIndex); + assert(sBack == 0); + DEBUGLOG(7, "Found repeat pattern match of len=%i, offset=%i", longest, offset); + } + { U32 const distToNextPattern = DELTANEXTU16(chainTable, matchIndex); + if (distToNextPattern > matchIndex) break; /* avoid overflow */ + matchIndex -= distToNextPattern; + } } } } } + continue; + } } + } } /* PA optimization */ + + /* follow current chain */ + matchIndex -= DELTANEXTU16(chainTable, matchIndex + matchChainPos); + + } /* while ((matchIndex>=lowestMatchIndex) && (nbAttempts)) */ + + if ( dict == usingDictCtxHc + && nbAttempts > 0 + && withinStartDistance) { + size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit; + U32 dictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)]; + assert(dictEndOffset <= 1 GB); + matchIndex = dictMatchIndex + lowestMatchIndex - (U32)dictEndOffset; + if (dictMatchIndex>0) DEBUGLOG(7, "dictEndOffset = %zu, dictMatchIndex = %u => relative matchIndex = %i", dictEndOffset, dictMatchIndex, (int)dictMatchIndex - (int)dictEndOffset); + while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX && nbAttempts--) { + const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + dictMatchIndex; + + if (LZ4_read32(matchPtr) == pattern) { + int mlt; + int back = 0; + const BYTE* vLimit = ip + (dictEndOffset - dictMatchIndex); + if (vLimit > iHighLimit) vLimit = iHighLimit; + mlt = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; + back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->prefixStart) : 0; + mlt -= back; + if (mlt > longest) { + longest = mlt; + offset = (int)(ipIndex - matchIndex); + sBack = back; + DEBUGLOG(7, "found match of length %i within extDictCtx", longest); + } } + + { U32 const nextOffset = DELTANEXTU16(dictCtx->chainTable, dictMatchIndex); + dictMatchIndex -= nextOffset; + matchIndex -= nextOffset; + } } } + + { LZ4HC_match_t md; + assert(longest >= 0); + md.len = longest; + md.off = offset; + md.back = sBack; + return md; + } +} + +LZ4_FORCE_INLINE LZ4HC_match_t +LZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4, /* Index table will be updated */ + const BYTE* const ip, const BYTE* const iLimit, + const int maxNbAttempts, + const int patternAnalysis, + const dictCtx_directive dict) +{ + DEBUGLOG(7, "LZ4HC_InsertAndFindBestMatch"); + /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), + * but this won't be the case here, as we define iLowLimit==ip, + * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */ + return LZ4HC_InsertAndGetWiderMatch(hc4, ip, ip, iLimit, MINMATCH-1, maxNbAttempts, patternAnalysis, 0 /*chainSwap*/, dict, favorCompressionRatio); +} + + +LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( + LZ4HC_CCtx_internal* const ctx, + const char* const source, + char* const dest, + int* srcSizePtr, + int const maxOutputSize, + int maxNbAttempts, + const limitedOutput_directive limit, + const dictCtx_directive dict + ) +{ + const int inputSize = *srcSizePtr; + const int patternAnalysis = (maxNbAttempts > 128); /* levels 9+ */ + + 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* optr = (BYTE*) dest; + BYTE* op = (BYTE*) dest; + BYTE* oend = op + maxOutputSize; + + const BYTE* start0; + const BYTE* start2 = NULL; + const BYTE* start3 = NULL; + LZ4HC_match_t m0, m1, m2, m3; + const LZ4HC_match_t nomatch = {0, 0, 0}; + + /* init */ + DEBUGLOG(5, "LZ4HC_compress_hashChain (dict?=>%i)", dict); + *srcSizePtr = 0; + if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */ + if (inputSize < LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */ + + /* Main Loop */ + while (ip <= mflimit) { + m1 = LZ4HC_InsertAndFindBestMatch(ctx, ip, matchlimit, maxNbAttempts, patternAnalysis, dict); + if (m1.len encode ML1 immediately */ + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + m1.len, m1.off, + limit, oend) ) + goto _dest_overflow; + continue; + } + + if (start0 < ip) { /* first match was skipped at least once */ + if (start2 < ip + m0.len) { /* squeezing ML1 between ML0(original ML1) and ML2 */ + ip = start0; m1 = m0; /* restore initial Match1 */ + } } + + /* Here, start0==ip */ + if ((start2 - ip) < 3) { /* First Match too small : removed */ + ip = start2; + m1 = m2; + goto _Search2; + } + +_Search3: + if ((start2 - ip) < OPTIMAL_ML) { + int correction; + int new_ml = m1.len; + if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML; + if (ip+new_ml > start2 + m2.len - MINMATCH) + new_ml = (int)(start2 - ip) + m2.len - MINMATCH; + correction = new_ml - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + m2.len -= correction; + } + } + + if (start2 + m2.len <= mflimit) { + start3 = start2 + m2.len - 3; + m3 = LZ4HC_InsertAndGetWiderMatch(ctx, + start3, start2, matchlimit, m2.len, + maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio); + start3 += m3.back; + } else { + m3 = nomatch; /* do not search further */ + } + + if (m3.len <= m2.len) { /* No better match => encode ML1 and ML2 */ + /* ip & ref are known; Now for ml */ + if (start2 < ip+m1.len) m1.len = (int)(start2 - ip); + /* Now, encode 2 sequences */ + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + m1.len, m1.off, + limit, oend) ) + goto _dest_overflow; + ip = start2; + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + m2.len, m2.off, + limit, oend) ) { + m1 = m2; + goto _dest_overflow; + } + continue; + } + + if (start3 < ip+m1.len+3) { /* Not enough space for match 2 : remove it */ + if (start3 >= (ip+m1.len)) { /* can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 */ + if (start2 < ip+m1.len) { + int correction = (int)(ip+m1.len - start2); + start2 += correction; + m2.len -= correction; + if (m2.len < MINMATCH) { + start2 = start3; + m2 = m3; + } + } + + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + m1.len, m1.off, + limit, oend) ) + goto _dest_overflow; + ip = start3; + m1 = m3; + + start0 = start2; + m0 = m2; + goto _Search2; + } + + start2 = start3; + m2 = m3; + goto _Search3; + } + + /* + * OK, now we have 3 ascending matches; + * let's write the first one ML1. + * ip & ref are known; Now decide ml. + */ + if (start2 < ip+m1.len) { + if ((start2 - ip) < OPTIMAL_ML) { + int correction; + if (m1.len > OPTIMAL_ML) m1.len = OPTIMAL_ML; + if (ip + m1.len > start2 + m2.len - MINMATCH) + m1.len = (int)(start2 - ip) + m2.len - MINMATCH; + correction = m1.len - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + m2.len -= correction; + } + } else { + m1.len = (int)(start2 - ip); + } + } + optr = op; + if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + m1.len, m1.off, + limit, oend) ) + goto _dest_overflow; + + /* ML2 becomes ML1 */ + ip = start2; m1 = m2; + + /* ML3 becomes ML2 */ + start2 = start3; m2 = m3; + + /* let's find a new ML3 */ + goto _Search3; + } + +_last_literals: + /* Encode Last Literals */ + { size_t lastRunSize = (size_t)(iend - anchor); /* literals */ + size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255; + size_t const totalSize = 1 + llAdd + lastRunSize; + if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ + if (limit && (op + totalSize > oend)) { + if (limit == limitedOutput) return 0; + /* adapt lastRunSize to fill 'dest' */ + lastRunSize = (size_t)(oend - op) - 1 /*token*/; + llAdd = (lastRunSize + 256 - RUN_MASK) / 256; + lastRunSize -= llAdd; + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize); + ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */ + + if (lastRunSize >= RUN_MASK) { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = (RUN_MASK << ML_BITS); + for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRunSize << ML_BITS); + } + LZ4_memcpy(op, anchor, lastRunSize); + op += lastRunSize; + } + + /* End */ + *srcSizePtr = (int) (((const char*)ip) - source); + return (int) (((char*)op)-dest); + +_dest_overflow: + if (limit == fillOutput) { + /* Assumption : @ip, @anchor, @optr and @m1 must be set correctly */ + size_t const ll = (size_t)(ip - anchor); + size_t const ll_addbytes = (ll + 240) / 255; + size_t const ll_totalCost = 1 + ll_addbytes + ll; + BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */ + DEBUGLOG(6, "Last sequence overflowing"); + op = optr; /* restore correct out pointer */ + if (op + ll_totalCost <= maxLitPos) { + /* ll validated; now adjust match length */ + size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost)); + size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255); + assert(maxMlSize < INT_MAX); assert(m1.len >= 0); + if ((size_t)m1.len > maxMlSize) m1.len = (int)maxMlSize; + if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + m1.len >= MFLIMIT) { + LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m1.len, m1.off, notLimited, oend); + } } + goto _last_literals; + } + /* compression failed */ + return 0; +} + + +static int LZ4HC_compress_optimal( LZ4HC_CCtx_internal* ctx, + const char* const source, char* dst, + int* srcSizePtr, int dstCapacity, + int const nbSearches, size_t sufficient_len, + const limitedOutput_directive limit, int const fullUpdate, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed); + + +LZ4_FORCE_INLINE int +LZ4HC_compress_generic_internal ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + const limitedOutput_directive limit, + const dictCtx_directive dict + ) +{ + typedef enum { lz4mid, lz4hc, lz4opt } lz4hc_strat_e; + typedef struct { + lz4hc_strat_e strat; + int nbSearches; + U32 targetLength; + } cParams_t; + static const cParams_t clTable[LZ4HC_CLEVEL_MAX+1] = { + { lz4mid, 2, 16 }, /* 0, unused */ + { lz4mid, 2, 16 }, /* 1, unused */ + { lz4mid, 2, 16 }, /* 2 */ + { lz4hc, 4, 16 }, /* 3 */ + { lz4hc, 8, 16 }, /* 4 */ + { lz4hc, 16, 16 }, /* 5 */ + { lz4hc, 32, 16 }, /* 6 */ + { lz4hc, 64, 16 }, /* 7 */ + { lz4hc, 128, 16 }, /* 8 */ + { lz4hc, 256, 16 }, /* 9 */ + { lz4opt, 96, 64 }, /*10==LZ4HC_CLEVEL_OPT_MIN*/ + { lz4opt, 512,128 }, /*11 */ + { lz4opt,16384,LZ4_OPT_NUM }, /* 12==LZ4HC_CLEVEL_MAX */ + }; + + DEBUGLOG(5, "LZ4HC_compress_generic_internal(src=%p, srcSize=%d)", + src, *srcSizePtr); + + if (limit == fillOutput && dstCapacity < 1) return 0; /* Impossible to store anything */ + if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size (too large or negative) */ + + ctx->end += *srcSizePtr; + /* note : clevel convention is a bit different from lz4frame, + * possibly something worth revisiting for consistency */ + if (cLevel < 1) + cLevel = LZ4HC_CLEVEL_DEFAULT; + cLevel = MIN(LZ4HC_CLEVEL_MAX, cLevel); + { cParams_t const cParam = clTable[cLevel]; + HCfavor_e const favor = ctx->favorDecSpeed ? favorDecompressionSpeed : favorCompressionRatio; + int result; + + if (cParam.strat == lz4mid) { + result = LZ4HC_compress_2hashes(ctx, + src, dst, srcSizePtr, dstCapacity, + limit, dict); + } else if (cParam.strat == lz4hc) { + result = LZ4HC_compress_hashChain(ctx, + src, dst, srcSizePtr, dstCapacity, + cParam.nbSearches, limit, dict); + } else { + assert(cParam.strat == lz4opt); + result = LZ4HC_compress_optimal(ctx, + src, dst, srcSizePtr, dstCapacity, + cParam.nbSearches, cParam.targetLength, limit, + cLevel == LZ4HC_CLEVEL_MAX, /* ultra mode */ + dict, favor); + } + if (result <= 0) ctx->dirty = 1; + return result; + } +} + +static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock); + +static int +LZ4HC_compress_generic_noDictCtx ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + limitedOutput_directive limit + ) +{ + assert(ctx->dictCtx == NULL); + return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, noDictCtx); +} + +static int +LZ4HC_compress_generic_dictCtx ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + limitedOutput_directive limit + ) +{ + const size_t position = (size_t)(ctx->end - ctx->prefixStart) + (ctx->dictLimit - ctx->lowLimit); + assert(ctx->dictCtx != NULL); + if (position >= 64 KB) { + ctx->dictCtx = NULL; + return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } else if (position == 0 && *srcSizePtr > 4 KB) { + LZ4_memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal)); + LZ4HC_setExternalDict(ctx, (const BYTE *)src); + ctx->compressionLevel = (short)cLevel; + return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } else { + return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, usingDictCtxHc); + } +} + +static int +LZ4HC_compress_generic ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + limitedOutput_directive limit + ) +{ + if (ctx->dictCtx == NULL) { + return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } else { + return LZ4HC_compress_generic_dictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } +} + + +int LZ4_sizeofStateHC(void) { return (int)sizeof(LZ4_streamHC_t); } + +static size_t LZ4_streamHC_t_alignment(void) +{ +#if LZ4_ALIGN_TEST + typedef struct { char c; LZ4_streamHC_t t; } t_a; + return sizeof(t_a) - sizeof(LZ4_streamHC_t); +#else + return 1; /* effectively disabled */ +#endif +} + +/* state is presumed correctly initialized, + * in which case its size and alignment have already been validate */ +int LZ4_compress_HC_extStateHC_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) +{ + LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)state)->internal_donotuse; + if (!LZ4_isAligned(state, LZ4_streamHC_t_alignment())) return 0; + LZ4_resetStreamHC_fast((LZ4_streamHC_t*)state, compressionLevel); + LZ4HC_init_internal (ctx, (const BYTE*)src); + if (dstCapacity < LZ4_compressBound(srcSize)) + return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, limitedOutput); + else + return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, notLimited); +} + +int LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) +{ + LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx)); + if (ctx==NULL) return 0; /* init failure */ + return LZ4_compress_HC_extStateHC_fastReset(state, src, dst, srcSize, dstCapacity, compressionLevel); +} + +int LZ4_compress_HC(const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) +{ + int cSize; +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + LZ4_streamHC_t* const statePtr = (LZ4_streamHC_t*)ALLOC(sizeof(LZ4_streamHC_t)); + if (statePtr==NULL) return 0; +#else + LZ4_streamHC_t state; + LZ4_streamHC_t* const statePtr = &state; +#endif + DEBUGLOG(5, "LZ4_compress_HC") + cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel); +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + FREEMEM(statePtr); +#endif + return cSize; +} + +/* state is presumed sized correctly (>= sizeof(LZ4_streamHC_t)) */ +int LZ4_compress_HC_destSize(void* state, const char* source, char* dest, int* sourceSizePtr, int targetDestSize, int cLevel) +{ + LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx)); + if (ctx==NULL) return 0; /* init failure */ + LZ4HC_init_internal(&ctx->internal_donotuse, (const BYTE*) source); + LZ4_setCompressionLevel(ctx, cLevel); + return LZ4HC_compress_generic(&ctx->internal_donotuse, source, dest, sourceSizePtr, targetDestSize, cLevel, fillOutput); +} + + + +/************************************** +* Streaming Functions +**************************************/ +/* allocation */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4_streamHC_t* LZ4_createStreamHC(void) +{ + LZ4_streamHC_t* const state = + (LZ4_streamHC_t*)ALLOC_AND_ZERO(sizeof(LZ4_streamHC_t)); + if (state == NULL) return NULL; + LZ4_setCompressionLevel(state, LZ4HC_CLEVEL_DEFAULT); + return state; +} + +int LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr) +{ + DEBUGLOG(4, "LZ4_freeStreamHC(%p)", LZ4_streamHCPtr); + if (!LZ4_streamHCPtr) return 0; /* support free on NULL */ + FREEMEM(LZ4_streamHCPtr); + return 0; +} +#endif + + +LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size) +{ + LZ4_streamHC_t* const LZ4_streamHCPtr = (LZ4_streamHC_t*)buffer; + DEBUGLOG(4, "LZ4_initStreamHC(%p, %u)", buffer, (unsigned)size); + /* check conditions */ + if (buffer == NULL) return NULL; + if (size < sizeof(LZ4_streamHC_t)) return NULL; + if (!LZ4_isAligned(buffer, LZ4_streamHC_t_alignment())) return NULL; + /* init */ + { LZ4HC_CCtx_internal* const hcstate = &(LZ4_streamHCPtr->internal_donotuse); + MEM_INIT(hcstate, 0, sizeof(*hcstate)); } + LZ4_setCompressionLevel(LZ4_streamHCPtr, LZ4HC_CLEVEL_DEFAULT); + return LZ4_streamHCPtr; +} + +/* just a stub */ +void LZ4_resetStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) +{ + LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); + LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel); +} + +void LZ4_resetStreamHC_fast (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) +{ + LZ4HC_CCtx_internal* const s = &LZ4_streamHCPtr->internal_donotuse; + DEBUGLOG(5, "LZ4_resetStreamHC_fast(%p, %d)", LZ4_streamHCPtr, compressionLevel); + if (s->dirty) { + LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); + } else { + assert(s->end >= s->prefixStart); + s->dictLimit += (U32)(s->end - s->prefixStart); + s->prefixStart = NULL; + s->end = NULL; + s->dictCtx = NULL; + } + LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel); +} + +void LZ4_setCompressionLevel(LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) +{ + DEBUGLOG(5, "LZ4_setCompressionLevel(%p, %d)", LZ4_streamHCPtr, compressionLevel); + if (compressionLevel < 1) compressionLevel = LZ4HC_CLEVEL_DEFAULT; + if (compressionLevel > LZ4HC_CLEVEL_MAX) compressionLevel = LZ4HC_CLEVEL_MAX; + LZ4_streamHCPtr->internal_donotuse.compressionLevel = (short)compressionLevel; +} + +void LZ4_favorDecompressionSpeed(LZ4_streamHC_t* LZ4_streamHCPtr, int favor) +{ + LZ4_streamHCPtr->internal_donotuse.favorDecSpeed = (favor!=0); +} + +/* LZ4_loadDictHC() : + * LZ4_streamHCPtr is presumed properly initialized */ +int LZ4_loadDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, + const char* dictionary, int dictSize) +{ + LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse; + DEBUGLOG(4, "LZ4_loadDictHC(ctx:%p, dict:%p, dictSize:%d)", LZ4_streamHCPtr, dictionary, dictSize); + assert(LZ4_streamHCPtr != NULL); + if (dictSize > 64 KB) { + dictionary += (size_t)dictSize - 64 KB; + dictSize = 64 KB; + } + /* need a full initialization, there are bad side-effects when using resetFast() */ + { int const cLevel = ctxPtr->compressionLevel; + LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); + LZ4_setCompressionLevel(LZ4_streamHCPtr, cLevel); + } + LZ4HC_init_internal (ctxPtr, (const BYTE*)dictionary); + ctxPtr->end = (const BYTE*)dictionary + dictSize; + if (dictSize >= LZ4HC_HASHSIZE) LZ4HC_Insert (ctxPtr, ctxPtr->end-3); + return dictSize; +} + +void LZ4_attach_HC_dictionary(LZ4_streamHC_t *working_stream, const LZ4_streamHC_t *dictionary_stream) { + working_stream->internal_donotuse.dictCtx = dictionary_stream != NULL ? &(dictionary_stream->internal_donotuse) : NULL; +} + +/* compression */ + +static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock) +{ + DEBUGLOG(4, "LZ4HC_setExternalDict(%p, %p)", ctxPtr, newBlock); + if (ctxPtr->end >= ctxPtr->prefixStart + 4) + LZ4HC_Insert (ctxPtr, ctxPtr->end-3); /* Referencing remaining dictionary content */ + + /* Only one memory segment for extDict, so any previous extDict is lost at this stage */ + ctxPtr->lowLimit = ctxPtr->dictLimit; + ctxPtr->dictStart = ctxPtr->prefixStart; + ctxPtr->dictLimit += (U32)(ctxPtr->end - ctxPtr->prefixStart); + ctxPtr->prefixStart = newBlock; + ctxPtr->end = newBlock; + ctxPtr->nextToUpdate = ctxPtr->dictLimit; /* match referencing will resume from there */ + + /* cannot reference an extDict and a dictCtx at the same time */ + ctxPtr->dictCtx = NULL; +} + +static int +LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr, + const char* src, char* dst, + int* srcSizePtr, int dstCapacity, + limitedOutput_directive limit) +{ + LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse; + DEBUGLOG(5, "LZ4_compressHC_continue_generic(ctx=%p, src=%p, srcSize=%d, limit=%d)", + LZ4_streamHCPtr, src, *srcSizePtr, limit); + assert(ctxPtr != NULL); + /* auto-init if forgotten */ + if (ctxPtr->prefixStart == NULL) LZ4HC_init_internal (ctxPtr, (const BYTE*) src); + + /* Check overflow */ + if ((size_t)(ctxPtr->end - ctxPtr->prefixStart) + ctxPtr->dictLimit > 2 GB) { + size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->prefixStart); + if (dictSize > 64 KB) dictSize = 64 KB; + LZ4_loadDictHC(LZ4_streamHCPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize); + } + + /* Check if blocks follow each other */ + if ((const BYTE*)src != ctxPtr->end) + LZ4HC_setExternalDict(ctxPtr, (const BYTE*)src); + + /* Check overlapping input/dictionary space */ + { const BYTE* sourceEnd = (const BYTE*) src + *srcSizePtr; + const BYTE* const dictBegin = ctxPtr->dictStart; + const BYTE* const dictEnd = ctxPtr->dictStart + (ctxPtr->dictLimit - ctxPtr->lowLimit); + if ((sourceEnd > dictBegin) && ((const BYTE*)src < dictEnd)) { + if (sourceEnd > dictEnd) sourceEnd = dictEnd; + ctxPtr->lowLimit += (U32)(sourceEnd - ctxPtr->dictStart); + ctxPtr->dictStart += (U32)(sourceEnd - ctxPtr->dictStart); + /* invalidate dictionary is it's too small */ + if (ctxPtr->dictLimit - ctxPtr->lowLimit < LZ4HC_HASHSIZE) { + ctxPtr->lowLimit = ctxPtr->dictLimit; + ctxPtr->dictStart = ctxPtr->prefixStart; + } } } + + return LZ4HC_compress_generic (ctxPtr, src, dst, srcSizePtr, dstCapacity, ctxPtr->compressionLevel, limit); +} + +int LZ4_compress_HC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int srcSize, int dstCapacity) +{ + DEBUGLOG(5, "LZ4_compress_HC_continue"); + if (dstCapacity < LZ4_compressBound(srcSize)) + return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, limitedOutput); + else + return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, notLimited); +} + +int LZ4_compress_HC_continue_destSize (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int* srcSizePtr, int targetDestSize) +{ + return LZ4_compressHC_continue_generic(LZ4_streamHCPtr, src, dst, srcSizePtr, targetDestSize, fillOutput); +} + + + +/* LZ4_saveDictHC : + * save history content + * into a user-provided buffer + * which is then used to continue compression + */ +int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize) +{ + LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse; + int const prefixSize = (int)(streamPtr->end - streamPtr->prefixStart); + DEBUGLOG(5, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize); + assert(prefixSize >= 0); + if (dictSize > 64 KB) dictSize = 64 KB; + if (dictSize < 4) dictSize = 0; + if (dictSize > prefixSize) dictSize = prefixSize; + if (safeBuffer == NULL) assert(dictSize == 0); + if (dictSize > 0) + LZ4_memmove(safeBuffer, streamPtr->end - dictSize, (size_t)dictSize); + { U32 const endIndex = (U32)(streamPtr->end - streamPtr->prefixStart) + streamPtr->dictLimit; + streamPtr->end = (safeBuffer == NULL) ? NULL : (const BYTE*)safeBuffer + dictSize; + streamPtr->prefixStart = (const BYTE*)safeBuffer; + streamPtr->dictLimit = endIndex - (U32)dictSize; + streamPtr->lowLimit = endIndex - (U32)dictSize; + streamPtr->dictStart = streamPtr->prefixStart; + if (streamPtr->nextToUpdate < streamPtr->dictLimit) + streamPtr->nextToUpdate = streamPtr->dictLimit; + } + return dictSize; +} + + +/*************************************************** +* Deprecated Functions +***************************************************/ + +/* These functions currently generate deprecation warnings */ + +/* Wrappers for deprecated compression functions */ +int LZ4_compressHC(const char* src, char* dst, int srcSize) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), 0); } +int LZ4_compressHC_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, 0); } +int LZ4_compressHC2(const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); } +int LZ4_compressHC2_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, cLevel); } +int LZ4_compressHC_withStateHC (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, LZ4_compressBound(srcSize), 0); } +int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, maxDstSize, 0); } +int LZ4_compressHC2_withStateHC (void* state, const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); } +int LZ4_compressHC2_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, cLevel); } +int LZ4_compressHC_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, LZ4_compressBound(srcSize)); } +int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, maxDstSize); } + + +/* Deprecated streaming functions */ +int LZ4_sizeofStreamStateHC(void) { return sizeof(LZ4_streamHC_t); } + +/* state is presumed correctly sized, aka >= sizeof(LZ4_streamHC_t) + * @return : 0 on success, !=0 if error */ +int LZ4_resetStreamStateHC(void* state, char* inputBuffer) +{ + LZ4_streamHC_t* const hc4 = LZ4_initStreamHC(state, sizeof(*hc4)); + if (hc4 == NULL) return 1; /* init failed */ + LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer); + return 0; +} + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +void* LZ4_createHC (const char* inputBuffer) +{ + LZ4_streamHC_t* const hc4 = LZ4_createStreamHC(); + if (hc4 == NULL) return NULL; /* not enough memory */ + LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer); + return hc4; +} + +int LZ4_freeHC (void* LZ4HC_Data) +{ + if (!LZ4HC_Data) return 0; /* support free on NULL */ + FREEMEM(LZ4HC_Data); + return 0; +} +#endif + +int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int cLevel) +{ + return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, 0, cLevel, notLimited); +} + +int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int dstCapacity, int cLevel) +{ + return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, dstCapacity, cLevel, limitedOutput); +} + +char* LZ4_slideInputBufferHC(void* LZ4HC_Data) +{ + LZ4HC_CCtx_internal* const s = &((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse; + const BYTE* const bufferStart = s->prefixStart - s->dictLimit + s->lowLimit; + LZ4_resetStreamHC_fast((LZ4_streamHC_t*)LZ4HC_Data, s->compressionLevel); + /* ugly conversion trick, required to evade (const char*) -> (char*) cast-qual warning :( */ + return (char*)(uptrval)bufferStart; +} + + +/* ================================================ + * LZ4 Optimal parser (levels [LZ4HC_CLEVEL_OPT_MIN - LZ4HC_CLEVEL_MAX]) + * ===============================================*/ +typedef struct { + int price; + int off; + int mlen; + int litlen; +} LZ4HC_optimal_t; + +#ifndef LZ4T +/* price in bytes */ +LZ4_FORCE_INLINE int LZ4HC_literalsPrice(int const litlen) +{ + int price = litlen; + assert(litlen >= 0); + if (litlen >= (int)RUN_MASK) + price += 1 + ((litlen-(int)RUN_MASK) / 255); + return price; +} + + +/* requires mlen >= MINMATCH */ +LZ4_FORCE_INLINE int LZ4HC_sequencePrice(int litlen, int mlen) +{ + int price = 1 + 2 ; /* token + 16-bit offset */ + assert(litlen >= 0); + assert(mlen >= MINMATCH); + + price += LZ4HC_literalsPrice(litlen); + + if (mlen >= (int)(ML_MASK+MINMATCH)) + price += 1 + ((mlen-(int)(ML_MASK+MINMATCH)) / 255); + + return price; +} +#else +LZ4_FORCE_INLINE int LZ4HC_literalsPrice(int const litlen); +LZ4_FORCE_INLINE int LZ4HC_sequencePrice(int litlen, int mlen); +#endif + + +LZ4_FORCE_INLINE LZ4HC_match_t +LZ4HC_FindLongerMatch(LZ4HC_CCtx_internal* const ctx, + const BYTE* ip, const BYTE* const iHighLimit, + int minLen, int nbSearches, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed) +{ + LZ4HC_match_t const match0 = { 0 , 0, 0 }; + /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), + * but this won't be the case here, as we define iLowLimit==ip, + ** so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */ + LZ4HC_match_t md = LZ4HC_InsertAndGetWiderMatch(ctx, ip, ip, iHighLimit, minLen, nbSearches, 1 /*patternAnalysis*/, 1 /*chainSwap*/, dict, favorDecSpeed); + assert(md.back == 0); + if (md.len <= minLen) return match0; + if (favorDecSpeed) { + if ((md.len>18) & (md.len<=36)) md.len=18; /* favor dec.speed (shortcut) */ + } + return md; +} + +#ifdef LZ4T +int LZ4T_lastLiterals ( + const BYTE** _ip, + BYTE** _op, + const BYTE** _anchor, + limitedOutput_directive limit, + BYTE* oend, + size_t length); +#endif + +static int LZ4HC_compress_optimal ( LZ4HC_CCtx_internal* ctx, + const char* const source, + char* dst, + int* srcSizePtr, + int dstCapacity, + int const nbSearches, + size_t sufficient_len, + const limitedOutput_directive limit, + int const fullUpdate, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed) +{ + int retval = 0; +#define TRAILING_LITERALS 3 +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + LZ4HC_optimal_t* const opt = (LZ4HC_optimal_t*)ALLOC(sizeof(LZ4HC_optimal_t) * (LZ4_OPT_NUM + TRAILING_LITERALS)); +#else + LZ4HC_optimal_t opt[LZ4_OPT_NUM + TRAILING_LITERALS]; /* ~64 KB, which is a bit large for stack... */ +#endif + + const BYTE* ip = (const BYTE*) source; + const BYTE* anchor = ip; + const BYTE* const iend = ip + *srcSizePtr; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = iend - LASTLITERALS; + BYTE* op = (BYTE*) dst; + BYTE* opSaved = (BYTE*) dst; + BYTE* oend = op + dstCapacity; + int ovml = MINMATCH; /* overflow - last sequence */ + int ovoff = 0; + + /* init */ +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + if (opt == NULL) goto _return_label; +#endif + DEBUGLOG(5, "LZ4HC_compress_optimal(dst=%p, dstCapa=%u)", dst, (unsigned)dstCapacity); + *srcSizePtr = 0; + if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */ + if (sufficient_len >= LZ4_OPT_NUM) sufficient_len = LZ4_OPT_NUM-1; + + /* Main Loop */ + while (ip <= mflimit) { + int const llen = (int)(ip - anchor); + int best_mlen, best_off; + int cur, last_match_pos = 0; + + LZ4HC_match_t const firstMatch = LZ4HC_FindLongerMatch(ctx, ip, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed); + if (firstMatch.len==0) { ip++; continue; } + + if ((size_t)firstMatch.len > sufficient_len) { + /* good enough solution : immediate encoding */ + int const firstML = firstMatch.len; + opSaved = op; + if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), firstML, firstMatch.off, limit, oend) ) { /* updates ip, op and anchor */ + ovml = firstML; + ovoff = firstMatch.off; + goto _dest_overflow; + } + continue; + } + + /* set prices for first positions (literals) */ + { int rPos; + for (rPos = 0 ; rPos < MINMATCH ; rPos++) { + int const cost = LZ4HC_literalsPrice(llen + rPos); + opt[rPos].mlen = 1; + opt[rPos].off = 0; + opt[rPos].litlen = llen + rPos; + opt[rPos].price = cost; + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup", + rPos, cost, opt[rPos].litlen); + } } + /* set prices using initial match */ + { int const matchML = firstMatch.len; /* necessarily < sufficient_len < LZ4_OPT_NUM */ + int const offset = firstMatch.off; + int mlen; + assert(matchML < LZ4_OPT_NUM); + for (mlen = MINMATCH ; mlen <= matchML ; mlen++) { + int const cost = LZ4HC_sequencePrice(llen, mlen); + opt[mlen].mlen = mlen; + opt[mlen].off = offset; + opt[mlen].litlen = llen; + opt[mlen].price = cost; + DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i) -- initial setup", + mlen, cost, mlen); + } } + last_match_pos = firstMatch.len; + { int addLit; + for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) { + opt[last_match_pos+addLit].mlen = 1; /* literal */ + opt[last_match_pos+addLit].off = 0; + opt[last_match_pos+addLit].litlen = addLit; + opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit); + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup", + last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit); + } } + + /* check further positions */ + for (cur = 1; cur < last_match_pos; cur++) { + const BYTE* const curPtr = ip + cur; + LZ4HC_match_t newMatch; + + if (curPtr > mflimit) break; + DEBUGLOG(7, "rPos:%u[%u] vs [%u]%u", + cur, opt[cur].price, opt[cur+1].price, cur+1); + if (fullUpdate) { + /* not useful to search here if next position has same (or lower) cost */ + if ( (opt[cur+1].price <= opt[cur].price) + /* in some cases, next position has same cost, but cost rises sharply after, so a small match would still be beneficial */ + && (opt[cur+MINMATCH].price < opt[cur].price + 3/*min seq price*/) ) + continue; + } else { + /* not useful to search here if next position has same (or lower) cost */ + if (opt[cur+1].price <= opt[cur].price) continue; + } + + DEBUGLOG(7, "search at rPos:%u", cur); + if (fullUpdate) + newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed); + else + /* only test matches of minimum length; slightly faster, but misses a few bytes */ + newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, last_match_pos - cur, nbSearches, dict, favorDecSpeed); + if (!newMatch.len) continue; + + if ( ((size_t)newMatch.len > sufficient_len) + || (newMatch.len + cur >= LZ4_OPT_NUM) ) { + /* immediate encoding */ + best_mlen = newMatch.len; + best_off = newMatch.off; + last_match_pos = cur + 1; + goto encode; + } + + /* before match : set price with literals at beginning */ + { int const baseLitlen = opt[cur].litlen; + int litlen; + for (litlen = 1; litlen < MINMATCH; litlen++) { + int const price = opt[cur].price - LZ4HC_literalsPrice(baseLitlen) + LZ4HC_literalsPrice(baseLitlen+litlen); + int const pos = cur + litlen; + if (price < opt[pos].price) { + opt[pos].mlen = 1; /* literal */ + opt[pos].off = 0; + opt[pos].litlen = baseLitlen+litlen; + opt[pos].price = price; + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)", + pos, price, opt[pos].litlen); + } } } + + /* set prices using match at position = cur */ + { int const matchML = newMatch.len; + int ml = MINMATCH; + + assert(cur + newMatch.len < LZ4_OPT_NUM); + for ( ; ml <= matchML ; ml++) { + int const pos = cur + ml; + int const offset = newMatch.off; + int price; + int ll; + DEBUGLOG(7, "testing price rPos %i (last_match_pos=%i)", + pos, last_match_pos); + if (opt[cur].mlen == 1) { + ll = opt[cur].litlen; + price = ((cur > ll) ? opt[cur - ll].price : 0) + + LZ4HC_sequencePrice(ll, ml); + } else { + ll = 0; + price = opt[cur].price + LZ4HC_sequencePrice(0, ml); + } + + assert((U32)favorDecSpeed <= 1); + if (pos > last_match_pos+TRAILING_LITERALS + || price <= opt[pos].price - (int)favorDecSpeed) { + DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i)", + pos, price, ml); + assert(pos < LZ4_OPT_NUM); + if ( (ml == matchML) /* last pos of last match */ + && (last_match_pos < pos) ) + last_match_pos = pos; + opt[pos].mlen = ml; + opt[pos].off = offset; + opt[pos].litlen = ll; + opt[pos].price = price; + } } } + /* complete following positions with literals */ + { int addLit; + for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) { + opt[last_match_pos+addLit].mlen = 1; /* literal */ + opt[last_match_pos+addLit].off = 0; + opt[last_match_pos+addLit].litlen = addLit; + opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit); + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)", last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit); + } } + } /* for (cur = 1; cur <= last_match_pos; cur++) */ + + assert(last_match_pos < LZ4_OPT_NUM + TRAILING_LITERALS); + best_mlen = opt[last_match_pos].mlen; + best_off = opt[last_match_pos].off; + cur = last_match_pos - best_mlen; + +encode: /* cur, last_match_pos, best_mlen, best_off must be set */ + assert(cur < LZ4_OPT_NUM); + assert(last_match_pos >= 1); /* == 1 when only one candidate */ + DEBUGLOG(6, "reverse traversal, looking for shortest path (last_match_pos=%i)", last_match_pos); + { int candidate_pos = cur; + int selected_matchLength = best_mlen; + int selected_offset = best_off; + while (1) { /* from end to beginning */ + int const next_matchLength = opt[candidate_pos].mlen; /* can be 1, means literal */ + int const next_offset = opt[candidate_pos].off; + DEBUGLOG(7, "pos %i: sequence length %i", candidate_pos, selected_matchLength); + opt[candidate_pos].mlen = selected_matchLength; + opt[candidate_pos].off = selected_offset; + selected_matchLength = next_matchLength; + selected_offset = next_offset; + if (next_matchLength > candidate_pos) break; /* last match elected, first match to encode */ + assert(next_matchLength > 0); /* can be 1, means literal */ + candidate_pos -= next_matchLength; + } } + + /* encode all recorded sequences in order */ + { int rPos = 0; /* relative position (to ip) */ + while (rPos < last_match_pos) { + int const ml = opt[rPos].mlen; + int const offset = opt[rPos].off; + if (ml == 1) { ip++; rPos++; continue; } /* literal; note: can end up with several literals, in which case, skip them */ + rPos += ml; + assert(ml >= MINMATCH); + assert((offset >= 1) && (offset <= LZ4_DISTANCE_MAX)); + opSaved = op; + if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, offset, limit, oend) ) { /* updates ip, op and anchor */ + ovml = ml; + ovoff = offset; + goto _dest_overflow; + } } } + } /* while (ip <= mflimit) */ + +_last_literals: +#ifndef LZ4T + /* Encode Last Literals */ + { size_t lastRunSize = (size_t)(iend - anchor); /* literals */ + size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255; + size_t const totalSize = 1 + llAdd + lastRunSize; + if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ + if (limit && (op + totalSize > oend)) { + if (limit == limitedOutput) { /* Check output limit */ + retval = 0; + goto _return_label; + } + /* adapt lastRunSize to fill 'dst' */ + lastRunSize = (size_t)(oend - op) - 1 /*token*/; + llAdd = (lastRunSize + 256 - RUN_MASK) / 256; + lastRunSize -= llAdd; + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize); + ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */ + + if (lastRunSize >= RUN_MASK) { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = (RUN_MASK << ML_BITS); + for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRunSize << ML_BITS); + } + LZ4_memcpy(op, anchor, lastRunSize); + op += lastRunSize; + } +#else + LZ4T_lastLiterals(UPDATABLE(ip, op, anchor), limit, oend, (size_t)(iend - anchor)); +#endif + + /* End */ + *srcSizePtr = (int) (((const char*)ip) - source); + retval = (int) ((char*)op-dst); + goto _return_label; + +_dest_overflow: +if (limit == fillOutput) { + /* Assumption : ip, anchor, ovml and ovref must be set correctly */ + size_t const ll = (size_t)(ip - anchor); + size_t const ll_addbytes = (ll + 240) / 255; + size_t const ll_totalCost = 1 + ll_addbytes + ll; + BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */ + DEBUGLOG(6, "Last sequence overflowing (only %i bytes remaining)", (int)(oend-1-opSaved)); + op = opSaved; /* restore correct out pointer */ + if (op + ll_totalCost <= maxLitPos) { + /* ll validated; now adjust match length */ + size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost)); + size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255); + assert(maxMlSize < INT_MAX); assert(ovml >= 0); + if ((size_t)ovml > maxMlSize) ovml = (int)maxMlSize; + if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + ovml >= MFLIMIT) { + DEBUGLOG(6, "Space to end : %i + ml (%i)", (int)((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1), ovml); + DEBUGLOG(6, "Before : ip = %p, anchor = %p", ip, anchor); + LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ovml, ovoff, notLimited, oend); + DEBUGLOG(6, "After : ip = %p, anchor = %p", ip, anchor); + } } + goto _last_literals; +} +_return_label: +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + if (opt) FREEMEM(opt); +#endif + return retval; +} diff --git a/tools/lz4hc.h b/tools/lz4hc.h new file mode 100644 index 00000000..f23db5f6 --- /dev/null +++ b/tools/lz4hc.h @@ -0,0 +1,413 @@ +/* + LZ4 HC - High Compression Mode of LZ4 + Header File + Copyright (C) 2011-2020, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +#ifndef LZ4_HC_H_19834876238432 +#define LZ4_HC_H_19834876238432 + +#if defined (__cplusplus) +extern "C" { +#endif + +/* --- Dependency --- */ +/* note : lz4hc requires lz4.h/lz4.c for compilation */ +#include "lz4.h" /* stddef, LZ4LIB_API, LZ4_DEPRECATED */ + + +/* --- Useful constants --- */ +#define LZ4HC_CLEVEL_MIN 2 +#define LZ4HC_CLEVEL_DEFAULT 9 +#define LZ4HC_CLEVEL_OPT_MIN 10 +#define LZ4HC_CLEVEL_MAX 12 + + +/*-************************************ + * Block Compression + **************************************/ +/*! LZ4_compress_HC() : + * Compress data from `src` into `dst`, using the powerful but slower "HC" algorithm. + * `dst` must be already allocated. + * Compression is guaranteed to succeed if `dstCapacity >= LZ4_compressBound(srcSize)` (see "lz4.h") + * Max supported `srcSize` value is LZ4_MAX_INPUT_SIZE (see "lz4.h") + * `compressionLevel` : any value between 1 and LZ4HC_CLEVEL_MAX will work. + * Values > LZ4HC_CLEVEL_MAX behave the same as LZ4HC_CLEVEL_MAX. + * @return : the number of bytes written into 'dst' + * or 0 if compression fails. + */ +LZ4LIB_API int LZ4_compress_HC (const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel); + + +/* Note : + * Decompression functions are provided within "lz4.h" (BSD license) + */ + + +/*! LZ4_compress_HC_extStateHC() : + * Same as LZ4_compress_HC(), but using an externally allocated memory segment for `state`. + * `state` size is provided by LZ4_sizeofStateHC(). + * Memory segment must be aligned on 8-bytes boundaries (which a normal malloc() should do properly). + */ +LZ4LIB_API int LZ4_sizeofStateHC(void); +LZ4LIB_API int LZ4_compress_HC_extStateHC(void* stateHC, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel); + + +/*! LZ4_compress_HC_destSize() : v1.9.0+ + * Will compress as much data as possible from `src` + * to fit into `targetDstSize` budget. + * Result is provided in 2 parts : + * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize) + * or 0 if compression fails. + * `srcSizePtr` : on success, *srcSizePtr is updated to indicate how much bytes were read from `src` + */ +LZ4LIB_API int LZ4_compress_HC_destSize(void* stateHC, + const char* src, char* dst, + int* srcSizePtr, int targetDstSize, + int compressionLevel); + + +/*-************************************ + * Streaming Compression + * Bufferless synchronous API + **************************************/ + typedef union LZ4_streamHC_u LZ4_streamHC_t; /* incomplete type (defined later) */ + +/*! LZ4_createStreamHC() and LZ4_freeStreamHC() : + * These functions create and release memory for LZ4 HC streaming state. + * Newly created states are automatically initialized. + * A same state can be used multiple times consecutively, + * starting with LZ4_resetStreamHC_fast() to start a new stream of blocks. + */ +LZ4LIB_API LZ4_streamHC_t* LZ4_createStreamHC(void); +LZ4LIB_API int LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr); + +/* + These functions compress data in successive blocks of any size, + using previous blocks as dictionary, to improve compression ratio. + One key assumption is that previous blocks (up to 64 KB) remain read-accessible while compressing next blocks. + There is an exception for ring buffers, which can be smaller than 64 KB. + Ring-buffer scenario is automatically detected and handled within LZ4_compress_HC_continue(). + + Before starting compression, state must be allocated and properly initialized. + LZ4_createStreamHC() does both, though compression level is set to LZ4HC_CLEVEL_DEFAULT. + + Selecting the compression level can be done with LZ4_resetStreamHC_fast() (starts a new stream) + or LZ4_setCompressionLevel() (anytime, between blocks in the same stream) (experimental). + LZ4_resetStreamHC_fast() only works on states which have been properly initialized at least once, + which is automatically the case when state is created using LZ4_createStreamHC(). + + After reset, a first "fictional block" can be designated as initial dictionary, + using LZ4_loadDictHC() (Optional). + + Invoke LZ4_compress_HC_continue() to compress each successive block. + The number of blocks is unlimited. + Previous input blocks, including initial dictionary when present, + must remain accessible and unmodified during compression. + + It's allowed to update compression level anytime between blocks, + using LZ4_setCompressionLevel() (experimental). + + 'dst' buffer should be sized to handle worst case scenarios + (see LZ4_compressBound(), it ensures compression success). + In case of failure, the API does not guarantee recovery, + so the state _must_ be reset. + To ensure compression success + whenever `dst` buffer size cannot be made >= LZ4_compressBound(), + consider using LZ4_compress_HC_continue_destSize(). + + Whenever previous input blocks can't be preserved unmodified in-place during compression of next blocks, + it's possible to copy the last blocks into a more stable memory space, using LZ4_saveDictHC(). + Return value of LZ4_saveDictHC() is the size of dictionary effectively saved into 'safeBuffer' (<= 64 KB) + + After completing a streaming compression, + it's possible to start a new stream of blocks, using the same LZ4_streamHC_t state, + just by resetting it, using LZ4_resetStreamHC_fast(). +*/ + +LZ4LIB_API void LZ4_resetStreamHC_fast(LZ4_streamHC_t* streamHCPtr, int compressionLevel); /* v1.9.0+ */ +LZ4LIB_API int LZ4_loadDictHC (LZ4_streamHC_t* streamHCPtr, const char* dictionary, int dictSize); + +LZ4LIB_API int LZ4_compress_HC_continue (LZ4_streamHC_t* streamHCPtr, + const char* src, char* dst, + int srcSize, int maxDstSize); + +/*! LZ4_compress_HC_continue_destSize() : v1.9.0+ + * Similar to LZ4_compress_HC_continue(), + * but will read as much data as possible from `src` + * to fit into `targetDstSize` budget. + * Result is provided into 2 parts : + * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize) + * or 0 if compression fails. + * `srcSizePtr` : on success, *srcSizePtr will be updated to indicate how much bytes were read from `src`. + * Note that this function may not consume the entire input. + */ +LZ4LIB_API int LZ4_compress_HC_continue_destSize(LZ4_streamHC_t* LZ4_streamHCPtr, + const char* src, char* dst, + int* srcSizePtr, int targetDstSize); + +LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize); + + + +/*^********************************************** + * !!!!!! STATIC LINKING ONLY !!!!!! + ***********************************************/ + +/*-****************************************************************** + * PRIVATE DEFINITIONS : + * Do not use these definitions directly. + * They are merely exposed to allow static allocation of `LZ4_streamHC_t`. + * Declare an `LZ4_streamHC_t` directly, rather than any type below. + * Even then, only do so in the context of static linking, as definitions may change between versions. + ********************************************************************/ + +#define LZ4HC_DICTIONARY_LOGSIZE 16 +#define LZ4HC_MAXD (1<= LZ4HC_CLEVEL_OPT_MIN. + */ +LZ4LIB_STATIC_API void LZ4_favorDecompressionSpeed( + LZ4_streamHC_t* LZ4_streamHCPtr, int favor); + +/*! LZ4_resetStreamHC_fast() : v1.9.0+ + * When an LZ4_streamHC_t is known to be in a internally coherent state, + * it can often be prepared for a new compression with almost no work, only + * sometimes falling back to the full, expensive reset that is always required + * when the stream is in an indeterminate state (i.e., the reset performed by + * LZ4_resetStreamHC()). + * + * LZ4_streamHCs are guaranteed to be in a valid state when: + * - returned from LZ4_createStreamHC() + * - reset by LZ4_resetStreamHC() + * - memset(stream, 0, sizeof(LZ4_streamHC_t)) + * - the stream was in a valid state and was reset by LZ4_resetStreamHC_fast() + * - the stream was in a valid state and was then used in any compression call + * that returned success + * - the stream was in an indeterminate state and was used in a compression + * call that fully reset the state (LZ4_compress_HC_extStateHC()) and that + * returned success + * + * Note: + * A stream that was last used in a compression call that returned an error + * may be passed to this function. However, it will be fully reset, which will + * clear any existing history and settings from the context. + */ +LZ4LIB_STATIC_API void LZ4_resetStreamHC_fast( + LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel); + +/*! LZ4_compress_HC_extStateHC_fastReset() : + * A variant of LZ4_compress_HC_extStateHC(). + * + * Using this variant avoids an expensive initialization step. It is only safe + * to call if the state buffer is known to be correctly initialized already + * (see above comment on LZ4_resetStreamHC_fast() for a definition of + * "correctly initialized"). From a high level, the difference is that this + * function initializes the provided state with a call to + * LZ4_resetStreamHC_fast() while LZ4_compress_HC_extStateHC() starts with a + * call to LZ4_resetStreamHC(). + */ +LZ4LIB_STATIC_API int LZ4_compress_HC_extStateHC_fastReset ( + void* state, + const char* src, char* dst, + int srcSize, int dstCapacity, + int compressionLevel); + +/*! LZ4_attach_HC_dictionary() : + * This is an experimental API that allows for the efficient use of a + * static dictionary many times. + * + * Rather than re-loading the dictionary buffer into a working context before + * each compression, or copying a pre-loaded dictionary's LZ4_streamHC_t into a + * working LZ4_streamHC_t, this function introduces a no-copy setup mechanism, + * in which the working stream references the dictionary stream in-place. + * + * Several assumptions are made about the state of the dictionary stream. + * Currently, only streams which have been prepared by LZ4_loadDictHC() should + * be expected to work. + * + * Alternatively, the provided dictionary stream pointer may be NULL, in which + * case any existing dictionary stream is unset. + * + * A dictionary should only be attached to a stream without any history (i.e., + * a stream that has just been reset). + * + * The dictionary will remain attached to the working stream only for the + * current stream session. Calls to LZ4_resetStreamHC(_fast) will remove the + * dictionary context association from the working stream. The dictionary + * stream (and source buffer) must remain in-place / accessible / unchanged + * through the lifetime of the stream session. + */ +LZ4LIB_STATIC_API void LZ4_attach_HC_dictionary( + LZ4_streamHC_t *working_stream, + const LZ4_streamHC_t *dictionary_stream); + +#if defined (__cplusplus) +} +#endif + +#endif /* LZ4_HC_SLO_098092834 */ +#endif /* LZ4_HC_STATIC_LINKING_ONLY */ diff --git a/tools/lz4tpack b/tools/lz4tpack new file mode 100755 index 0000000000000000000000000000000000000000..3f729e14359f4639c91d34982f7c9bbb724f45f5 GIT binary patch literal 89128 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 Y%W3I0JhMi+5i9m literal 0 HcmV?d00001 diff --git a/tools/lz4tpack.c b/tools/lz4tpack.c new file mode 100644 index 00000000..70488214 --- /dev/null +++ b/tools/lz4tpack.c @@ -0,0 +1,780 @@ +// Written by aglab2 + +#define LZ4_HC_STATIC_LINKING_ONLY +#include "lz4hc.c" + +#include +#include +#include +#include + +#define VERIFIER +#define MAX_COMP_SIZE (8*1024*1024) +// #define DEBUG +// #define STATS +// #define FAVOR_DECOMPRESSION_SPEED +#define COMPRESSION_LEVEL LZ4HC_CLEVEL_MAX + +// MARK: LZ4HC ops + +#ifdef DEBUG +#define LOG(...) printf(__VA_ARGS__) +#else +#define LOG(...) +#endif + +#ifdef STATS +#define STAT(...) printf(__VA_ARGS__) +#else +#define STAT(...) +#endif + +int LZ4T_distanceMax = 65535; +uint32_t LZ4T_hashMask = 0xffffffffU; +int LZ4T_minMatch = 4; + +#define LZ4T_IS_SHORT_OFFSET (4096 == LZ4T_distanceMax) + +static uint32_t* sCurrentNibblePendingOutputPtr = NULL; +static uint32_t sCurrentNibble = 0; +static uint32_t sCurrentNibbleShift = 8; +static uint32_t sNibblesPushed = 0; + +static uint16_t* sDeferredNibblePtr = NULL; + +static void LZ4T_resetGlobals(void) +{ + sCurrentNibblePendingOutputPtr = NULL; + sCurrentNibble = 0; + sCurrentNibbleShift = 8; + sNibblesPushed = 0; + sDeferredNibblePtr = NULL; +} + +#define TINY_LITERAL_LIMIT 21 +#define TINY_MINMATCH (MINMATCH - 1) +// 0b0000 and 0b0111 are reserved +#define TINY_MATCH_LIMIT (6 + TINY_MINMATCH) +// 0b0000 and 0b1111 are reserved +#define TINY_MATCH_LIMIT_EX (14 + TINY_MINMATCH) + +LZ4_FORCE_INLINE void LZ4T_pushNibble(BYTE** _op, uint32_t newNibble) +{ + sNibblesPushed++; + LOG("%d: Pushing nibble: %x\n", sNibblesPushed, newNibble); + + if (sDeferredNibblePtr) + { + LOG("Deferred nibble: %x\n", newNibble); + uint16_t packedOffset = __builtin_bswap16(*sDeferredNibblePtr); + packedOffset |= (newNibble << 12); + *sDeferredNibblePtr = __builtin_bswap16(packedOffset); + sDeferredNibblePtr = NULL; + return; + } + + if (0 == sCurrentNibbleShift) + { + LOG("Requesting fresh nibbles\n"); + uint32_t nibbleBE = __builtin_bswap32(sCurrentNibble); + memcpy(sCurrentNibblePendingOutputPtr, &nibbleBE, 4); + sCurrentNibblePendingOutputPtr = (uint32_t*)(*_op); + memset(sCurrentNibblePendingOutputPtr, 0, 4); + sCurrentNibble = 0; + sCurrentNibbleShift = 8; + (*_op) += 4; + } + + sCurrentNibbleShift--; + newNibble <<= sCurrentNibbleShift * 4; + sCurrentNibble |= newNibble; + LOG("New nibble batch: 0x%x\n", sCurrentNibble); +} + +LZ4_FORCE_INLINE void LZ4T_encodeBitLen(BYTE** _op, int len) +{ +#define op (*_op) + if (0 == len) + { + *op++ = 0; + return; + } + + while (len) { + if (len <= 127) { + LOG("Pushing final len: %d\n", len); + *op++ = len; + } else { + LOG("Pushing partial len: %d\n", len & 127); + *op++ = 128 | (len & 127); + } + len >>= 7; + } +} + +int LZ4_compressBound(int isize) +{ MAX_COMP_SIZE; } + +LZ4_FORCE_INLINE int LZ4HC_encodeSequence ( + const BYTE** _ip, + BYTE** _op, + const BYTE** _anchor, + int matchLength, + int offset, + limitedOutput_directive, + BYTE*) +{ +#define ip (*_ip) +#define op (*_op) +#define anchor (*_anchor) + + /* Encode Literal length */ + size_t length = (size_t)(ip - anchor); + LOG("Encoding literals: %d\n", length); + int tinyMatchLimit = TINY_MATCH_LIMIT; + if (length > TINY_LITERAL_LIMIT) { + // means that encoding is extended + LZ4T_pushNibble(&op, 8); + LZ4T_encodeBitLen(&op, length - TINY_LITERAL_LIMIT - 1); + LOG("Copying literals wildly: %p %p %u\n", op, anchor, length); + LZ4_wildCopy8(op, anchor, op + length); + op += length; + tinyMatchLimit = TINY_MATCH_LIMIT_EX; + } else { + size_t len = length; + const void* src = anchor; + // put raw nibbles + while (len) { + LOG("Copying literals sizely: %p %p %u\n", op, anchor, length); + uint32_t cut = len > 7 ? 7 : len; + LZ4T_pushNibble(&op, 8 | cut); + *(uint64_t*) op = *(uint64_t*)src; + op += cut; + src += cut; + len -= cut; + } + + if ((length % 7) == 0) + tinyMatchLimit = TINY_MATCH_LIMIT; + else + tinyMatchLimit = TINY_MATCH_LIMIT_EX; + } + + /* Encode Offset */ + LOG("Encoding match with tiny limit %d at offset %d for length %d\n", tinyMatchLimit, offset, matchLength); + if (matchLength > tinyMatchLimit) { + // 7 or 15 means that matchLength is extended + LZ4T_pushNibble(&op, tinyMatchLimit - TINY_MINMATCH + 1); + } else { + LZ4T_pushNibble(&op, matchLength - TINY_MINMATCH); + } + assert(offset <= LZ4_DISTANCE_MAX ); + assert(offset > 0); + LZ4_write16(op, __builtin_bswap16((U16)(offset - 1))); + if (LZ4T_IS_SHORT_OFFSET) + { + sDeferredNibblePtr = (uint16_t*)op; + } + op += 2; + LOG("Pushing offset: %d\n", offset); + + /* Encode MatchLength */ + assert(matchLength >= MINMATCH); + if (matchLength > tinyMatchLimit) { + LZ4T_encodeBitLen(&op, matchLength - tinyMatchLimit - 1); + } + + /* Prepare next loop */ + ip += matchLength; + anchor = ip; + + return 0; + +#undef ip +#undef op +#undef anchor +} + +int LZ4T_lastLiterals ( + const BYTE** _ip, + BYTE** _op, + const BYTE** _anchor, + limitedOutput_directive limit, + BYTE* oend, + size_t length) +{ +#define ip (*_ip) +#define op (*_op) +#define anchor (*_anchor) + + if (length) + { + LOG("Encoding last literals: %d\n", length); + if (length > TINY_LITERAL_LIMIT) { + // means that encoding is extended + LZ4T_pushNibble(&op, 8); + LZ4T_encodeBitLen(&op, length - TINY_LITERAL_LIMIT - 1); + LZ4_wildCopy8(op, anchor, op + length); + op += length; + } else { + size_t len = length; + const void* src = anchor; + // put raw nibbles + while (len) { + uint32_t cut = len > 7 ? 7 : len; + LZ4T_pushNibble(&op, 8 | cut); + *(uint64_t*) op = *(uint64_t*)src; + op += cut; + src += cut; + len -= cut; + } + } + } + + // push ending marker + uint32_t nibbleBE = __builtin_bswap32(sCurrentNibble); + memcpy(sCurrentNibblePendingOutputPtr, &nibbleBE, 4); + sCurrentNibblePendingOutputPtr = (uint32_t*)(*_op); + memset(sCurrentNibblePendingOutputPtr, 0, 4); + (*_op) += 4; + sCurrentNibblePendingOutputPtr = NULL; + + ip = anchor + length; + return 0; + +#undef ip +#undef op +#undef anchor +} + +LZ4_FORCE_INLINE int LZ4T_bitCodePrice(int len) +{ + if (0 == len) + return 2; + + int price = 0; + while (len) + { + // each ex size byte is 2 nibbles + price += 2; + len >>= 7; + } + + return price; +} + +// Counts are in nibbles so 1 byte is 2 nibbles +LZ4_FORCE_INLINE int LZ4HC_literalsPrice(int const litlen) +{ + int price = litlen * 2; + assert(litlen >= 0); + if (litlen > TINY_LITERAL_LIMIT) { + int len = litlen - TINY_LITERAL_LIMIT - 1; + // encode 1 nibble for ex size + price += 1 + LZ4T_bitCodePrice(len); + } else { + int len = litlen; + while (len) { + // single nibble for short sizes + int cut = len > 7 ? 7 : len; + price += 1; + len -= cut; + } + } + return price; +} + +LZ4_FORCE_INLINE int LZ4HC_sequencePrice(int litlen, int mlen) +{ + int price = 4 ; // 16-bit offset is 4 nibbles + assert(litlen >= 0); + assert(mlen >= MINMATCH); + + price += LZ4HC_literalsPrice(litlen); + // nibble for encoding match is saved because it is rolled from offset in the stream + if (!LZ4T_IS_SHORT_OFFSET) + { + price += 1; + } + int tinyMatchLimit = TINY_MATCH_LIMIT_EX; + if (litlen <= TINY_LITERAL_LIMIT) { + if ((litlen % 7) == 0) + tinyMatchLimit = TINY_MATCH_LIMIT; + } + + if (mlen > tinyMatchLimit) { + // ex size encoding + int len = mlen - tinyMatchLimit - 1; + price += LZ4T_bitCodePrice(len); + } + return price; +} + +// MARK: LZ4T Verifier + +struct SizedBuffer +{ + void* buffer; + size_t size; +}; + +static void verifySizedBuffer(struct SizedBuffer* data, int amount) +{ + if (data->size < amount) + { + printf("Verifier fail: not enough data to verify %d bytes\n", amount); + abort(); + } +} + +static void moveSizedBuffer(struct SizedBuffer* data, int amount) +{ + data->buffer = (uint8_t*)data->buffer + amount; + data->size -= amount; +} + +static uint8_t loadVerifyU8(struct SizedBuffer* data) +{ + uint8_t value; + size_t size = sizeof(value); + + verifySizedBuffer(data, size); + value = *(uint8_t*)data->buffer; + moveSizedBuffer(data, size); + + return value; +} + +static uint16_t loadVerifyU16(struct SizedBuffer* data) +{ + uint16_t value; + size_t size = sizeof(value); + + verifySizedBuffer(data, size); + value = LZ4_read16(data->buffer); + moveSizedBuffer(data, size); + + return value; +} + +static uint32_t loadVerifyU32(struct SizedBuffer* data) +{ + uint32_t value; + size_t size = sizeof(value); + + verifySizedBuffer(data, size); + value = LZ4_read32(data->buffer); + moveSizedBuffer(data, size); + + return value; +} + +static void wildCopyVerify(struct SizedBuffer* in, struct SizedBuffer* out, int amount) +{ + verifySizedBuffer(in, amount); + verifySizedBuffer(out, amount); + + LZ4_wildCopy8(out->buffer, in->buffer, out->buffer + amount); + + moveSizedBuffer(in, amount); + moveSizedBuffer(out, amount); +} + +static uint64_t wildLoadU64Verify(struct SizedBuffer* data, int size) +{ + uint64_t value; + verifySizedBuffer(data, size); + LZ4_memcpy(&value, data->buffer, sizeof(uint64_t)); + moveSizedBuffer(data, size); + + return value; +} + +static void wildStoreU64Verify(struct SizedBuffer* data, uint64_t value, int size) +{ + verifySizedBuffer(data, size); + LZ4_memcpy(data->buffer, &value, sizeof(uint64_t)); + moveSizedBuffer(data, size); +} + +static int LZ4T_unpack_size(struct SizedBuffer* in, int* _giganticCounts) +{ +#define giganticCounts (*_giganticCounts) + int amount = 0; + int shift = 0; + int iteration = -1; + while (1) + { + if (iteration > 4) + { + printf("Verifier fail: too many iterations in unpack_size\n"); + abort(); + } + + iteration++; + int8_t next = loadVerifyU8(in); + LOG("Next: %d\n", next); + if (next < 0) + { + amount |= (next & 0x7f) << shift; + shift += 7; + LOG("New amount: %d\n", amount); + } + else + { + amount |= (next << shift); + LOG("Amount is %d\n", amount); + break; + } + } + + giganticCounts += iteration; + + return amount; +#undef giganticCounts +} + +// TODO: Take in compressedSize +// !!! Make sure to avoid all LZ4 constants, it should be all packed in the header +static char* LZ4T_unpack(struct SizedBuffer in) +{ + uint32_t magicHeader = __builtin_bswap32(loadVerifyU32(&in)); + uint32_t srcSize = __builtin_bswap32(loadVerifyU32(&in)); + uint16_t shortOffsetMask = loadVerifyU8(&in); + shortOffsetMask <<= 12; + int minMatch = loadVerifyU8(&in); + (void) loadVerifyU16(&in); + + int32_t nibbles = 0; + int nibblesHandled = 0; + int largeLiteralsCounts = 0; + int giganticLiteralsCounts = 0; + int largeMatchesCounts = 0; + int giganticMatchesCounts = 0; + int literalsCounts = 0; + int matchesCounts = 0; + int exMatchesCounts = 0; + int exMatchesAfterLimLiterals = 0; + + char* dst = malloc(srcSize + 16); + struct SizedBuffer out = { dst, srcSize }; + while (1) + { + if (0 == nibbles) + { + nibbles = __builtin_bswap32(loadVerifyU32(&in)); + LOG("Loaded new pack of nibbles: %x\n", nibbles); + if (!nibbles) + { + break; + } + } + + int tinyMatchLimit = minMatch + 7; + nibblesHandled++; + + // literals + if (nibbles < 0) + { + LOG("%d: Handle literal nibble 0x%x\n", nibblesHandled, ((uint32_t) nibbles) >> 28); + // literal + literalsCounts++; + int amount = 7 & (nibbles >> 28); + LOG("Amount: %d\n", amount); + if (amount == 0) + { + largeLiteralsCounts++; + LOG("Amount is 0, unpacking extras\n"); + amount = LZ4T_unpack_size(&in, &giganticLiteralsCounts) + TINY_LITERAL_LIMIT + 1; + LOG("Copying amount %d via memcpy: %p %p\n", amount, out.buffer, in.buffer); + wildCopyVerify(&in, &out, amount); + } + else + { + uint64_t value = wildLoadU64Verify(&in, amount); + wildStoreU64Verify(&out, value, amount); + LOG("Copying amount %d wildly: %p %p\n", amount, out.buffer, in.buffer); + if (7 == amount) + { + nibbles <<= 4; + continue; + } + else + { + exMatchesAfterLimLiterals++; + } + } + + nibbles <<= 4; + if (0 == nibbles) + { + nibbles = __builtin_bswap32(loadVerifyU32(&in)); + LOG("Loaded new pack of nibbles: %x\n", nibbles); + if (!nibbles) + { + break; + } + } + nibblesHandled++; + exMatchesCounts++; + tinyMatchLimit = minMatch + 15; + } + + // matches + { + LOG("%d: Handle match nibble 0x%x | 0x%x\n", nibblesHandled, ((uint32_t) nibbles) >> 28, tinyMatchLimit); + matchesCounts++; + uint16_t offset = __builtin_bswap16(loadVerifyU16(&in)); + + uint32_t nibble = 0; + if (shortOffsetMask) + { + nibble = ((uint32_t) (offset & 0xf000)) << 16; + offset &= 0xfff; + } + offset++; + + LOG("Offset: %d, Nibble: 0x%x\n", offset, nibble); + int amount = minMatch + (((uint32_t) nibbles) >> 28); + LOG("Amount: %d\n", amount); + if (amount == tinyMatchLimit) + { + LOG("Amount is %d, unpacking extras\n", tinyMatchLimit); + largeMatchesCounts++; + amount = LZ4T_unpack_size(&in, &giganticMatchesCounts) + tinyMatchLimit; + } + + LOG("Copying amount %d: %p %p\n", amount, out.buffer, out.buffer - offset); + verifySizedBuffer(&out, amount); + uint8_t* cpy = (uint8_t*) out.buffer + amount; + LZ4_memcpy_using_offset(out.buffer, (uint8_t*) out.buffer - offset, cpy, offset); + moveSizedBuffer(&out, amount); + + if (shortOffsetMask) + { + LOG("Push in nibble: 0x%x\n", nibble); + nibbles &= ~0xf0000000; + nibbles |= nibble; + } + else + { + nibbles <<= 4; + } + } + } + + STAT("Literals: %d (%d large, %d gigantic)\n", literalsCounts, largeLiteralsCounts, giganticLiteralsCounts); + STAT("Matches: %d (%d large, %d gigantic)\n", matchesCounts, largeMatchesCounts, giganticMatchesCounts); + STAT("Ex matches: %d (%d after !7)\n", exMatchesCounts, exMatchesAfterLimLiterals); + + return dst; +} + +static void saveBufferToFile(const char* buffer, size_t size, const char* filename); +static void LZ4T_verify(const struct SizedBuffer* orig, const struct SizedBuffer* compressed) +{ +#ifdef VERIFIER + + char* dec = LZ4T_unpack(*compressed); + + if (0 != memcmp(dec, orig->buffer, orig->size)) + { + printf("Compression failed!\n"); + for (int i = 0; i < orig->size; i++) + { + if (dec[i] != ((uint8_t*) orig->buffer)[i]) + { + printf("Mismatch at %d: %x != %x\n", i, dec[i], ((uint8_t*) orig->buffer)[i]); + saveBufferToFile(dec, orig->size, "dec.bin"); + break; + } + } + + abort(); + } + + free(dec); +#else + (void) orig; + (void) compressed; +#endif +} + +// MARK: LZ4T Block + +static void freeSizedBuffer(struct SizedBuffer* data) +{ + free(data->buffer); +} + +struct LZ4T_BlockHeader +{ + uint8_t magicHeader[4]; + uint32_t srcSize; + uint8_t shortOffsetMode; + uint8_t minMatch; + uint16_t stub; + uint32_t firstNibble; +}; + +static void LZ4T_fillHeader(struct LZ4T_BlockHeader* header, int srcSize, uint32_t firstNibble, bool isShortOffset, uint8_t minMatch) +{ + header->magicHeader[0] = 'L'; + header->magicHeader[1] = 'Z'; + header->magicHeader[2] = '0' + minMatch; + header->magicHeader[3] = 'T' + isShortOffset; + header->srcSize = __builtin_bswap32(srcSize); + header->shortOffsetMode = isShortOffset ? 0xf : 0; + header->minMatch = minMatch - 1; + header->stub = 0; + header->firstNibble = firstNibble; +} + +static struct SizedBuffer LZ4T_doCompress(const char* src, int srcSize) +{ + LZ4T_resetGlobals(); + uint32_t firstNibble = 0; + char* dst = malloc(MAX_COMP_SIZE); + LZ4_streamHC_t* state = LZ4_createStreamHC(); +#ifdef FAVOR_DECOMPRESSION_SPEED + LZ4_favorDecompressionSpeed(state, 1); +#endif + LZ4_setCompressionLevel(state, COMPRESSION_LEVEL); + sCurrentNibblePendingOutputPtr = &firstNibble; + LOG("src=%p dst=%p srcSize=%u", src, dst, srcSize); + int compSize = LZ4_compress_HC_continue(state, (char*)src, dst + sizeof(struct LZ4T_BlockHeader), srcSize, MAX_COMP_SIZE - sizeof(struct LZ4T_BlockHeader)); + LZ4_freeStreamHC(state); + if (sCurrentNibblePendingOutputPtr) + { + printf("Nibble output pointer is not NULL after compression\n"); + abort(); + } + if (0 == compSize) + { + printf("Compression failed!\n"); + abort(); + } + + LZ4T_fillHeader((struct LZ4T_BlockHeader*)dst, srcSize, firstNibble, LZ4T_IS_SHORT_OFFSET, MINMATCH); + +#if 0 + char* buf = malloc(8 * 1024 * 1024); + patch_lz4_block(dst, compSize, buf, 8 * 1024 * 1024); + free(buf); +#endif + + return (struct SizedBuffer) { dst, compSize + sizeof(struct LZ4T_BlockHeader) }; +} + +static void LZ4T_setOffsetMode(bool isShort) +{ + LZ4T_distanceMax = isShort ? 4096 : 65535; +} + +static void LZ4T_setMinMatchMode(bool isShort) +{ + if (isShort) + { + LZ4T_hashMask = 0x00ffffffU; + LZ4T_minMatch = 3; + } + else + { + LZ4T_hashMask = 0xffffffffU; + LZ4T_minMatch = 4; + } +} + +static struct SizedBuffer LZ4T_compress(const struct SizedBuffer* data) +{ + struct SizedBuffer comp = (struct SizedBuffer) { NULL, MAX_COMP_SIZE }; + + for (int i = 0; i < 4; i++) + { + LZ4T_setMinMatchMode(!!(i & 1)); + LZ4T_setOffsetMode(!!(i & 2)); + struct SizedBuffer newCandidate = LZ4T_doCompress(data->buffer, data->size); + STAT("Compressed to %d bytes with minMatch=%d, isShortOffset=%d\n", newCandidate.size, MINMATCH, LZ4T_IS_SHORT_OFFSET); + + LZ4T_verify(data, &newCandidate); + + if (newCandidate.size <= comp.size) + { + freeSizedBuffer(&comp); + comp = newCandidate; + } + else + { + freeSizedBuffer(&newCandidate); + } + } + + return comp; +} + +// MARK: Tools to work with files and output blocks + +static void saveBufferToFile(const char* buffer, size_t size, const char* filename) +{ + FILE* out = fopen(filename, "wb"); + if (out == NULL) + { + printf("Cannot create output file '%s'!\n", filename); + abort(); + } + + fwrite(buffer, size, 1, out); + fclose(out); +} + +static struct SizedBuffer readFile(const char* filename, int margin) +{ + FILE* in = fopen(filename, "rb"); + if (in == NULL) + { + printf("Cannot open input file '%s'\n", filename); + abort(); + } + + fseek(in, 0, SEEK_END); + int size = ftell(in); + fseek(in, 0, SEEK_SET); + char* buffer = malloc(size + margin); + size_t fread_result = fread(buffer, size, 1, in); + fclose(in); + if (1 != fread_result) + { + printf("Failed to read input file '%s': %d != %d\n", filename, (int) fread_result, size); + abort(); + } + + return (struct SizedBuffer) { buffer, size }; +} + +int main(int argc, char *argv[]) +{ + if (argc < 3) + { + printf("Usage: %s [SRC_PATH] [DST_PATH]\n", argv[0]); + return -1; + } + + struct SizedBuffer src = readFile(argv[1], 0); + + // Compression + { + struct SizedBuffer data = LZ4T_compress(&src); + saveBufferToFile(data.buffer, data.size, argv[2]); + freeSizedBuffer(&data); + } + + // Verifier + { + struct SizedBuffer compTest = readFile(argv[2], 8 /*need a slight margin for unpacker*/); + LZ4T_verify(&src, &compTest); + freeSizedBuffer(&compTest); + } + + freeSizedBuffer(&src); + return 0; +}