Date: Sat, 28 Jul 2018 22:12:55 -0400
Subject: [PATCH 21/45] fixed spelling mistake in lz4_manual
---
doc/lz4_manual.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/lz4_manual.html b/doc/lz4_manual.html
index e5044fe70..3fc71e4af 100644
--- a/doc/lz4_manual.html
+++ b/doc/lz4_manual.html
@@ -214,7 +214,7 @@ 1.8.2 Manual
int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
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 optionnally be set. Use NULL or size 0 for a reset order.
+ 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
From 2e52f03a12b8bb5fba8a86e063e0211e8afa503b Mon Sep 17 00:00:00 2001
From: Jack Luo
Date: Sat, 28 Jul 2018 22:21:57 -0400
Subject: [PATCH 22/45] fixed spelling mistake in lz4.h
---
lib/lz4.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/lz4.h b/lib/lz4.h
index 64914abf9..a0eddcead 100644
--- a/lib/lz4.h
+++ b/lib/lz4.h
@@ -305,7 +305,7 @@ LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_str
/*! 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 optionnally be set. Use NULL or size 0 for a reset order.
+ * 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
*/
From 672ff590b2e0d1cc9cb237d05af8490e68f9e94d Mon Sep 17 00:00:00 2001
From: Joel Johnson
Date: Thu, 2 Aug 2018 00:23:00 -0600
Subject: [PATCH 23/45] Add CMake option to not build legacy lz4c program
---
contrib/cmake_unofficial/CMakeLists.txt | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/contrib/cmake_unofficial/CMakeLists.txt b/contrib/cmake_unofficial/CMakeLists.txt
index 27c3a7881..b09c4fb0e 100644
--- a/contrib/cmake_unofficial/CMakeLists.txt
+++ b/contrib/cmake_unofficial/CMakeLists.txt
@@ -12,6 +12,8 @@
set(LZ4_TOP_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../..")
+option(LZ4_BUILD_LEGACY_LZ4C "Build lz4c progam with legacy argument support" ON)
+
# Parse version information
file(STRINGS "${LZ4_TOP_SOURCE_DIR}/lib/lz4.h" LZ4_VERSION_MAJOR REGEX "^#define LZ4_VERSION_MAJOR +([0-9]+) +.*$")
string(REGEX REPLACE "^#define LZ4_VERSION_MAJOR +([0-9]+) +.*$" "\\1" LZ4_VERSION_MAJOR "${LZ4_VERSION_MAJOR}")
@@ -122,14 +124,18 @@ else()
endif()
# lz4
+set(LZ4_PROGRAMS_BUILT lz4cli)
add_executable(lz4cli ${LZ4_CLI_SOURCES})
set_target_properties(lz4cli PROPERTIES OUTPUT_NAME lz4)
target_link_libraries(lz4cli ${LZ4_LINK_LIBRARY})
# lz4c
-add_executable(lz4c ${LZ4_CLI_SOURCES})
-set_target_properties(lz4c PROPERTIES COMPILE_DEFINITIONS "ENABLE_LZ4C_LEGACY_OPTIONS")
-target_link_libraries(lz4c ${LZ4_LINK_LIBRARY})
+if (LZ4_BUILD_LEGACY_LZ4C)
+ list(APPEND LZ4_PROGRAMS_BUILT lz4c)
+ add_executable(lz4c ${LZ4_CLI_SOURCES})
+ set_target_properties(lz4c PROPERTIES COMPILE_DEFINITIONS "ENABLE_LZ4C_LEGACY_OPTIONS")
+ target_link_libraries(lz4c ${LZ4_LINK_LIBRARY})
+endif()
# Extra warning flags
include (CheckCCompilerFlag)
@@ -165,7 +171,7 @@ endforeach (flag)
if(NOT LZ4_BUNDLED_MODE)
include(GNUInstallDirs)
- install(TARGETS lz4cli lz4c
+ install(TARGETS ${LZ4_PROGRAMS_BUILT}
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
install(TARGETS ${LZ4_LIBRARIES_BUILT}
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
From 9c457ccb7aed26edda7a3299207a4f329057866d Mon Sep 17 00:00:00 2001
From: Kouhei Sutou
Date: Mon, 13 Aug 2018 14:17:54 +0900
Subject: [PATCH 24/45] Add missing $(EXT)
---
programs/Makefile | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/programs/Makefile b/programs/Makefile
index 72bdcaac1..9cc2d948d 100644
--- a/programs/Makefile
+++ b/programs/Makefile
@@ -82,7 +82,7 @@ lz4-release: DEBUGFLAGS=
lz4-release: lz4
lz4c: lz4
- ln -s lz4 lz4c
+ ln -s lz4$(EXT) lz4c$(EXT)
lz4c32: CFLAGS += -m32
lz4c32 : $(SRCFILES)
@@ -102,7 +102,7 @@ preview-man: clean-man man
clean:
@$(MAKE) -C $(LZ4DIR) $@ > $(VOID)
@$(RM) core *.o *.test tmp* \
- lz4$(EXT) lz4c$(EXT) lz4c32$(EXT) unlz4 lz4cat
+ lz4$(EXT) lz4c$(EXT) lz4c32$(EXT) unlz4$(EXT) lz4cat$(EXT)
@echo Cleaning completed
@@ -112,10 +112,10 @@ clean:
ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS))
unlz4: lz4
- ln -s lz4 unlz4
+ ln -s lz4$(EXT) unlz4$(EXT)
lz4cat: lz4
- ln -s lz4 lz4cat
+ ln -s lz4$(EXT) lz4cat$(EXT)
DESTDIR ?=
# directory variables : GNU conventions prefer lowercase
@@ -147,10 +147,10 @@ INSTALL_DATA ?= $(INSTALL) -m 644
install: lz4
@echo Installing binaries
@$(INSTALL) -d -m 755 $(DESTDIR)$(bindir)/ $(DESTDIR)$(man1dir)/
- @$(INSTALL_PROGRAM) lz4 $(DESTDIR)$(bindir)/lz4
- @ln -sf lz4 $(DESTDIR)$(bindir)/lz4c
- @ln -sf lz4 $(DESTDIR)$(bindir)/lz4cat
- @ln -sf lz4 $(DESTDIR)$(bindir)/unlz4
+ @$(INSTALL_PROGRAM) lz4$(EXT) $(DESTDIR)$(bindir)/lz4$(EXT)
+ @ln -sf lz4$(EXT) $(DESTDIR)$(bindir)/lz4c$(EXT)
+ @ln -sf lz4$(EXT) $(DESTDIR)$(bindir)/lz4cat$(EXT)
+ @ln -sf lz4$(EXT) $(DESTDIR)$(bindir)/unlz4$(EXT)
@echo Installing man pages
@$(INSTALL_DATA) lz4.1 $(DESTDIR)$(man1dir)/lz4.1
@ln -sf lz4.1 $(DESTDIR)$(man1dir)/lz4c.1
@@ -159,10 +159,10 @@ install: lz4
@echo lz4 installation completed
uninstall:
- @$(RM) $(DESTDIR)$(bindir)/lz4cat
- @$(RM) $(DESTDIR)$(bindir)/unlz4
- @$(RM) $(DESTDIR)$(bindir)/lz4
- @$(RM) $(DESTDIR)$(bindir)/lz4c
+ @$(RM) $(DESTDIR)$(bindir)/lz4cat$(EXT)
+ @$(RM) $(DESTDIR)$(bindir)/unlz4$(EXT)
+ @$(RM) $(DESTDIR)$(bindir)/lz4$(EXT)
+ @$(RM) $(DESTDIR)$(bindir)/lz4c$(EXT)
@$(RM) $(DESTDIR)$(man1dir)/lz4.1
@$(RM) $(DESTDIR)$(man1dir)/lz4c.1
@$(RM) $(DESTDIR)$(man1dir)/lz4cat.1
From d2d566097031e5119980eb5ebbfa47023bc33c55 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Tue, 4 Sep 2018 16:50:34 -0700
Subject: [PATCH 25/45] new test program : roundTripTest
make a round trip test with arbitrary input file,
generate an `abort()` on error,
to work in tandem with `afl`.
note : currently locked on level 9, to investigate #560.
---
tests/.gitignore | 1 +
tests/Makefile | 5 +-
tests/roundTripTest.c | 203 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 208 insertions(+), 1 deletion(-)
create mode 100644 tests/roundTripTest.c
diff --git a/tests/.gitignore b/tests/.gitignore
index 36dff4207..58947f7a4 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -8,6 +8,7 @@ fullbench32
fuzzer
fuzzer32
fasttest
+roundTripTest
checkTag
# test artefacts
diff --git a/tests/Makefile b/tests/Makefile
index 81033b59b..bc432341d 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -63,7 +63,7 @@ NB_LOOPS ?= -i1
default: all
-all: fullbench fuzzer frametest datagen
+all: fullbench fuzzer frametest roundTripTest datagen
all32: CFLAGS+=-m32
all32: all
@@ -103,6 +103,9 @@ fuzzer : lz4.o lz4hc.o xxhash.o fuzzer.c
frametest: lz4frame.o lz4.o lz4hc.o xxhash.o frametest.c
$(CC) $(FLAGS) $^ -o $@$(EXT)
+roundTripTest : lz4.o lz4hc.o xxhash.o roundTripTest.c
+ $(CC) $(FLAGS) $^ -o $@$(EXT)
+
datagen : $(PRGDIR)/datagen.c datagencli.c
$(CC) $(FLAGS) -I$(PRGDIR) $^ -o $@$(EXT)
diff --git a/tests/roundTripTest.c b/tests/roundTripTest.c
new file mode 100644
index 000000000..2f161d84f
--- /dev/null
+++ b/tests/roundTripTest.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/*
+ * This program takes a file in input,
+ * performs an LZ4 round-trip test (compression - decompress)
+ * compares the result with original
+ * and generates an abort() on corruption detection,
+ * in order for afl to register the event as a crash.
+*/
+
+
+/*===========================================
+* Dependencies
+*==========================================*/
+#include /* size_t */
+#include /* malloc, free, exit */
+#include /* fprintf */
+#include /* strcmp */
+#include
+#include /* stat */
+#include /* stat */
+#include "xxhash.h"
+
+#include "lz4.h"
+#include "lz4hc.h"
+
+
+/*===========================================
+* Macros
+*==========================================*/
+#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
+
+#define MSG(...) fprintf(stderr, __VA_ARGS__)
+
+#define CONTROL_MSG(c, ...) { \
+ if ((c)) { \
+ MSG(__VA_ARGS__); \
+ MSG(" \n"); \
+ abort(); \
+ } \
+}
+
+
+static size_t checkBuffers(const void* buff1, const void* buff2, size_t buffSize)
+{
+ const char* const ip1 = (const char*)buff1;
+ const char* const ip2 = (const char*)buff2;
+ size_t pos;
+
+ for (pos=0; pos= LZ4_compressBound(srcSize)`
+ * for compression to be guaranteed to work */
+static void roundTripTest(void* resultBuff, size_t resultBuffCapacity,
+ void* compressedBuff, size_t compressedBuffCapacity,
+ const void* srcBuff, size_t srcSize)
+{
+ const int minCLevel = 1;
+ const int maxClevel = 12;
+ const int cLevelSpan = maxClevel - minCLevel;
+ size_t const hashLength = MIN(16, srcSize);
+ unsigned const h32 = XXH32(srcBuff, hashLength, 0);
+ int const randL = h32 % (cLevelSpan+1);
+ int const cLevel = minCLevel + randL;
+ int const realCLevel = (cLevel * 0) + 9; /* <=== Currently : only test level 9 */
+ int const cSize = LZ4_compress_HC(srcBuff, compressedBuff, srcSize, compressedBuffCapacity, realCLevel);
+ CONTROL_MSG(cSize == 0, "Compression error !");
+
+ { int const dSize = LZ4_decompress_safe(compressedBuff, resultBuff, cSize, resultBuffCapacity);
+ CONTROL_MSG(dSize < 0, "Decompression detected an error !");
+ CONTROL_MSG(dSize != (int)srcSize, "Decompression corruption error : wrong decompressed size !");
+ }
+
+ /* check potential content corruption error */
+ assert(resultBuffCapacity >= srcSize);
+ { size_t const errorPos = checkBuffers(srcBuff, resultBuff, srcSize);
+ CONTROL_MSG(errorPos != srcSize,
+ "Silent decoding corruption, at pos %u !!!",
+ (unsigned)errorPos);
+ }
+
+}
+
+static void roundTripCheck(const void* srcBuff, size_t srcSize)
+{
+ size_t const cBuffSize = LZ4_compressBound(srcSize);
+ void* const cBuff = malloc(cBuffSize);
+ void* const rBuff = malloc(cBuffSize);
+
+ if (!cBuff || !rBuff) {
+ fprintf(stderr, "not enough memory ! \n");
+ exit(1);
+ }
+
+ roundTripTest(rBuff, cBuffSize,
+ cBuff, cBuffSize,
+ srcBuff, srcSize);
+
+ free(rBuff);
+ free(cBuff);
+}
+
+
+static size_t getFileSize(const char* infilename)
+{
+ int r;
+#if defined(_MSC_VER)
+ struct _stat64 statbuf;
+ r = _stat64(infilename, &statbuf);
+ if (r || !(statbuf.st_mode & S_IFREG)) return 0; /* No good... */
+#else
+ struct stat statbuf;
+ r = stat(infilename, &statbuf);
+ if (r || !S_ISREG(statbuf.st_mode)) return 0; /* No good... */
+#endif
+ return (size_t)statbuf.st_size;
+}
+
+
+static int isDirectory(const char* infilename)
+{
+ int r;
+#if defined(_MSC_VER)
+ struct _stat64 statbuf;
+ r = _stat64(infilename, &statbuf);
+ if (!r && (statbuf.st_mode & _S_IFDIR)) return 1;
+#else
+ struct stat statbuf;
+ r = stat(infilename, &statbuf);
+ if (!r && S_ISDIR(statbuf.st_mode)) return 1;
+#endif
+ return 0;
+}
+
+
+/** loadFile() :
+ * requirement : `buffer` size >= `fileSize` */
+static void loadFile(void* buffer, const char* fileName, size_t fileSize)
+{
+ FILE* const f = fopen(fileName, "rb");
+ if (isDirectory(fileName)) {
+ fprintf(stderr, "Ignoring %s directory \n", fileName);
+ exit(2);
+ }
+ if (f==NULL) {
+ fprintf(stderr, "Impossible to open %s \n", fileName);
+ exit(3);
+ }
+ { size_t const readSize = fread(buffer, 1, fileSize, f);
+ if (readSize != fileSize) {
+ fprintf(stderr, "Error reading %s \n", fileName);
+ exit(5);
+ } }
+ fclose(f);
+}
+
+
+static void fileCheck(const char* fileName)
+{
+ size_t const fileSize = getFileSize(fileName);
+ void* const buffer = malloc(fileSize + !fileSize /* avoid 0 */);
+ if (!buffer) {
+ fprintf(stderr, "not enough memory \n");
+ exit(4);
+ }
+ loadFile(buffer, fileName, fileSize);
+ roundTripCheck(buffer, fileSize);
+ free (buffer);
+}
+
+
+int main(int argCount, const char** argv)
+{
+ int const argNb = 1;
+
+ if (argCount < 2) {
+ fprintf(stderr, "Error : no argument : need input file \n");
+ exit(9);
+ }
+
+ fileCheck(argv[argNb]);
+ fprintf(stderr, "no pb detected \n");
+ return 0;
+}
From 943fa6244a3985541bc93250d35ea4f4517fb266 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Tue, 4 Sep 2018 17:37:56 -0700
Subject: [PATCH 26/45] fix minor cast warning for C++ compilation
---
tests/.gitignore | 5 ++++-
tests/roundTripTest.c | 6 +++---
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/tests/.gitignore b/tests/.gitignore
index 58947f7a4..9aa42a064 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,5 +1,5 @@
-# test build artefacts
+# build artefacts
datagen
frametest
frametest32
@@ -14,3 +14,6 @@ checkTag
# test artefacts
tmp*
versionsTest
+
+# local tests
+afl
diff --git a/tests/roundTripTest.c b/tests/roundTripTest.c
index 2f161d84f..2caa2bc25 100644
--- a/tests/roundTripTest.c
+++ b/tests/roundTripTest.c
@@ -82,10 +82,10 @@ static void roundTripTest(void* resultBuff, size_t resultBuffCapacity,
int const randL = h32 % (cLevelSpan+1);
int const cLevel = minCLevel + randL;
int const realCLevel = (cLevel * 0) + 9; /* <=== Currently : only test level 9 */
- int const cSize = LZ4_compress_HC(srcBuff, compressedBuff, srcSize, compressedBuffCapacity, realCLevel);
+ int const cSize = LZ4_compress_HC((const char*)srcBuff, (char*)compressedBuff, (int)srcSize, (int)compressedBuffCapacity, realCLevel);
CONTROL_MSG(cSize == 0, "Compression error !");
- { int const dSize = LZ4_decompress_safe(compressedBuff, resultBuff, cSize, resultBuffCapacity);
+ { int const dSize = LZ4_decompress_safe((const char*)compressedBuff, (char*)resultBuff, cSize, (int)resultBuffCapacity);
CONTROL_MSG(dSize < 0, "Decompression detected an error !");
CONTROL_MSG(dSize != (int)srcSize, "Decompression corruption error : wrong decompressed size !");
}
@@ -102,7 +102,7 @@ static void roundTripTest(void* resultBuff, size_t resultBuffCapacity,
static void roundTripCheck(const void* srcBuff, size_t srcSize)
{
- size_t const cBuffSize = LZ4_compressBound(srcSize);
+ size_t const cBuffSize = LZ4_compressBound((int)srcSize);
void* const cBuff = malloc(cBuffSize);
void* const rBuff = malloc(cBuffSize);
From 2e4847c2d55fb4c4f5dc833e61b5f374da8407e6 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Tue, 4 Sep 2018 18:21:40 -0700
Subject: [PATCH 27/45] fixed #560
it was a fairly complex scenario,
involving source files > 64K
and some extraordinary conditions related to specific layout of ranges of zeroes.
and only on level 9.
---
lib/lz4hc.c | 4 ++++
programs/bench.c | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/lib/lz4hc.c b/lib/lz4hc.c
index 8108ea011..236dc5b3c 100644
--- a/lib/lz4hc.c
+++ b/lib/lz4hc.c
@@ -327,6 +327,8 @@ LZ4HC_InsertAndGetWiderMatch (
if (lookBackLength==0) { /* no back possible */
size_t const maxML = MIN(currentSegmentLength, srcPatternLength);
if ((size_t)longest < maxML) {
+ assert(base + matchIndex < ip);
+ if (ip - (base+matchIndex) > MAX_DISTANCE) break;
assert(maxML < 2 GB);
longest = (int)maxML;
*matchpos = base + matchIndex; /* virtual pos, relative to ip, to retrieve offset */
@@ -450,6 +452,8 @@ LZ4_FORCE_INLINE int LZ4HC_encodeSequence (
*op += length;
/* Encode Offset */
+ assert(*ip > match);
+ assert( (*ip - match) <= MAX_DISTANCE );
LZ4_writeLE16(*op, (U16)(*ip-match)); *op += 2;
/* Encode MatchLength */
diff --git a/programs/bench.c b/programs/bench.c
index 9dc31c4ce..11bf0440c 100644
--- a/programs/bench.c
+++ b/programs/bench.c
@@ -329,7 +329,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize,
{ U64 const crcCheck = XXH64(resultBuffer, srcSize, 0);
if (crcOrig!=crcCheck) {
size_t u;
- DISPLAY("!!! WARNING !!! %14s : Invalid Checksum : %x != %x \n", displayName, (unsigned)crcOrig, (unsigned)crcCheck);
+ DISPLAY("\n!!! WARNING !!! %17s : Invalid Checksum : %x != %x \n", displayName, (unsigned)crcOrig, (unsigned)crcCheck);
for (u=0; u
Date: Tue, 4 Sep 2018 18:23:21 -0700
Subject: [PATCH 28/45] made roundTripTest fully general
no longer "locked" on level 9
---
tests/roundTripTest.c | 101 ++++++++++++++++++++++++++++++------------
1 file changed, 73 insertions(+), 28 deletions(-)
diff --git a/tests/roundTripTest.c b/tests/roundTripTest.c
index 2caa2bc25..2d344518e 100644
--- a/tests/roundTripTest.c
+++ b/tests/roundTripTest.c
@@ -10,13 +10,22 @@
/*
* This program takes a file in input,
- * performs an LZ4 round-trip test (compression - decompress)
+ * performs an LZ4 round-trip test (compress + decompress)
* compares the result with original
* and generates an abort() on corruption detection,
* in order for afl to register the event as a crash.
*/
+/*===========================================
+* Tuning Constant
+*==========================================*/
+#ifndef MIN_CLEVEL
+# define MIN_CLEVEL (int)(-5)
+#endif
+
+
+
/*===========================================
* Dependencies
*==========================================*/
@@ -62,27 +71,42 @@ static size_t checkBuffers(const void* buff1, const void* buff2, size_t buffSize
return pos;
}
+
+/* select a compression level
+ * based on first bytes present in a reference buffer */
+static int select_clevel(const void* refBuff, size_t refBuffSize)
+{
+ const int minCLevel = MIN_CLEVEL;
+ const int maxClevel = LZ4HC_CLEVEL_MAX;
+ const int cLevelSpan = maxClevel - minCLevel;
+ size_t const hashLength = MIN(16, refBuffSize);
+ unsigned const h32 = XXH32(refBuff, hashLength, 0);
+ int const randL = h32 % (cLevelSpan+1);
+
+ return minCLevel + randL;
+}
+
+
+typedef int (*compressFn)(const char* src, char* dst, int srcSize, int dstSize, int cLevel);
+
+
/** roundTripTest() :
* Compresses `srcBuff` into `compressedBuff`,
* then decompresses `compressedBuff` into `resultBuff`.
- * Compression level used is derived from content's head bytes.
- * This function abort() if it detects any round-trip error,
- * so if it returns, round trip is considered successfully validated.
+ * If clevel==0, compression level is derived from srcBuff's content head bytes.
+ * This function abort() if it detects any round-trip error.
+ * Therefore, if it returns, round trip is considered successfully validated.
* Note : `compressedBuffCapacity` should be `>= LZ4_compressBound(srcSize)`
* for compression to be guaranteed to work */
static void roundTripTest(void* resultBuff, size_t resultBuffCapacity,
void* compressedBuff, size_t compressedBuffCapacity,
- const void* srcBuff, size_t srcSize)
+ const void* srcBuff, size_t srcSize,
+ int clevel)
{
- const int minCLevel = 1;
- const int maxClevel = 12;
- const int cLevelSpan = maxClevel - minCLevel;
- size_t const hashLength = MIN(16, srcSize);
- unsigned const h32 = XXH32(srcBuff, hashLength, 0);
- int const randL = h32 % (cLevelSpan+1);
- int const cLevel = minCLevel + randL;
- int const realCLevel = (cLevel * 0) + 9; /* <=== Currently : only test level 9 */
- int const cSize = LZ4_compress_HC((const char*)srcBuff, (char*)compressedBuff, (int)srcSize, (int)compressedBuffCapacity, realCLevel);
+ int const proposed_clevel = clevel ? clevel : select_clevel(srcBuff, srcSize);
+ int const selected_clevel = proposed_clevel < 0 ? -proposed_clevel : proposed_clevel; /* if level < 0, it becomes an accelearion value */
+ compressFn compress = selected_clevel >= LZ4HC_CLEVEL_MIN ? LZ4_compress_HC : LZ4_compress_fast;
+ int const cSize = compress((const char*)srcBuff, (char*)compressedBuff, (int)srcSize, (int)compressedBuffCapacity, selected_clevel);
CONTROL_MSG(cSize == 0, "Compression error !");
{ int const dSize = LZ4_decompress_safe((const char*)compressedBuff, (char*)resultBuff, cSize, (int)resultBuffCapacity);
@@ -100,7 +124,7 @@ static void roundTripTest(void* resultBuff, size_t resultBuffCapacity,
}
-static void roundTripCheck(const void* srcBuff, size_t srcSize)
+static void roundTripCheck(const void* srcBuff, size_t srcSize, int clevel)
{
size_t const cBuffSize = LZ4_compressBound((int)srcSize);
void* const cBuff = malloc(cBuffSize);
@@ -113,7 +137,8 @@ static void roundTripCheck(const void* srcBuff, size_t srcSize)
roundTripTest(rBuff, cBuffSize,
cBuff, cBuffSize,
- srcBuff, srcSize);
+ srcBuff, srcSize,
+ clevel);
free(rBuff);
free(cBuff);
@@ -158,46 +183,66 @@ static void loadFile(void* buffer, const char* fileName, size_t fileSize)
{
FILE* const f = fopen(fileName, "rb");
if (isDirectory(fileName)) {
- fprintf(stderr, "Ignoring %s directory \n", fileName);
+ MSG("Ignoring %s directory \n", fileName);
exit(2);
}
if (f==NULL) {
- fprintf(stderr, "Impossible to open %s \n", fileName);
+ MSG("Impossible to open %s \n", fileName);
exit(3);
}
{ size_t const readSize = fread(buffer, 1, fileSize, f);
if (readSize != fileSize) {
- fprintf(stderr, "Error reading %s \n", fileName);
+ MSG("Error reading %s \n", fileName);
exit(5);
} }
fclose(f);
}
-static void fileCheck(const char* fileName)
+static void fileCheck(const char* fileName, int clevel)
{
size_t const fileSize = getFileSize(fileName);
void* const buffer = malloc(fileSize + !fileSize /* avoid 0 */);
if (!buffer) {
- fprintf(stderr, "not enough memory \n");
+ MSG("not enough memory \n");
exit(4);
}
loadFile(buffer, fileName, fileSize);
- roundTripCheck(buffer, fileSize);
+ roundTripCheck(buffer, fileSize, clevel);
free (buffer);
}
+int bad_usage(const char* exeName)
+{
+ MSG(" \n");
+ MSG("bad usage: \n");
+ MSG(" \n");
+ MSG("%s [Options] fileName \n", exeName);
+ MSG(" \n");
+ MSG("Options: \n");
+ MSG("-# : use #=[0-9] compression level (default:0 == random) \n");
+ return 1;
+}
+
+
int main(int argCount, const char** argv)
{
- int const argNb = 1;
+ const char* const exeName = argv[0];
+ int argNb = 1;
+ int clevel = 0;
+
+ assert(argCount >= 1);
+ if (argCount < 2) return bad_usage(exeName);
- if (argCount < 2) {
- fprintf(stderr, "Error : no argument : need input file \n");
- exit(9);
+ if (argv[1][0] == '-') {
+ clevel = argv[1][1] - '0';
+ argNb = 2;
}
- fileCheck(argv[argNb]);
- fprintf(stderr, "no pb detected \n");
+ if (argNb >= argCount) return bad_usage(exeName);
+
+ fileCheck(argv[argNb], clevel);
+ MSG("no pb detected \n");
return 0;
}
From 30f6f34328733ec4e74c78c06f667810db0417df Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 5 Sep 2018 11:25:10 -0700
Subject: [PATCH 29/45] removed one assert() condition
which is not correct when using LZ4_HC with dictionary and starting from a low address (<0x10000).
---
lib/lz4hc.c | 3 +--
tests/fuzzer.c | 15 ++++++++++-----
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/lib/lz4hc.c b/lib/lz4hc.c
index 236dc5b3c..e913ee7b3 100644
--- a/lib/lz4hc.c
+++ b/lib/lz4hc.c
@@ -452,8 +452,7 @@ LZ4_FORCE_INLINE int LZ4HC_encodeSequence (
*op += length;
/* Encode Offset */
- assert(*ip > match);
- assert( (*ip - match) <= MAX_DISTANCE );
+ assert( (*ip - match) <= MAX_DISTANCE ); /* note : consider providing offset as a value, rather than as a pointer difference */
LZ4_writeLE16(*op, (U16)(*ip-match)); *op += 2;
/* Encode MatchLength */
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 5a92f8f06..6c7951596 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -323,12 +323,17 @@ static int FUZ_test(U32 seed, U32 nbCycles, const U32 startCycle, const double c
int result = 0;
unsigned cycleNb;
-# define FUZ_CHECKTEST(cond, ...) if (cond) { printf("Test %u : ", testNb); printf(__VA_ARGS__); \
- printf(" (seed %u, cycle %u) \n", seed, cycleNb); goto _output_error; }
+# define FUZ_CHECKTEST(cond, ...) \
+ if (cond) { \
+ printf("Test %u : ", testNb); printf(__VA_ARGS__); \
+ printf(" (seed %u, cycle %u) \n", seed, cycleNb); \
+ goto _output_error; \
+ }
+
# define FUZ_DISPLAYTEST(...) { \
testNb++; \
if (g_displayLevel>=4) { \
- printf("\r%4u - %2u ", cycleNb, testNb); \
+ printf("\r%4u - %2u :", cycleNb, testNb); \
printf(" " __VA_ARGS__); \
printf(" "); \
fflush(stdout); \
@@ -805,7 +810,7 @@ static int FUZ_test(U32 seed, U32 nbCycles, const U32 startCycle, const double c
FUZ_CHECKTEST(ret>=0, "LZ4_decompress_safe_usingDict should have failed : not enough output size (-1 byte)");
FUZ_CHECKTEST(decodedBuffer[blockSize-1], "LZ4_decompress_safe_usingDict overrun specified output buffer size");
- FUZ_DISPLAYTEST();
+ FUZ_DISPLAYTEST("LZ4_decompress_safe_usingDict with a too small output buffer");
{ U32 const missingBytes = (FUZ_rand(&randState) & 0xF) + 2;
if ((U32)blockSize > missingBytes) {
decodedBuffer[blockSize-missingBytes] = 0;
@@ -815,7 +820,7 @@ static int FUZ_test(U32 seed, U32 nbCycles, const U32 startCycle, const double c
} }
/* Compress HC using External dictionary */
- FUZ_DISPLAYTEST();
+ FUZ_DISPLAYTEST("LZ4_compress_HC_continue with an external dictionary");
dict -= (FUZ_rand(&randState) & 7); /* even bigger separation */
if (dict < (char*)CNBuffer) dict = (char*)CNBuffer;
LZ4_resetStreamHC (&LZ4dictHC, compressionLevel);
From 0fea528e3a70f8578ca6e7f15d922dab8aa9ff25 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 5 Sep 2018 14:05:08 -0700
Subject: [PATCH 30/45] updated documentation regarding dictionary compression
following suggestion from @stbrumme (#558)
Also : bumped version number, regenerated man page and html doc
---
NEWS | 6 ++++++
README.md | 23 ++++++++++++++---------
doc/lz4_manual.html | 6 +++---
doc/lz4frame_manual.html | 4 ++--
lib/lz4.h | 2 +-
programs/lz4.1 | 14 +++++++++++---
programs/lz4.1.md | 20 +++++++++++++-------
programs/lz4cli.c | 2 +-
tests/.gitignore | 6 +++++-
tests/Makefile | 3 ++-
10 files changed, 58 insertions(+), 28 deletions(-)
diff --git a/NEWS b/NEWS
index 0139e6123..8ee3c92d4 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+v1.8.3
+fix : data corruption for files > 64KB at level 9 under specific conditions (#560)
+cli : new command --fast, by @jennifermliu
+build : added Haiku target, by @fbrosson
+doc : updated documentation regarding dictionary compression
+
v1.8.2
perf: *much* faster dictionary compression on small files, by @felixhandte
perf: improved decompression speed and binary size, by Alexey Tourbin (@svpv)
diff --git a/README.md b/README.md
index 406792a11..e64020d1c 100644
--- a/README.md
+++ b/README.md
@@ -2,18 +2,23 @@ LZ4 - Extremely fast compression
================================
LZ4 is lossless compression algorithm,
-providing compression speed at 400 MB/s per core,
+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.
Speed can be tuned dynamically, selecting an "acceleration" factor
-which trades compression ratio for more speed up.
+which trades compression ratio for faster speed.
On the other end, a high compression derivative, LZ4_HC, is also provided,
trading CPU time for improved compression ratio.
All versions feature the same decompression speed.
+LZ4 is also compatible with [dictionary compression](https://github.com/facebook/zstd#the-case-for-small-data-compression),
+and can ingest any input file as dictionary,
+including those created by [Zstandard Dictionary Builder](https://github.com/facebook/zstd/blob/v1.3.5/programs/zstd.1.md#dictionary-builder).
+(note: only the final 64KB are used).
+
LZ4 library is provided as open-source software using BSD 2-Clause license.
@@ -67,8 +72,8 @@ in single-thread mode.
[zlib]: http://www.zlib.net/
[Zstandard]: http://www.zstd.net/
-LZ4 is also compatible and well optimized for x32 mode,
-for which it provides some additional speed performance.
+LZ4 is also compatible and optimized for x32 mode,
+for which it provides additional speed performance.
Installation
@@ -76,7 +81,7 @@ Installation
```
make
-make install # this command may require root access
+make install # this command may require root permissions
```
LZ4's `Makefile` supports standard [Makefile conventions],
@@ -94,10 +99,10 @@ Documentation
The raw LZ4 block compression format is detailed within [lz4_Block_format].
-To compress an arbitrarily long file or data stream, multiple blocks are required.
-Organizing these blocks and providing a common header format to handle their content
-is the purpose of the Frame format, defined into [lz4_Frame_format].
-Interoperable versions of LZ4 must respect this frame format.
+Arbitrarily long files or data streams are compressed using multiple blocks,
+for streaming requirements. These blocks are organized into a frame,
+defined into [lz4_Frame_format].
+Interoperable versions of LZ4 must also respect the frame format.
[lz4_Block_format]: doc/lz4_Block_format.md
[lz4_Frame_format]: doc/lz4_Frame_format.md
diff --git a/doc/lz4_manual.html b/doc/lz4_manual.html
index 3fc71e4af..c7c576346 100644
--- a/doc/lz4_manual.html
+++ b/doc/lz4_manual.html
@@ -1,10 +1,10 @@
-1.8.2 Manual
+1.8.3 Manual
-1.8.2 Manual
+1.8.3 Manual
Contents
@@ -179,7 +179,7 @@ 1.8.2 Manual
'dst' buffer must be already allocated.
If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
- Important : The previous 64KB of compressed data is assumed to remain present and unmodified in memory!
+ Important : The previous 64KB of source data is assumed to remain present and unmodified in memory!
Special 1 : When input is a double-buffer, they can have any size, including < 64 KB.
Make sure that buffers are separated by at least one byte.
diff --git a/doc/lz4frame_manual.html b/doc/lz4frame_manual.html
index 53ea7eb19..fb8e0ceb3 100644
--- a/doc/lz4frame_manual.html
+++ b/doc/lz4frame_manual.html
@@ -1,10 +1,10 @@
-1.8.2 Manual
+1.8.3 Manual
-1.8.2 Manual
+1.8.3 Manual
Contents
diff --git a/lib/lz4.h b/lib/lz4.h
index a0eddcead..491b67a2a 100644
--- a/lib/lz4.h
+++ b/lib/lz4.h
@@ -93,7 +93,7 @@ extern "C" {
/*------ Version ------*/
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
#define LZ4_VERSION_MINOR 8 /* for new (non-breaking) interface capabilities */
-#define LZ4_VERSION_RELEASE 2 /* for tweaks, bug-fixes, or development */
+#define LZ4_VERSION_RELEASE 3 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
diff --git a/programs/lz4.1 b/programs/lz4.1
index e0f6a81b3..f35e29d2b 100644
--- a/programs/lz4.1
+++ b/programs/lz4.1
@@ -1,5 +1,5 @@
.
-.TH "LZ4" "1" "2018-01-13" "lz4 1.8.1" "User Commands"
+.TH "LZ4" "1" "September 2018" "lz4 1.8.3" "User Commands"
.
.SH "NAME"
\fBlz4\fR \- lz4, unlz4, lz4cat \- Compress or decompress \.lz4 files
@@ -115,7 +115,11 @@ Benchmark mode, using \fB#\fR compression level\.
.
.TP
\fB\-#\fR
-Compression level, with # being any value from 1 to 16\. Higher values trade compression speed for compression ratio\. Values above 16 are considered the same as 16\. Recommended values are 1 for fast compression (default), and 9 for high compression\. Speed/compression trade\-off will vary depending on data to compress\. Decompression speed remains fast at all settings\.
+Compression level, with # being any value from 1 to 12\. Higher values trade compression speed for compression ratio\. Values above 12 are considered the same as 12\. Recommended values are 1 for fast compression (default), and 9 for high compression\. Speed/compression trade\-off will vary depending on data to compress\. Decompression speed remains fast at all settings\.
+.
+.TP
+\fB\-D dictionaryName\fR
+Compress, decompress or benchmark using dictionary \fIdictionaryName\fR\. Compression and decompression must use the same dictionary to be compatible\. Using a different dictionary during decompression will either abort due to decompression error, or generate a checksum error\.
.
.TP
\fB\-f\fR \fB\-\-[no\-]force\fR
@@ -151,6 +155,10 @@ Block size [4\-7](default : 7)
Block Dependency (improves compression ratio on small blocks)
.
.TP
+\fB\-\-fast[=#]\fR
+switch to ultra\-fast compression levels\. If \fB=#\fR is not present, it defaults to \fB1\fR\. The higher the value, the faster the compression speed, at the cost of some compression ratio\. This setting overwrites compression level if one was set previously\. Similarly, if a compression level is set after \fB\-\-fast\fR, it overrides it\.
+.
+.TP
\fB\-\-[no\-]frame\-crc\fR
Select frame checksum (default:enabled)
.
@@ -214,7 +222,7 @@ Benchmark multiple compression levels, from b# to e# (included)
.
.TP
\fB\-i#\fR
-Minimum evaluation in seconds [1\-9] (default : 3)
+Minimum evaluation time in seconds [1\-9] (default : 3)
.
.SH "BUGS"
Report bugs at: https://github\.com/lz4/lz4/issues
diff --git a/programs/lz4.1.md b/programs/lz4.1.md
index d4eaf8aab..12b8e29de 100644
--- a/programs/lz4.1.md
+++ b/programs/lz4.1.md
@@ -125,6 +125,19 @@ only the latest one will be applied.
Speed/compression trade-off will vary depending on data to compress.
Decompression speed remains fast at all settings.
+* `--fast[=#]`:
+ switch to ultra-fast compression levels.
+ The higher the value, the faster the compression speed, at the cost of some compression ratio.
+ If `=#` is not present, it defaults to `1`.
+ This setting overrides compression level if one was set previously.
+ Similarly, if a compression level is set after `--fast`, it overrides it.
+
+* `-D dictionaryName`:
+ Compress, decompress or benchmark using dictionary _dictionaryName_.
+ Compression and decompression must use the same dictionary to be compatible.
+ Using a different dictionary during decompression will either
+ abort due to decompression error, or generate a checksum error.
+
* `-f` `--[no-]force`:
This option has several effects:
@@ -156,13 +169,6 @@ only the latest one will be applied.
* `-BD`:
Block Dependency (improves compression ratio on small blocks)
-* `--fast[=#]`:
- switch to ultra-fast compression levels.
- If `=#` is not present, it defaults to `1`.
- The higher the value, the faster the compression speed, at the cost of some compression ratio.
- This setting overwrites compression level if one was set previously.
- Similarly, if a compression level is set after `--fast`, it overrides it.
-
* `--[no-]frame-crc`:
Select frame checksum (default:enabled)
diff --git a/programs/lz4cli.c b/programs/lz4cli.c
index dc60b00bb..26a8089bd 100644
--- a/programs/lz4cli.c
+++ b/programs/lz4cli.c
@@ -110,7 +110,7 @@ static int usage(const char* exeName)
DISPLAY( " -9 : High compression \n");
DISPLAY( " -d : decompression (default for %s extension)\n", LZ4_EXTENSION);
DISPLAY( " -z : force compression \n");
- DISPLAY( " -D FILE: use dictionary in FILE \n");
+ DISPLAY( " -D FILE: use FILE as dictionary \n");
DISPLAY( " -f : overwrite output without prompting \n");
DISPLAY( " -k : preserve source files(s) (default) \n");
DISPLAY( "--rm : remove source file(s) after successful de/compression \n");
diff --git a/tests/.gitignore b/tests/.gitignore
index 36dff4207..9aa42a064 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,5 +1,5 @@
-# test build artefacts
+# build artefacts
datagen
frametest
frametest32
@@ -8,8 +8,12 @@ fullbench32
fuzzer
fuzzer32
fasttest
+roundTripTest
checkTag
# test artefacts
tmp*
versionsTest
+
+# local tests
+afl
diff --git a/tests/Makefile b/tests/Makefile
index 81033b59b..f270c46de 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -114,7 +114,8 @@ clean:
fullbench$(EXT) fullbench32$(EXT) \
fuzzer$(EXT) fuzzer32$(EXT) \
frametest$(EXT) frametest32$(EXT) \
- fasttest$(EXT) datagen$(EXT) checkTag$(EXT)
+ fasttest$(EXT) roundTripTest$(EXT) \
+ datagen$(EXT) checkTag$(EXT)
@rm -fR $(TESTDIR)
@echo Cleaning completed
From b61991491ab000229d39028c9f80939721825c72 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 5 Sep 2018 15:16:03 -0700
Subject: [PATCH 31/45] fixed compression time
displayed at the end (#555)
---
programs/lz4io.c | 31 +++++++++++++++++++++----------
1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/programs/lz4io.c b/programs/lz4io.c
index b52c1f32f..28d6537b5 100644
--- a/programs/lz4io.c
+++ b/programs/lz4io.c
@@ -628,16 +628,23 @@ static int LZ4IO_compressFilename_extRess(cRess_t ress, const char* srcFileName,
/* Copy owner, file permissions and modification time */
{ stat_t statbuf;
- if (strcmp (srcFileName, stdinmark) && strcmp (dstFileName, stdoutmark) && strcmp (dstFileName, nulmark) && UTIL_getFileStat(srcFileName, &statbuf))
+ if (strcmp (srcFileName, stdinmark)
+ && strcmp (dstFileName, stdoutmark)
+ && strcmp (dstFileName, nulmark)
+ && UTIL_getFileStat(srcFileName, &statbuf)) {
UTIL_setFileStat(dstFileName, &statbuf);
- }
+ } }
- if (g_removeSrcFile) { if (remove(srcFileName)) EXM_THROW(40, "Remove error : %s: %s", srcFileName, strerror(errno)); } /* remove source file : --rm */
+ if (g_removeSrcFile) { /* remove source file : --rm */
+ if (remove(srcFileName))
+ EXM_THROW(40, "Remove error : %s: %s", srcFileName, strerror(errno));
+ }
/* Final Status */
DISPLAYLEVEL(2, "\r%79s\r", "");
DISPLAYLEVEL(2, "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
- filesize, compressedfilesize, (double)compressedfilesize/(filesize + !filesize)*100); /* avoid division by zero */
+ filesize, compressedfilesize,
+ (double)compressedfilesize / (filesize + !filesize /* avoid division by zero */ ) * 100);
return 0;
}
@@ -645,21 +652,25 @@ static int LZ4IO_compressFilename_extRess(cRess_t ress, const char* srcFileName,
int LZ4IO_compressFilename(const char* srcFileName, const char* dstFileName, int compressionLevel)
{
- clock_t const start = clock();
+ UTIL_time_t const timeStart = UTIL_getTime();
+ clock_t const cpuStart = clock();
cRess_t const ress = LZ4IO_createCResources();
- int const issueWithSrcFile = LZ4IO_compressFilename_extRess(ress, srcFileName, dstFileName, compressionLevel);
+ int const result = LZ4IO_compressFilename_extRess(ress, srcFileName, dstFileName, compressionLevel);
/* Free resources */
LZ4IO_freeCResources(ress);
/* Final Status */
- { clock_t const end = clock();
- double const seconds = (double)(end - start) / CLOCKS_PER_SEC;
- DISPLAYLEVEL(4, "Completed in %.2f sec \n", seconds);
+ { clock_t const cpuEnd = clock();
+ double const cpuLoad_s = (double)(cpuEnd - cpuStart) / CLOCKS_PER_SEC;
+ U64 const timeLength_ns = UTIL_clockSpanNano(timeStart);
+ double const timeLength_s = (double)timeLength_ns / 1000000000;
+ DISPLAYLEVEL(4, "Completed in %.2f sec (cpu load : %.0f%%)\n",
+ timeLength_s, (cpuLoad_s / timeLength_s) * 100);
}
- return issueWithSrcFile;
+ return result;
}
From b2e56d82bf73f50484c9cf71e81c1af4ebe3cc0b Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 5 Sep 2018 16:04:05 -0700
Subject: [PATCH 32/45] Introduced constants LZ4F_INIT_*
to simplify initialization of lz4frame.h structures.
Partially in response to #546.
---
examples/frameCompress.c | 21 +++++++++++----------
lib/lz4frame.h | 26 +++++++++++++++++---------
2 files changed, 28 insertions(+), 19 deletions(-)
diff --git a/examples/frameCompress.c b/examples/frameCompress.c
index 9bfea483f..6580e9642 100644
--- a/examples/frameCompress.c
+++ b/examples/frameCompress.c
@@ -31,12 +31,13 @@ static const LZ4F_preferences_t kPrefs = {
static void safe_fwrite(void* buf, size_t eltSize, size_t nbElt, FILE* f)
{
size_t const writtenSize = fwrite(buf, eltSize, nbElt, f);
- size_t const expectedSize = eltSize * nbElt; /* note : should check for overflow */
+ size_t const expectedSize = eltSize * nbElt;
+ assert(expectedSize / nbElt == eltSize); /* check overflow */
if (writtenSize < expectedSize) {
if (ferror(f)) /* note : ferror() must follow fwrite */
- printf("Write failed\n");
+ fprintf(stderr, "Write failed \n");
else
- printf("Short write\n");
+ fprintf(stderr, "Short write \n");
exit(1);
}
}
@@ -54,9 +55,9 @@ typedef struct {
static compressResult_t
compress_file_internal(FILE* f_in, FILE* f_out,
- LZ4F_compressionContext_t ctx,
- void* inBuff, size_t inChunkSize,
- void* outBuff, size_t outCapacity)
+ LZ4F_compressionContext_t ctx,
+ void* inBuff, size_t inChunkSize,
+ void* outBuff, size_t outCapacity)
{
compressResult_t result = { 1, 0, 0 }; /* result for an error */
unsigned long long count_in = 0, count_out;
@@ -167,9 +168,9 @@ static size_t get_block_size(const LZ4F_frameInfo_t* info) {
/* @return : 1==error, 0==success */
static int
decompress_file_internal(FILE* f_in, FILE* f_out,
- LZ4F_dctx* dctx,
- void* src, size_t srcCapacity, size_t filled, size_t alreadyConsumed,
- void* dst, size_t dstCapacity)
+ LZ4F_dctx* dctx,
+ void* src, size_t srcCapacity, size_t filled, size_t alreadyConsumed,
+ void* dst, size_t dstCapacity)
{
int firstChunk = 1;
size_t ret = 1;
@@ -278,7 +279,7 @@ static int decompress_file(FILE* f_in, FILE* f_out)
if (!src) { perror("decompress_file(src)"); return 1; }
LZ4F_dctx* dctx;
- { size_t const dctxStatus = LZ4F_createDecompressionContext(&dctx, 100);
+ { size_t const dctxStatus = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
if (LZ4F_isError(dctxStatus)) {
printf("LZ4F_dctx creation error: %s\n", LZ4F_getErrorName(dctxStatus));
} }
diff --git a/lib/lz4frame.h b/lib/lz4frame.h
index fb434ff76..2a67c69e8 100644
--- a/lib/lz4frame.h
+++ b/lib/lz4frame.h
@@ -33,7 +33,7 @@
*/
/* LZ4F is a stand-alone API to create LZ4-compressed frames
- * conformant with specification v1.5.1.
+ * conformant with specification v1.6.1.
* It also offers streaming capabilities.
* lz4.h is not required when using lz4frame.h.
* */
@@ -159,8 +159,9 @@ typedef LZ4F_contentChecksum_t contentChecksum_t;
/*! LZ4F_frameInfo_t :
* makes it possible to set or read frame parameters.
- * It's not required to set all fields, as long as the structure was initially memset() to zero.
- * For all fields, 0 sets it to default value */
+ * Structure must be first init to 0, using memset() or LZ4F_INIT_FRAMEINFO,
+ * setting all parameters to default.
+ * It's then possible to update selectively some parameters */
typedef struct {
LZ4F_blockSizeID_t blockSizeID; /* max64KB, max256KB, max1MB, max4MB; 0 == default */
LZ4F_blockMode_t blockMode; /* LZ4F_blockLinked, LZ4F_blockIndependent; 0 == default */
@@ -171,24 +172,30 @@ typedef struct {
LZ4F_blockChecksum_t blockChecksumFlag; /* 1: each block followed by a checksum of block's compressed data; 0: disabled (default) */
} LZ4F_frameInfo_t;
+#define LZ4F_INIT_FRAMEINFO { 0, 0, 0, 0, 0, 0, 0 }
+
/*! LZ4F_preferences_t :
- * makes it possible to supply detailed compression parameters to the stream interface.
- * Structure is presumed initially memset() to zero, representing default settings.
+ * makes it possible to supply advanced compression instructions to streaming interface.
+ * Structure must be first init to 0, using memset() or LZ4F_INIT_PREFERENCES,
+ * setting all parameters to default.
* All reserved fields must be set to zero. */
typedef struct {
LZ4F_frameInfo_t frameInfo;
int compressionLevel; /* 0: default (fast mode); values > LZ4HC_CLEVEL_MAX count as LZ4HC_CLEVEL_MAX; values < 0 trigger "fast acceleration" */
- unsigned autoFlush; /* 1: always flush, to reduce usage of internal buffers */
- unsigned favorDecSpeed; /* 1: parser favors decompression speed vs compression ratio. Only works for high compression modes (>= LZ4LZ4HC_CLEVEL_OPT_MIN) */ /* >= v1.8.2 */
+ unsigned autoFlush; /* 1: always flush; reduces usage of internal buffers */
+ unsigned favorDecSpeed; /* 1: parser favors decompression speed vs compression ratio. Only works for high compression modes (>= LZ4HC_CLEVEL_OPT_MIN) */ /* >= v1.8.2 */
unsigned reserved[3]; /* must be zero for forward compatibility */
} LZ4F_preferences_t;
-LZ4FLIB_API int LZ4F_compressionLevel_max(void);
+#define LZ4F_INIT_PREFERENCES { LZ4F_INIT_FRAMEINFO, 0, 0, 0, { 0, 0, 0 } }
/*-*********************************
* Simple compression function
***********************************/
+
+LZ4FLIB_API int LZ4F_compressionLevel_max(void);
+
/*! LZ4F_compressFrameBound() :
* Returns the maximum possible compressed size with LZ4F_compressFrame() given srcSize and preferences.
* `preferencesPtr` is optional. It can be replaced by NULL, in which case, the function will assume default preferences.
@@ -222,8 +229,9 @@ typedef struct {
/*--- Resource Management ---*/
-#define LZ4F_VERSION 100
+#define LZ4F_VERSION 100 /* API version, signal an API breaking change */
LZ4FLIB_API unsigned LZ4F_getVersion(void);
+
/*! LZ4F_createCompressionContext() :
* The first thing to do is to create a compressionContext object, which will be used in all compression operations.
* This is achieved using LZ4F_createCompressionContext(), which takes as argument a version.
From 858b6ad7f3f9aae072bccc1dbc484cbc2793a45a Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 5 Sep 2018 16:45:46 -0700
Subject: [PATCH 33/45] frameCompress : added an error detection case
check for potential input data not consumed.
---
examples/frameCompress.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/examples/frameCompress.c b/examples/frameCompress.c
index a97dc723c..a0c5d3d80 100644
--- a/examples/frameCompress.c
+++ b/examples/frameCompress.c
@@ -195,7 +195,7 @@ decompress_file_internal(FILE* f_in, FILE* f_out,
* Continue while there is more input to read (srcPtr != srcEnd)
* and the frame isn't over (ret != 0)
*/
- while (srcPtr != srcEnd && ret != 0) {
+ while (srcPtr < srcEnd && ret != 0) {
/* Any data within dst has been flushed at this stage */
size_t dstSize = dstCapacity;
size_t srcSize = srcEnd - srcPtr;
@@ -209,9 +209,20 @@ decompress_file_internal(FILE* f_in, FILE* f_out,
/* Update input */
srcPtr += srcSize;
}
+
+ assert(srcPtr <= srcEnd);
+
+ /* Ensure all input data has been consumed.
+ * It is valid to have multiple frames in the same file,
+ * but this example only supports one frame.
+ */
+ if (srcPtr < srcEnd) {
+ printf("Decompress: Trailing data left in file after frame\n");
+ return 1;
+ }
}
- /* Check that there isn't trailing input data after the frame.
+ /* Check that there isn't trailing data in the file after the frame.
* It is valid to have multiple frames in the same file,
* but this example only supports one frame.
*/
From 26c42d7ad1c394538adb76879ec8d209b3ff5c84 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 5 Sep 2018 17:57:15 -0700
Subject: [PATCH 34/45] added comments on version numbers
---
lib/lz4frame.h | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/lib/lz4frame.h b/lib/lz4frame.h
index 2a67c69e8..75f1fd91b 100644
--- a/lib/lz4frame.h
+++ b/lib/lz4frame.h
@@ -35,7 +35,8 @@
/* LZ4F is a stand-alone API to create LZ4-compressed frames
* conformant with specification v1.6.1.
* It also offers streaming capabilities.
- * lz4.h is not required when using lz4frame.h.
+ * lz4.h is not required when using lz4frame.h,
+ * except to get constant such as LZ4_VERSION_NUMBER.
* */
#ifndef LZ4F_H_09782039843
@@ -172,7 +173,7 @@ typedef struct {
LZ4F_blockChecksum_t blockChecksumFlag; /* 1: each block followed by a checksum of block's compressed data; 0: disabled (default) */
} LZ4F_frameInfo_t;
-#define LZ4F_INIT_FRAMEINFO { 0, 0, 0, 0, 0, 0, 0 }
+#define LZ4F_INIT_FRAMEINFO { 0, 0, 0, 0, 0, 0, 0 } /* v1.8.3+ */
/*! LZ4F_preferences_t :
* makes it possible to supply advanced compression instructions to streaming interface.
@@ -183,11 +184,11 @@ typedef struct {
LZ4F_frameInfo_t frameInfo;
int compressionLevel; /* 0: default (fast mode); values > LZ4HC_CLEVEL_MAX count as LZ4HC_CLEVEL_MAX; values < 0 trigger "fast acceleration" */
unsigned autoFlush; /* 1: always flush; reduces usage of internal buffers */
- unsigned favorDecSpeed; /* 1: parser favors decompression speed vs compression ratio. Only works for high compression modes (>= LZ4HC_CLEVEL_OPT_MIN) */ /* >= v1.8.2 */
+ unsigned favorDecSpeed; /* 1: parser favors decompression speed vs compression ratio. Only works for high compression modes (>= LZ4HC_CLEVEL_OPT_MIN) */ /* v1.8.2+ */
unsigned reserved[3]; /* must be zero for forward compatibility */
} LZ4F_preferences_t;
-#define LZ4F_INIT_PREFERENCES { LZ4F_INIT_FRAMEINFO, 0, 0, 0, { 0, 0, 0 } }
+#define LZ4F_INIT_PREFERENCES { LZ4F_INIT_FRAMEINFO, 0, 0, 0, { 0, 0, 0 } } /* v1.8.3+ */
/*-*********************************
@@ -229,7 +230,7 @@ typedef struct {
/*--- Resource Management ---*/
-#define LZ4F_VERSION 100 /* API version, signal an API breaking change */
+#define LZ4F_VERSION 100 /* This number can be used to check for an incompatible API breaking change */
LZ4FLIB_API unsigned LZ4F_getVersion(void);
/*! LZ4F_createCompressionContext() :
From e32766cc345fb811cdcf25192144c85734bc636e Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 7 Sep 2018 11:02:42 -0700
Subject: [PATCH 35/45] updated API documentation
---
lib/lz4.c | 4 +--
lib/lz4.h | 102 ++++++++++++++++++++++++++++++------------------------
2 files changed, 58 insertions(+), 48 deletions(-)
diff --git a/lib/lz4.c b/lib/lz4.c
index 05454fc09..35df7f5a4 100644
--- a/lib/lz4.c
+++ b/lib/lz4.c
@@ -1394,8 +1394,8 @@ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
* Note that it is important for performance that this function really get inlined,
* in order to remove useless branches during compilation optimization.
*/
-LZ4_FORCE_O2_GCC_PPC64LE
-LZ4_FORCE_INLINE int LZ4_decompress_generic(
+LZ4_FORCE_INLINE int
+LZ4_decompress_generic(
const char* const src,
char* const dst,
int srcSize,
diff --git a/lib/lz4.h b/lib/lz4.h
index 491b67a2a..9d3890a61 100644
--- a/lib/lz4.h
+++ b/lib/lz4.h
@@ -183,55 +183,58 @@ LZ4_compress_fast_extState() :
Same compression function, just using an externally allocated memory space to store compression state.
Use LZ4_sizeofState() to know how much memory must be allocated,
and allocate it on 8-bytes boundaries (using malloc() typically).
- Then, provide it as 'void* state' to compression function.
+ 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 'targetDestSize'.
- 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'.
- *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
- New value is necessarily <= old value.
- return : Nb bytes written into 'dst' (necessarily <= targetDestSize)
- or 0 if compression fails
+/*! LZ4_compress_destSize() :
+ * Reverse the logic : compresses as much data as possible from 'src' buffer
+ * into already allocated buffer 'dst', of size >= 'targetDestSize'.
+ * 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 : 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 <= targetDestSize)
+ * or 0 if compression fails.
*/
LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);
-/*!
-LZ4_decompress_fast() : **unsafe!**
-This function is a bit faster than LZ4_decompress_safe(),
-but it may misbehave on malformed input because it doesn't perform full validation of compressed data.
- originalSize : is the uncompressed size to regenerate
- Destination buffer must be already allocated, and its size must be >= 'originalSize' bytes.
- return : number of bytes read from source buffer (== compressed size).
- If the source stream is detected malformed, the function stops decoding and return a negative result.
- note : This function is only usable if the originalSize of uncompressed data is known in advance.
- The caller should also check that all the compressed input has been consumed properly,
- i.e. that the return value matches the size of the buffer with compressed input.
- The function never writes past the output buffer. However, since it doesn't know its 'src' size,
- it may read past the intended input. Also, because match offsets are not validated during decoding,
- reads from 'src' may underflow. Use this function in trusted environment **only**.
-*/
+/*! LZ4_decompress_fast() : **unsafe!**
+ * This function used to be a bit faster than LZ4_decompress_safe(),
+ * though situation has changed in recent versions,
+ * and now `LZ4_decompress_safe()` can be as fast and sometimes faster than `LZ4_decompress_fast()`.
+ * Moreover, LZ4_decompress_fast() is not protected vs malformed input, as it doesn't perform full validation of compressed data.
+ * As a consequence, this function is no longer recommended, and may be deprecated in future versions.
+ * It's only remaining specificity is that it can decompress data without knowing its compressed size.
+ *
+ * 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).
+ * If the source stream is detected malformed, the function stops decoding and returns a negative result.
+ * note : This function requires uncompressed originalSize to be known in advance.
+ * The function never writes past the output buffer.
+ * However, since it doesn't know its 'src' size, it may read past the intended input.
+ * Also, because match offsets are not validated during decoding,
+ * reads from 'src' may underflow.
+ * Use this function in trusted environment **only**.
+ */
LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
-/*!
-LZ4_decompress_safe_partial() :
- This function decompress a compressed block of size 'srcSize' at position 'src'
- into destination buffer 'dst' of size 'dstCapacity'.
- The function will decompress a minimum of 'targetOutputSize' bytes, and stop after that.
- However, it's not accurate, and may write more than 'targetOutputSize' (but always <= dstCapacity).
- @return : the number of bytes decoded in the destination buffer (necessarily <= dstCapacity)
- Note : this number can also be < targetOutputSize, if compressed block contains less data.
- Therefore, always control how many bytes were decoded.
- If source stream is detected malformed, function returns a negative result.
- This function is protected against malicious data packets.
-*/
+/*! LZ4_decompress_safe_partial() :
+ * This function decompresses a compressed block of size 'srcSize' at position 'src'
+ * into destination buffer 'dst' of size 'dstCapacity'.
+ * The function will decompress a minimum of 'targetOutputSize' bytes, and stop after that.
+ * However, it's not accurate, and may write more than 'targetOutputSize' (but always <= dstCapacity).
+ * @return : the number of bytes decoded in `dst` (necessarily <= dstCapacity)
+ * Note : this number can also be < targetOutputSize, if compressed block contains less data.
+ * If source stream is detected malformed, function returns a negative result.
+ * This function is protected against malicious data packets.
+ */
LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
@@ -266,16 +269,23 @@ LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, in
* 'dst' buffer must be already allocated.
* If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
*
- * Important : The previous 64KB of source data is assumed to remain present and unmodified in memory!
- *
- * Special 1 : When input is a double-buffer, they can have any size, including < 64 KB.
- * Make sure that buffers are separated by at least one byte.
- * This way, each block only depends on previous block.
- * Special 2 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.
- *
* @return : size of compressed block
* or 0 if there is an error (typically, cannot fit into 'dst').
- * After an error, the stream status is invalid, it can only be reset or freed.
+ *
+ * Note 1 : Each invocation to LZ4_compress_fast_continue() will generate a new block.
+ * Each block has precise boundaries.
+ * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.
+ * Each block must be decompressed separately, calling LZ4_decompress_*() with associated metadata.
+ *
+ * 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 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);
From bf614d3c51c9774df0f64285db314545f05bb5ef Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 7 Sep 2018 15:44:19 -0700
Subject: [PATCH 36/45] first sketch for a byte-accurate partial decoder
---
lib/lz4.c | 126 +++++++++++++++++++++++++++++-----------------
lib/lz4.h | 4 +-
tests/fullbench.c | 32 ++++++++----
tests/fuzzer.c | 22 ++++----
4 files changed, 113 insertions(+), 71 deletions(-)
diff --git a/lib/lz4.c b/lib/lz4.c
index 35df7f5a4..6febb90d1 100644
--- a/lib/lz4.c
+++ b/lib/lz4.c
@@ -483,9 +483,6 @@ typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;
typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;
typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
-typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
-typedef enum { full = 0, partial = 1 } earlyEnd_directive;
-
/*-************************************
* Local Utils
@@ -684,9 +681,9 @@ LZ4_FORCE_INLINE int LZ4_compress_generic(
/* the dictCtx currentOffset is indexed on the start of the dictionary,
* while a dictionary in the current context precedes the currentOffset */
- const BYTE* dictBase = dictDirective == usingDictCtx ?
- dictionary + dictSize - dictCtx->currentOffset :
- dictionary + dictSize - startIndex;
+ const BYTE* dictBase = (dictDirective == usingDictCtx) ?
+ dictionary + dictSize - dictCtx->currentOffset :
+ dictionary + dictSize - startIndex;
BYTE* op = (BYTE*) dest;
BYTE* const olimit = op + maxOutputSize;
@@ -1385,25 +1382,32 @@ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
-/*-*****************************
-* Decompression functions
-*******************************/
+/*-*******************************
+ * Decompression functions
+ ********************************/
+
+typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
+typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive;
+
+#undef MIN
+#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
+
/*! 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_FORCE_INLINE
+int
LZ4_decompress_generic(
const char* const src,
char* const dst,
int srcSize,
int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */
- int endOnInput, /* endOnOutputSize, endOnInputSize */
- int partialDecoding, /* full, partial */
- int targetOutputSize, /* only used if partialDecoding==partial */
+ endCondition_directive endOnInput, /* endOnOutputSize, endOnInputSize */
+ earlyEnd_directive partialDecoding, /* full, partial */
int dict, /* noDict, withPrefix64k, usingExtDict */
const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */
const BYTE* const dictStart, /* only if dict==usingExtDict */
@@ -1416,7 +1420,6 @@ LZ4_decompress_generic(
BYTE* op = (BYTE*) dst;
BYTE* const oend = op + outputSize;
BYTE* cpy;
- BYTE* oexit = op + targetOutputSize;
const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize;
const unsigned inc32table[8] = {0, 1, 2, 1, 0, 4, 4, 4};
@@ -1432,9 +1435,9 @@ LZ4_decompress_generic(
DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i)", srcSize);
/* Special cases */
- if ((partialDecoding) && (oexit > oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => just decode everything */
+ assert(src != NULL);
if ((endOnInput) && (unlikely(outputSize==0))) return ((srcSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */
- if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1);
+ if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0 ? 1 : -1);
if ((endOnInput) && unlikely(srcSize==0)) return -1;
/* Main Loop : decode sequences */
@@ -1443,7 +1446,7 @@ LZ4_decompress_generic(
size_t offset;
unsigned const token = *ip++;
- size_t length = token >> ML_BITS; /* literal length */
+ size_t length = token >> ML_BITS; /* literal length */
assert(!endOnInput || ip <= iend); /* ip < iend before the increment */
@@ -1468,6 +1471,7 @@ LZ4_decompress_generic(
length = token & ML_MASK; /* match length */
offset = LZ4_readLE16(ip); ip += 2;
match = op - offset;
+ assert(match <= op); /* check overflow */
/* Do not deal with overlapping matches. */
if ( (length != ML_MASK)
@@ -1501,11 +1505,11 @@ LZ4_decompress_generic(
/* copy literals */
cpy = op+length;
- if ( ((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )
- || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )
+ if ( ((endOnInput) && ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) )
+ || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )
{
if (partialDecoding) {
- if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */
+ if (cpy > oend) { cpy = oend; length = oend-op; } /* Partial decoding : stop in the middle of literal segment */
if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */
} else {
if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */
@@ -1514,10 +1518,15 @@ LZ4_decompress_generic(
memcpy(op, ip, length);
ip += length;
op += length;
- break; /* Necessarily EOF, due to parsing restrictions */
+ if (!partialDecoding || (cpy == oend)) {
+ /* Necessarily EOF, due to parsing restrictions */
+ break;
+ }
+
+ } else {
+ LZ4_wildCopy(op, ip, cpy);
+ ip += length; op = cpy;
}
- LZ4_wildCopy(op, ip, cpy);
- ip += length; op = cpy;
/* get offset */
offset = LZ4_readLE16(ip); ip+=2;
@@ -1541,21 +1550,24 @@ LZ4_decompress_generic(
}
length += MINMATCH;
- /* check external dictionary */
+ /* match starting within external dictionary */
if ((dict==usingExtDict) && (match < lowPrefix)) {
- if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */
+ 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 can be copied as a single segment from external dictionary */
+ /* match fits entirely within external dictionary : just copy */
memmove(op, dictEnd - (lowPrefix-match), length);
op += length;
} else {
- /* match encompass external dictionary and current block */
- size_t const copySize = (size_t)(lowPrefix-match);
+ /* match stretches into both external dictionary and current block */
+ size_t const copySize = (size_t)(lowPrefix - match);
size_t const restSize = length - copySize;
memcpy(op, dictEnd - copySize, copySize);
op += copySize;
- if (restSize > (size_t)(op-lowPrefix)) { /* overlap copy */
+ if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */
BYTE* const endOfMatch = op + restSize;
const BYTE* copyFrom = lowPrefix;
while (op < endOfMatch) *op++ = *copyFrom++;
@@ -1568,6 +1580,22 @@ LZ4_decompress_generic(
/* copy match within block */
cpy = op + length;
+
+ /* specific : partial decode : does not respect end parsing restrictions */
+ assert(op<=oend);
+ if (partialDecoding && (cpy > oend-12)) {
+ size_t const mlen = MIN(length, (size_t)(oend-op));
+ const BYTE* const matchEnd = match + mlen;
+ BYTE* const copyEnd = op + mlen;
+ if (matchEnd > op) { /* overlap copy */
+ while (op < copyEnd) *op++ = *match++;
+ } else {
+ memcpy(op, match, mlen);
+ }
+ op = copyEnd;
+ continue;
+ }
+
if (unlikely(offset<8)) {
op[0] = match[0];
op[1] = match[1];
@@ -1576,23 +1604,26 @@ LZ4_decompress_generic(
match += inc32table[offset];
memcpy(op+4, match, 4);
match -= dec64table[offset];
- } else { memcpy(op, match, 8); match+=8; }
+ } else {
+ memcpy(op, match, 8);
+ match += 8;
+ }
op += 8;
- if (unlikely(cpy>oend-12)) {
- BYTE* const oCopyLimit = oend-(WILDCOPYLENGTH-1);
+ if (unlikely(cpy > oend-12)) {
+ 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_wildCopy(op, match, oCopyLimit);
match += oCopyLimit - op;
op = oCopyLimit;
}
- while (op16) LZ4_wildCopy(op+8, match+8, cpy);
+ if (length > 16) LZ4_wildCopy(op+8, match+8, cpy);
}
- op = cpy; /* correction */
+ op = cpy; /* wildcopy correction */
}
/* end of decoding */
@@ -1613,23 +1644,24 @@ LZ4_FORCE_O2_GCC_PPC64LE
int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
{
return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,
- endOnInputSize, full, 0, noDict,
+ endOnInputSize, decode_full_block, noDict,
(BYTE*)dest, NULL, 0);
}
LZ4_FORCE_O2_GCC_PPC64LE
-int LZ4_decompress_safe_partial(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize)
+int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity)
{
- return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,
- endOnInputSize, partial, targetOutputSize,
- noDict, (BYTE*)dest, NULL, 0);
+ dstCapacity = MIN(targetOutputSize, dstCapacity);
+ return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity,
+ endOnInputSize, partial_decode,
+ noDict, (BYTE*)dst, NULL, 0);
}
LZ4_FORCE_O2_GCC_PPC64LE
int LZ4_decompress_fast(const char* source, char* dest, int originalSize)
{
return LZ4_decompress_generic(source, dest, 0, originalSize,
- endOnOutputSize, full, 0, withPrefix64k,
+ endOnOutputSize, decode_full_block, withPrefix64k,
(BYTE*)dest - 64 KB, NULL, 0);
}
@@ -1639,7 +1671,7 @@ LZ4_FORCE_O2_GCC_PPC64LE /* Exported, an obsolete API function. */
int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
{
return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- endOnInputSize, full, 0, withPrefix64k,
+ endOnInputSize, decode_full_block, withPrefix64k,
(BYTE*)dest - 64 KB, NULL, 0);
}
@@ -1656,7 +1688,7 @@ static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, i
size_t prefixSize)
{
return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- endOnInputSize, full, 0, noDict,
+ endOnInputSize, decode_full_block, noDict,
(BYTE*)dest-prefixSize, NULL, 0);
}
@@ -1666,7 +1698,7 @@ int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,
const void* dictStart, size_t dictSize)
{
return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- endOnInputSize, full, 0, usingExtDict,
+ endOnInputSize, decode_full_block, usingExtDict,
(BYTE*)dest, (const BYTE*)dictStart, dictSize);
}
@@ -1675,7 +1707,7 @@ static int LZ4_decompress_fast_extDict(const char* source, char* dest, int origi
const void* dictStart, size_t dictSize)
{
return LZ4_decompress_generic(source, dest, 0, originalSize,
- endOnOutputSize, full, 0, usingExtDict,
+ endOnOutputSize, decode_full_block, usingExtDict,
(BYTE*)dest, (const BYTE*)dictStart, dictSize);
}
@@ -1688,7 +1720,7 @@ int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compresse
size_t prefixSize, const void* dictStart, size_t dictSize)
{
return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- endOnInputSize, full, 0, usingExtDict,
+ endOnInputSize, decode_full_block, usingExtDict,
(BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);
}
@@ -1697,7 +1729,7 @@ int LZ4_decompress_fast_doubleDict(const char* source, char* dest, int originalS
size_t prefixSize, const void* dictStart, size_t dictSize)
{
return LZ4_decompress_generic(source, dest, 0, originalSize,
- endOnOutputSize, full, 0, usingExtDict,
+ endOnOutputSize, decode_full_block, usingExtDict,
(BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);
}
diff --git a/lib/lz4.h b/lib/lz4.h
index 9d3890a61..ce4d033c4 100644
--- a/lib/lz4.h
+++ b/lib/lz4.h
@@ -272,12 +272,12 @@ LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, in
* @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() will generate a new block.
+ * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.
* Each block has precise boundaries.
* It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.
* Each block must be decompressed separately, calling LZ4_decompress_*() with associated metadata.
*
- * Note 2 : The previous 64KB of source data is assumed to remain present, unmodified, at same address in memory!
+ * 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.
diff --git a/tests/fullbench.c b/tests/fullbench.c
index 2818ea28b..fd1202df1 100644
--- a/tests/fullbench.c
+++ b/tests/fullbench.c
@@ -317,7 +317,9 @@ static int local_LZ4_decompress_safe_forceExtDict(const char* in, char* out, int
static int local_LZ4_decompress_safe_partial(const char* in, char* out, int inSize, int outSize)
{
- return LZ4_decompress_safe_partial(in, out, inSize, outSize - 5, outSize);
+ int result = LZ4_decompress_safe_partial(in, out, inSize, outSize - 5, outSize);
+ if (result < 0) return result;
+ return outSize;
}
@@ -462,9 +464,9 @@ int fullSpeedBench(const char** fileNamesTable, int nbFiles)
case 12: compressionFunction = local_LZ4_compress_HC_extStateHC; compressorName = "LZ4_compress_HC_extStateHC"; break;
case 14: compressionFunction = local_LZ4_compress_HC_continue; initFunction = local_LZ4_resetStreamHC; compressorName = "LZ4_compress_HC_continue"; break;
#ifndef LZ4_DLL_IMPORT
- case 20: compressionFunction = local_LZ4_compress_forceDict; initFunction = local_LZ4_resetDictT; compressorName = "LZ4_compress_forceDict"; break;
+ case 20: compressionFunction = local_LZ4_compress_forceDict; initFunction = local_LZ4_resetDictT; compressorName = "LZ4_compress_forceDict"; break;
#endif
- case 30: compressionFunction = local_LZ4F_compressFrame; compressorName = "LZ4F_compressFrame";
+ case 30: compressionFunction = local_LZ4F_compressFrame; compressorName = "LZ4F_compressFrame";
chunkP[0].origSize = (int)benchedSize; nbChunks=1;
break;
case 40: compressionFunction = local_LZ4_saveDict; compressorName = "LZ4_saveDict";
@@ -542,6 +544,7 @@ int fullSpeedBench(const char** fileNamesTable, int nbFiles)
const char* dName;
int (*decompressionFunction)(const char*, char*, int, int);
double bestTime = 100000000.;
+ int checkResult = 1;
if ((g_decompressionAlgo != ALL_DECOMPRESSORS) && (g_decompressionAlgo != dAlgNb)) continue;
@@ -553,11 +556,11 @@ int fullSpeedBench(const char** fileNamesTable, int nbFiles)
case 3: decompressionFunction = local_LZ4_decompress_fast_usingExtDict; dName = "LZ4_decompress_fast_using(Ext)Dict"; break;
case 4: decompressionFunction = LZ4_decompress_safe; dName = "LZ4_decompress_safe"; break;
case 6: decompressionFunction = local_LZ4_decompress_safe_usingDict; dName = "LZ4_decompress_safe_usingDict"; break;
- case 7: decompressionFunction = local_LZ4_decompress_safe_partial; dName = "LZ4_decompress_safe_partial"; break;
+ case 7: decompressionFunction = local_LZ4_decompress_safe_partial; dName = "LZ4_decompress_safe_partial"; checkResult = 0; break;
#ifndef LZ4_DLL_IMPORT
- case 8: decompressionFunction = local_LZ4_decompress_safe_forceExtDict; dName = "LZ4_decompress_safe_forceExtDict"; break;
+ case 8: decompressionFunction = local_LZ4_decompress_safe_forceExtDict; dName = "LZ4_decompress_safe_forceExtDict"; break;
#endif
- case 9: decompressionFunction = local_LZ4F_decompress; dName = "LZ4F_decompress";
+ case 9: decompressionFunction = local_LZ4F_decompress; dName = "LZ4F_decompress";
errorCode = LZ4F_compressFrame(compressed_buff, compressedBuffSize, orig_buff, benchedSize, NULL);
if (LZ4F_isError(errorCode)) {
DISPLAY("Error while preparing compressed frame\n");
@@ -589,9 +592,13 @@ int fullSpeedBench(const char** fileNamesTable, int nbFiles)
clockTime = clock();
while(BMK_GetClockSpan(clockTime) < TIMELOOP) {
for (chunkNb=0; chunkNb %7.1f MB/s\n", dAlgNb, dName, (int)benchedSize, (double)benchedSize / bestTime / 1000000);
}
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 6c7951596..bdb784113 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -504,7 +504,7 @@ static int FUZ_test(U32 seed, U32 nbCycles, const U32 startCycle, const double c
/* Test decoding with empty input */
FUZ_DISPLAYTEST("LZ4_decompress_safe() with empty input");
- LZ4_decompress_safe(NULL, decodedBuffer, 0, blockSize);
+ LZ4_decompress_safe(compressedBuffer, decodedBuffer, 0, blockSize);
/* Test decoding with a one byte input */
FUZ_DISPLAYTEST("LZ4_decompress_safe() with one byte input");
@@ -545,7 +545,6 @@ static int FUZ_test(U32 seed, U32 nbCycles, const U32 startCycle, const double c
ret = LZ4_decompress_safe(compressedBuffer, decodedBuffer, compressedSize, blockSize+1);
FUZ_CHECKTEST(ret<0, "LZ4_decompress_safe failed despite amply sufficient space");
FUZ_CHECKTEST(ret!=blockSize, "LZ4_decompress_safe did not regenerate original data");
- //FUZ_CHECKTEST(decodedBuffer[blockSize], "LZ4_decompress_safe wrote more than (unknown) target size"); // well, is that an issue ?
FUZ_CHECKTEST(decodedBuffer[blockSize+1], "LZ4_decompress_safe overrun specified output buffer size");
{ U32 const crcCheck = XXH32(decodedBuffer, blockSize, 0);
FUZ_CHECKTEST(crcCheck!=crcOrig, "LZ4_decompress_safe corrupted decoded data");
@@ -579,15 +578,16 @@ static int FUZ_test(U32 seed, U32 nbCycles, const U32 startCycle, const double c
FUZ_CHECKTEST(ret>=0, "LZ4_decompress_safe should have failed, due to input size being too large");
FUZ_CHECKTEST(decodedBuffer[blockSize], "LZ4_decompress_safe overrun specified output buffer size");
- // Test partial decoding with target output size being max/2 => must work
- FUZ_DISPLAYTEST();
- ret = LZ4_decompress_safe_partial(compressedBuffer, decodedBuffer, compressedSize, blockSize/2, blockSize);
- FUZ_CHECKTEST(ret<0, "LZ4_decompress_safe_partial failed despite sufficient space");
-
- // Test partial decoding with target output size being just below max => must work
- FUZ_DISPLAYTEST();
- ret = LZ4_decompress_safe_partial(compressedBuffer, decodedBuffer, compressedSize, blockSize-3, blockSize);
- FUZ_CHECKTEST(ret<0, "LZ4_decompress_safe_partial failed despite sufficient space");
+ /* Test partial decoding => must work */
+ FUZ_DISPLAYTEST("test LZ4_decompress_safe_partial");
+ { size_t const missingBytes = FUZ_rand(&randState) % blockSize;
+ int const targetSize = (int)(blockSize - missingBytes);
+ char const sentinel = compressedBuffer[targetSize] = block[targetSize] ^ 0x5A;
+ int const decResult = LZ4_decompress_safe_partial(compressedBuffer, decodedBuffer, compressedSize, targetSize, blockSize);
+ FUZ_CHECKTEST(decResult<0, "LZ4_decompress_safe_partial failed despite valid input data");
+ FUZ_CHECKTEST(decResult != targetSize, "LZ4_decompress_safe_partial did not regenerated required amount of data (%i < %i <= %i)", decResult, targetSize, blockSize);
+ FUZ_CHECKTEST(compressedBuffer[targetSize] != sentinel, "LZ4_decompress_safe_partial overwrite beyond requested size (though %i <= %i <= %i)", decResult, targetSize, blockSize);
+ }
/* Test Compression with limited output size */
From eaed9ea4a15934e59f74902f0ed692a07151579d Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 7 Sep 2018 16:21:31 -0700
Subject: [PATCH 37/45] updated function interface documentation
---
doc/lz4_manual.html | 105 ++++++++++++++++++++++++++++----------------
lib/lz4.h | 24 +++++++---
2 files changed, 86 insertions(+), 43 deletions(-)
diff --git a/doc/lz4_manual.html b/doc/lz4_manual.html
index c7c576346..6ebf8d281 100644
--- a/doc/lz4_manual.html
+++ b/doc/lz4_manual.html
@@ -110,45 +110,67 @@ 1.8.3 Manual
Same compression function, just using an externally allocated memory space to store compression state.
Use LZ4_sizeofState() to know how much memory must be allocated,
and allocate it on 8-bytes boundaries (using malloc() typically).
- Then, provide it as 'void* state' to compression function.
+ Then, provide this buffer as 'void* state' to compression function.
int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);
- Reverse the logic : compresses as much data as possible from 'src' buffer
- into already allocated buffer 'dst' of size 'targetDestSize'.
- 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'.
- *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
- New value is necessarily <= old value.
- return : Nb bytes written into 'dst' (necessarily <= targetDestSize)
- or 0 if compression fails
+
Reverse the logic : compresses as much data as possible from 'src' buffer
+ into already allocated buffer 'dst', of size >= 'targetDestSize'.
+ 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 : 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 <= targetDestSize)
+ or 0 if compression fails.
int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
-This function is a bit faster than LZ4_decompress_safe(),
-but it may misbehave on malformed input because it doesn't perform full validation of compressed data.
- originalSize : is the uncompressed size to regenerate
- Destination buffer must be already allocated, and its size must be >= 'originalSize' bytes.
- return : number of bytes read from source buffer (== compressed size).
- If the source stream is detected malformed, the function stops decoding and return a negative result.
- note : This function is only usable if the originalSize of uncompressed data is known in advance.
- The caller should also check that all the compressed input has been consumed properly,
- i.e. that the return value matches the size of the buffer with compressed input.
- The function never writes past the output buffer. However, since it doesn't know its 'src' size,
- it may read past the intended input. Also, because match offsets are not validated during decoding,
- reads from 'src' may underflow. Use this function in trusted environment **only**.
+
This function used to be a bit faster than LZ4_decompress_safe(),
+ though situation has changed in recent versions,
+ and now `LZ4_decompress_safe()` can be as fast and sometimes faster than `LZ4_decompress_fast()`.
+ Moreover, LZ4_decompress_fast() is not protected vs malformed input, as it doesn't perform full validation of compressed data.
+ As a consequence, this function is no longer recommended, and may be deprecated in future versions.
+ It's only remaining specificity is that it can decompress data without knowing its compressed size.
+
+ 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).
+ If the source stream is detected malformed, the function stops decoding and returns a negative result.
+ note : This function requires uncompressed originalSize to be known in advance.
+ The function never writes past the output buffer.
+ However, since it doesn't know its 'src' size, it may read past the intended input.
+ Also, because match offsets are not validated during decoding,
+ reads from 'src' may underflow.
+ Use this function in trusted environment **only**.
+
int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
- This function decompress a compressed block of size 'srcSize' at position 'src'
- into destination buffer 'dst' of size 'dstCapacity'.
- The function will decompress a minimum of 'targetOutputSize' bytes, and stop after that.
- However, it's not accurate, and may write more than 'targetOutputSize' (but always <= dstCapacity).
- @return : the number of bytes decoded in the destination buffer (necessarily <= dstCapacity)
- Note : this number can also be < targetOutputSize, if compressed block contains less data.
- Therefore, always control how many bytes were decoded.
- If source stream is detected malformed, function returns a negative result.
- This function is protected against malicious data packets.
+
Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',
+ into destination buffer 'dst' of size 'dstCapacity'.
+ Up to 'targetOutputSize' bytes will be decoded.
+ The function stops decoding on reaching this objective,
+ which can boost performance when only the beginning of a block is required.
+
+ @return : the number of bytes decoded in `dst` (necessarily <= dstCapacity)
+ If source stream is detected malformed, function returns a negative result.
+
+ Note : @return can be < targetOutputSize, if compressed block contains less data.
+
+ Note 2 : this function features 2 parameters, targetOutputSize and dstCapacity,
+ and expects targetOutputSize <= dstCapacity.
+ It effectively stops decoding on reaching targetOutputSize,
+ so dstCapacity is kind of redundant.
+ This is because in a previous version of this function,
+ decoding operation would not "break" a sequence in the middle.
+ As a consequence, there was no guarantee that decoding would stop at exactly targetOutputSize,
+ it could write more bytes, though only up to dstCapacity.
+ Some "margin" used to be required for this operation to work properly.
+ This is no longer necessary.
+ The function nonetheless keeps its signature, in an effort to not break API.
+
Streaming Compression Functions
@@ -179,16 +201,23 @@ 1.8.3 Manual
'dst' buffer must be already allocated.
If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
- Important : The previous 64KB of source data is assumed to remain present and unmodified in memory!
-
- Special 1 : When input is a double-buffer, they can have any size, including < 64 KB.
- Make sure that buffers are separated by at least one byte.
- This way, each block only depends on previous block.
- Special 2 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.
-
@return : size of compressed block
or 0 if there is an error (typically, cannot fit into 'dst').
- After an error, the stream status is invalid, it can only be reset or freed.
+
+ Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.
+ Each block has precise boundaries.
+ It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.
+ Each block must be decompressed separately, calling LZ4_decompress_*() with associated metadata.
+
+ 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 invalid, it can only be reset or freed.