summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYann Collet <Cyan4973@users.noreply.github.com>2016-11-10 19:26:48 (GMT)
committerGitHub <noreply@github.com>2016-11-10 19:26:48 (GMT)
commitcd4462629e98d6c62ec147ada0d63cd389c8e5f9 (patch)
tree14a6c2e07b308d19d9f7a2f38a90f5a926cbc13c
parent924f00c86ce9075d520967c893dbfbe13a7bf073 (diff)
parent52251d970956c992801b6b7434fe092751f7731e (diff)
downloadlz4-cd4462629e98d6c62ec147ada0d63cd389c8e5f9.zip
lz4-cd4462629e98d6c62ec147ada0d63cd389c8e5f9.tar.gz
lz4-cd4462629e98d6c62ec147ada0d63cd389c8e5f9.tar.bz2
Merge pull request #264 from inikep/dev
Dev
-rw-r--r--.travis.yml28
-rw-r--r--Makefile48
-rw-r--r--contrib/gen_manual/Makefile36
-rw-r--r--contrib/gen_manual/README.md31
-rw-r--r--contrib/gen_manual/gen-lz4-manual.sh9
-rw-r--r--contrib/gen_manual/gen_manual.cpp216
-rw-r--r--doc/lz4_manual.html240
-rw-r--r--lib/README.md37
-rw-r--r--lib/lz4.h67
-rw-r--r--programs/bench.c6
-rw-r--r--programs/lz4cli.c39
-rw-r--r--programs/lz4io.c2
-rw-r--r--tests/Makefile6
-rw-r--r--tests/README.md48
14 files changed, 742 insertions, 71 deletions
diff --git a/.travis.yml b/.travis.yml
index 1de67aa..4bef528 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,19 +9,19 @@ matrix:
# Container-based 12.04 LTS Server Edition 64 bit (doesn't support 32-bit includes)
- os: linux
sudo: false
- env: Ubu=12.04cont Cmd='make test' COMPILER=cc
-
+ env: Ubu=12.04cont Cmd='make -C tests test-lz4 test-lz4c test-fasttest test-fullbench' COMPILER=cc
+
- os: linux
sudo: false
- env: Ubu=12.04cont Cmd="make gpptest && make clean && make examples && make clean && make cmake && make clean && make travis-install" COMPILER=cc
+ env: Ubu=12.04cont Cmd='make -C tests test-frametest test-fuzzer' COMPILER=cc
- os: linux
sudo: false
- env: Ubu=12.04cont Cmd='make clangtest' COMPILER=clang
+ env: Ubu=12.04cont Cmd="make gpptest && make clean && make examples && make clean && make cmake && make clean && make travis-install && make clean && make clangtest" COMPILER=cc
# 14.04 LTS Server Edition 64 bit
- - env: Ubu=14.04 Cmd='make -C tests test32 versionsTest' COMPILER=cc
+ - env: Ubu=14.04 Cmd='make -C tests test-lz4c32 test-fullbench32 versionsTest' COMPILER=cc
dist: trusty
sudo: required
addons:
@@ -31,13 +31,29 @@ matrix:
- libc6-dev-i386
- gcc-multilib
- - env: Ubu=14.04 Cmd='make staticAnalyze sanitize' COMPILER=clang
+ - env: Ubu=14.04 Cmd='make -C tests test-frametest32 test-fuzzer32' COMPILER=cc
+ dist: trusty
+ sudo: required
+ addons:
+ apt:
+ packages:
+ - libc6-dev-i386
+ - gcc-multilib
+
+ - env: Ubu=14.04 Cmd='make sanitize' COMPILER=clang
dist: trusty
sudo: required
addons:
apt:
packages:
- valgrind
+
+ - env: Ubu=14.04 Cmd='make staticAnalyze' COMPILER=clang
+ dist: trusty
+ sudo: required
+ addons:
+ apt:
+ packages:
- clang
- env: Ubu=14.04 Cmd='make platformTest CC=arm-linux-gnueabi-gcc QEMU_SYS=qemu-arm-static && make platformTest CC=aarch64-linux-gnu-gcc QEMU_SYS=qemu-aarch64-static' COMPILER=arm-linux-gnueabi-gcc
diff --git a/Makefile b/Makefile
index 8f9d544..1fd6ec3 100644
--- a/Makefile
+++ b/Makefile
@@ -75,6 +75,7 @@ clean:
#make install is validated only for Linux, OSX, kFreeBSD, Hurd and
#FreeBSD targets
ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU FreeBSD))
+HOST_OS = POSIX
install:
@$(MAKE) -C $(LZ4DIR) $@
@@ -90,23 +91,8 @@ travis-install:
test:
$(MAKE) -C $(TESTDIR) test
-cmake:
- @cd contrib/cmake_unofficial; cmake CMakeLists.txt; $(MAKE)
-
-gpptest: clean
- $(MAKE) all CC=g++ CFLAGS="-O3 -I../lib -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror"
-
-c_standards: clean
- $(MAKE) all MOREFLAGS="-std=gnu90 -Werror"
- $(MAKE) clean
- $(MAKE) all MOREFLAGS="-std=c99 -Werror"
- $(MAKE) clean
- $(MAKE) all MOREFLAGS="-std=gnu99 -Werror"
- $(MAKE) clean
- $(MAKE) all MOREFLAGS="-std=c11 -Werror"
- $(MAKE) clean
-
clangtest: clean
+ clang -v
CFLAGS="-O3 -Werror -Wconversion -Wno-sign-conversion" $(MAKE) all CC=clang
sanitize: clean
@@ -132,3 +118,33 @@ examples:
$(MAKE) -C examples test
endif
+
+
+ifneq (,$(filter MSYS%,$(shell uname)))
+HOST_OS = MSYS
+CMAKE_PARAMS = -G"MSYS Makefiles"
+endif
+
+
+#------------------------------------------------------------------------
+#make tests validated only for MSYS, Linux, OSX, kFreeBSD and Hurd targets
+#------------------------------------------------------------------------
+ifneq (,$(filter $(HOST_OS),MSYS POSIX))
+
+cmake:
+ @cd contrib/cmake_unofficial; cmake $(CMAKE_PARAMS) CMakeLists.txt; $(MAKE)
+
+gpptest: clean
+ $(MAKE) all CC=g++ CFLAGS="-O3 -I../lib -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror"
+
+c_standards: clean
+ $(MAKE) all MOREFLAGS="-std=gnu90 -Werror"
+ $(MAKE) clean
+ $(MAKE) all MOREFLAGS="-std=c99 -Werror"
+ $(MAKE) clean
+ $(MAKE) all MOREFLAGS="-std=gnu99 -Werror"
+ $(MAKE) clean
+ $(MAKE) all MOREFLAGS="-std=c11 -Werror"
+ $(MAKE) clean
+
+endif
diff --git a/contrib/gen_manual/Makefile b/contrib/gen_manual/Makefile
new file mode 100644
index 0000000..49616ee
--- /dev/null
+++ b/contrib/gen_manual/Makefile
@@ -0,0 +1,36 @@
+# ##########################################################################
+# Copyright (c) 2016-present, Przemyslaw Skibinski
+# All rights reserved.
+#
+# This source code is licensed under the BSD-style license found in the
+# LICENSE file in the root directory of this source tree. An additional grant
+# of patent rights can be found in the PATENTS file in the same directory.
+# ##########################################################################
+
+
+CFLAGS ?= -O3
+CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 -Wswitch-enum -Wno-comment
+CFLAGS += $(MOREFLAGS)
+FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS)
+
+
+
+# Define *.exe as extension for Windows systems
+ifneq (,$(filter Windows%,$(OS)))
+EXT =.exe
+else
+EXT =
+endif
+
+
+.PHONY: default gen_manual
+
+default: gen_manual
+
+gen_manual: gen_manual.cpp
+ $(CXX) $(FLAGS) $^ -o $@$(EXT)
+
+
+clean:
+ @$(RM) gen_manual$(EXT)
+ @echo Cleaning completed
diff --git a/contrib/gen_manual/README.md b/contrib/gen_manual/README.md
new file mode 100644
index 0000000..3d146d4
--- /dev/null
+++ b/contrib/gen_manual/README.md
@@ -0,0 +1,31 @@
+gen_manual - a program for automatic generation of zstd manual
+============================================================
+
+#### Introduction
+
+This simple C++ program generates a single-page HTML manual from `lz4.h`.
+
+The format of recognized comment blocks is following:
+- comments of type `/*!` mean: this is a function declaration; switch comments with declarations
+- comments of type `/**` and `/*-` mean: this is a comment; use a `<H2>` header for the first line
+- comments of type `/*=` and `/**=` mean: use a `<H3>` header and show also all functions until first empty line
+- comments of type `/*X` where `X` is different from above-mentioned are ignored
+
+Moreover:
+- `LZ4LIB_API` is removed to improve readability
+- `typedef` are detected and included even if uncommented
+- comments of type `/**<` and `/*!<` are detected and only function declaration is highlighted (bold)
+
+
+#### Usage
+
+The program requires 3 parameters:
+```
+gen_manual [lz4_version] [input_file] [output_html]
+```
+
+To compile program and generate lz4 manual we have used:
+```
+make
+./gen_manual.exe 1.7.3 ../../lib/lz4.h zstd_manual.html
+```
diff --git a/contrib/gen_manual/gen-lz4-manual.sh b/contrib/gen_manual/gen-lz4-manual.sh
new file mode 100644
index 0000000..55d31a4
--- /dev/null
+++ b/contrib/gen_manual/gen-lz4-manual.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+LIBVER_MAJOR_SCRIPT=`sed -n '/define LZ4_VERSION_MAJOR/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < ../../lib/lz4.h`
+LIBVER_MINOR_SCRIPT=`sed -n '/define LZ4_VERSION_MINOR/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < ../../lib/lz4.h`
+LIBVER_PATCH_SCRIPT=`sed -n '/define LZ4_VERSION_RELEASE/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < ../../lib/lz4.h`
+LIBVER_SCRIPT=$LIBVER_MAJOR_SCRIPT.$LIBVER_MINOR_SCRIPT.$LIBVER_PATCH_SCRIPT
+
+echo LZ4_VERSION=$LIBVER_SCRIPT
+./gen_manual $LIBVER_SCRIPT ../../lib/lz4.h ./lz4_manual.html
diff --git a/contrib/gen_manual/gen_manual.cpp b/contrib/gen_manual/gen_manual.cpp
new file mode 100644
index 0000000..caf03fc
--- /dev/null
+++ b/contrib/gen_manual/gen_manual.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2016-present, Przemyslaw Skibinski
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+using namespace std;
+
+
+/* trim string at the beginning and at the end */
+void trim(string& s, string characters)
+{
+ size_t p = s.find_first_not_of(characters);
+ s.erase(0, p);
+
+ p = s.find_last_not_of(characters);
+ if (string::npos != p)
+ s.erase(p+1);
+}
+
+
+/* trim C++ style comments */
+void trim_comments(string &s)
+{
+ size_t spos, epos;
+
+ spos = s.find("/*");
+ epos = s.find("*/");
+ s = s.substr(spos+3, epos-(spos+3));
+}
+
+
+/* get lines until a given terminator */
+vector<string> get_lines(vector<string>& input, int& linenum, string terminator)
+{
+ vector<string> out;
+ string line;
+ size_t epos;
+
+ while ((size_t)linenum < input.size()) {
+ line = input[linenum];
+
+ if (terminator.empty() && line.empty()) { linenum--; break; }
+
+ epos = line.find(terminator);
+ if (!terminator.empty() && epos!=string::npos) {
+ out.push_back(line);
+ break;
+ }
+ out.push_back(line);
+ linenum++;
+ }
+ return out;
+}
+
+
+/* print line with LZ4LIB_API removed and C++ comments not bold */
+void print_line(stringstream &sout, string line)
+{
+ size_t spos;
+
+ if (line.substr(0,11) == "LZ4LIB_API ") line = line.substr(11);
+ spos = line.find("/*");
+ if (spos!=string::npos) {
+ sout << line.substr(0, spos);
+ sout << "</b>" << line.substr(spos) << "<b>" << endl;
+ } else {
+ // fprintf(stderr, "lines=%s\n", line.c_str());
+ sout << line << endl;
+ }
+}
+
+
+int main(int argc, char *argv[]) {
+ char exclam;
+ int linenum, chapter = 1;
+ vector<string> input, lines, comments, chapters;
+ string line, version;
+ size_t spos, l;
+ stringstream sout;
+ ifstream istream;
+ ofstream ostream;
+
+ if (argc < 4) {
+ cout << "usage: " << argv[0] << " [lz4_version] [input_file] [output_html]" << endl;
+ return 1;
+ }
+
+ version = "lz4 " + string(argv[1]) + " Manual";
+
+ istream.open(argv[2], ifstream::in);
+ if (!istream.is_open()) {
+ cout << "Error opening file " << argv[2] << endl;
+ return 1;
+ }
+
+ ostream.open(argv[3], ifstream::out);
+ if (!ostream.is_open()) {
+ cout << "Error opening file " << argv[3] << endl;
+ return 1;
+ }
+
+ while (getline(istream, line)) {
+ input.push_back(line);
+ }
+
+ for (linenum=0; (size_t)linenum < input.size(); linenum++) {
+ line = input[linenum];
+
+ /* typedefs are detected and included even if uncommented */
+ if (line.substr(0,7) == "typedef" && line.find("{")!=string::npos) {
+ lines = get_lines(input, linenum, "}");
+ sout << "<pre><b>";
+ for (l=0; l<lines.size(); l++) {
+ print_line(sout, lines[l]);
+ }
+ sout << "</b></pre><BR>" << endl;
+ continue;
+ }
+
+ /* comments of type /**< and /*!< are detected and only function declaration is highlighted (bold) */
+ if ((line.find("/**<")!=string::npos || line.find("/*!<")!=string::npos) && line.find("*/")!=string::npos) {
+ sout << "<pre><b>";
+ print_line(sout, line);
+ sout << "</b></pre><BR>" << endl;
+ continue;
+ }
+
+ /* comments of type /*= and /**= mean: use a <H3> header and show also all functions until first empty line */
+ if ((line.substr(0,3) == "/*=" || line.substr(0,4) == "/**=") && line.find("*/")!=string::npos) {
+ trim_comments(line);
+ trim(line, "= ");
+ sout << "<h3>" << line << "</h3><pre><b>";
+ lines = get_lines(input, ++linenum, "");
+ for (l=0; l<lines.size(); l++) {
+ print_line(sout, lines[l]);
+ }
+ sout << "</b></pre><BR>" << endl;
+ continue;
+ }
+
+ spos = line.find("/*!");
+ if (spos==string::npos)
+ spos = line.find("/**");
+ if (spos==string::npos)
+ spos = line.find("/*-");
+
+ if (spos==string::npos)
+ continue;
+
+ exclam = line[spos+2];
+ comments = get_lines(input, linenum, "*/");
+ if (!comments.empty()) comments[0] = line.substr(spos+3);
+ if (!comments.empty()) comments[comments.size()-1] = comments[comments.size()-1].substr(0, comments[comments.size()-1].find("*/"));
+ for (l=0; l<comments.size(); l++) {
+ if (comments[l].find(" *")==0) comments[l] = comments[l].substr(2);
+ else if (comments[l].find(" *")==0) comments[l] = comments[l].substr(3);
+ trim(comments[l], "*-");
+ }
+ while (!comments.empty() && comments[comments.size()-1].empty()) comments.pop_back(); // remove empty line at the end
+ while (!comments.empty() && comments[0].empty()) comments.erase(comments.begin()); // remove empty line at the start
+
+ /* comments of type /*! mean: this is a function declaration; switch comments with declarations */
+ if (exclam == '!') {
+ if (!comments.empty()) comments.erase(comments.begin()); /* remove first line like "LZ4_XXX() :" */
+ linenum++;
+ lines = get_lines(input, linenum, "");
+
+ sout << "<pre><b>";
+ for (l=0; l<lines.size(); l++) {
+ // fprintf(stderr, "line[%d]=%s\n", l, lines[l].c_str());
+ print_line(sout, lines[l]);
+ }
+ sout << "</b><p>";
+ for (l=0; l<comments.size(); l++) {
+ print_line(sout, comments[l]);
+ }
+ sout << "</p></pre><BR>" << endl << endl;
+ } else { /* comments of type /** and /*- mean: this is a comment; use a <H2> header for the first line */
+ if (comments.empty()) continue;
+
+ trim(comments[0], " ");
+ sout << "<a name=\"Chapter" << chapter << "\"></a><h2>" << comments[0] << "</h2><pre>";
+ chapters.push_back(comments[0]);
+ chapter++;
+
+ for (l=1; l<comments.size(); l++) {
+ print_line(sout, comments[l]);
+ }
+ if (comments.size() > 1)
+ sout << "<BR></pre>" << endl << endl;
+ else
+ sout << "</pre>" << endl << endl;
+ }
+ }
+
+ ostream << "<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\n<title>" << version << "</title>\n</head>\n<body>" << endl;
+ ostream << "<h1>" << version << "</h1>\n";
+
+ ostream << "<hr>\n<a name=\"Contents\"></a><h2>Contents</h2>\n<ol>\n";
+ for (size_t i=0; i<chapters.size(); i++)
+ ostream << "<li><a href=\"#Chapter" << i+1 << "\">" << chapters[i].c_str() << "</a></li>\n";
+ ostream << "</ol>\n<hr>\n";
+
+ ostream << sout.str();
+ ostream << "</html>" << endl << "</body>" << endl;
+
+ return 0;
+} \ No newline at end of file
diff --git a/doc/lz4_manual.html b/doc/lz4_manual.html
new file mode 100644
index 0000000..838dbf4
--- /dev/null
+++ b/doc/lz4_manual.html
@@ -0,0 +1,240 @@
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>lz4 1.7.2 Manual</title>
+</head>
+<body>
+<h1>lz4 1.7.2 Manual</h1>
+<hr>
+<a name="Contents"></a><h2>Contents</h2>
+<ol>
+<li><a href="#Chapter1">Introduction</a></li>
+<li><a href="#Chapter2">Tuning parameter</a></li>
+<li><a href="#Chapter3">Simple Functions</a></li>
+<li><a href="#Chapter4">Advanced Functions</a></li>
+<li><a href="#Chapter5">Streaming Compression Functions</a></li>
+<li><a href="#Chapter6">Streaming Decompression Functions</a></li>
+</ol>
+<hr>
+<a name="Chapter1"></a><h2>Introduction</h2><pre>
+ LZ4 is lossless compression algorithm, providing compression speed at 400 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.
+ 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 provides block compression functions. It gives full buffer control to user.
+ Block compression functions are not-enough to send information,
+ since it's still necessary to provide metadata (such as compressed size),
+ and each application can do it in whichever way it wants.
+ For interoperability, there is LZ4 frame specification (doc/lz4_Frame_format.md).
+ A library is provided to take care of it, see lz4frame.h.
+<BR></pre>
+
+<h3>Version</h3><pre><b>int LZ4_versionNumber (void);
+const char* LZ4_versionString (void);
+</b></pre><BR>
+<a name="Chapter2"></a><h2>Tuning parameter</h2><pre></pre>
+
+<pre><b>#define LZ4_MEMORY_USAGE 14
+</b><p> Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
+ Increasing memory usage improves compression ratio
+ Reduced memory usage can improve speed, due to cache effect
+ Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
+
+</p></pre><BR>
+
+<a name="Chapter3"></a><h2>Simple Functions</h2><pre></pre>
+
+<pre><b>int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
+</b><p> Compresses 'sourceSize' bytes from buffer 'source'
+ into already allocated 'dest' buffer of size 'maxDestSize'.
+ Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
+ It also runs faster, so it's a recommended setting.
+ If the function cannot compress 'source' into a more limited 'dest' budget,
+ compression stops *immediately*, and the function result is zero.
+ As a consequence, 'dest' content is not valid.
+ This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
+ sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE
+ maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
+ return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
+ or 0 if compression fails
+</p></pre><BR>
+
+<pre><b>int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
+</b><p> compressedSize : is the precise full size of the compressed block.
+ maxDecompressedSize : is the size of destination buffer, which must be already allocated.
+ return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
+ If destination buffer is not large enough, decoding will stop and output an error code (<0).
+ If the source stream is detected malformed, the function will stop decoding and return a negative result.
+ This function is protected against buffer overflow exploits, including malicious data packets.
+ It never writes outside output buffer, nor reads outside input buffer.
+</p></pre><BR>
+
+<a name="Chapter4"></a><h2>Advanced Functions</h2><pre></pre>
+
+<pre><b>int LZ4_compressBound(int inputSize);
+</b><p> 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() compress faster when dest buffer size 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 too large ( > LZ4_MAX_INPUT_SIZE)
+</p></pre><BR>
+
+<pre><b>int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration);
+</b><p> Same as LZ4_compress_default(), but allows to select an "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 ACCELERATION_DEFAULT (see lz4.c), which is 1.
+</p></pre><BR>
+
+<pre><b>int LZ4_sizeofState(void);
+int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration);
+</b><p> 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.
+</p></pre><BR>
+
+<pre><b>int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize);
+</b><p> Reverse the logic, by compressing as much data as possible from 'source' buffer
+ into already allocated buffer 'dest' of size 'targetDestSize'.
+ This function either compresses the entire 'source' content into 'dest' if it's large enough,
+ or fill 'dest' buffer completely with as much data as possible from 'source'.
+ *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
+ New value is necessarily <= old value.
+ return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
+ or 0 if compression fails
+</p></pre><BR>
+
+<pre><b>int LZ4_decompress_fast (const char* source, char* dest, int originalSize);
+</b><p> originalSize : is the original and therefore uncompressed size
+ return : the number of bytes read from the source buffer (in other words, the compressed size)
+ If the source stream is detected malformed, the function will stop decoding and return a negative result.
+ Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
+ note : This function fully respect memory boundaries for properly formed compressed data.
+ It is a bit faster than LZ4_decompress_safe().
+ However, it does not provide any protection against intentionally modified data stream (malicious input).
+ Use this function in trusted environment only (data to decode comes from a trusted source).
+</p></pre><BR>
+
+<pre><b>int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize);
+</b><p> This function decompress a compressed block of size 'compressedSize' at position 'source'
+ into destination buffer 'dest' of size 'maxDecompressedSize'.
+ The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
+ reducing decompression time.
+ return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
+ Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
+ Always control how many bytes were decoded.
+ If the source stream is detected malformed, the function will stop decoding and return a negative result.
+ This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets
+</p></pre><BR>
+
+<a name="Chapter5"></a><h2>Streaming Compression Functions</h2><pre></pre>
+
+<pre><b>typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t;
+</b><p> information structure to track an LZ4 stream.
+ important : init this structure content before first use !
+ note : only allocated directly the structure if you are statically linking LZ4
+ If you are using liblz4 as a DLL, please use below construction methods instead.
+
+</p></pre><BR>
+
+<pre><b>void LZ4_resetStream (LZ4_stream_t* streamPtr);
+</b><p> Use this function to init an allocated `LZ4_stream_t` structure
+
+</p></pre><BR>
+
+<pre><b>LZ4_stream_t* LZ4_createStream(void);
+int LZ4_freeStream (LZ4_stream_t* streamPtr);
+</b><p> LZ4_createStream() will allocate and initialize an `LZ4_stream_t` structure.
+ LZ4_freeStream() releases its memory.
+ In the context of a DLL (liblz4), please use these methods rather than the static struct.
+ They are more future proof, in case of a change of `LZ4_stream_t` size.
+
+</p></pre><BR>
+
+<pre><b>int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
+</b><p> Use this function to load a static dictionary into LZ4_stream.
+ Any previous data will be forgotten, only 'dictionary' will remain in memory.
+ Loading a size of 0 is allowed.
+ Return : dictionary size, in bytes (necessarily <= 64 KB)
+
+</p></pre><BR>
+
+<pre><b>int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration);
+</b><p> Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio.
+ Important : Previous data blocks are assumed to still be present and unmodified !
+ 'dst' buffer must be already allocated.
+ If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
+ If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
+
+</p></pre><BR>
+
+<pre><b>int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize);
+</b><p> If previously compressed data block is not guaranteed to remain available at its memory location,
+ save it into a safer place (char* safeBuffer).
+ Note : you don't need to call LZ4_loadDict() afterwards,
+ dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue().
+ Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
+
+</p></pre><BR>
+
+<a name="Chapter6"></a><h2>Streaming Decompression Functions</h2><pre></pre>
+
+<pre><b>typedef struct { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; } LZ4_streamDecode_t;
+</b></pre><BR>
+<pre><b>LZ4_streamDecode_t* LZ4_createStreamDecode(void);
+int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
+</b><p> information structure to track an LZ4 stream.
+ init this structure content using LZ4_setStreamDecode or memset() before first use !
+
+ In the context of a DLL (liblz4) please prefer usage of construction methods below.
+ They are more future proof, in case of a change of LZ4_streamDecode_t size in the future.
+ LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure
+ LZ4_freeStreamDecode releases its memory.
+
+</p></pre><BR>
+
+<pre><b>int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
+</b><p> Use this function to instruct where to find the dictionary.
+ Setting a size of 0 is allowed (same effect as reset).
+ @return : 1 if OK, 0 if error
+
+</p></pre><BR>
+
+<pre><b>int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize);
+int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize);
+</b><p> These decoding functions allow decompression of multiple blocks in "streaming" mode.
+ Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
+ In the case of a ring buffers, decoding buffer must be either :
+ - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
+ In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
+ - Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
+ maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
+ 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).
+ - _At least_ 64 KB + 8 bytes + maxBlockSize.
+ In which case, encoding and decoding buffers do not need to be synchronized,
+ and encoding ring buffer can have any size, including larger than decoding buffer.
+ Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
+ and indicate where it is saved using LZ4_setStreamDecode()
+</p></pre><BR>
+
+<pre><b>int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize);
+int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize);
+</b><p>Advanced decoding functions :
+ These decoding functions work the same as
+ a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue()
+ They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure.
+</p></pre><BR>
+
+</html>
+</body>
diff --git a/lib/README.md b/lib/README.md
index 209cac1..051ef54 100644
--- a/lib/README.md
+++ b/lib/README.md
@@ -1,22 +1,26 @@
LZ4 - Library Files
================================
-All source material within __lib__ directory are BSD 2-Clause licensed.
-See [LICENSE](LICENSE) for details.
-The license is also repeated at the top of each source file.
-
The directory contains many files, but depending on project's objectives,
not all of them are necessary.
+#### Minimal LZ4 build
+
The minimum required is **`lz4.c`** and **`lz4.h`**,
which will provide the fast compression and decompression algorithm.
+
+#### The High Compression variant od LZ4
+
For more compression at the cost of compression speed,
the High Compression variant **lz4hc** is available.
It's necessary to add **`lz4hc.c`** and **`lz4hc.h`**.
The variant still depends on regular `lz4` source files.
In particular, the decompression is still provided by `lz4.c`.
+
+#### Compatibility issues
+
In order to produce files or streams compatible with `lz4` command line utility,
it's necessary to encode lz4-compressed blocks using the [official interoperable frame format].
This format is generated and decoded automatically by the **lz4frame** library.
@@ -26,10 +30,28 @@ which provides error detection.
This is what `liblz4` does, to avoid symbol duplication
in case a user program would link to several libraries containing xxhash symbols.)
+
+#### Advanced API
+
A more complex `lz4frame_static.h` is also provided.
It contains definitions which are not guaranteed to remain stable within future versions.
It must be used with static linking ***only***.
+
+#### Using MinGW+MSYS to create DLL
+
+DLL can be created using MinGW+MSYS with the "make liblz4" command.
+This command creates "liblz4.dll" and the import library "liblz4.dll.a".
+To compile a project the import library has to be added to linking options.
+It means that if a project that uses LZ4 consists of a single test-dll.c
+file it should be compiled with "liblz4.dll.a". For example:
+```
+ gcc $CFLAGS test-dll.c -o test-dll liblz4.dll.a
+```
+
+
+#### Miscellaneous
+
Other files present in the directory are not source code. There are :
- LICENSE : contains the BSD license text
@@ -38,3 +60,10 @@ Other files present in the directory are not source code. There are :
- README.md : this file
[official interoperable frame format]: ../doc/lz4_Frame_format.md
+
+
+#### License
+
+All source material within __lib__ directory are BSD 2-Clause licensed.
+See [LICENSE](LICENSE) for details.
+The license is also repeated at the top of each source file.
diff --git a/lib/lz4.h b/lib/lz4.h
index 1a66227..724bce6 100644
--- a/lib/lz4.h
+++ b/lib/lz4.h
@@ -39,19 +39,31 @@
extern "C" {
#endif
-/*
- * lz4.h provides block compression functions. It gives full buffer control to user.
- * Block compression functions are not-enough to send information,
- * since it's still necessary to provide metadata (such as compressed size),
- * and each application can do it in whichever way it wants.
- * For interoperability, there is LZ4 frame specification (doc/lz4_Frame_format.md).
- * A library is provided to take care of it, see lz4frame.h.
+/**
+ Introduction
+
+ LZ4 is lossless compression algorithm, providing compression speed at 400 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.
+ 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 provides block compression functions. It gives full buffer control to user.
+ Block compression functions are not-enough to send information,
+ since it's still necessary to provide metadata (such as compressed size),
+ and each application can do it in whichever way it wants.
+ For interoperability, there is LZ4 frame specification (doc/lz4_Frame_format.md).
+ A library is provided to take care of it, see lz4frame.h.
*/
-/*-***************************************************************
+/*^***************************************************************
* Export parameters
*****************************************************************/
-/*!
+/*
* LZ4_DLL_EXPORT :
* Enable exporting of functions when building a Windows DLL
*/
@@ -64,27 +76,26 @@ extern "C" {
#endif
-/*-************************************
-* Version
-**************************************/
+/*========== Version =========== */
+LZ4LIB_API int LZ4_versionNumber (void);
+LZ4LIB_API const char* LZ4_versionString (void);
+
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
#define LZ4_VERSION_RELEASE 2 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
-LZ4LIB_API int LZ4_versionNumber (void);
#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)
-LZ4LIB_API const char* LZ4_versionString (void);
/*-************************************
* Tuning parameter
**************************************/
-/*
+/*!
* LZ4_MEMORY_USAGE :
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
* Increasing memory usage improves compression ratio
@@ -97,12 +108,7 @@ LZ4LIB_API const char* LZ4_versionString (void);
/*-************************************
* Simple Functions
**************************************/
-
-LZ4LIB_API int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
-LZ4LIB_API int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
-
-/*
-LZ4_compress_default() :
+/*! LZ4_compress_default() :
Compresses 'sourceSize' bytes from buffer 'source'
into already allocated 'dest' buffer of size 'maxDestSize'.
Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
@@ -114,9 +120,10 @@ LZ4_compress_default() :
sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE
maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
- or 0 if compression fails
+ or 0 if compression fails */
+LZ4LIB_API int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
-LZ4_decompress_safe() :
+/*! LZ4_decompress_safe() :
compressedSize : is the precise full size of the compressed block.
maxDecompressedSize : is the size of destination buffer, which must be already allocated.
return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
@@ -125,6 +132,7 @@ LZ4_decompress_safe() :
This function is protected against buffer overflow exploits, including malicious data packets.
It never writes outside output buffer, nor reads outside input buffer.
*/
+LZ4LIB_API int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
/*-************************************
@@ -214,7 +222,7 @@ LZ4LIB_API int LZ4_decompress_safe_partial (const char* source, char* dest, int
***********************************************/
#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long))
-/*
+/*!
* LZ4_stream_t :
* information structure to track an LZ4 stream.
* important : init this structure content before first use !
@@ -228,7 +236,8 @@ typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t;
*/
LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
-/*! LZ4_createStream() will allocate and initialize an `LZ4_stream_t` structure.
+/*! LZ4_createStream() and LZ4_freeStream() :
+ * LZ4_createStream() will allocate and initialize an `LZ4_stream_t` structure.
* LZ4_freeStream() releases its memory.
* In the context of a DLL (liblz4), please use these methods rather than the static struct.
* They are more future proof, in case of a change of `LZ4_stream_t` size.
@@ -270,7 +279,7 @@ LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dict
#define LZ4_STREAMDECODESIZE_U64 4
#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
typedef struct { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; } LZ4_streamDecode_t;
-/*
+/*!
* LZ4_streamDecode_t
* information structure to track an LZ4 stream.
* init this structure content using LZ4_setStreamDecode or memset() before first use !
@@ -290,7 +299,7 @@ LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_str
*/
LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
-/*
+/*!
*_continue() :
These decoding functions allow decompression of multiple blocks in "streaming" mode.
Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
@@ -311,9 +320,9 @@ LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecod
LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize);
-/*
-Advanced decoding functions :
+/*!
*_usingDict() :
+Advanced decoding functions :
These decoding functions work the same as
a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue()
They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure.
diff --git a/programs/bench.c b/programs/bench.c
index c493682..cfd60db 100644
--- a/programs/bench.c
+++ b/programs/bench.c
@@ -122,7 +122,6 @@ void BMK_SetNbSeconds(unsigned nbSeconds)
void BMK_SetBlockSize(size_t blockSize)
{
g_blockSize = blockSize;
- DISPLAYLEVEL(2, "using blocks of size %u KB \n", (U32)(blockSize>>10));
}
@@ -494,6 +493,11 @@ int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles,
{
double const compressibility = (double)g_compressibilityDefault / 100;
+ if (cLevel > LZ4HC_MAX_CLEVEL) cLevel = LZ4HC_MAX_CLEVEL;
+ if (cLevelLast > LZ4HC_MAX_CLEVEL) cLevelLast = LZ4HC_MAX_CLEVEL;
+ if (cLevelLast < cLevel) cLevelLast = cLevel;
+ if (cLevelLast > cLevel) DISPLAYLEVEL(2, "Benchmarking levels from %d to %d\n", cLevel, cLevelLast);
+
if (nbFiles == 0)
BMK_syntheticTest(cLevel, cLevelLast, compressibility);
else
diff --git a/programs/lz4cli.c b/programs/lz4cli.c
index 1f6b391..1f25a21 100644
--- a/programs/lz4cli.c
+++ b/programs/lz4cli.c
@@ -161,7 +161,7 @@ static int usage_advanced(const char* exeName)
DISPLAY( " -r : operate recursively on directories (sets also -m)\n");
#endif
DISPLAY( " -l : compress using Legacy format (Linux kernel compression)\n");
- DISPLAY( " -B# : Block size [4-7](default : 7)\n");
+ DISPLAY( " -B# : Block size [4-7] (default : 7)\n");
DISPLAY( " -BD : Block dependency (improve compression ratio)\n");
/* DISPLAY( " -BX : enable block checksum (default:disabled)\n"); *//* Option currently inactive */
DISPLAY( "--no-frame-crc : disable stream checksum (default:enabled)\n");
@@ -171,6 +171,8 @@ static int usage_advanced(const char* exeName)
DISPLAY( " -b# : benchmark file(s), using # compression level (default : 1) \n");
DISPLAY( " -e# : test all compression levels from -bX to # (default : 1)\n");
DISPLAY( " -i# : minimum evaluation time in seconds (default : 3s)\n");
+ DISPLAY( " -B# : cut file into independent blocks of size # bytes [32+]\n");
+ DISPLAY( " or predefined block size [4-7] (default: 7)\n");
#if defined(ENABLE_LZ4C_LEGACY_OPTIONS)
DISPLAY( "Legacy arguments :\n");
DISPLAY( " -c0 : fast compression\n");
@@ -417,20 +419,33 @@ int main(int argc, const char** argv)
int exitBlockProperties=0;
switch(argument[1])
{
- case '4':
- case '5':
- case '6':
- case '7':
- { int const B = argument[1] - '0';
- blockSize = LZ4IO_setBlockSizeID(B);
- BMK_setNotificationLevel(displayLevel);
- BMK_SetBlockSize(blockSize);
+ case 'D': LZ4IO_setBlockMode(LZ4IO_blockLinked); argument++; break;
+ case 'X': LZ4IO_setBlockChecksumMode(1); argument ++; break; /* disabled by default */
+ default :
+ if (argument[1] < '0' || argument[1] > '9') {
+ exitBlockProperties=1;
+ break;
+ } else {
+ unsigned B;
argument++;
+ B = readU32FromChar(&argument);
+ argument--;
+ if (B < 4) badusage(exeName);
+ if (B <= 7) {
+ blockSize = LZ4IO_setBlockSizeID(B);
+ BMK_SetBlockSize(blockSize);
+ DISPLAYLEVEL(2, "using blocks of size %u KB \n", (U32)(blockSize>>10));
+ } else {
+ if (B < 32) badusage(exeName);
+ BMK_SetBlockSize(B);
+ if (B >= 1024) {
+ DISPLAYLEVEL(2, "bench: using blocks of size %u KB \n", (U32)(B>>10));
+ } else {
+ DISPLAYLEVEL(2, "bench: using blocks of size %u bytes \n", (U32)(B));
+ }
+ }
break;
}
- case 'D': LZ4IO_setBlockMode(LZ4IO_blockLinked); argument++; break;
- case 'X': LZ4IO_setBlockChecksumMode(1); argument ++; break; /* disabled by default */
- default : exitBlockProperties=1;
}
if (exitBlockProperties) break;
}
diff --git a/programs/lz4io.c b/programs/lz4io.c
index 61d8009..725d3a8 100644
--- a/programs/lz4io.c
+++ b/programs/lz4io.c
@@ -301,7 +301,7 @@ static FILE* LZ4IO_openDstFile(const char* dstFileName)
}
/* sparse file */
- if (f && g_sparseFileSupport) { SET_SPARSE_FILE_MODE(foutput); }
+ if (f && g_sparseFileSupport) { SET_SPARSE_FILE_MODE(f); }
return f;
}
diff --git a/tests/Makefile b/tests/Makefile
index 16771da..2b88f73 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -39,7 +39,7 @@ MANDIR := $(PREFIX)/share/man/man1
LZ4DIR := ../lib
PRGDIR := ../programs
TESTDIR := versionsTest
-PYTHON?= python3
+PYTHON ?= python3
CFLAGS ?= -O3 # can select custom flags. For example : CFLAGS="-O2 -g" make
CFLAGS += -Wall -Wextra -Wundef -Wcast-qual -Wcast-align -Wshadow -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes
@@ -58,7 +58,8 @@ endif
# Default test parameters
TEST_FILES := COPYING
-FUZZER_TIME := -T9mn
+FUZZER_TIME := -T3mn
+NB_LOOPS ?= -i1
default: bins
@@ -114,6 +115,7 @@ datagen : $(PRGDIR)/datagen.c datagencli.c
clean:
@$(MAKE) -C $(LZ4DIR) $@ > $(VOID)
@$(RM) core *.o *.test tmp* \
+ fullbench-dll$(EXT) fullbench-lib$(EXT) \
fullbench$(EXT) fullbench32$(EXT) \
fuzzer$(EXT) fuzzer32$(EXT) \
frametest$(EXT) frametest32$(EXT) \
diff --git a/tests/README.md b/tests/README.md
index 9070317..75b7b9f 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -6,6 +6,7 @@ This directory contains the following programs and scripts:
- `frametest` : Test tool that checks lz4frame integrity on target platform
- `fullbench` : Precisely measure speed for each lz4 inner functions
- `fuzzer` : Test tool, to check lz4 integrity on target platform
+- `test-lz4-speed.py` : script for testing lz4 speed difference between commits
- `test-lz4-versions.py` : compatibility test between lz4 versions stored on Github
@@ -16,6 +17,53 @@ Then all taged (released) versions of lz4 are compiled.
In the following step interoperability between lz4 versions is checked.
+#### `test-lz4-speed.py` - script for testing lz4 speed difference between commits
+
+This script creates `speedTest` directory to which lz4 repository is cloned.
+Then it compiles all branches of lz4 and performs a speed benchmark for a given list of files (the `testFileNames` parameter).
+After `sleepTime` (an optional parameter, default 300 seconds) seconds the script checks repository for new commits.
+If a new commit is found it is compiled and a speed benchmark for this commit is performed.
+The results of the speed benchmark are compared to the previous results.
+If compression or decompression speed for one of lz4 levels is lower than `lowerLimit` (an optional parameter, default 0.98) the speed benchmark is restarted.
+If second results are also lower than `lowerLimit` the warning e-mail is send to recipients from the list (the `emails` parameter).
+
+Additional remarks:
+- To be sure that speed results are accurate the script should be run on a "stable" target system with no other jobs running in parallel
+- Using the script with virtual machines can lead to large variations of speed results
+- The speed benchmark is not performed until computers' load average is lower than `maxLoadAvg` (an optional parameter, default 0.75)
+- The script sends e-mails using `mutt`; if `mutt` is not available it sends e-mails without attachments using `mail`; if both are not available it only prints a warning
+
+
+The example usage with two test files, one e-mail address, and with an additional message:
+```
+./test-lz4-speed.py "silesia.tar calgary.tar" "email@gmail.com" --message "tested on my laptop" --sleepTime 60
+```
+
+To run the script in background please use:
+```
+nohup ./test-lz4-speed.py testFileNames emails &
+```
+
+The full list of parameters:
+```
+positional arguments:
+ testFileNames file names list for speed benchmark
+ emails list of e-mail addresses to send warnings
+
+optional arguments:
+ -h, --help show this help message and exit
+ --message MESSAGE attach an additional message to e-mail
+ --lowerLimit LOWERLIMIT
+ send email if speed is lower than given limit
+ --maxLoadAvg MAXLOADAVG
+ maximum load average to start testing
+ --lastCLevel LASTCLEVEL
+ last compression level for testing
+ --sleepTime SLEEPTIME
+ frequency of repository checking in seconds
+```
+
+
#### License
All files in this directory are licensed under GPL-v2.