diff --git a/NEWS b/NEWS index bea7bd36..f4cab704 100644 --- a/NEWS +++ b/NEWS @@ -29,7 +29,7 @@ perf : `LZ4_FAST_DEC_LOOP` enabled on aarch64 with clang, including Android (up perf : `LZ4_FAST_DEC_LOOP` enabled on riscv64 when unaligned accesses are fast (`-mno-strict-align`, or a `-mcpu` with fast unaligned accesses) perf : faster compression with reused contexts, for blocks >= 1 KB : linked frames (up to +69%), `LZ4_resetStream_fast()` (up to +94%) perf : faster compression of 2-4 KB blocks with `LZ4_attach_dictionary()` (+3% to +30% on Zen 4) -perf : level 2 compresses slightly better (~0.1-0.5%) and faster (~+1-2%) +perf : level 2 compresses better (~0.2-1.5%), at similar speed (-3% to +1% on Zen 4) perf : faster levels 11 (~+25%) and 12 (~+10%) perf : `--favor-decSpeed` : faster decompression (~+2-4% at level 12), with fewer sequences perf : `lz4` multithreaded compression relies on liblz4, reading files in place (memory-mapped, `--[no-]mmap`) : faster at fast levels diff --git a/lib/lz4hc.c b/lib/lz4hc.c index a98bb881..52edc784 100644 --- a/lib/lz4hc.c +++ b/lib/lz4hc.c @@ -723,35 +723,30 @@ static int LZ4MID_compress ( LZ4MID_addPosition(hash8Table, h8, ipIndex); matchLength = LZ4MID_countMatch(ip, ipIndex, pos8, matchlimit, prefixPtr, prefixIdx, dictStart, dictIdx); matchDistance = ipIndex - pos8; - if (matchLength >= MINMATCH) goto _lz4mid_encode_sequence; } /* search short match */ - { U32 const h4 = LZ4MID_hash4Ptr(ip); + if (matchLength < MINMATCH) { + U32 const h4 = LZ4MID_hash4Ptr(ip); U32 const pos4 = hash4Table[h4]; assert(h4 < LZ4MID_HASHTABLESIZE); assert(pos4 < ipIndex); LZ4MID_addPosition(hash4Table, h4, ipIndex); matchLength = LZ4MID_countMatch(ip, ipIndex, pos4, matchlimit, prefixPtr, prefixIdx, dictStart, dictIdx); matchDistance = ipIndex - pos4; - if ((matchLength >= MINMATCH) && (pos4 >= prefixIdx)) { - /* short match found in prefix, let's just check ip+1 for longer */ + } + if (matchLength >= MINMATCH) { + if ((matchLength < 8) && likely(ip < mflimit)) { + /* short match : check ip+1 for longer, except ip itself */ U32 const h8 = LZ4MID_hash8Ptr(ip+1); U32 const pos8 = hash8Table[h8]; - U32 const m2Distance = ipIndex + 1 - pos8; - 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; - } } - } - if (matchLength >= MINMATCH) goto _lz4mid_encode_sequence; + unsigned const ml2 = LZ4MID_countMatch(ip+1, ipIndex+1, pos8, matchlimit, prefixPtr, prefixIdx, dictStart, dictIdx); + if ((ml2 > matchLength) && (pos8 != ipIndex)) { + LZ4MID_addPosition(hash8Table, h8, ipIndex+1); + ip++; + matchLength = ml2; + matchDistance = ipIndex + 1 - pos8; + } } + goto _lz4mid_encode_sequence; } /* no match found in prefix nor extDict : search into the attached dictionary */ if ( (dict == usingDictCtxHc) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index fafc099d..5edb7b43 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -1734,6 +1734,17 @@ static void FUZ_unitTests(int compressionLevel) } DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "level 2 : the match tested at ip+1 must not be ip itself, inside runs : "); + { char runs[2 + 4096]; /* 0xFF 0x00, then runs of 10 x 0xFF and 10 x 0x00 */ + int i; + runs[0] = (char)0xFF; runs[1] = 0; + for (i = 0; i < 4096; i++) runs[2 + i] = (i % 20) < 10 ? (char)0xFF : 0; + result = LZ4_compress_HC(runs, testCompressed, (int)sizeof(runs), (int)testCompressedSize, 2); + /* ~1.6 KB (one literal and one offset 1 per run), instead of < 50 bytes */ + FUZ_CHECKTEST(result <= 0 || result > 100, "%i bytes", result); + } + DISPLAYLEVEL(3, "OK \n"); + /* saveDictHC test #926 */ DISPLAYLEVEL(3, "saveDictHC test #926 : "); { LZ4_streamHC_t* const ctx = LZ4_initStreamHC(&sHC, sizeof(sHC));