From daffe435c0f8d24bb767cf1152ab9f4506ce9a3e Mon Sep 17 00:00:00 2001 From: Shawn Landden Date: Thu, 21 Dec 2017 15:41:58 -0800 Subject: [PATCH 001/255] meson: support differn't legacy levels. Default to v0.4.0+ --- contrib/meson/meson.build | 38 ++++++++++++++++++++++----------- contrib/meson/meson_options.txt | 2 +- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/contrib/meson/meson.build b/contrib/meson/meson.build index 5ff370fa7..ce3fb83e3 100644 --- a/contrib/meson/meson.build +++ b/contrib/meson/meson.build @@ -38,21 +38,35 @@ libzstd_srcs = [ libzstd_includes = [include_directories(common_dir, dictbuilder_dir, compress_dir, lib_dir)] -if get_option('legacy_support') - message('Enabling legacy support') - libzstd_cflags = ['-DZSTD_LEGACY_SUPPORT=4'] +legacy = get_option('legacy_support') +if legacy != 'false' + message('Enabling legacy support back to version 0.' + legacy) + legacy_int = legacy.to_int() + libzstd_cflags = ['-DZSTD_LEGACY_SUPPORT=' + legacy] legacy_dir = join_paths(lib_dir, 'legacy') libzstd_includes += [include_directories(legacy_dir)] - libzstd_srcs += [ - join_paths(legacy_dir, 'zstd_v01.c'), - join_paths(legacy_dir, 'zstd_v02.c'), - join_paths(legacy_dir, 'zstd_v03.c'), - join_paths(legacy_dir, 'zstd_v04.c'), - join_paths(legacy_dir, 'zstd_v05.c'), - join_paths(legacy_dir, 'zstd_v06.c'), - join_paths(legacy_dir, 'zstd_v07.c') - ] + if legacy_int <= 1 + libzstd_srcs += join_paths(legacy_dir, 'zstd_v01.c') + endif + if legacy_int <= 2 + libzstd_srcs += join_paths(legacy_dir, 'zstd_v02.c') + endif + if legacy_int <= 3 + libzstd_srcs += join_paths(legacy_dir, 'zstd_v03.c') + endif + if legacy_int <= 4 + libzstd_srcs += join_paths(legacy_dir, 'zstd_v04.c') + endif + if legacy_int <= 5 + libzstd_srcs += join_paths(legacy_dir, 'zstd_v05.c') + endif + if legacy_int <= 6 + libzstd_srcs += join_paths(legacy_dir, 'zstd_v06.c') + endif + if legacy_int <= 7 + libzstd_srcs += join_paths(legacy_dir, 'zstd_v07.c') + endif else libzstd_cflags = [] endif diff --git a/contrib/meson/meson_options.txt b/contrib/meson/meson_options.txt index 0a12f43e1..de9fea073 100644 --- a/contrib/meson/meson_options.txt +++ b/contrib/meson/meson_options.txt @@ -1,2 +1,2 @@ option('multithread', type: 'boolean', value: false) -option('legacy_support', type: 'boolean', value: false) +option('legacy_support', type: 'string', value: '4') From 914d9838795c6d797a3a98e2cccf735ea1661f52 Mon Sep 17 00:00:00 2001 From: Shawn Landden Date: Thu, 21 Dec 2017 16:15:12 -0800 Subject: [PATCH 002/255] fix unbounded range I think you meant 8 MiB or smaller, instead of an unbounded (and illogical) range --- doc/zstd_compression_format.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index 86a7ace91..7bf36c491 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -257,7 +257,7 @@ a decoder is allowed to reject a compressed frame which requests a memory size beyond decoder's authorized range. For improved interoperability, -decoders are recommended to be compatible with `Window_Size >= 8 MB`, +decoders are recommended to be compatible with `Window_Size <= 8 MB`, and encoders are recommended to not request more than 8 MB. It's merely a recommendation though, decoders are free to support larger or lower limits, From 6ff43c00511577cdd1f1b560965d5c2104821897 Mon Sep 17 00:00:00 2001 From: Shawn Landden Date: Sun, 24 Dec 2017 10:05:43 -0800 Subject: [PATCH 003/255] get soversion right --- contrib/meson/meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/meson/meson.build b/contrib/meson/meson.build index ce3fb83e3..b045c9944 100644 --- a/contrib/meson/meson.build +++ b/contrib/meson/meson.build @@ -84,7 +84,9 @@ libzstd = library('zstd', include_directories: libzstd_includes, c_args: libzstd_cflags, dependencies: libzstd_deps, - install: true) + install: true, + soversion: '1', + ) programs_dir = join_paths('..', '..', 'programs') From c707c6e9f27e2720c69d7461a50fdc74e4f4889f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Dec 2017 13:32:05 +0100 Subject: [PATCH 004/255] fix: bench can accept hlog custom parameter was ignored during initialization --- programs/bench.c | 1 + programs/zstdcli.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 5d2568f10..843920c89 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -307,6 +307,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmHashEveryLog, g_ldmHashEveryLog); } ZSTD_CCtx_setParameter(ctx, ZSTD_p_windowLog, comprParams->windowLog); + ZSTD_CCtx_setParameter(ctx, ZSTD_p_hashLog, comprParams->hashLog); ZSTD_CCtx_setParameter(ctx, ZSTD_p_chainLog, comprParams->chainLog); ZSTD_CCtx_setParameter(ctx, ZSTD_p_searchLog, comprParams->searchLog); ZSTD_CCtx_setParameter(ctx, ZSTD_p_minMatch, comprParams->searchLength); diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 7e29998b0..eae208c52 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -318,12 +318,13 @@ static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressi if (longCommandWArg(&stringPtr, "ldmSearchLength=") || longCommandWArg(&stringPtr, "ldmslen=")) { g_ldmMinMatch = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } if (longCommandWArg(&stringPtr, "ldmBucketSizeLog=") || longCommandWArg(&stringPtr, "ldmblog=")) { g_ldmBucketSizeLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } if (longCommandWArg(&stringPtr, "ldmHashEveryLog=") || longCommandWArg(&stringPtr, "ldmhevery=")) { g_ldmHashEveryLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + DISPLAYLEVEL(4, "invalid compression parameter \n"); return 0; } + DISPLAYLEVEL(4, "windowLog=%d, chainLog=%d, hashLog=%d, searchLog=%d \n", params->windowLog, params->chainLog, params->hashLog, params->searchLog); + DISPLAYLEVEL(4, "searchLength=%d, targetLength=%d, strategy=%d \n", params->searchLength, params->targetLength, params->strategy); if (stringPtr[0] != 0) return 0; /* check the end of string */ - DISPLAYLEVEL(4, "windowLog=%d\nchainLog=%d\nhashLog=%d\nsearchLog=%d\n", params->windowLog, params->chainLog, params->hashLog, params->searchLog); - DISPLAYLEVEL(4, "searchLength=%d\ntargetLength=%d\nstrategy=%d\n", params->searchLength, params->targetLength, params->strategy); return 1; } From 2126ca8a5f3a4cb2a19c5b2923f4f9bb7f41f630 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Dec 2017 17:49:18 +0100 Subject: [PATCH 005/255] %.o objects files in /tests Recipe in /tests rebuild everything from source for each target. zstd is still a "small" project, so it's not prohibitive, yet, rebuilding same files over and over represents substantial redundant work. This patch replaces *.c files from /lib by their corresponding *.o files. They cannot be compiled and stored directly within /lib, since /tests triggers additional debug capabilities unwelcome in release binary. So the resulting *.o are stored directly within /tests. It turns out, it's difficult to find several target using *exactly* the same rules. Using only the default rules (debug enabled, multi-threading disabled, no legacy) a surprisingly small amount of targets share their work. It's because, in many cases there are additional modifications requested : some targets are 32-bits, some enable multi-threading, some enable legacy support, some disable asserts, some want different kind of sanitizer, etc. I created 2 sets of object files : with and without multithreading. Several targets share their work, saving compilation time when running `make all`. Also, obviously, when modifying one source file, only this one needs rebuilding. For targets requiring some different setting, build from source *.c remain the rule. The new rules have been tested within `-j` parallel compilation, and work fine with it. --- tests/Makefile | 129 ++++++++++++++++++++++++++++-------------- tests/namespaceTest.c | 24 -------- 2 files changed, 87 insertions(+), 66 deletions(-) delete mode 100644 tests/namespaceTest.c diff --git a/tests/Makefile b/tests/Makefile index 853f4ee89..258212db1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -21,7 +21,7 @@ ZSTDDIR = ../lib PRGDIR = ../programs PYTHON ?= python3 -TESTARTEFACT := versionsTest namespaceTest +TESTARTEFACT := versionsTest DEBUGLEVEL ?= 1 DEBUGFLAGS = -g -DZSTD_DEBUG=$(DEBUGLEVEL) @@ -44,6 +44,16 @@ ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) ZBUFF_FILES := $(ZSTDDIR)/deprecated/*.c ZDICT_FILES := $(ZSTDDIR)/dictBuilder/*.c +ZSTD_F1 := $(wildcard $(ZSTD_FILES)) +ZSTD_OBJ1 := $(subst $(ZSTDDIR)/common/,zstdm_,$(ZSTD_F1)) +ZSTD_OBJ2 := $(subst $(ZSTDDIR)/compress/,zstdc_,$(ZSTD_OBJ1)) +ZSTD_OBJ3 := $(subst $(ZSTDDIR)/decompress/,zstdd_,$(ZSTD_OBJ2)) +ZSTD_OBJECTS := $(ZSTD_OBJ3:.c=.o) + +ZSTDMT_OBJ1 := $(subst $(ZSTDDIR)/common/,zstdmt_m_,$(ZSTD_F1)) +ZSTDMT_OBJ2 := $(subst $(ZSTDDIR)/compress/,zstdmt_c_,$(ZSTDMT_OBJ1)) +ZSTDMT_OBJ3 := $(subst $(ZSTDDIR)/decompress/,zstdmt_d_,$(ZSTDMT_OBJ2)) +ZSTDMT_OBJECTS := $(ZSTDMT_OBJ3:.c=.o) # Define *.exe as extension for Windows systems ifneq (,$(filter Windows%,$(OS))) @@ -63,11 +73,12 @@ FUZZERTEST ?= -T200s ZSTDRTTEST = --test-large-data DECODECORPUS_TESTTIME ?= -T30 -.PHONY: default all all32 allnothread dll clean test test32 test-all namespaceTest versionsTest +.PHONY: default all all32 allnothread dll clean test test32 test-all versionsTest default: fullbench + @echo $(ZSTDMT_OBJECTS) -all: fullbench fuzzer zstreamtest paramgrill datagen decodecorpus +all: fullbench fuzzer zstreamtest paramgrill datagen decodecorpus roundTripCrash all32: fullbench32 fuzzer32 zstreamtest32 @@ -87,35 +98,69 @@ zstd-nolegacy: gzstd: $(MAKE) -C $(PRGDIR) zstd HAVE_ZLIB=1 MOREFLAGS="$(DEBUGFLAGS)" +.PHONY: +zstd-dll : + $(MAKE) -C $(ZSTDDIR) libzstd + +.PHONY: +zstd-staticLib : + $(MAKE) -C $(ZSTDDIR) libzstd.a + +zstdm_%.o : $(ZSTDDIR)/common/%.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +zstdc_%.o : $(ZSTDDIR)/compress/%.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +zstdd_%.o : $(ZSTDDIR)/decompress/%.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +zstdmt%.o : CPPFLAGS += -DZSTD_MULTITHREAD=1 + +zstdmt_m_%.o : $(ZSTDDIR)/common/%.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +zstdmt_c_%.o : $(ZSTDDIR)/compress/%.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +zstdmt_d_%.o : $(ZSTDDIR)/decompress/%.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + fullbench32: CPPFLAGS += -m32 fullbench fullbench32 : CPPFLAGS += $(MULTITHREAD_CPP) fullbench fullbench32 : LDFLAGS += $(MULTITHREAD_LD) fullbench fullbench32 : DEBUGFLAGS = # turn off assert() for speed measurements -fullbench fullbench32 : $(ZSTD_FILES) $(PRGDIR)/datagen.c fullbench.c +fullbench fullbench32 : $(ZSTD_FILES) +fullbench fullbench32 : $(PRGDIR)/datagen.c fullbench.c $(CC) $(FLAGS) $^ -o $@$(EXT) -fullbench-lib: $(PRGDIR)/datagen.c fullbench.c - $(MAKE) -C $(ZSTDDIR) libzstd.a - $(CC) $(FLAGS) $^ -o $@$(EXT) $(ZSTDDIR)/libzstd.a +fullbench-lib : zstd-staticLib +fullbench-lib : $(PRGDIR)/datagen.c fullbench.c + $(CC) $(FLAGS) $(filter %.c,$^) -o $@$(EXT) $(ZSTDDIR)/libzstd.a +# note : broken : requires unavailable symbols +fullbench-dll : zstd-dll +fullbench-dll : LDFLAGS+= -L$(ZSTDDIR) -lzstd fullbench-dll: $(PRGDIR)/datagen.c fullbench.c - $(MAKE) -C $(ZSTDDIR) libzstd - $(CC) $(FLAGS) $^ -o $@$(EXT) -DZSTD_DLL_IMPORT=1 $(ZSTDDIR)/dll/libzstd.dll +# $(CC) $(FLAGS) $(filter %.c,$^) -o $@$(EXT) -DZSTD_DLL_IMPORT=1 $(ZSTDDIR)/dll/libzstd.dll + $(CC) $(FLAGS) $(filter %.c,$^) -o $@$(EXT) -fuzzer : CPPFLAGS += $(MULTITHREAD_CPP) -fuzzer : LDFLAGS += $(MULTITHREAD_LD) +fuzzer : CPPFLAGS += $(MULTITHREAD_CPP) +fuzzer : LDFLAGS += $(MULTITHREAD_LD) fuzzer32: CFLAGS += -m32 -fuzzer fuzzer32 : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c fuzzer.c +fuzzer : $(ZSTDMT_OBJECTS) +fuzzer32: $(ZSTD_FILES) +fuzzer fuzzer32 : $(ZDICT_FILES) $(PRGDIR)/datagen.c fuzzer.c $(CC) $(FLAGS) $^ -o $@$(EXT) +fuzzer-dll : zstd-dll fuzzer-dll : LDFLAGS+= -L$(ZSTDDIR) -lzstd fuzzer-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c fuzzer.c - $(MAKE) -C $(ZSTDDIR) libzstd - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@$(EXT) + $(CC) $(CPPFLAGS) $(CFLAGS) $(filter %.c,$^) $(LDFLAGS) -o $@$(EXT) zbufftest : CPPFLAGS += -I$(ZSTDDIR)/deprecated zbufftest : CFLAGS += -Wno-deprecated-declarations # required to silence deprecation warnings -zbufftest : $(ZSTD_FILES) $(ZBUFF_FILES) $(PRGDIR)/datagen.c zbufftest.c +zbufftest : $(ZSTD_OBJECTS) $(ZBUFF_FILES) $(PRGDIR)/datagen.c zbufftest.c $(CC) $(FLAGS) $^ -o $@$(EXT) zbufftest32 : CPPFLAGS += -I$(ZSTDDIR)/deprecated @@ -123,18 +168,22 @@ zbufftest32 : CFLAGS += -Wno-deprecated-declarations -m32 zbufftest32 : $(ZSTD_FILES) $(ZBUFF_FILES) $(PRGDIR)/datagen.c zbufftest.c $(CC) $(FLAGS) $^ -o $@$(EXT) +zbufftest-dll : zstd-dll zbufftest-dll : CPPFLAGS += -I$(ZSTDDIR)/deprecated zbufftest-dll : CFLAGS += -Wno-deprecated-declarations # required to silence deprecation warnings zbufftest-dll : LDFLAGS+= -L$(ZSTDDIR) -lzstd zbufftest-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c zbufftest.c - $(MAKE) -C $(ZSTDDIR) libzstd - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@$(EXT) + $(CC) $(CPPFLAGS) $(CFLAGS) $(filter %.c,$^) $(LDFLAGS) -o $@$(EXT) -ZSTREAMFILES := $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c seqgen.c zstreamtest.c +ZSTREAM_LOCAL_FILES := $(PRGDIR)/datagen.c seqgen.c zstreamtest.c +ZSTREAM_PROPER_FILES := $(ZDICT_FILES) $(ZSTREAM_LOCAL_FILES) +ZSTREAMFILES := $(ZSTD_FILES) $(ZSTREAM_PROPER_FILES) zstreamtest32 : CFLAGS += -m32 zstreamtest zstreamtest32 : CPPFLAGS += $(MULTITHREAD_CPP) zstreamtest zstreamtest32 : LDFLAGS += $(MULTITHREAD_LD) -zstreamtest zstreamtest32 : $(ZSTREAMFILES) +zstreamtest : $(ZSTDMT_OBJECTS) $(ZSTREAM_PROPER_FILES) +zstreamtest32 : $(ZSTREAMFILES) +zstreamtest zstreamtest32 : $(CC) $(FLAGS) $^ -o $@$(EXT) zstreamtest_asan : CFLAGS += -fsanitize=address @@ -145,51 +194,47 @@ zstreamtest_tsan : CFLAGS += -fsanitize=thread zstreamtest_tsan : $(ZSTREAMFILES) $(CC) $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT) +zstreamtest-dll : zstd-dll zstreamtest-dll : LDFLAGS+= -L$(ZSTDDIR) -lzstd -zstreamtest-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c zstreamtest.c - $(MAKE) -C $(ZSTDDIR) libzstd - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@$(EXT) +zstreamtest-dll : $(ZSTDDIR)/common/xxhash.c # xxh symbols not exposed from dll +zstreamtest-dll : $(ZSTREAM_LOCAL_FILES) + $(CC) $(CPPFLAGS) $(CFLAGS) $(filter %.c,$^) $(LDFLAGS) -o $@$(EXT) -paramgrill : DEBUGFLAGS = +paramgrill : DEBUGFLAGS = # turn off assert() for speed measurements paramgrill : $(ZSTD_FILES) $(PRGDIR)/datagen.c paramgrill.c - $(CC) $(FLAGS) $^ -lm -o $@$(EXT) + $(CC) $(FLAGS) $^ -lm -o $@$(EXT) datagen : $(PRGDIR)/datagen.c datagencli.c - $(CC) $(FLAGS) $^ -o $@$(EXT) + $(CC) $(FLAGS) $^ -o $@$(EXT) -roundTripCrash : $(ZSTD_FILES) roundTripCrash.c - $(CC) $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT) +roundTripCrash : $(ZSTD_OBJECTS) roundTripCrash.c + $(CC) $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT) -longmatch : $(ZSTD_FILES) longmatch.c - $(CC) $(FLAGS) $^ -o $@$(EXT) +longmatch : $(ZSTD_OBJECTS) longmatch.c + $(CC) $(FLAGS) $^ -o $@$(EXT) -invalidDictionaries : $(ZSTD_FILES) invalidDictionaries.c - $(CC) $(FLAGS) $^ -o $@$(EXT) +invalidDictionaries : $(ZSTD_OBJECTS) invalidDictionaries.c + $(CC) $(FLAGS) $^ -o $@$(EXT) legacy : CFLAGS+= -DZSTD_LEGACY_SUPPORT=4 legacy : CPPFLAGS+= -I$(ZSTDDIR)/legacy legacy : $(ZSTD_FILES) $(wildcard $(ZSTDDIR)/legacy/*.c) legacy.c - $(CC) $(FLAGS) $^ -o $@$(EXT) + $(CC) $(FLAGS) $^ -o $@$(EXT) -decodecorpus : $(filter-out $(ZSTDDIR)/compress/zstd_compress.c, $(wildcard $(ZSTD_FILES))) $(ZDICT_FILES) decodecorpus.c - $(CC) $(FLAGS) $^ -o $@$(EXT) -lm +decodecorpus : $(filter-out zstdc_zstd_compress.o, $(ZSTD_OBJECTS)) $(ZDICT_FILES) decodecorpus.c + $(CC) $(FLAGS) $^ -o $@$(EXT) -lm -symbols : symbols.c - $(MAKE) -C $(ZSTDDIR) libzstd +symbols : symbols.c zstd-dll ifneq (,$(filter Windows%,$(OS))) cp $(ZSTDDIR)/dll/libzstd.dll . - $(CC) $(FLAGS) $^ -o $@$(EXT) -DZSTD_DLL_IMPORT=1 libzstd.dll + $(CC) $(FLAGS) $< -o $@$(EXT) -DZSTD_DLL_IMPORT=1 libzstd.dll else - $(CC) $(FLAGS) $^ -o $@$(EXT) -Wl,-rpath=$(ZSTDDIR) $(ZSTDDIR)/libzstd.so + $(CC) $(FLAGS) $< -o $@$(EXT) -Wl,-rpath=$(ZSTDDIR) $(ZSTDDIR)/libzstd.so # broken on Mac endif poolTests : poolTests.c $(ZSTDDIR)/common/pool.c $(ZSTDDIR)/common/threading.c $(ZSTDDIR)/common/zstd_common.c $(ZSTDDIR)/common/error_private.c $(CC) $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT) -namespaceTest: - if $(CC) namespaceTest.c ../lib/common/xxhash.c -o $@ ; then echo compilation should fail; exit 1 ; fi - $(RM) $@ - versionsTest: clean $(PYTHON) test-zstd-versions.py diff --git a/tests/namespaceTest.c b/tests/namespaceTest.c deleted file mode 100644 index 5b7095f67..000000000 --- a/tests/namespaceTest.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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. - */ - - - -#include /* size_t */ -#include /* strlen */ - -/* symbol definition */ -extern unsigned XXH32(const void* src, size_t srcSize, unsigned seed); - -int main(int argc, const char** argv) -{ - const char* exename = argv[0]; - unsigned result = XXH32(exename, strlen(exename), argc); - return !result; -} From 63aebdf9ea7a04e3965389fcdbfe5bfb94a0cc12 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Dec 2017 19:27:54 +0100 Subject: [PATCH 006/255] windows ci : removed fullbench-dll from appveyor the test is broken previous version was too Windows-specific. It needs to be either refactored to be cross-platform, or be removed if it doesn't bring any value (which is indeed unclear). --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 0f9142b47..7d6ca99bb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ - COMPILER: "gcc" HOST: "mingw" PLATFORM: "x64" - SCRIPT: "make allzstd MOREFLAGS=-static && make -C tests test-symbols fullbench-dll fullbench-lib" + SCRIPT: "make allzstd MOREFLAGS=-static && make -C tests test-symbols fullbench-lib" ARTIFACT: "true" BUILD: "true" - COMPILER: "gcc" From 5235d8d6ba523ee655f4966f95f59987f13cfae9 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 28 Dec 2017 16:58:57 +0100 Subject: [PATCH 007/255] first implementation of delayed update for btlazy2 This is a pretty nice speed win. The new strategy consists in stacking new candidates as if it was a hash chain. Then, only if there is a need to actually consult the chain, they are batch-updated, before starting the match search itself. This is supposed to be beneficial when skipping positions, which happens a lot when using lazy strategy. The baseline performance for btlazy2 on my laptop is : 15#calgary.tar : 3265536 -> 955985 (3.416), 7.06 MB/s , 618.0 MB/s 15#enwik7 : 10000000 -> 3067341 (3.260), 4.65 MB/s , 521.2 MB/s 15#silesia.tar : 211984896 -> 58095131 (3.649), 6.20 MB/s , 682.4 MB/s (only level 15 remains for btlazy2, as this strategy is squeezed between lazy2 and btopt) After this patch, and keeping all parameters identical, speed is increased by a pretty good margin (+30-50%), but compression ratio suffers a bit : 15#calgary.tar : 3265536 -> 958060 (3.408), 9.12 MB/s , 621.1 MB/s 15#enwik7 : 10000000 -> 3078318 (3.249), 6.37 MB/s , 525.1 MB/s 15#silesia.tar : 211984896 -> 58444111 (3.627), 9.89 MB/s , 680.4 MB/s That's because I kept `1< 956311 (3.415), 8.32 MB/s , 611.4 MB/s 15#enwik7 : 10000000 -> 3067655 (3.260), 5.43 MB/s , 535.5 MB/s 15#silesia.tar : 211984896 -> 58113144 (3.648), 8.35 MB/s , 679.3 MB/s aka, almost the same compression ratio as before, but with a noticeable speed increase (+20-30%). This modification makes btlazy2 more competitive. A new round of paramgrill will be necessary to determine which levels are impacted and could adopt the new strategy. --- lib/compress/zstd_compress.c | 6 +- lib/compress/zstd_lazy.c | 317 +++++++++++++++++++---------------- lib/compress/zstd_lazy.h | 5 +- lib/compress/zstd_opt.c | 141 ++++++++++++++++ lib/compress/zstd_opt.h | 3 + 5 files changed, 322 insertions(+), 150 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 8d1629246..bafc1ce95 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1922,6 +1922,10 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_CCtx* zc, const void* src, size_t break; case ZSTD_btlazy2: + if (srcSize >= HASH_READ_SIZE) + ZSTD_updateDUBT(zc, iend-HASH_READ_SIZE, iend, zc->appliedParams.cParams.searchLength); + break; + case ZSTD_btopt: case ZSTD_btultra: if (srcSize >= HASH_READ_SIZE) @@ -2974,7 +2978,7 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 22, 20, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */ { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 13 */ { 22, 21, 22, 6, 5, 16, ZSTD_lazy2 }, /* level 14 */ - { 22, 21, 22, 4, 5, 16, ZSTD_btlazy2 }, /* level 15 */ + { 22, 21, 22, 5, 5, 16, ZSTD_btlazy2 }, /* level 15 */ { 22, 21, 22, 4, 5, 48, ZSTD_btopt }, /* level 16 */ { 23, 22, 22, 4, 4, 48, ZSTD_btopt }, /* level 17 */ { 23, 22, 22, 5, 3, 64, ZSTD_btopt }, /* level 18 */ diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 6d4804961..431157741 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -15,73 +15,85 @@ /*-************************************* * Binary Tree search ***************************************/ -/** ZSTD_insertBt1() : add one or multiple positions to tree. - * ip : assumed <= iend-8 . - * @return : nb of positions added */ -static U32 ZSTD_insertBt1(ZSTD_CCtx* zc, - const BYTE* const ip, const BYTE* const iend, - U32 nbCompares, U32 const mls, U32 const extDict) +#define ZSTD_DUBT_UNSORTED ((U32)(-1)) + +void ZSTD_updateDUBT(ZSTD_CCtx* zc, + const BYTE* ip, const BYTE* iend, + U32 mls) +{ + U32* const hashTable = zc->hashTable; + U32 const hashLog = zc->appliedParams.cParams.hashLog; + + U32* const bt = zc->chainTable; + U32 const btLog = zc->appliedParams.cParams.chainLog - 1; + U32 const btMask = (1 << btLog) - 1; + + const BYTE* const base = zc->base; + U32 const target = (U32)(ip - base); + U32 idx = zc->nextToUpdate; + + DEBUGLOG(7, "ZSTD_updateDUBT, from %u to %u ", + idx, target); + assert(ip + 8 <= iend); /* condition for ZSTD_hashPtr */ + (void)iend; + + + assert(idx >= zc->dictLimit); /* condition for valid base+idx */ + for ( ; idx < target ; idx++) { + size_t const h = ZSTD_hashPtr(base + idx, hashLog, mls); /* assumption : ip + 8 <= iend */ + U32 const matchIndex = hashTable[h]; + + U32* const nextCandidatePtr = bt + 2*(idx&btMask); + U32* const sortMarkPtr = nextCandidatePtr + 1; + + hashTable[h] = idx; /* Update Hash Table */ + *nextCandidatePtr = matchIndex; /* update BT like a chain */ + *sortMarkPtr = ZSTD_DUBT_UNSORTED; + } + zc->nextToUpdate = target; +} + + +/** ZSTD_insertDUBT1() : + * sort one already inserted but unsorted position + * assumption : current >= btlow == (current - btmask) + * doesn't fail */ +static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, + U32 current, const BYTE* iend, + U32 nbCompares, U32 btLow, int extDict) { - U32* const hashTable = zc->hashTable; - U32 const hashLog = zc->appliedParams.cParams.hashLog; - size_t const h = ZSTD_hashPtr(ip, hashLog, mls); U32* const bt = zc->chainTable; U32 const btLog = zc->appliedParams.cParams.chainLog - 1; U32 const btMask = (1 << btLog) - 1; - U32 matchIndex = hashTable[h]; size_t commonLengthSmaller=0, commonLengthLarger=0; const BYTE* const base = zc->base; + const BYTE* const ip = base + current; const BYTE* const dictBase = zc->dictBase; const U32 dictLimit = zc->dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* match; - const U32 current = (U32)(ip-base); - const U32 btLow = btMask >= current ? 0 : current - btMask; U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = smallerPtr + 1; + U32 matchIndex = *smallerPtr; U32 dummy32; /* to be nullified at the end */ U32 const windowLow = zc->lowLimit; - U32 matchEndIdx = current+8+1; - size_t bestLength = 8; -#ifdef ZSTD_C_PREDICT - U32 predictedSmall = *(bt + 2*((current-1)&btMask) + 0); - U32 predictedLarge = *(bt + 2*((current-1)&btMask) + 1); - predictedSmall += (predictedSmall>0); - predictedLarge += (predictedLarge>0); -#endif /* ZSTD_C_PREDICT */ - DEBUGLOG(8, "ZSTD_insertBt1 (%u)", current); + DEBUGLOG(8, "ZSTD_insertDUBT1 (%u)", current); + assert(current >= btLow); - assert(ip <= iend-8); /* required for h calculation */ - hashTable[h] = current; /* Update Hash Table */ + if (extDict && (current < dictLimit)) { /* do not sort candidates in _extDict (simplification, for easier ZSTD_count, detrimental to compression ratio in streaming mode) */ + *largerPtr = *smallerPtr = 0; + return; + } + assert(current >= dictLimit); /* ip=base+current within current memory segment */ while (nbCompares-- && (matchIndex > windowLow)) { U32* const nextPtr = bt + 2*(matchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ + DEBUGLOG(8, "ZSTD_insertDUBT1: comparing %u with %u", current, matchIndex); assert(matchIndex < current); -#ifdef ZSTD_C_PREDICT /* note : can create issues when hlog small <= 11 */ - const U32* predictPtr = bt + 2*((matchIndex-1) & btMask); /* written this way, as bt is a roll buffer */ - if (matchIndex == predictedSmall) { - /* no need to check length, result known */ - *smallerPtr = matchIndex; - if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */ - smallerPtr = nextPtr+1; /* new "smaller" => larger of match */ - matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ - predictedSmall = predictPtr[1] + (predictPtr[1]>0); - continue; - } - if (matchIndex == predictedLarge) { - *largerPtr = matchIndex; - if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */ - largerPtr = nextPtr; - matchIndex = nextPtr[0]; - predictedLarge = predictPtr[0] + (predictPtr[0]>0); - continue; - } -#endif - if ((!extDict) || (matchIndex+matchLength >= dictLimit)) { assert(matchIndex+matchLength >= dictLimit); /* might be wrong if extDict is incorrectly set to 0 */ match = base + matchIndex; @@ -93,12 +105,6 @@ static U32 ZSTD_insertBt1(ZSTD_CCtx* zc, match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ } - if (matchLength > bestLength) { - bestLength = matchLength; - if (matchLength > matchEndIdx - matchIndex) - matchEndIdx = matchIndex + (U32)matchLength; - } - if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */ break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt tree */ } @@ -108,6 +114,8 @@ static U32 ZSTD_insertBt1(ZSTD_CCtx* zc, *smallerPtr = matchIndex; /* update smaller idx */ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop searching */ + DEBUGLOG(8, "ZSTD_insertDUBT1: selecting next candidate from %u (>btLow=%u) => %u", + matchIndex, btLow, nextPtr[1]); smallerPtr = nextPtr+1; /* new "candidate" => larger than match, which was smaller than target */ matchIndex = nextPtr[1]; /* new matchIndex, larger than previous and closer to current */ } else { @@ -115,125 +123,142 @@ static U32 ZSTD_insertBt1(ZSTD_CCtx* zc, *largerPtr = matchIndex; commonLengthLarger = matchLength; if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop searching */ + DEBUGLOG(8, "ZSTD_insertDUBT1: selecting next candidate from %u (>btLow=%u) => %u", + matchIndex, btLow, nextPtr[0]); largerPtr = nextPtr; matchIndex = nextPtr[0]; } } *smallerPtr = *largerPtr = 0; - if (bestLength > 384) return MIN(192, (U32)(bestLength - 384)); /* speed optimization */ - assert(matchEndIdx > current + 8); - return matchEndIdx - (current + 8); -} - -FORCE_INLINE_TEMPLATE -void ZSTD_updateTree_internal(ZSTD_CCtx* zc, - const BYTE* const ip, const BYTE* const iend, - const U32 nbCompares, const U32 mls, const U32 extDict) -{ - const BYTE* const base = zc->base; - U32 const target = (U32)(ip - base); - U32 idx = zc->nextToUpdate; - DEBUGLOG(7, "ZSTD_updateTree_internal, from %u to %u (extDict:%u)", - idx, target, extDict); - - while(idx < target) - idx += ZSTD_insertBt1(zc, base+idx, iend, nbCompares, mls, extDict); - zc->nextToUpdate = target; -} - -void ZSTD_updateTree(ZSTD_CCtx* zc, - const BYTE* const ip, const BYTE* const iend, - const U32 nbCompares, const U32 mls) -{ - ZSTD_updateTree_internal(zc, ip, iend, nbCompares, mls, 0 /*extDict*/); -} - -void ZSTD_updateTree_extDict(ZSTD_CCtx* zc, - const BYTE* const ip, const BYTE* const iend, - const U32 nbCompares, const U32 mls) -{ - ZSTD_updateTree_internal(zc, ip, iend, nbCompares, mls, 1 /*extDict*/); } static size_t ZSTD_insertBtAndFindBestMatch ( - ZSTD_CCtx* zc, - const BYTE* const ip, const BYTE* const iend, - size_t* offsetPtr, - U32 nbCompares, const U32 mls, - U32 extDict) + ZSTD_CCtx* zc, + const BYTE* const ip, const BYTE* const iend, + size_t* offsetPtr, + U32 nbCompares, const U32 mls, + U32 extDict) { U32* const hashTable = zc->hashTable; U32 const hashLog = zc->appliedParams.cParams.hashLog; size_t const h = ZSTD_hashPtr(ip, hashLog, mls); + U32 matchIndex = hashTable[h]; + + const BYTE* const base = zc->base; + U32 const current = (U32)(ip-base); + U32* const bt = zc->chainTable; U32 const btLog = zc->appliedParams.cParams.chainLog - 1; U32 const btMask = (1 << btLog) - 1; - U32 matchIndex = hashTable[h]; - size_t commonLengthSmaller=0, commonLengthLarger=0; - const BYTE* const base = zc->base; - const BYTE* const dictBase = zc->dictBase; - const U32 dictLimit = zc->dictLimit; - const BYTE* const dictEnd = dictBase + dictLimit; - const BYTE* const prefixStart = base + dictLimit; - const U32 current = (U32)(ip-base); - const U32 btLow = btMask >= current ? 0 : current - btMask; - const U32 windowLow = zc->lowLimit; - U32* smallerPtr = bt + 2*(current&btMask); - U32* largerPtr = bt + 2*(current&btMask) + 1; - U32 matchEndIdx = current+8+1; - U32 dummy32; /* to be nullified at the end */ - size_t bestLength = 0; + U32 const btLow = (btMask >= current) ? 0 : current - btMask; + U32* nextCandidate = bt + 2*(matchIndex&btMask); + U32* unsortedMark = bt + 2*(matchIndex&btMask) + 1; + U32 nbCandidates = nbCompares; + U32 previousCandidate = 0; + + DEBUGLOG(7, "ZSTD_insertBtAndFindBestMatch (%u) ", current); assert(ip <= iend-8); /* required for h calculation */ - hashTable[h] = current; /* Update Hash Table */ - while (nbCompares-- && (matchIndex > windowLow)) { - U32* const nextPtr = bt + 2*(matchIndex & btMask); - size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ - const BYTE* match; + /* reach end of unsorted candidates list */ + while ( (matchIndex > btLow) + && (*unsortedMark == ZSTD_DUBT_UNSORTED) + && (nbCandidates > 1) ) { + DEBUGLOG(8, "ZSTD_insertBtAndFindBestMatch: candidate %u is unsorted", + matchIndex); + *unsortedMark = previousCandidate; + previousCandidate = matchIndex; + matchIndex = *nextCandidate; + nextCandidate = bt + 2*(matchIndex&btMask); + unsortedMark = bt + 2*(matchIndex&btMask) + 1; + nbCandidates --; + } - if ((!extDict) || (matchIndex+matchLength >= dictLimit)) { - match = base + matchIndex; - matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend); - } else { - match = dictBase + matchIndex; - matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart); - if (matchIndex+matchLength >= dictLimit) - match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ - } + if ( (matchIndex > btLow) + && (*unsortedMark==ZSTD_DUBT_UNSORTED) ) { + DEBUGLOG(8, "ZSTD_insertBtAndFindBestMatch: nullify last unsorted candidate %u", + matchIndex); + *nextCandidate = *unsortedMark = 0; /* nullify last candidate if it's still unsorted (note : detrimental to compression ratio) */ + } - if (matchLength > bestLength) { - if (matchLength > matchEndIdx - matchIndex) - matchEndIdx = matchIndex + (U32)matchLength; - if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(current-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) - bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + current - matchIndex; - if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */ - break; /* drop, to guarantee consistency (miss a little bit of compression) */ + /* batch sort stacked candidates */ + matchIndex = previousCandidate; + while (matchIndex) { /* will end on matchIndex == 0 */ + U32* const nextCandidateIdxPtr = bt + 2*(matchIndex&btMask) + 1; + U32 const nextCandidateIdx = *nextCandidateIdxPtr; + ZSTD_insertDUBT1(zc, matchIndex, iend, + nbCandidates, btLow, extDict); + matchIndex = nextCandidateIdx; + nbCandidates++; + } + + /* find longest match */ + { size_t commonLengthSmaller=0, commonLengthLarger=0; + const BYTE* const dictBase = zc->dictBase; + const U32 dictLimit = zc->dictLimit; + const BYTE* const dictEnd = dictBase + dictLimit; + const BYTE* const prefixStart = base + dictLimit; + const U32 windowLow = zc->lowLimit; + U32* smallerPtr = bt + 2*(current&btMask); + U32* largerPtr = bt + 2*(current&btMask) + 1; + U32 matchEndIdx = current+8+1; + U32 dummy32; /* to be nullified at the end */ + size_t bestLength = 0; + + matchIndex = hashTable[h]; + hashTable[h] = current; /* Update Hash Table */ + + while (nbCompares-- && (matchIndex > windowLow)) { + U32* const nextPtr = bt + 2*(matchIndex & btMask); + size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ + const BYTE* match; + + if ((!extDict) || (matchIndex+matchLength >= dictLimit)) { + match = base + matchIndex; + matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend); + } else { + match = dictBase + matchIndex; + matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart); + if (matchIndex+matchLength >= dictLimit) + match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ } - } - if (match[matchLength] < ip[matchLength]) { - /* match is smaller than current */ - *smallerPtr = matchIndex; /* update smaller idx */ - commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ - if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */ - smallerPtr = nextPtr+1; /* new "smaller" => larger of match */ - matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ - } else { - /* match is larger than current */ - *largerPtr = matchIndex; - commonLengthLarger = matchLength; - if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */ - largerPtr = nextPtr; - matchIndex = nextPtr[0]; - } } + if (matchLength > bestLength) { + if (matchLength > matchEndIdx - matchIndex) + matchEndIdx = matchIndex + (U32)matchLength; + if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(current-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) + bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + current - matchIndex; + if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */ + break; /* drop, to guarantee consistency (miss a little bit of compression) */ + } + } - *smallerPtr = *largerPtr = 0; + if (match[matchLength] < ip[matchLength]) { + /* match is smaller than current */ + *smallerPtr = matchIndex; /* update smaller idx */ + commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ + if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */ + smallerPtr = nextPtr+1; /* new "smaller" => larger of match */ + matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ + } else { + /* match is larger than current */ + *largerPtr = matchIndex; + commonLengthLarger = matchLength; + if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */ + largerPtr = nextPtr; + matchIndex = nextPtr[0]; + } } - assert(matchEndIdx > current+8); - zc->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ - return bestLength; + *smallerPtr = *largerPtr = 0; + + assert(matchEndIdx > current+8); /* ensure nextToUpdate is increased */ + zc->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ + if (bestLength) + DEBUGLOG(7, "ZSTD_insertBtAndFindBestMatch(%u) : found match of length %u", + current, (U32)bestLength); + return bestLength; + } } @@ -245,7 +270,7 @@ static size_t ZSTD_BtFindBestMatch ( const U32 maxNbAttempts, const U32 mls) { if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */ - ZSTD_updateTree(zc, ip, iLimit, maxNbAttempts, mls); + ZSTD_updateDUBT(zc, ip, iLimit, mls); return ZSTD_insertBtAndFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 0); } @@ -275,7 +300,7 @@ static size_t ZSTD_BtFindBestMatch_extDict ( const U32 maxNbAttempts, const U32 mls) { if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */ - ZSTD_updateTree_extDict(zc, ip, iLimit, maxNbAttempts, mls); + ZSTD_updateDUBT(zc, ip, iLimit, mls); return ZSTD_insertBtAndFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 1); } diff --git a/lib/compress/zstd_lazy.h b/lib/compress/zstd_lazy.h index 74e1fd697..8918e5ca4 100644 --- a/lib/compress/zstd_lazy.h +++ b/lib/compress/zstd_lazy.h @@ -18,9 +18,8 @@ extern "C" { #include "mem.h" /* U32 */ #include "zstd.h" /* ZSTD_CCtx, size_t */ -U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls); -void ZSTD_updateTree(ZSTD_CCtx* zc, const BYTE* const ip, const BYTE* const iend, const U32 nbCompares, const U32 mls); -void ZSTD_updateTree_extDict(ZSTD_CCtx* zc, const BYTE* const ip, const BYTE* const iend, const U32 nbCompares, const U32 mls); +U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls); /* used in ZSTD_loadDictionaryContent() */ +void ZSTD_updateDUBT(ZSTD_CCtx* zc, const BYTE* ip, const BYTE* iend, U32 mls); /* used in ZSTD_loadDictionaryContent() */ size_t ZSTD_compressBlock_btlazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize); size_t ZSTD_compressBlock_lazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize); diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 7171ff537..6af5c7111 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -265,6 +265,147 @@ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_CCtx* const cctx, const BYTE* /*-************************************* * Binary Tree search ***************************************/ +/** ZSTD_insertBt1() : add one or multiple positions to tree. + * ip : assumed <= iend-8 . + * @return : nb of positions added */ +static U32 ZSTD_insertBt1(ZSTD_CCtx* zc, + const BYTE* const ip, const BYTE* const iend, + U32 nbCompares, U32 const mls, U32 const extDict) +{ + U32* const hashTable = zc->hashTable; + U32 const hashLog = zc->appliedParams.cParams.hashLog; + size_t const h = ZSTD_hashPtr(ip, hashLog, mls); + U32* const bt = zc->chainTable; + U32 const btLog = zc->appliedParams.cParams.chainLog - 1; + U32 const btMask = (1 << btLog) - 1; + U32 matchIndex = hashTable[h]; + size_t commonLengthSmaller=0, commonLengthLarger=0; + const BYTE* const base = zc->base; + const BYTE* const dictBase = zc->dictBase; + const U32 dictLimit = zc->dictLimit; + const BYTE* const dictEnd = dictBase + dictLimit; + const BYTE* const prefixStart = base + dictLimit; + const BYTE* match; + const U32 current = (U32)(ip-base); + const U32 btLow = btMask >= current ? 0 : current - btMask; + U32* smallerPtr = bt + 2*(current&btMask); + U32* largerPtr = smallerPtr + 1; + U32 dummy32; /* to be nullified at the end */ + U32 const windowLow = zc->lowLimit; + U32 matchEndIdx = current+8+1; + size_t bestLength = 8; +#ifdef ZSTD_C_PREDICT + U32 predictedSmall = *(bt + 2*((current-1)&btMask) + 0); + U32 predictedLarge = *(bt + 2*((current-1)&btMask) + 1); + predictedSmall += (predictedSmall>0); + predictedLarge += (predictedLarge>0); +#endif /* ZSTD_C_PREDICT */ + + DEBUGLOG(8, "ZSTD_insertBt1 (%u)", current); + + assert(ip <= iend-8); /* required for h calculation */ + hashTable[h] = current; /* Update Hash Table */ + + while (nbCompares-- && (matchIndex > windowLow)) { + U32* const nextPtr = bt + 2*(matchIndex & btMask); + size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ + assert(matchIndex < current); + +#ifdef ZSTD_C_PREDICT /* note : can create issues when hlog small <= 11 */ + const U32* predictPtr = bt + 2*((matchIndex-1) & btMask); /* written this way, as bt is a roll buffer */ + if (matchIndex == predictedSmall) { + /* no need to check length, result known */ + *smallerPtr = matchIndex; + if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */ + smallerPtr = nextPtr+1; /* new "smaller" => larger of match */ + matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ + predictedSmall = predictPtr[1] + (predictPtr[1]>0); + continue; + } + if (matchIndex == predictedLarge) { + *largerPtr = matchIndex; + if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */ + largerPtr = nextPtr; + matchIndex = nextPtr[0]; + predictedLarge = predictPtr[0] + (predictPtr[0]>0); + continue; + } +#endif + + if ((!extDict) || (matchIndex+matchLength >= dictLimit)) { + assert(matchIndex+matchLength >= dictLimit); /* might be wrong if extDict is incorrectly set to 0 */ + match = base + matchIndex; + matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend); + } else { + match = dictBase + matchIndex; + matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart); + if (matchIndex+matchLength >= dictLimit) + match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ + } + + if (matchLength > bestLength) { + bestLength = matchLength; + if (matchLength > matchEndIdx - matchIndex) + matchEndIdx = matchIndex + (U32)matchLength; + } + + if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */ + break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt tree */ + } + + if (match[matchLength] < ip[matchLength]) { /* necessarily within buffer */ + /* match is smaller than current */ + *smallerPtr = matchIndex; /* update smaller idx */ + commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ + if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop searching */ + smallerPtr = nextPtr+1; /* new "candidate" => larger than match, which was smaller than target */ + matchIndex = nextPtr[1]; /* new matchIndex, larger than previous and closer to current */ + } else { + /* match is larger than current */ + *largerPtr = matchIndex; + commonLengthLarger = matchLength; + if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop searching */ + largerPtr = nextPtr; + matchIndex = nextPtr[0]; + } } + + *smallerPtr = *largerPtr = 0; + if (bestLength > 384) return MIN(192, (U32)(bestLength - 384)); /* speed optimization */ + assert(matchEndIdx > current + 8); + return matchEndIdx - (current + 8); +} + +FORCE_INLINE_TEMPLATE +void ZSTD_updateTree_internal(ZSTD_CCtx* zc, + const BYTE* const ip, const BYTE* const iend, + const U32 nbCompares, const U32 mls, const U32 extDict) +{ + const BYTE* const base = zc->base; + U32 const target = (U32)(ip - base); + U32 idx = zc->nextToUpdate; + DEBUGLOG(7, "ZSTD_updateTree_internal, from %u to %u (extDict:%u)", + idx, target, extDict); + + while(idx < target) + idx += ZSTD_insertBt1(zc, base+idx, iend, nbCompares, mls, extDict); + zc->nextToUpdate = target; +} + +void ZSTD_updateTree(ZSTD_CCtx* zc, + const BYTE* const ip, const BYTE* const iend, + const U32 nbCompares, const U32 mls) +{ + ZSTD_updateTree_internal(zc, ip, iend, nbCompares, mls, 0 /*extDict*/); +} + +void ZSTD_updateTree_extDict(ZSTD_CCtx* zc, + const BYTE* const ip, const BYTE* const iend, + const U32 nbCompares, const U32 mls) +{ + ZSTD_updateTree_internal(zc, ip, iend, nbCompares, mls, 1 /*extDict*/); +} + + FORCE_INLINE_TEMPLATE U32 ZSTD_insertBtAndGetAllMatches ( ZSTD_CCtx* zc, diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index 82e810c29..b803a0f2e 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -15,8 +15,11 @@ extern "C" { #endif +#include "mem.h" /* U32 */ #include "zstd.h" /* ZSTD_CCtx, size_t */ +void ZSTD_updateTree(ZSTD_CCtx* ctx, const BYTE* ip, const BYTE* iend, U32 nbCompares, U32 mls); /* used in ZSTD_loadDictionaryContent() */ + size_t ZSTD_compressBlock_btopt(ZSTD_CCtx* ctx, const void* src, size_t srcSize); size_t ZSTD_compressBlock_btultra(ZSTD_CCtx* ctx, const void* src, size_t srcSize); From ffc335bccf4ae80311a6fbe0e687d0198cb35a64 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 29 Dec 2017 14:39:49 +0100 Subject: [PATCH 008/255] complete ignore list fuzz tests artifacts --- tests/fuzz/.gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/fuzz/.gitignore b/tests/fuzz/.gitignore index 4ff28de98..9409cf837 100644 --- a/tests/fuzz/.gitignore +++ b/tests/fuzz/.gitignore @@ -2,4 +2,7 @@ corpora block_decompress block_round_trip +simple_decompress simple_round_trip +stream_decompress +stream_round_trip From f36da5b4d9f6e382c7045124c015fdeaec7345ca Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 29 Dec 2017 14:40:33 +0100 Subject: [PATCH 009/255] minor speed optimization : index overflow prevention new code supposed to be easier to auto-vectorize --- lib/compress/zstd_compress.c | 81 ++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index bafc1ce95..91e281660 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -59,18 +59,16 @@ ZSTD_CCtx* ZSTD_createCCtx(void) ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem) { - ZSTD_CCtx* cctx; - - if (!customMem.customAlloc ^ !customMem.customFree) return NULL; - - cctx = (ZSTD_CCtx*) ZSTD_calloc(sizeof(ZSTD_CCtx), customMem); - if (!cctx) return NULL; - cctx->customMem = customMem; - cctx->requestedParams.compressionLevel = ZSTD_CLEVEL_DEFAULT; - cctx->requestedParams.fParams.contentSizeFlag = 1; ZSTD_STATIC_ASSERT(zcss_init==0); ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN==(0ULL - 1)); - return cctx; + if (!customMem.customAlloc ^ !customMem.customFree) return NULL; + { ZSTD_CCtx* const cctx = (ZSTD_CCtx*)ZSTD_calloc(sizeof(ZSTD_CCtx), customMem); + if (!cctx) return NULL; + cctx->customMem = customMem; + cctx->requestedParams.compressionLevel = ZSTD_CLEVEL_DEFAULT; + cctx->requestedParams.fParams.contentSizeFlag = 1; + return cctx; + } } ZSTD_CCtx* ZSTD_initStaticCCtx(void *workspace, size_t workspaceSize) @@ -95,13 +93,10 @@ size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx) { if (cctx==NULL) return 0; /* support free on NULL */ if (cctx->staticSize) return ERROR(memory_allocation); /* not compatible with static CCtx */ - ZSTD_free(cctx->workSpace, cctx->customMem); - cctx->workSpace = NULL; - ZSTD_freeCDict(cctx->cdictLocal); - cctx->cdictLocal = NULL; + ZSTD_free(cctx->workSpace, cctx->customMem); cctx->workSpace = NULL; + ZSTD_freeCDict(cctx->cdictLocal); cctx->cdictLocal = NULL; #ifdef ZSTD_MULTITHREAD - ZSTDMT_freeCCtx(cctx->mtctx); - cctx->mtctx = NULL; + ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = NULL; #endif ZSTD_free(cctx, cctx->customMem); return 0; /* reserved as a potential error code in the future */ @@ -122,10 +117,6 @@ static size_t ZSTD_sizeof_mtctx(const ZSTD_CCtx* cctx) size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx) { if (cctx==NULL) return 0; /* support sizeof on NULL */ - DEBUGLOG(3, "sizeof(*cctx) : %u", (U32)sizeof(*cctx)); - DEBUGLOG(3, "workSpaceSize (including streaming buffers): %u", (U32)cctx->workSpaceSize); - DEBUGLOG(3, "inner cdict : %u", (U32)ZSTD_sizeof_CDict(cctx->cdictLocal)); - DEBUGLOG(3, "inner MTCTX : %u", (U32)ZSTD_sizeof_mtctx(cctx)); return sizeof(*cctx) + cctx->workSpaceSize + ZSTD_sizeof_CDict(cctx->cdictLocal) + ZSTD_sizeof_mtctx(cctx); @@ -1085,15 +1076,31 @@ size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long } +#define ZSTD_ROWSIZE 16 +/*! ZSTD_reduceTable_internal() : + * reduce table indexes by `reducerValue` + * presume table size is a multiple of ZSTD_ROWSIZE. + * Helps auto-vectorization */ +static void ZSTD_reduceTable_internal (U32* const table, int const nbRows, U32 const reducerValue) +{ + int cellNb = 0; + int rowNb; + for (rowNb=0 ; rowNb < nbRows ; rowNb++) { + int column; + for (column=0; columnappliedParams.cParams.hashLog; - ZSTD_reduceTable(zc->hashTable, hSize, reducerValue); } + { U32 const hSize = (U32)1 << zc->appliedParams.cParams.hashLog; + ZSTD_reduceTable(zc->hashTable, hSize, reducerValue); + } - { U32 const chainSize = (zc->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((U32)1 << zc->appliedParams.cParams.chainLog); - ZSTD_reduceTable(zc->chainTable, chainSize, reducerValue); } + if (zc->appliedParams.cParams.strategy != ZSTD_fast) { + U32 const chainSize = (U32)1 << zc->appliedParams.cParams.chainLog; + ZSTD_reduceTable(zc->chainTable, chainSize, reducerValue); + } - { U32 const h3Size = (zc->hashLog3) ? (U32)1 << zc->hashLog3 : 0; - ZSTD_reduceTable(zc->hashTable3, h3Size, reducerValue); } + if (zc->hashLog3) { + U32 const h3Size = (U32)1 << zc->hashLog3; + ZSTD_reduceTable(zc->hashTable3, h3Size, reducerValue); + } - { if (zc->appliedParams.ldmParams.enableLdm) { - U32 const ldmHSize = (U32)1 << zc->appliedParams.ldmParams.hashLog; - ZSTD_ldm_reduceTable(zc->ldmState.hashTable, ldmHSize, reducerValue); - } + if (zc->appliedParams.ldmParams.enableLdm) { + U32 const ldmHSize = (U32)1 << zc->appliedParams.ldmParams.hashLog; + ZSTD_ldm_reduceTable(zc->ldmState.hashTable, ldmHSize, reducerValue); } } From 64482c2c9756cbe4f0003ee92fa0955aead7a6b8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 29 Dec 2017 17:04:37 +0100 Subject: [PATCH 010/255] fixed bug in dubt the chain of unsorted candidates could grow beyond lowLimit. --- lib/compress/zstd_compress.c | 42 ++-- lib/compress/zstd_lazy.c | 22 ++- tests/fuzzer.c | 371 ++++++++++++++++++----------------- 3 files changed, 228 insertions(+), 207 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 91e281660..01d1b5d74 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -752,9 +752,9 @@ static U32 ZSTD_sufficientBuff(size_t bufferSize1, size_t blockSize1, size_t const windowSize2 = MAX(1, (size_t)MIN(((U64)1 << cParams2.windowLog), pledgedSrcSize)); size_t const blockSize2 = MIN(ZSTD_BLOCKSIZE_MAX, windowSize2); size_t const neededBufferSize2 = (buffPol2==ZSTDb_buffered) ? windowSize2 + blockSize2 : 0; - DEBUGLOG(4, "ZSTD_sufficientBuff: windowSize2=%u from wlog=%u", + DEBUGLOG(4, "ZSTD_sufficientBuff: is windowSize2=%u <= wlog1=%u", (U32)windowSize2, cParams2.windowLog); - DEBUGLOG(4, "ZSTD_sufficientBuff: blockSize2 %u <=? blockSize1 %u", + DEBUGLOG(4, "ZSTD_sufficientBuff: is blockSize2=%u <= blockSize1=%u", (U32)blockSize2, (U32)blockSize1); return (blockSize2 <= blockSize1) /* seqStore space depends on blockSize */ & (neededBufferSize2 <= bufferSize1); @@ -777,10 +777,12 @@ static U32 ZSTD_equivalentParams(ZSTD_CCtx_params params1, * reuse CCtx without reset (note : requires no dictionary) */ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pledgedSrcSize) { - U32 const end = (U32)(cctx->nextSrc - cctx->base); + size_t const endT = (size_t)(cctx->nextSrc - cctx->base); + U32 const end = (U32)endT; size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize)); size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); - DEBUGLOG(4, "ZSTD_continueCCtx"); + DEBUGLOG(4, "ZSTD_continueCCtx: re-use context in place"); + assert(endT < (3U<<30)); cctx->blockSize = blockSize; /* previous block size could be different even for same windowLog, due to pledgedSrcSize */ cctx->appliedParams = params; @@ -912,12 +914,12 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->entropy->offcode_repeatMode = FSE_repeat_none; zc->entropy->matchlength_repeatMode = FSE_repeat_none; zc->entropy->litlength_repeatMode = FSE_repeat_none; - zc->nextToUpdate = 1; - zc->nextSrc = NULL; zc->base = NULL; zc->dictBase = NULL; - zc->dictLimit = 0; zc->lowLimit = 0; + zc->dictLimit = 0; + zc->nextToUpdate = 1; + zc->nextSrc = NULL; { int i; for (i=0; iseqStore.rep[i] = repStartValue[i]; } zc->hashLog3 = hashLog3; zc->optState.litLengthSum = 0; @@ -925,7 +927,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ptr = zc->entropy + 1; /* opt parser space */ - if ((params.cParams.strategy == ZSTD_btopt) || (params.cParams.strategy == ZSTD_btultra)) { + if ((params.cParams.strategy == ZSTD_btopt) | (params.cParams.strategy == ZSTD_btultra)) { DEBUGLOG(4, "reserving optimal parser space"); assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ zc->optState.litFreq = (U32*)ptr; @@ -1643,9 +1645,12 @@ static void ZSTD_resetSeqStore(seqStore_t* ssPtr) ssPtr->longLengthID = 0; } -static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize) +static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) { - DEBUGLOG(5, "ZSTD_compressBlock_internal : dstCapacity = %u", (U32)dstCapacity); + DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u) (dictLimit=%u, nextToUpdate=%u)", + (U32)dstCapacity, zc->dictLimit, zc->nextToUpdate); if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) return 0; /* don't even attempt compression below a certain srcSize */ ZSTD_resetSeqStore(&(zc->seqStore)); @@ -1737,13 +1742,17 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, cctx->dictLimit -= correction; if (cctx->nextToUpdate < correction) cctx->nextToUpdate = 0; else cctx->nextToUpdate -= correction; - DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x\n", correction, cctx->lowLimit); + DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x", correction, cctx->lowLimit); } /* enforce maxDist */ if ((U32)(ip+blockSize - cctx->base) > cctx->loadedDictEnd + maxDist) { U32 const newLowLimit = (U32)(ip+blockSize - cctx->base) - maxDist; if (cctx->lowLimit < newLowLimit) cctx->lowLimit = newLowLimit; + if (cctx->dictLimit < cctx->lowLimit) + DEBUGLOG(2, "ZSTD_compress_frameChunk : update dictLimit from %u to %u ", + cctx->dictLimit, cctx->lowLimit); if (cctx->dictLimit < cctx->lowLimit) cctx->dictLimit = cctx->lowLimit; + if (cctx->nextToUpdate < cctx->lowLimit) cctx->nextToUpdate = cctx->lowLimit; } { size_t cSize = ZSTD_compressBlock_internal(cctx, @@ -1846,8 +1855,9 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, /* Check if blocks follow each other */ if (src != cctx->nextSrc) { - /* not contiguous */ size_t const distanceFromBase = (size_t)(cctx->nextSrc - cctx->base); + DEBUGLOG(5, "ZSTD_compressContinue_internal: non contiguous blocks, new segment starts at %u", + cctx->dictLimit); cctx->lowLimit = cctx->dictLimit; assert(distanceFromBase == (size_t)(U32)distanceFromBase); /* should never overflow */ cctx->dictLimit = (U32)distanceFromBase; @@ -1879,6 +1889,7 @@ size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { + DEBUGLOG(5, "ZSTD_compressContinue (srcSize=%u)", (U32)srcSize); return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1 /* frame mode */, 0 /* last chunk */); } @@ -2144,10 +2155,10 @@ size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) { - ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, dictSize); + ZSTD_parameters const params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize); ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params); - DEBUGLOG(4, "ZSTD_compressBegin_usingDict"); + DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (U32)dictSize); return ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dm_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered); } @@ -2275,6 +2286,7 @@ size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, co size_t ZSTD_compressCCtx (ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) { + DEBUGLOG(4, "ZSTD_compressCCtx (srcSize=%u)", (U32)srcSize); return ZSTD_compress_usingDict(ctx, dst, dstCapacity, src, srcSize, NULL, 0, compressionLevel); } @@ -2885,7 +2897,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, if (params.nbThreads > 1) { if (cctx->mtctx == NULL || (params.nbThreads != ZSTDMT_getNbThreads(cctx->mtctx))) { DEBUGLOG(4, "ZSTD_compress_generic: creating new mtctx for nbThreads=%u (previous: %u)", - params.nbThreads, ZSTDMT_getNbThreads(cctx->mtctx)); + params.nbThreads, (unsigned)ZSTDMT_getNbThreads(cctx->mtctx)); ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbThreads, cctx->customMem); if (cctx->mtctx == NULL) return ERROR(memory_allocation); diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 431157741..5249c5e58 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -32,12 +32,12 @@ void ZSTD_updateDUBT(ZSTD_CCtx* zc, U32 const target = (U32)(ip - base); U32 idx = zc->nextToUpdate; - DEBUGLOG(7, "ZSTD_updateDUBT, from %u to %u ", - idx, target); + if (idx != target) + DEBUGLOG(2, "ZSTD_updateDUBT, from %u to %u (dictLimit:%u)", + idx, target, zc->dictLimit); assert(ip + 8 <= iend); /* condition for ZSTD_hashPtr */ (void)iend; - assert(idx >= zc->dictLimit); /* condition for valid base+idx */ for ( ; idx < target ; idx++) { size_t const h = ZSTD_hashPtr(base + idx, hashLog, mls); /* assumption : ip + 8 <= iend */ @@ -79,10 +79,11 @@ static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, U32 dummy32; /* to be nullified at the end */ U32 const windowLow = zc->lowLimit; - DEBUGLOG(8, "ZSTD_insertDUBT1 (%u)", current); + DEBUGLOG(8, "ZSTD_insertDUBT1(%u) (dictLimit=%u, lowLimit=%u)", + current, dictLimit, windowLow); assert(current >= btLow); - if (extDict && (current < dictLimit)) { /* do not sort candidates in _extDict (simplification, for easier ZSTD_count, detrimental to compression ratio in streaming mode) */ + if (extDict && (current < dictLimit)) { /* candidates in _extDict are not sorted (simplification, for easier ZSTD_count, detrimental to compression ratio in streaming mode) */ *largerPtr = *smallerPtr = 0; return; } @@ -147,11 +148,13 @@ static size_t ZSTD_insertBtAndFindBestMatch ( const BYTE* const base = zc->base; U32 const current = (U32)(ip-base); + U32 const windowLow = zc->lowLimit; U32* const bt = zc->chainTable; U32 const btLog = zc->appliedParams.cParams.chainLog - 1; U32 const btMask = (1 << btLog) - 1; U32 const btLow = (btMask >= current) ? 0 : current - btMask; + U32 const unsortLimit = MAX(btLow, windowLow); U32* nextCandidate = bt + 2*(matchIndex&btMask); U32* unsortedMark = bt + 2*(matchIndex&btMask) + 1; @@ -162,7 +165,7 @@ static size_t ZSTD_insertBtAndFindBestMatch ( assert(ip <= iend-8); /* required for h calculation */ /* reach end of unsorted candidates list */ - while ( (matchIndex > btLow) + while ( (matchIndex > unsortLimit) && (*unsortedMark == ZSTD_DUBT_UNSORTED) && (nbCandidates > 1) ) { DEBUGLOG(8, "ZSTD_insertBtAndFindBestMatch: candidate %u is unsorted", @@ -175,11 +178,11 @@ static size_t ZSTD_insertBtAndFindBestMatch ( nbCandidates --; } - if ( (matchIndex > btLow) + if ( (matchIndex > unsortLimit) && (*unsortedMark==ZSTD_DUBT_UNSORTED) ) { DEBUGLOG(8, "ZSTD_insertBtAndFindBestMatch: nullify last unsorted candidate %u", matchIndex); - *nextCandidate = *unsortedMark = 0; /* nullify last candidate if it's still unsorted (note : detrimental to compression ratio) */ + *nextCandidate = *unsortedMark = 0; /* nullify next candidate if it's still unsorted (note : simplification, detrimental to compression ratio, beneficial for speed) */ } /* batch sort stacked candidates */ @@ -188,7 +191,7 @@ static size_t ZSTD_insertBtAndFindBestMatch ( U32* const nextCandidateIdxPtr = bt + 2*(matchIndex&btMask) + 1; U32 const nextCandidateIdx = *nextCandidateIdxPtr; ZSTD_insertDUBT1(zc, matchIndex, iend, - nbCandidates, btLow, extDict); + nbCandidates, unsortLimit, extDict); matchIndex = nextCandidateIdx; nbCandidates++; } @@ -199,7 +202,6 @@ static size_t ZSTD_insertBtAndFindBestMatch ( const U32 dictLimit = zc->dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; - const U32 windowLow = zc->lowLimit; U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = bt + 2*(current&btMask) + 1; U32 matchEndIdx = current+8+1; diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 141bf6548..3e911d7ca 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -286,91 +286,91 @@ static int basicUnitTests(U32 seed, double compressibility) RDG_genBuffer(CNBuffer, CNBuffSize, compressibility, 0., seed); /* Basic tests */ - DISPLAYLEVEL(4, "test%3i : ZSTD_getErrorName : ", testNb++); + DISPLAYLEVEL(3, "test%3i : ZSTD_getErrorName : ", testNb++); { const char* errorString = ZSTD_getErrorName(0); - DISPLAYLEVEL(4, "OK : %s \n", errorString); + DISPLAYLEVEL(3, "OK : %s \n", errorString); } - DISPLAYLEVEL(4, "test%3i : ZSTD_getErrorName with wrong value : ", testNb++); + DISPLAYLEVEL(3, "test%3i : ZSTD_getErrorName with wrong value : ", testNb++); { const char* errorString = ZSTD_getErrorName(499); - DISPLAYLEVEL(4, "OK : %s \n", errorString); + DISPLAYLEVEL(3, "OK : %s \n", errorString); } - DISPLAYLEVEL(4, "test%3i : compress %u bytes : ", testNb++, (U32)CNBuffSize); + DISPLAYLEVEL(3, "test%3i : compress %u bytes : ", testNb++, (U32)CNBuffSize); { ZSTD_CCtx* cctx = ZSTD_createCCtx(); if (cctx==NULL) goto _output_error; CHECKPLUS(r, ZSTD_compressCCtx(cctx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize, 1), cSize=r ); - DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); + DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); - DISPLAYLEVEL(4, "test%3i : size of cctx for level 1 : ", testNb++); + DISPLAYLEVEL(3, "test%3i : size of cctx for level 1 : ", testNb++); { size_t const cctxSize = ZSTD_sizeof_CCtx(cctx); - DISPLAYLEVEL(4, "%u bytes \n", (U32)cctxSize); + DISPLAYLEVEL(3, "%u bytes \n", (U32)cctxSize); } ZSTD_freeCCtx(cctx); } - DISPLAYLEVEL(4, "test%3i : ZSTD_getFrameContentSize test : ", testNb++); + DISPLAYLEVEL(3, "test%3i : ZSTD_getFrameContentSize test : ", testNb++); { unsigned long long const rSize = ZSTD_getFrameContentSize(compressedBuffer, cSize); if (rSize != CNBuffSize) goto _output_error; } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%3i : ZSTD_findDecompressedSize test : ", testNb++); + DISPLAYLEVEL(3, "test%3i : ZSTD_findDecompressedSize test : ", testNb++); { unsigned long long const rSize = ZSTD_findDecompressedSize(compressedBuffer, cSize); if (rSize != CNBuffSize) goto _output_error; } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%3i : decompress %u bytes : ", testNb++, (U32)CNBuffSize); + DISPLAYLEVEL(3, "test%3i : decompress %u bytes : ", testNb++, (U32)CNBuffSize); { size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize); if (r != CNBuffSize) goto _output_error; } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%3i : check decompressed result : ", testNb++); + DISPLAYLEVEL(3, "test%3i : check decompressed result : ", testNb++); { size_t u; for (u=0; u (1U << 20)) goto _output_error; ZSTD_freeCCtx(cctx); } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%di : re-use CCtx with expanding block size : ", testNb++); + DISPLAYLEVEL(3, "test%3d : re-use CCtx with expanding block size : ", testNb++); { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); ZSTD_parameters const params = ZSTD_getParams(1, ZSTD_CONTENTSIZE_UNKNOWN, 0); assert(params.fParams.contentSizeFlag == 1); /* block size will be adapted if pledgedSrcSize is enabled */ @@ -385,11 +385,11 @@ static int basicUnitTests(U32 seed, double compressibility) } ZSTD_freeCCtx(cctx); } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); /* Static CCtx tests */ #define STATIC_CCTX_LEVEL 3 - DISPLAYLEVEL(4, "test%3i : create static CCtx for level %u :", testNb++, STATIC_CCTX_LEVEL); + DISPLAYLEVEL(3, "test%3i : create static CCtx for level %u :", testNb++, STATIC_CCTX_LEVEL); { size_t const staticCCtxSize = ZSTD_estimateCStreamSize(STATIC_CCTX_LEVEL); void* const staticCCtxBuffer = malloc(staticCCtxSize); size_t const staticDCtxSize = ZSTD_estimateDCtxSize(); @@ -404,57 +404,57 @@ static int basicUnitTests(U32 seed, double compressibility) { ZSTD_CCtx* staticCCtx = ZSTD_initStaticCCtx(staticCCtxBuffer, staticCCtxSize); ZSTD_DCtx* staticDCtx = ZSTD_initStaticDCtx(staticDCtxBuffer, staticDCtxSize); if ((staticCCtx==NULL) || (staticDCtx==NULL)) goto _output_error; - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%3i : init CCtx for level %u : ", testNb++, STATIC_CCTX_LEVEL); + DISPLAYLEVEL(3, "test%3i : init CCtx for level %u : ", testNb++, STATIC_CCTX_LEVEL); { size_t const r = ZSTD_compressBegin(staticCCtx, STATIC_CCTX_LEVEL); if (ZSTD_isError(r)) goto _output_error; } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%3i : simple compression test with static CCtx : ", testNb++); + DISPLAYLEVEL(3, "test%3i : simple compression test with static CCtx : ", testNb++); CHECKPLUS(r, ZSTD_compressCCtx(staticCCtx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize, STATIC_CCTX_LEVEL), cSize=r ); - DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", + DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); - DISPLAYLEVEL(4, "test%3i : simple decompression test with static DCtx : ", testNb++); + DISPLAYLEVEL(3, "test%3i : simple decompression test with static DCtx : ", testNb++); { size_t const r = ZSTD_decompressDCtx(staticDCtx, decodedBuffer, CNBuffSize, compressedBuffer, cSize); if (r != CNBuffSize) goto _output_error; } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%3i : check decompressed result : ", testNb++); + DISPLAYLEVEL(3, "test%3i : check decompressed result : ", testNb++); { size_t u; for (u=0; u same size */ } - DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); + DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); - DISPLAYLEVEL(4, "test%3i : frame built with duplicated context should be decompressible : ", testNb++); + DISPLAYLEVEL(3, "test%3i : frame built with duplicated context should be decompressible : ", testNb++); CHECKPLUS(r, ZSTD_decompress_usingDict(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize, CNBuffer, dictSize), if (r != CNBuffSize - dictSize) goto _output_error); - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%3i : decompress with DDict : ", testNb++); + DISPLAYLEVEL(3, "test%3i : decompress with DDict : ", testNb++); { ZSTD_DDict* const ddict = ZSTD_createDDict(CNBuffer, dictSize); size_t const r = ZSTD_decompress_usingDDict(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize, ddict); if (r != CNBuffSize - dictSize) goto _output_error; - DISPLAYLEVEL(4, "OK (size of DDict : %u) \n", (U32)ZSTD_sizeof_DDict(ddict)); + DISPLAYLEVEL(3, "OK (size of DDict : %u) \n", (U32)ZSTD_sizeof_DDict(ddict)); ZSTD_freeDDict(ddict); } - DISPLAYLEVEL(4, "test%3i : decompress with static DDict : ", testNb++); + DISPLAYLEVEL(3, "test%3i : decompress with static DDict : ", testNb++); { size_t const ddictBufferSize = ZSTD_estimateDDictSize(dictSize, ZSTD_dlm_byCopy); void* ddictBuffer = malloc(ddictBufferSize); if (ddictBuffer == NULL) goto _output_error; @@ -633,10 +632,10 @@ static int basicUnitTests(U32 seed, double compressibility) if (r != CNBuffSize - dictSize) goto _output_error; } free(ddictBuffer); - DISPLAYLEVEL(4, "OK (size of static DDict : %u) \n", (U32)ddictBufferSize); + DISPLAYLEVEL(3, "OK (size of static DDict : %u) \n", (U32)ddictBufferSize); } - DISPLAYLEVEL(4, "test%3i : check content size on duplicated context : ", testNb++); + DISPLAYLEVEL(3, "test%3i : check content size on duplicated context : ", testNb++); { size_t const testSize = CNBuffSize / 3; { ZSTD_parameters p = ZSTD_getParams(2, testSize, dictSize); p.fParams.contentSizeFlag = 1; @@ -651,7 +650,7 @@ static int basicUnitTests(U32 seed, double compressibility) if (ZSTD_getFrameHeader(&zfh, compressedBuffer, cSize)) goto _output_error; if ((zfh.frameContentSize != testSize) && (zfh.frameContentSize != 0)) goto _output_error; } } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); ZSTD_freeCCtx(ctxOrig); ZSTD_freeCCtx(ctxDuplicated); @@ -673,79 +672,79 @@ static int basicUnitTests(U32 seed, double compressibility) goto _output_error; } - DISPLAYLEVEL(4, "test%3i : dictBuilder : ", testNb++); + DISPLAYLEVEL(3, "test%3i : dictBuilder : ", testNb++); { U32 u; for (u=0; u %u bytes)\n", (U32)inputSize, (U32)cSize); + DISPLAYLEVEL(3, "OK (compress : %u -> %u bytes)\n", (U32)inputSize, (U32)cSize); - DISPLAYLEVEL(4, "test%3i : decompress normally (should fail) : ", testNb++); + DISPLAYLEVEL(3, "test%3i : decompress normally (should fail) : ", testNb++); { size_t const decodeResult = ZSTD_decompressDCtx(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize); if (ZSTD_getErrorCode(decodeResult) != ZSTD_error_prefix_unknown) goto _output_error; - DISPLAYLEVEL(4, "OK : %s \n", ZSTD_getErrorName(decodeResult)); + DISPLAYLEVEL(3, "OK : %s \n", ZSTD_getErrorName(decodeResult)); } - DISPLAYLEVEL(4, "test%3i : decompress with magic-less instruction : ", testNb++); + DISPLAYLEVEL(3, "test%3i : decompress with magic-less instruction : ", testNb++); ZSTD_DCtx_reset(dctx); CHECK( ZSTD_DCtx_setFormat(dctx, ZSTD_f_zstd1_magicless) ); { ZSTD_inBuffer in = { compressedBuffer, cSize, 0 }; @@ -1019,7 +1018,7 @@ static int basicUnitTests(U32 seed, double compressibility) if (result != 0) goto _output_error; if (in.pos != in.size) goto _output_error; if (out.pos != inputSize) goto _output_error; - DISPLAYLEVEL(4, "OK : regenerated %u bytes \n", (U32)out.pos); + DISPLAYLEVEL(3, "OK : regenerated %u bytes \n", (U32)out.pos); } ZSTD_freeCCtx(cctx); @@ -1032,21 +1031,21 @@ static int basicUnitTests(U32 seed, double compressibility) size_t cSize2; /* basic block compression */ - DISPLAYLEVEL(4, "test%3i : Block compression test : ", testNb++); + DISPLAYLEVEL(3, "test%3i : Block compression test : ", testNb++); CHECK( ZSTD_compressBegin(cctx, 5) ); CHECK( ZSTD_getBlockSize(cctx) >= blockSize); cSize = ZSTD_compressBlock(cctx, compressedBuffer, ZSTD_compressBound(blockSize), CNBuffer, blockSize); if (ZSTD_isError(cSize)) goto _output_error; - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%3i : Block decompression test : ", testNb++); + DISPLAYLEVEL(3, "test%3i : Block decompression test : ", testNb++); CHECK( ZSTD_decompressBegin(dctx) ); { CHECK_V(r, ZSTD_decompressBlock(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize) ); if (r != blockSize) goto _output_error; } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); /* dictionary block compression */ - DISPLAYLEVEL(4, "test%3i : Dictionary Block compression test : ", testNb++); + DISPLAYLEVEL(3, "test%3i : Dictionary Block compression test : ", testNb++); CHECK( ZSTD_compressBegin_usingDict(cctx, CNBuffer, dictSize, 5) ); cSize = ZSTD_compressBlock(cctx, compressedBuffer, ZSTD_compressBound(blockSize), (char*)CNBuffer+dictSize, blockSize); if (ZSTD_isError(cSize)) goto _output_error; @@ -1056,16 +1055,16 @@ static int basicUnitTests(U32 seed, double compressibility) cSize2 = ZSTD_compressBlock(cctx, (char*)compressedBuffer+cSize+blockSize, ZSTD_compressBound(blockSize), (char*)CNBuffer+dictSize+2*blockSize, blockSize); if (ZSTD_isError(cSize2)) goto _output_error; - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%3i : Dictionary Block decompression test : ", testNb++); + DISPLAYLEVEL(3, "test%3i : Dictionary Block decompression test : ", testNb++); CHECK( ZSTD_decompressBegin_usingDict(dctx, CNBuffer, dictSize) ); { CHECK_V( r, ZSTD_decompressBlock(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize) ); if (r != blockSize) goto _output_error; } ZSTD_insertBlock(dctx, (char*)decodedBuffer+blockSize, blockSize); /* insert non-compressed block into dctx history */ { CHECK_V( r, ZSTD_decompressBlock(dctx, (char*)decodedBuffer+2*blockSize, CNBuffSize, (char*)compressedBuffer+cSize+blockSize, cSize2) ); if (r != blockSize) goto _output_error; } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); ZSTD_freeCCtx(cctx); } @@ -1073,7 +1072,7 @@ static int basicUnitTests(U32 seed, double compressibility) /* long rle test */ { size_t sampleSize = 0; - DISPLAYLEVEL(4, "test%3i : Long RLE test : ", testNb++); + DISPLAYLEVEL(3, "test%3i : Long RLE test : ", testNb++); RDG_genBuffer(CNBuffer, sampleSize, compressibility, 0., seed+1); memset((char*)CNBuffer+sampleSize, 'B', 256 KB - 1); sampleSize += 256 KB - 1; @@ -1083,21 +1082,21 @@ static int basicUnitTests(U32 seed, double compressibility) if (ZSTD_isError(cSize)) goto _output_error; { CHECK_V(regenSize, ZSTD_decompress(decodedBuffer, sampleSize, compressedBuffer, cSize)); if (regenSize!=sampleSize) goto _output_error; } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); } /* All zeroes test (test bug #137) */ #define ZEROESLENGTH 100 - DISPLAYLEVEL(4, "test%3i : compress %u zeroes : ", testNb++, ZEROESLENGTH); + DISPLAYLEVEL(3, "test%3i : compress %u zeroes : ", testNb++, ZEROESLENGTH); memset(CNBuffer, 0, ZEROESLENGTH); { CHECK_V(r, ZSTD_compress(compressedBuffer, ZSTD_compressBound(ZEROESLENGTH), CNBuffer, ZEROESLENGTH, 1) ); cSize = r; } - DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/ZEROESLENGTH*100); + DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/ZEROESLENGTH*100); - DISPLAYLEVEL(4, "test%3i : decompress %u zeroes : ", testNb++, ZEROESLENGTH); + DISPLAYLEVEL(3, "test%3i : decompress %u zeroes : ", testNb++, ZEROESLENGTH); { CHECK_V(r, ZSTD_decompress(decodedBuffer, ZEROESLENGTH, compressedBuffer, cSize) ); if (r != ZEROESLENGTH) goto _output_error; } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); /* nbSeq limit test */ #define _3BYTESTESTLENGTH 131000 @@ -1124,18 +1123,18 @@ static int basicUnitTests(U32 seed, double compressibility) ((BYTE*)CNBuffer)[i+1] = _3BytesSeqs[id][1]; ((BYTE*)CNBuffer)[i+2] = _3BytesSeqs[id][2]; } } } - DISPLAYLEVEL(4, "test%3i : compress lots 3-bytes sequences : ", testNb++); + DISPLAYLEVEL(3, "test%3i : compress lots 3-bytes sequences : ", testNb++); { CHECK_V(r, ZSTD_compress(compressedBuffer, ZSTD_compressBound(_3BYTESTESTLENGTH), CNBuffer, _3BYTESTESTLENGTH, 19) ); cSize = r; } - DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/_3BYTESTESTLENGTH*100); + DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/_3BYTESTESTLENGTH*100); - DISPLAYLEVEL(4, "test%3i : decompress lots 3-bytes sequence : ", testNb++); + DISPLAYLEVEL(3, "test%3i : decompress lots 3-bytes sequence : ", testNb++); { CHECK_V(r, ZSTD_decompress(decodedBuffer, _3BYTESTESTLENGTH, compressedBuffer, cSize) ); if (r != _3BYTESTESTLENGTH) goto _output_error; } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%3i : incompressible data and ill suited dictionary : ", testNb++); + DISPLAYLEVEL(3, "test%3i : incompressible data and ill suited dictionary : ", testNb++); RDG_genBuffer(CNBuffer, CNBuffSize, 0.0, 0.1, seed); { /* Train a dictionary on low characters */ size_t dictSize = 16 KB; @@ -1168,25 +1167,25 @@ static int basicUnitTests(U32 seed, double compressibility) free(dictBuffer); free(samplesSizes); } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); /* findFrameCompressedSize on skippable frames */ - DISPLAYLEVEL(4, "test%3i : frame compressed size of skippable frame : ", testNb++); + DISPLAYLEVEL(3, "test%3i : frame compressed size of skippable frame : ", testNb++); { const char* frame = "\x50\x2a\x4d\x18\x05\x0\x0\0abcde"; size_t const frameSrcSize = 13; if (ZSTD_findFrameCompressedSize(frame, frameSrcSize) != frameSrcSize) goto _output_error; } - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); /* error string tests */ - DISPLAYLEVEL(4, "test%3i : testing ZSTD error code strings : ", testNb++); + DISPLAYLEVEL(3, "test%3i : testing ZSTD error code strings : ", testNb++); if (strcmp("No error detected", ZSTD_getErrorName((ZSTD_ErrorCode)(0-ZSTD_error_no_error))) != 0) goto _output_error; if (strcmp("No error detected", ZSTD_getErrorString(ZSTD_error_no_error)) != 0) goto _output_error; if (strcmp("Unspecified error code", ZSTD_getErrorString((ZSTD_ErrorCode)(0-ZSTD_error_GENERIC))) != 0) goto _output_error; if (strcmp("Error (generic)", ZSTD_getErrorName((size_t)0-ZSTD_error_GENERIC)) != 0) goto _output_error; if (strcmp("Error (generic)", ZSTD_getErrorString(ZSTD_error_GENERIC)) != 0) goto _output_error; if (strcmp("No error detected", ZSTD_getErrorName(ZSTD_error_GENERIC)) != 0) goto _output_error; - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); _end: free(CNBuffer); @@ -1369,6 +1368,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD } /* successful decompression test */ + DISPLAYLEVEL(5, "fuzzer t%u: simple decompression test \n", testNb); { size_t const margin = (FUZ_rand(&lseed) & 1) ? 0 : (FUZ_rand(&lseed) & 31) + 1; size_t const dSize = ZSTD_decompress(dstBuffer, sampleSize + margin, cBuffer, cSize); CHECK(dSize != sampleSize, "ZSTD_decompress failed (%s) (srcSize : %u ; cSize : %u)", ZSTD_getErrorName(dSize), (U32)sampleSize, (U32)cSize); @@ -1379,6 +1379,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD free(sampleBuffer); /* no longer useful after this point */ /* truncated src decompression test */ + DISPLAYLEVEL(5, "fuzzer t%u: decompression of truncated source \n", testNb); { size_t const missing = (FUZ_rand(&lseed) % (cSize-2)) + 1; /* no problem, as cSize > 4 (frameHeaderSizer) */ size_t const tooSmallSize = cSize - missing; void* cBufferTooSmall = malloc(tooSmallSize); /* valgrind will catch read overflows */ @@ -1390,6 +1391,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD } /* too small dst decompression test */ + DISPLAYLEVEL(5, "fuzzer t%u: decompress into too small dst buffer \n", testNb); if (sampleSize > 3) { size_t const missing = (FUZ_rand(&lseed) % (sampleSize-2)) + 1; /* no problem, as cSize > 4 (frameHeaderSizer) */ size_t const tooSmallSize = sampleSize - missing; @@ -1425,6 +1427,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD } } } /* decompress noisy source */ + DISPLAYLEVEL(5, "fuzzer t%u: decompress noisy source \n", testNb); { U32 const endMark = 0xA9B1C3D6; memcpy(dstBuffer+sampleSize, &endMark, 4); { size_t const decompressResult = ZSTD_decompress(dstBuffer, sampleSize, cBuffer, cSize); @@ -1436,8 +1439,8 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD CHECK(endMark!=endCheck, "ZSTD_decompress on noisy src : dst buffer overflow"); } } } /* noisy src decompression test */ - /*===== Streaming compression test, scattered segments and dictionary =====*/ - + /*===== Bufferless streaming compression test, scattered segments and dictionary =====*/ + DISPLAYLEVEL(5, "fuzzer t%u: Bufferless streaming compression test \n", testNb); { U32 const testLog = FUZ_rand(&lseed) % maxSrcLog; U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog; int const cLevel = (FUZ_rand(&lseed) % @@ -1450,10 +1453,13 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD dictSize = FUZ_rLogLength(&lseed, dictLog); /* needed also for decompression */ dict = srcBuffer + (FUZ_rand(&lseed) % (srcBufferSize - dictSize)); + DISPLAYLEVEL(6, "fuzzer t%u: Compressing up to <=%u bytes at level %i with dictionary size %u \n", + testNb, (U32)maxTestSize, cLevel, (U32)dictSize); + if (FUZ_rand(&lseed) & 0xF) { CHECK_Z ( ZSTD_compressBegin_usingDict(refCtx, dict, dictSize, cLevel) ); } else { - ZSTD_compressionParameters const cPar = ZSTD_getCParams(cLevel, 0, dictSize); + ZSTD_compressionParameters const cPar = ZSTD_getCParams(cLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize); ZSTD_frameParameters const fPar = { FUZ_rand(&lseed)&1 /* contentSizeFlag */, !(FUZ_rand(&lseed)&3) /* contentChecksumFlag*/, 0 /*NodictID*/ }; /* note : since dictionary is fake, dictIDflag has no impact */ @@ -1491,6 +1497,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD } /* streaming decompression test */ + DISPLAYLEVEL(5, "fuzzer t%u: Bufferless streaming decompression test \n", testNb); /* ensure memory requirement is good enough (should always be true) */ { ZSTD_frameHeader zfh; CHECK( ZSTD_getFrameHeader(&zfh, cBuffer, ZSTD_frameHeaderSize_max), @@ -1633,7 +1640,7 @@ int main(int argc, const char** argv) case 'v': argument++; - g_displayLevel = 4; + g_displayLevel++; break; case 'q': From 4c7f137bd2790815b3536776bbd86556bcdc9066 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 29 Dec 2017 17:40:36 +0100 Subject: [PATCH 011/255] add test case which reliably produces btlazy2 rescale overflow bug The unsorted_mark is handled like any index, which fails after a rescale. --- tests/playTests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index c93c58fe8..fa1efeb59 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -712,6 +712,7 @@ roundTripTest -g18000018 -P94 18 roundTripTest -g18000019 -P96 19 roundTripTest -g5000000000 -P99 1 +roundTripTest -g1700000000 -P0 "1 --zstd=strategy=6" # ensure btlazy2 can survive an overflow rescale fileRoundTripTest -g4193M -P99 1 From 02f64ef955997c450fbd80fbbeb31bf77c55cd56 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 29 Dec 2017 19:08:51 +0100 Subject: [PATCH 012/255] btlazy2: fixed interaction between unsortedMark and reduceTable --- lib/compress/zstd_compress.c | 10 +++++++-- lib/compress/zstd_lazy.c | 39 +++++++++++++++++++++++++++++++----- lib/compress/zstd_lazy.h | 1 + tests/fuzzer.c | 7 ++++--- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 01d1b5d74..4e05e287c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1125,8 +1125,15 @@ static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue) ZSTD_reduceTable(zc->hashTable, hSize, reducerValue); } + if (zc->appliedParams.cParams.strategy != ZSTD_btlazy2) { + U32 const chainSize = (U32)1 << zc->appliedParams.cParams.chainLog; + ZSTD_reduceTable(zc->chainTable, chainSize, reducerValue); + } + if (zc->appliedParams.cParams.strategy != ZSTD_fast) { U32 const chainSize = (U32)1 << zc->appliedParams.cParams.chainLog; + if (zc->appliedParams.cParams.strategy != ZSTD_btlazy2) + ZSTD_preserveUnsortedMark(zc->chainTable, chainSize, reducerValue); ZSTD_reduceTable(zc->chainTable, chainSize, reducerValue); } @@ -1749,7 +1756,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, U32 const newLowLimit = (U32)(ip+blockSize - cctx->base) - maxDist; if (cctx->lowLimit < newLowLimit) cctx->lowLimit = newLowLimit; if (cctx->dictLimit < cctx->lowLimit) - DEBUGLOG(2, "ZSTD_compress_frameChunk : update dictLimit from %u to %u ", + DEBUGLOG(5, "ZSTD_compress_frameChunk : update dictLimit from %u to %u ", cctx->dictLimit, cctx->lowLimit); if (cctx->dictLimit < cctx->lowLimit) cctx->dictLimit = cctx->lowLimit; if (cctx->nextToUpdate < cctx->lowLimit) cctx->nextToUpdate = cctx->lowLimit; @@ -2210,7 +2217,6 @@ static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) return op-ostart; } - size_t ZSTD_compressEnd (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 5249c5e58..1ed63b9de 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -15,7 +15,36 @@ /*-************************************* * Binary Tree search ***************************************/ -#define ZSTD_DUBT_UNSORTED ((U32)(-1)) +#define ZSTD_DUBT_UNSORTED_MARK 1 /* note : index 1 will now be confused with "unsorted" if sorted as larger than its predecessor. + It's not a big deal though : the candidate will just be considered unsorted, and be sorted again. + Additionnally, candidate position 1 will be lost. + But candidate 1 cannot hide a large tree of candidates, so it's a moderate loss. + The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled by a table re-use using a different strategy */ + +#define ZSTD_ROWSIZE 16 +/*! ZSTD_preserveUnsortedMark_internal() : + * Helps auto-vectorization */ +static void ZSTD_preserveUnsortedMark_internal (U32* const table, int const nbRows, U32 const reducerValue) +{ + int cellNb = 0; + int rowNb; + for (rowNb=0 ; rowNb < nbRows ; rowNb++) { + int column; + for (column=0; columnnextToUpdate; if (idx != target) - DEBUGLOG(2, "ZSTD_updateDUBT, from %u to %u (dictLimit:%u)", + DEBUGLOG(7, "ZSTD_updateDUBT, from %u to %u (dictLimit:%u)", idx, target, zc->dictLimit); assert(ip + 8 <= iend); /* condition for ZSTD_hashPtr */ (void)iend; @@ -48,7 +77,7 @@ void ZSTD_updateDUBT(ZSTD_CCtx* zc, hashTable[h] = idx; /* Update Hash Table */ *nextCandidatePtr = matchIndex; /* update BT like a chain */ - *sortMarkPtr = ZSTD_DUBT_UNSORTED; + *sortMarkPtr = ZSTD_DUBT_UNSORTED_MARK; } zc->nextToUpdate = target; } @@ -166,7 +195,7 @@ static size_t ZSTD_insertBtAndFindBestMatch ( /* reach end of unsorted candidates list */ while ( (matchIndex > unsortLimit) - && (*unsortedMark == ZSTD_DUBT_UNSORTED) + && (*unsortedMark == ZSTD_DUBT_UNSORTED_MARK) && (nbCandidates > 1) ) { DEBUGLOG(8, "ZSTD_insertBtAndFindBestMatch: candidate %u is unsorted", matchIndex); @@ -179,7 +208,7 @@ static size_t ZSTD_insertBtAndFindBestMatch ( } if ( (matchIndex > unsortLimit) - && (*unsortedMark==ZSTD_DUBT_UNSORTED) ) { + && (*unsortedMark==ZSTD_DUBT_UNSORTED_MARK) ) { DEBUGLOG(8, "ZSTD_insertBtAndFindBestMatch: nullify last unsorted candidate %u", matchIndex); *nextCandidate = *unsortedMark = 0; /* nullify next candidate if it's still unsorted (note : simplification, detrimental to compression ratio, beneficial for speed) */ diff --git a/lib/compress/zstd_lazy.h b/lib/compress/zstd_lazy.h index 8918e5ca4..5c02f1661 100644 --- a/lib/compress/zstd_lazy.h +++ b/lib/compress/zstd_lazy.h @@ -20,6 +20,7 @@ extern "C" { U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls); /* used in ZSTD_loadDictionaryContent() */ void ZSTD_updateDUBT(ZSTD_CCtx* zc, const BYTE* ip, const BYTE* iend, U32 mls); /* used in ZSTD_loadDictionaryContent() */ +void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const reducerValue); /*! used in ZSTD_reduceIndex(). pre-emptively increase value of ZSTD_DUBT_UNSORTED_MARK */ size_t ZSTD_compressBlock_btlazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize); size_t ZSTD_compressBlock_lazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize); diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 3e911d7ca..c599a15de 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -63,7 +63,7 @@ static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stderr); } } + if (g_displayLevel>=4) fflush(stdout); } } /*-******************************************************* * Fuzzer functions @@ -1337,10 +1337,11 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD crcOrig = XXH64(sampleBuffer, sampleSize, 0); /* compression tests */ - { unsigned const cLevel = + { int const cLevel = ( FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (FUZ_highbit32((U32)sampleSize) / cLevelLimiter)) ) - + 1; + + 1; + DISPLAYLEVEL(5, "fuzzer t%u: Simple compression test (level %i) \n", testNb, cLevel); cSize = ZSTD_compressCCtx(ctx, cBuffer, cBufferSize, sampleBuffer, sampleSize, cLevel); CHECK(ZSTD_isError(cSize), "ZSTD_compressCCtx failed : %s", ZSTD_getErrorName(cSize)); From d228b6b0d0c83cbec268f4917fe3480bccdf362c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 29 Dec 2017 19:14:18 +0100 Subject: [PATCH 013/255] btlazy2 : optimization for dictionary compression we want the dictionary table to be fully sorted, not just lazily filled. Dictionary loading is a bit more intensive, but it saves cpu cycles for match search during compression. --- lib/compress/zstd_compress.c | 6 +----- tests/playTests.sh | 5 +++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 4e05e287c..32d15116b 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1950,11 +1950,7 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_CCtx* zc, const void* src, size_t ZSTD_insertAndFindFirstIndex(zc, iend-HASH_READ_SIZE, zc->appliedParams.cParams.searchLength); break; - case ZSTD_btlazy2: - if (srcSize >= HASH_READ_SIZE) - ZSTD_updateDUBT(zc, iend-HASH_READ_SIZE, iend, zc->appliedParams.cParams.searchLength); - break; - + case ZSTD_btlazy2: /* we want the dictionary table fully sorted */ case ZSTD_btopt: case ZSTD_btultra: if (srcSize >= HASH_READ_SIZE) diff --git a/tests/playTests.sh b/tests/playTests.sh index fa1efeb59..ee93b10a5 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -322,9 +322,14 @@ $ECHO "- Create first dictionary " TESTFILE=../programs/zstdcli.c $ZSTD --train *.c ../programs/*.c -o tmpDict cp $TESTFILE tmp +$ECHO "- Dictionary compression roundtrip" $ZSTD -f tmp -D tmpDict $ZSTD -d tmp.zst -D tmpDict -fo result $DIFF $TESTFILE result +$ECHO "- Dictionary compression with btlazy2 strategy" +$ZSTD -f tmp -D tmpDict --zstd=strategy=6 +$ZSTD -d tmp.zst -D tmpDict -fo result +$DIFF $TESTFILE result if [ -n "$hasMT" ] then $ECHO "- Test dictionary compression with multithreading " From ea41b580eb20848e9383b705be34341d6f950fa8 Mon Sep 17 00:00:00 2001 From: Shawn Landden Date: Sun, 24 Dec 2017 16:01:15 -0800 Subject: [PATCH 014/255] meson: allow -Dlegacy_support=true, fix -Dlegacy_support=0 --- contrib/meson/meson.build | 10 ++++++++++ contrib/meson/meson_options.txt | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/contrib/meson/meson.build b/contrib/meson/meson.build index b045c9944..079c045a1 100644 --- a/contrib/meson/meson.build +++ b/contrib/meson/meson.build @@ -39,9 +39,19 @@ libzstd_srcs = [ libzstd_includes = [include_directories(common_dir, dictbuilder_dir, compress_dir, lib_dir)] legacy = get_option('legacy_support') +if legacy == '0' + legacy = 'false' +endif if legacy != 'false' + if legacy == 'true' + legacy = '1' + endif + #See ZSTD_LEGACY_SUPPORT of programs/README.md message('Enabling legacy support back to version 0.' + legacy) legacy_int = legacy.to_int() + if legacy_int > 7 + legacy_int = 7 + endif libzstd_cflags = ['-DZSTD_LEGACY_SUPPORT=' + legacy] legacy_dir = join_paths(lib_dir, 'legacy') diff --git a/contrib/meson/meson_options.txt b/contrib/meson/meson_options.txt index de9fea073..99845c8aa 100644 --- a/contrib/meson/meson_options.txt +++ b/contrib/meson/meson_options.txt @@ -1,2 +1,3 @@ option('multithread', type: 'boolean', value: false) -option('legacy_support', type: 'string', value: '4') +option('legacy_support', type: 'string', value: '4', + description: 'True or false, or 7 to 1 for v0.7+ to v0.1+.') From eb52e2f45e91bfea0531b2e23ca13c44a0837b83 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Dec 2017 11:13:52 +0100 Subject: [PATCH 015/255] simplify ZSTD_preserveUnsortedMark() implementation since no compiler attempts to auto-vectorize it. --- lib/compress/zstd_lazy.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 1ed63b9de..2688a78d5 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -21,29 +21,16 @@ But candidate 1 cannot hide a large tree of candidates, so it's a moderate loss. The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled by a table re-use using a different strategy */ -#define ZSTD_ROWSIZE 16 -/*! ZSTD_preserveUnsortedMark_internal() : - * Helps auto-vectorization */ -static void ZSTD_preserveUnsortedMark_internal (U32* const table, int const nbRows, U32 const reducerValue) -{ - int cellNb = 0; - int rowNb; - for (rowNb=0 ; rowNb < nbRows ; rowNb++) { - int column; - for (column=0; column Date: Sat, 30 Dec 2017 11:37:18 +0100 Subject: [PATCH 016/255] updated compression level table for btlazy2 now selected for levels 13, 14 and 15. Also : dropped the requirement for monotonic memory budget increase of compression levels,, which was required for ZSTD_estimateCCtxSize() in order to ensure that a memory budget for level L is large enough for any level <= L. This condition is now ensured at run time inside ZSTD_estimateCCtxSize(). --- lib/compress/zstd_compress.c | 58 ++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 32d15116b..96cd0c2a2 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -687,12 +687,23 @@ size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams) return ZSTD_estimateCCtxSize_usingCCtxParams(¶ms); } -size_t ZSTD_estimateCCtxSize(int compressionLevel) +static size_t ZSTD_estimateCCtxSize_internal(int compressionLevel) { ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0); return ZSTD_estimateCCtxSize_usingCParams(cParams); } +size_t ZSTD_estimateCCtxSize(int compressionLevel) +{ + int level; + size_t memBudget = 0; + for (level=1; level<=compressionLevel; level++) { + size_t const newMB = ZSTD_estimateCCtxSize_internal(level); + if (newMB > memBudget) memBudget = newMB; + } + return memBudget; +} + size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params) { if (params->nbThreads > 1) { return ERROR(GENERIC); } @@ -712,11 +723,21 @@ size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams) return ZSTD_estimateCStreamSize_usingCCtxParams(¶ms); } -size_t ZSTD_estimateCStreamSize(int compressionLevel) { +static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel) { ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0); return ZSTD_estimateCStreamSize_usingCParams(cParams); } +size_t ZSTD_estimateCStreamSize(int compressionLevel) { + int level; + size_t memBudget = 0; + for (level=1; level<=compressionLevel; level++) { + size_t const newMB = ZSTD_estimateCStreamSize_internal(level); + if (newMB > memBudget) memBudget = newMB; + } + return memBudget; +} + static U32 ZSTD_equivalentCParams(ZSTD_compressionParameters cParams1, ZSTD_compressionParameters cParams2) { @@ -3001,9 +3022,9 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 21, 19, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */ { 22, 20, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */ { 22, 20, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */ - { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 13 */ - { 22, 21, 22, 6, 5, 16, ZSTD_lazy2 }, /* level 14 */ - { 22, 21, 22, 5, 5, 16, ZSTD_btlazy2 }, /* level 15 */ + { 22, 21, 22, 4, 5, 16, ZSTD_btlazy2 }, /* level 13 */ + { 22, 21, 22, 5, 5, 16, ZSTD_btlazy2 }, /* level 14 */ + { 22, 22, 22, 6, 5, 16, ZSTD_btlazy2 }, /* level 15 */ { 22, 21, 22, 4, 5, 48, ZSTD_btopt }, /* level 16 */ { 23, 22, 22, 4, 4, 48, ZSTD_btopt }, /* level 17 */ { 23, 22, 22, 5, 3, 64, ZSTD_btopt }, /* level 18 */ @@ -3092,24 +3113,6 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV }, }; -#if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=1) -/* This function just controls - * the monotonic memory budget increase of ZSTD_defaultCParameters[0]. - * Run once, on first ZSTD_getCParams() usage, if ZSTD_DEBUG is enabled - */ -MEM_STATIC void ZSTD_check_compressionLevel_monotonicIncrease_memoryBudget(void) -{ - int level; - for (level=1; level=1) - static int g_monotonicTest = 1; - if (g_monotonicTest) { - ZSTD_check_compressionLevel_monotonicIncrease_memoryBudget(); - g_monotonicTest=0; - } -#endif - DEBUGLOG(4, "ZSTD_getCParams: cLevel=%i, srcSize=%u, dictSize=%u => table %u", compressionLevel, (U32)srcSizeHint, (U32)dictSize, tableID); if (compressionLevel <= 0) compressionLevel = ZSTD_CLEVEL_DEFAULT; /* 0 == default; no negative compressionLevel yet */ From a68b76afefec6876f8e8a538155109a5aeac0143 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Dec 2017 11:37:18 +0100 Subject: [PATCH 017/255] updated compression level table for btlazy2 now selected for levels 13, 14 and 15. Also : dropped the requirement for monotonic memory budget increase of compression levels,, which was required for ZSTD_estimateCCtxSize() in order to ensure that a memory budget for level L is large enough for any level <= L. This condition is now ensured at run time inside ZSTD_estimateCCtxSize(). --- lib/compress/zstd_compress.c | 60 ++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 32d15116b..a804efca5 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -687,12 +687,23 @@ size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams) return ZSTD_estimateCCtxSize_usingCCtxParams(¶ms); } -size_t ZSTD_estimateCCtxSize(int compressionLevel) +static size_t ZSTD_estimateCCtxSize_internal(int compressionLevel) { ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0); return ZSTD_estimateCCtxSize_usingCParams(cParams); } +size_t ZSTD_estimateCCtxSize(int compressionLevel) +{ + int level; + size_t memBudget = 0; + for (level=1; level<=compressionLevel; level++) { + size_t const newMB = ZSTD_estimateCCtxSize_internal(level); + if (newMB > memBudget) memBudget = newMB; + } + return memBudget; +} + size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params) { if (params->nbThreads > 1) { return ERROR(GENERIC); } @@ -712,11 +723,21 @@ size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams) return ZSTD_estimateCStreamSize_usingCCtxParams(¶ms); } -size_t ZSTD_estimateCStreamSize(int compressionLevel) { +static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel) { ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0); return ZSTD_estimateCStreamSize_usingCParams(cParams); } +size_t ZSTD_estimateCStreamSize(int compressionLevel) { + int level; + size_t memBudget = 0; + for (level=1; level<=compressionLevel; level++) { + size_t const newMB = ZSTD_estimateCStreamSize_internal(level); + if (newMB > memBudget) memBudget = newMB; + } + return memBudget; +} + static U32 ZSTD_equivalentCParams(ZSTD_compressionParameters cParams1, ZSTD_compressionParameters cParams2) { @@ -3001,9 +3022,9 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 21, 19, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */ { 22, 20, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */ { 22, 20, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */ - { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 13 */ - { 22, 21, 22, 6, 5, 16, ZSTD_lazy2 }, /* level 14 */ - { 22, 21, 22, 5, 5, 16, ZSTD_btlazy2 }, /* level 15 */ + { 22, 21, 22, 4, 5, 16, ZSTD_btlazy2 }, /* level 13 */ + { 22, 21, 22, 5, 5, 16, ZSTD_btlazy2 }, /* level 14 */ + { 22, 22, 22, 6, 5, 16, ZSTD_btlazy2 }, /* level 15 */ { 22, 21, 22, 4, 5, 48, ZSTD_btopt }, /* level 16 */ { 23, 22, 22, 4, 4, 48, ZSTD_btopt }, /* level 17 */ { 23, 22, 22, 5, 3, 64, ZSTD_btopt }, /* level 18 */ @@ -3092,41 +3113,14 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV }, }; -#if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=1) -/* This function just controls - * the monotonic memory budget increase of ZSTD_defaultCParameters[0]. - * Run once, on first ZSTD_getCParams() usage, if ZSTD_DEBUG is enabled - */ -MEM_STATIC void ZSTD_check_compressionLevel_monotonicIncrease_memoryBudget(void) -{ - int level; - for (level=1; level=1) - static int g_monotonicTest = 1; - if (g_monotonicTest) { - ZSTD_check_compressionLevel_monotonicIncrease_memoryBudget(); - g_monotonicTest=0; - } -#endif - DEBUGLOG(4, "ZSTD_getCParams: cLevel=%i, srcSize=%u, dictSize=%u => table %u", compressionLevel, (U32)srcSizeHint, (U32)dictSize, tableID); if (compressionLevel <= 0) compressionLevel = ZSTD_CLEVEL_DEFAULT; /* 0 == default; no negative compressionLevel yet */ From f597f556752ec1d0defe6aa6e9d6470f5f73509c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Dec 2017 15:12:59 +0100 Subject: [PATCH 018/255] improved btlazy2 : list of unsorted candidates can reach extDict It used to stop on reaching extDict, for simplification. As a consequence, there was a small loss of performance each time the round buffer would restart from beginning. It's not a large difference though, just several hundreds of bytes on silesia. This patch fixes it. --- lib/compress/zstd_lazy.c | 43 +++++++++++++++++++------------- lib/decompress/zstd_decompress.c | 5 ++++ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 2688a78d5..9385bf423 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -62,6 +62,7 @@ void ZSTD_updateDUBT(ZSTD_CCtx* zc, U32* const nextCandidatePtr = bt + 2*(idx&btMask); U32* const sortMarkPtr = nextCandidatePtr + 1; + DEBUGLOG(8, "ZSTD_updateDUBT: insert %u", idx); hashTable[h] = idx; /* Update Hash Table */ *nextCandidatePtr = matchIndex; /* update BT like a chain */ *sortMarkPtr = ZSTD_DUBT_UNSORTED_MARK; @@ -75,7 +76,7 @@ void ZSTD_updateDUBT(ZSTD_CCtx* zc, * assumption : current >= btlow == (current - btmask) * doesn't fail */ static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, - U32 current, const BYTE* iend, + U32 current, const BYTE* inputEnd, U32 nbCompares, U32 btLow, int extDict) { U32* const bt = zc->chainTable; @@ -83,9 +84,10 @@ static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, U32 const btMask = (1 << btLog) - 1; size_t commonLengthSmaller=0, commonLengthLarger=0; const BYTE* const base = zc->base; - const BYTE* const ip = base + current; const BYTE* const dictBase = zc->dictBase; const U32 dictLimit = zc->dictLimit; + const BYTE* const ip = (current>=dictLimit) ? base + current : dictBase + current; + const BYTE* const iend = (current>=dictLimit) ? inputEnd : dictBase + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* match; @@ -98,22 +100,20 @@ static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, DEBUGLOG(8, "ZSTD_insertDUBT1(%u) (dictLimit=%u, lowLimit=%u)", current, dictLimit, windowLow); assert(current >= btLow); - - if (extDict && (current < dictLimit)) { /* candidates in _extDict are not sorted (simplification, for easier ZSTD_count, detrimental to compression ratio in streaming mode) */ - *largerPtr = *smallerPtr = 0; - return; - } - assert(current >= dictLimit); /* ip=base+current within current memory segment */ + assert(ip < iend); /* condition for ZSTD_count */ while (nbCompares-- && (matchIndex > windowLow)) { U32* const nextPtr = bt + 2*(matchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ - DEBUGLOG(8, "ZSTD_insertDUBT1: comparing %u with %u", current, matchIndex); assert(matchIndex < current); - if ((!extDict) || (matchIndex+matchLength >= dictLimit)) { - assert(matchIndex+matchLength >= dictLimit); /* might be wrong if extDict is incorrectly set to 0 */ - match = base + matchIndex; + if ( (!extDict) + || (matchIndex+matchLength >= dictLimit) /* both in current segment*/ + || (current < dictLimit) /* both in extDict */) { + const BYTE* const mBase = !extDict || (matchIndex >= dictLimit) ? base : dictBase; + assert( (matchIndex+matchLength >= dictLimit) /* might be wrong if extDict is incorrectly set to 0 */ + || (current < dictLimit) ); + match = mBase + matchIndex; matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend); } else { match = dictBase + matchIndex; @@ -122,6 +122,9 @@ static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ } + DEBUGLOG(8, "ZSTD_insertDUBT1: comparing %u with %u : found %u common bytes ", + current, matchIndex, (U32)matchLength); + if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */ break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt tree */ } @@ -131,7 +134,7 @@ static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, *smallerPtr = matchIndex; /* update smaller idx */ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop searching */ - DEBUGLOG(8, "ZSTD_insertDUBT1: selecting next candidate from %u (>btLow=%u) => %u", + DEBUGLOG(8, "ZSTD_insertDUBT1: %u (>btLow=%u) is smaller : next => %u", matchIndex, btLow, nextPtr[1]); smallerPtr = nextPtr+1; /* new "candidate" => larger than match, which was smaller than target */ matchIndex = nextPtr[1]; /* new matchIndex, larger than previous and closer to current */ @@ -140,7 +143,7 @@ static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, *largerPtr = matchIndex; commonLengthLarger = matchLength; if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop searching */ - DEBUGLOG(8, "ZSTD_insertDUBT1: selecting next candidate from %u (>btLow=%u) => %u", + DEBUGLOG(8, "ZSTD_insertDUBT1: %u (>btLow=%u) is larger => %u", matchIndex, btLow, nextPtr[0]); largerPtr = nextPtr; matchIndex = nextPtr[0]; @@ -196,7 +199,7 @@ static size_t ZSTD_insertBtAndFindBestMatch ( if ( (matchIndex > unsortLimit) && (*unsortedMark==ZSTD_DUBT_UNSORTED_MARK) ) { - DEBUGLOG(8, "ZSTD_insertBtAndFindBestMatch: nullify last unsorted candidate %u", + DEBUGLOG(7, "ZSTD_insertBtAndFindBestMatch: nullify last unsorted candidate %u", matchIndex); *nextCandidate = *unsortedMark = 0; /* nullify next candidate if it's still unsorted (note : simplification, detrimental to compression ratio, beneficial for speed) */ } @@ -272,9 +275,11 @@ static size_t ZSTD_insertBtAndFindBestMatch ( assert(matchEndIdx > current+8); /* ensure nextToUpdate is increased */ zc->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ - if (bestLength) - DEBUGLOG(7, "ZSTD_insertBtAndFindBestMatch(%u) : found match of length %u", - current, (U32)bestLength); + if (bestLength >= MINMATCH) { + U32 const mIndex = current - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex; + DEBUGLOG(8, "ZSTD_insertBtAndFindBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)", + current, (U32)bestLength, (U32)*offsetPtr, mIndex); + } return bestLength; } } @@ -287,6 +292,7 @@ static size_t ZSTD_BtFindBestMatch ( size_t* offsetPtr, const U32 maxNbAttempts, const U32 mls) { + DEBUGLOG(7, "ZSTD_BtFindBestMatch"); if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */ ZSTD_updateDUBT(zc, ip, iLimit, mls); return ZSTD_insertBtAndFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 0); @@ -317,6 +323,7 @@ static size_t ZSTD_BtFindBestMatch_extDict ( size_t* offsetPtr, const U32 maxNbAttempts, const U32 mls) { + DEBUGLOG(7, "ZSTD_BtFindBestMatch_extDict"); if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */ ZSTD_updateDUBT(zc, ip, iLimit, mls); return ZSTD_insertBtAndFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 1); diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index a59d94411..ab6c2edc5 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -204,6 +204,7 @@ ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem) ZSTD_DCtx* ZSTD_createDCtx(void) { + DEBUGLOG(3, "ZSTD_createDCtx"); return ZSTD_createDCtx_advanced(ZSTD_defaultCMem); } @@ -2247,6 +2248,7 @@ size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx, ZSTD_DStream* ZSTD_createDStream(void) { + DEBUGLOG(3, "ZSTD_createDStream"); return ZSTD_createDStream_advanced(ZSTD_defaultCMem); } @@ -2273,6 +2275,7 @@ size_t ZSTD_DStreamOutSize(void) { return ZSTD_BLOCKSIZE_MAX; } size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize) { + DEBUGLOG(4, "ZSTD_initDStream_usingDict"); zds->streamStage = zdss_loadHeader; zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; ZSTD_freeDDict(zds->ddictLocal); @@ -2289,6 +2292,7 @@ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t di /* note : this variant can't fail */ size_t ZSTD_initDStream(ZSTD_DStream* zds) { + DEBUGLOG(4, "ZSTD_initDStream"); return ZSTD_initDStream_usingDict(zds, NULL, 0); } @@ -2304,6 +2308,7 @@ size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict) size_t ZSTD_resetDStream(ZSTD_DStream* zds) { + DEBUGLOG(4, "ZSTD_resetDStream"); zds->streamStage = zdss_loadHeader; zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; zds->legacyVersion = 0; From 00db4dbbb345ffd4e85d0360439333ee7107277b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Dec 2017 15:42:28 +0100 Subject: [PATCH 019/255] fixed minor argument property for Visual --- lib/compress/zstd_opt.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 6af5c7111..04824ccf2 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -10,7 +10,6 @@ #include "zstd_compress_internal.h" #include "zstd_opt.h" -#include "zstd_lazy.h" /* ZSTD_updateTree, ZSTD_updateTree_extDict */ #define ZSTD_LITFREQ_ADD 2 /* scaling factor for litFreq, so that frequencies adapt faster to new stats. Also used for matchSum (?) */ @@ -392,13 +391,13 @@ void ZSTD_updateTree_internal(ZSTD_CCtx* zc, } void ZSTD_updateTree(ZSTD_CCtx* zc, - const BYTE* const ip, const BYTE* const iend, - const U32 nbCompares, const U32 mls) + const BYTE* ip, const BYTE* iend, + U32 nbCompares, U32 mls) { ZSTD_updateTree_internal(zc, ip, iend, nbCompares, mls, 0 /*extDict*/); } -void ZSTD_updateTree_extDict(ZSTD_CCtx* zc, +static void ZSTD_updateTree_extDict(ZSTD_CCtx* zc, const BYTE* const ip, const BYTE* const iend, const U32 nbCompares, const U32 mls) { From 2eff2171366239a129c35aae4cd610661770e812 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 31 Dec 2017 15:50:00 +0100 Subject: [PATCH 020/255] updated /lib documentation --- lib/README.md | 57 +++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/lib/README.md b/lib/README.md index df136c486..8a13b4e3d 100644 --- a/lib/README.md +++ b/lib/README.md @@ -2,16 +2,19 @@ Zstandard library files ================================ The __lib__ directory is split into several sub-directories, -in order to make it easier to select or exclude specific features. +in order to make it easier to select or exclude features. #### Building -`Makefile` script is provided, supporting the standard set of commands, -directories, and variables (see https://www.gnu.org/prep/standards/html_node/Command-Variables.html). +`Makefile` script is provided, supporting all standard [Makefile conventions](https://www.gnu.org/prep/standards/html_node/Makefile-Conventions.html#Makefile-Conventions), +including commands variables, staged install, directory variables and standard targets. - `make` : generates both static and dynamic libraries - `make install` : install libraries in default system directories +`libzstd` default scope includes compression, decompression, dictionary building, +and decoding support for legacy formats >= v0.4.0. + #### API @@ -27,29 +30,37 @@ Optional advanced features are exposed via : - `ZSTD_STATIC_LINKING_ONLY` : if this macro is defined _before_ including `zstd.h`, it unlocks access to advanced experimental API, exposed in second part of `zstd.h`. - These APIs shall ___never be used with dynamic library___ ! - They are not "stable", their definition may change in the future. + These APIs are not "stable", their definition may change in the future. + As a consequencem, it shall ___never be used with dynamic library___ ! Only static linking is allowed. #### Modular build +It's possible to compile only a limited set of features. + - Directory `lib/common` is always required, for all variants. - Compression source code lies in `lib/compress` - Decompression source code lies in `lib/decompress` - It's possible to include only `compress` or only `decompress`, they don't depend on each other. - `lib/dictBuilder` : makes it possible to generate dictionaries from a set of samples. - The API is exposed in `lib/dictBuilder/zdict.h`. - This module depends on both `lib/common` and `lib/compress` . -- `lib/legacy` : source code to decompress older zstd formats, starting from `v0.1`. - This module depends on `lib/common` and `lib/decompress`. - To enable this feature, it's necessary to define `ZSTD_LEGACY_SUPPORT = 1` during compilation. - Typically, with `gcc`, add argument `-DZSTD_LEGACY_SUPPORT=1`. - Using higher number limits the number of version supported. - For example, `ZSTD_LEGACY_SUPPORT=2` means : "support legacy formats starting from v0.2+". - The API is exposed in `lib/legacy/zstd_legacy.h`. - Each version also provides a (dedicated) set of advanced API. - For example, advanced API for version `v0.4` is exposed in `lib/legacy/zstd_v04.h` . + The API is exposed in `lib/dictBuilder/zdict.h`. + This module depends on both `lib/common` and `lib/compress` . +- `lib/legacy` : source code to decompress legacy zstd formats, starting from `v0.1.0`. + This module depends on `lib/common` and `lib/decompress`. + To enable this feature, it's required to define `ZSTD_LEGACY_SUPPORT` during compilation. + Typically, with `gcc`, add argument `-DZSTD_LEGACY_SUPPORT=1`. + Using higher number limits versions supported. + For example, `ZSTD_LEGACY_SUPPORT=2` means : "support legacy formats >= v0.2.0". + `ZSTD_LEGACY_SUPPORT=3` means : "support legacy formats >= v0.3.0", and so on. + Starting v0.8.0, all versions of `zstd` produce frames compliant with specification. + As a consequence, `ZSTD_LEGACY_SUPPORT=8` (or more) doesn't trigger legacy support. + Also, `ZSTD_LEGACY_SUPPORT=0` means "do __not__ support legacy formats". + Once enabled, this capability is transparently triggered within decompression functions. + It's also possible to invoke directly legacy API, as exposed in `lib/legacy/zstd_legacy.h`. + Each version also provides an additional dedicated set of advanced API. + For example, advanced API for version `v0.4` is exposed in `lib/legacy/zstd_v04.h` . + Note : `lib/legacy` only supports _decoding_ legacy formats. #### Multithreading support @@ -57,19 +68,16 @@ Optional advanced features are exposed via : Multithreading is disabled by default when building with `make`. Enabling multithreading requires 2 conditions : - set macro `ZSTD_MULTITHREAD` -- on POSIX systems : compile with pthread (`-pthread` compilation flag for `gcc` for example) +- on POSIX systems : compile with pthread (`-pthread` compilation flag for `gcc`) Both conditions are automatically triggered by invoking `make lib-mt` target. Note that, when linking a POSIX program with a multithreaded version of `libzstd`, it's necessary to trigger `-pthread` flag during link stage. -Multithreading capabilities are exposed via : -- private API `lib/compress/zstdmt_compress.h`. - Symbols defined in this header are currently exposed in `libzstd`, hence usable. - Note however that this API is planned to be locked and remain strictly internal in the future. -- advanced API `ZSTD_compress_generic()`, defined in `lib/zstd.h`, experimental section. - This API is still considered experimental, but is designed to be labelled "stable" at some point in the future. - It's the recommended entry point for multi-threading operations. +Multithreading capabilities are exposed +via [advanced API `ZSTD_compress_generic()` defined in `lib/zstd.h`](https://github.com/facebook/zstd/blob/dev/lib/zstd.h#L919). +This API is still considered experimental, +but is expected to become "stable" at some point in the future. #### Windows : using MinGW+MSYS to create DLL @@ -92,7 +100,6 @@ The compiled executable will require ZSTD DLL which is available at `dll\libzstd Obsolete API on their way out are stored in directory `lib/deprecated`. At this stage, it contains older streaming prototypes, in `lib/deprecated/zbuff.h`. -Presence in this directory is temporary. These prototypes will be removed in some future version. Consider migrating code towards supported streaming API exposed in `zstd.h`. From e0596715dcf57cd431394d923d63dfe5aaf49749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Tue, 2 Jan 2018 15:17:32 +0000 Subject: [PATCH 021/255] zstd: fix crash when not overwriting existing files This fixes the following crash: $ touch exists $ programs/zstd -r examples/ -o exists zstd: exists already exists; not overwritten Segmentation fault (core dumped) * programs/fileio.c (FIO_compressMultipleFilenames): Handle the case where we're not overwriting the destination. Reported at https://bugzilla.redhat.com/1530049 --- programs/fileio.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index b57ce14d2..f9c9087ba 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -951,10 +951,14 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile if (outFileName != NULL) { unsigned u; ress.dstFile = FIO_openDstFile(outFileName); - for (u=0; u Date: Wed, 3 Jan 2018 14:02:44 -0800 Subject: [PATCH 022/255] Add test case for PR #972 --- tests/playTests.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index c93c58fe8..b5ffadc23 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -190,6 +190,13 @@ $ZSTD -t tmp1.zst tmp2.zst $ZSTD -dc tmp1.zst tmp2.zst $ZSTD tmp1.zst tmp2.zst -o "$INTOVOID" $ZSTD -d tmp1.zst tmp2.zst -o tmp +touch tmpexists +$ZSTD tmp1 tmp2 -f -o tmpexists +$ZSTD tmp1 tmp2 -o tmpexists && die "should have refused to overwrite" +# Bug: PR #972 +if [ "$?" -eq 139 ]; then + die "should not have segfaulted" +fi rm tmp* From fd63140e1c79369e6d1e90bf1849569e1477f4ec Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 5 Jan 2018 11:44:25 -0800 Subject: [PATCH 023/255] [util] Refuse to set file stat on non-regular file --- programs/util.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/programs/util.h b/programs/util.h index af1fa7fca..4f22236a9 100644 --- a/programs/util.h +++ b/programs/util.h @@ -246,11 +246,17 @@ UTIL_STATIC void UTIL_waitForNextTick(void) #endif +UTIL_STATIC int UTIL_isRegularFile(const char* infilename); + + UTIL_STATIC int UTIL_setFileStat(const char *filename, stat_t *statbuf) { int res = 0; struct utimbuf timebuf; + if (!UTIL_isRegularFile(filename)) + return -1; + timebuf.actime = time(NULL); timebuf.modtime = statbuf->st_mtime; res += utime(filename, &timebuf); /* set access and modification times */ From 282ad05e0a4214e429682062716ccd9ad5bafc23 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 5 Jan 2018 11:44:45 -0800 Subject: [PATCH 024/255] [fileio] Use FIO_remove() everywhere for safety --- programs/fileio.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index f9c9087ba..71b87d4d5 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -139,7 +139,10 @@ static void INThandler(int sig) #if !defined(_MSC_VER) signal(sig, SIG_IGN); /* this invocation generates a buggy warning in Visual Studio */ #endif - if (g_artefact) remove(g_artefact); + if (g_artefact) { + assert(UTIL_isRegularFile(g_artefact)); + remove(g_artefact); + } DISPLAY("\n"); exit(2); } @@ -861,7 +864,7 @@ static int FIO_compressFilename_srcFile(cRess_t ress, * delete both the source and destination files. */ clearHandler(); - if (remove(srcFileName)) + if (FIO_remove(srcFileName)) EXM_THROW(1, "zstd: %s: %s", srcFileName, strerror(errno)); } return result; @@ -899,10 +902,12 @@ static int FIO_compressFilename_dstFile(cRess_t ress, result=1; } if (result!=0) { /* remove operation artefact */ - if (remove(dstFileName)) + if (FIO_remove(dstFileName)) EXM_THROW(1, "zstd: %s: %s", dstFileName, strerror(errno)); } - else if (strcmp (dstFileName, stdoutmark) && stat_result) + else if ( strcmp(dstFileName, stdoutmark) + && strcmp(dstFileName, nulmark) + && stat_result) UTIL_setFileStat(dstFileName, &statbuf); return result; @@ -1575,7 +1580,7 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* dstFileName, const ch * delete both the source and destination files. */ clearHandler(); - if (remove(srcFileName)) { + if (FIO_remove(srcFileName)) { /* failed to remove src file */ DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno)); return 1; @@ -1618,7 +1623,7 @@ static int FIO_decompressDstFile(dRess_t ress, if ( (result != 0) /* operation failure */ && strcmp(dstFileName, nulmark) /* special case : don't remove() /dev/null (#316) */ && strcmp(dstFileName, stdoutmark) ) /* special case : don't remove() stdout */ - remove(dstFileName); /* remove decompression artefact; note don't do anything special if remove() fails */ + FIO_remove(dstFileName); /* remove decompression artefact; note don't do anything special if remove() fails */ else { /* operation success */ if ( strcmp(dstFileName, stdoutmark) /* special case : don't chmod stdout */ && strcmp(dstFileName, nulmark) /* special case : don't chmod /dev/null */ From ed9611dc62bb1376012d229b0e6f7f974f5266db Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 5 Jan 2018 11:49:55 -0800 Subject: [PATCH 025/255] [fileio] Don't call FIO_remove() on stdout or /dev/null --- programs/fileio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 71b87d4d5..887cbeb37 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -901,10 +901,10 @@ static int FIO_compressFilename_dstFile(cRess_t ress, DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno)); result=1; } - if (result!=0) { /* remove operation artefact */ - if (FIO_remove(dstFileName)) - EXM_THROW(1, "zstd: %s: %s", dstFileName, strerror(errno)); - } + if ( (result != 0) /* operation failure */ + && strcmp(dstFileName, nulmark) /* special case : don't remove() /dev/null */ + && strcmp(dstFileName, stdoutmark) ) /* special case : don't remove() stdout */ + FIO_remove(dstFileName); /* remove compression artefact; note don't do anything special if remove() fails */ else if ( strcmp(dstFileName, stdoutmark) && strcmp(dstFileName, nulmark) && stat_result) From 3d578eef0c899200e407b737726bd181e6ecd5af Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 6 Jan 2018 07:13:09 +0100 Subject: [PATCH 026/255] minor tests/Makefile change, as suggested by @terrelln https://github.com/facebook/zstd/pull/965#discussion_r160008006 --- tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index 258212db1..48ae17538 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -115,7 +115,7 @@ zstdc_%.o : $(ZSTDDIR)/compress/%.c zstdd_%.o : $(ZSTDDIR)/decompress/%.c $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ -zstdmt%.o : CPPFLAGS += -DZSTD_MULTITHREAD=1 +zstdmt%.o : CPPFLAGS += $(MULTITHREAD_CPP) zstdmt_m_%.o : $(ZSTDDIR)/common/%.c $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ From a927fae2a190fda3b72845e3f795664c4c4ab16a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 6 Jan 2018 12:31:26 +0100 Subject: [PATCH 027/255] fixed ZSTD_reduceIndex() following suggestions from @terrelln. Also added some comments to present logic behind ZSTD_preserveUnsortedMark(). --- lib/compress/zstd_compress.c | 7 +------ lib/compress/zstd_lazy.c | 18 +++++++++++++++--- tests/playTests.sh | 1 + 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index a804efca5..108229dea 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1146,14 +1146,9 @@ static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue) ZSTD_reduceTable(zc->hashTable, hSize, reducerValue); } - if (zc->appliedParams.cParams.strategy != ZSTD_btlazy2) { - U32 const chainSize = (U32)1 << zc->appliedParams.cParams.chainLog; - ZSTD_reduceTable(zc->chainTable, chainSize, reducerValue); - } - if (zc->appliedParams.cParams.strategy != ZSTD_fast) { U32 const chainSize = (U32)1 << zc->appliedParams.cParams.chainLog; - if (zc->appliedParams.cParams.strategy != ZSTD_btlazy2) + if (zc->appliedParams.cParams.strategy == ZSTD_btlazy2) ZSTD_preserveUnsortedMark(zc->chainTable, chainSize, reducerValue); ZSTD_reduceTable(zc->chainTable, chainSize, reducerValue); } diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 9385bf423..7261cffcf 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -22,9 +22,21 @@ The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled by a table re-use using a different strategy */ /*! ZSTD_preserveUnsortedMark() : - * pre-emptively increase value of ZSTD_DUBT_UNSORTED_MARK - * before ZSTD_reduceTable() - * sp that final operation preserves its value */ + * pre-emptively increase value of ZSTD_DUBT_UNSORTED_MARK before ZSTD_reduceTable() + * so that combined operation preserves its value. + * Without it, ZSTD_DUBT_UNSORTED_MARK==1 would be squashed to 0. + * As a consequence, the list of unsorted elements would stop on the first element, + * removing candidates, resulting in a negligible loss to compression ratio + * (since overflow protection with ZSTD_reduceTable() is relatively rare). + * Another potential risk is that a position will be promoted from *unsorted* + * to *sorted=>smaller:0*, meaning the next candidate will be considered smaller. + * This could be wrong, and result in data corruption. + * On second thought, this corruption might be impossible, + * because unsorted elements are always at the beginning of the list, + * and squashing to zero reduce the list to a single element, + * which needs to be sorted anyway. + * I haven't spent much thoughts into this possible scenario, + * and just felt it was safer to implement ZSTD_preserveUnsortedMark() */ void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const reducerValue) { U32 u; diff --git a/tests/playTests.sh b/tests/playTests.sh index ee93b10a5..bb68e6529 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -93,6 +93,7 @@ else hasMT="true" fi + $ECHO "\n===> simple tests " ./datagen > tmp From b17fb488b0fd57e7b3c028a54ffd87c1f6878045 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 6 Jan 2018 20:50:36 +0100 Subject: [PATCH 028/255] fixed msan test a pointer calculation was wrong in a corner case --- lib/compress/zstd_lazy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 7261cffcf..4af843d1b 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -122,7 +122,7 @@ static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, if ( (!extDict) || (matchIndex+matchLength >= dictLimit) /* both in current segment*/ || (current < dictLimit) /* both in extDict */) { - const BYTE* const mBase = !extDict || (matchIndex >= dictLimit) ? base : dictBase; + const BYTE* const mBase = !extDict || ((matchIndex+matchLength) >= dictLimit) ? base : dictBase; assert( (matchIndex+matchLength >= dictLimit) /* might be wrong if extDict is incorrectly set to 0 */ || (current < dictLimit) ); match = mBase + matchIndex; From 3ea156368c4b49c0c445c307acba44e98c21c1a6 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 10 Jan 2018 08:47:19 -0800 Subject: [PATCH 029/255] API doc : grouped ZSTD_initStatic*() together within "memory management" category. --- lib/compress/zstd_compress.c | 1 - lib/zstd.h | 173 ++++++++++++++--------------------- 2 files changed, 69 insertions(+), 105 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 8d1629246..ef7d3a343 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2416,7 +2416,6 @@ ZSTD_CDict* ZSTD_initStaticCDict(void* workspace, size_t workspaceSize, + cctxSize; ZSTD_CDict* const cdict = (ZSTD_CDict*) workspace; void* ptr; - DEBUGLOG(4, "(size_t)workspace & 7 : %u", (U32)(size_t)workspace & 7); if ((size_t)workspace & 7) return NULL; /* 8-aligned */ DEBUGLOG(4, "(workspaceSize < neededSize) : (%u < %u) => %u", (U32)workspaceSize, (U32)neededSize, (U32)(workspaceSize < neededSize)); diff --git a/lib/zstd.h b/lib/zstd.h index 9ac0a73dc..9971d4762 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -45,11 +45,11 @@ extern "C" { Levels >= 20, labeled `--ultra`, should be used with caution, as they require more memory. Compression can be done in: - a single step (described as Simple API) - - a single step, reusing a context (described as Explicit memory management) + - a single step, reusing a context (described as Explicit context) - unbounded multiple steps (described as Streaming compression) The compression ratio achievable on small data can be highly improved using a dictionary in: - a single step (described as Simple dictionary API) - - a single step, reusing a dictionary (described as Fast dictionary API) + - a single step, reusing a dictionary (described as Bulk-processing dictionary API) Advanced experimental functions can be accessed using #define ZSTD_STATIC_LINKING_ONLY before including zstd.h. Advanced experimental APIs shall never be used with a dynamic library. @@ -68,7 +68,7 @@ ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< useful to check dll versio #define ZSTD_QUOTE(str) #str #define ZSTD_EXPAND_AND_QUOTE(str) ZSTD_QUOTE(str) #define ZSTD_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LIB_VERSION) -ZSTDLIB_API const char* ZSTD_versionString(void); /* v1.3.0 */ +ZSTDLIB_API const char* ZSTD_versionString(void); /* added in v1.3.0 */ /*************************************** @@ -92,7 +92,7 @@ ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity, ZSTDLIB_API size_t ZSTD_decompress( void* dst, size_t dstCapacity, const void* src, size_t compressedSize); -/*! ZSTD_getFrameContentSize() : v1.3.0 +/*! ZSTD_getFrameContentSize() : added in v1.3.0 * `src` should point to the start of a ZSTD encoded frame. * `srcSize` must be at least as large as the frame header. * hint : any size >= `ZSTD_frameHeaderSize_max` is large enough. @@ -120,26 +120,24 @@ ZSTDLIB_API unsigned long long ZSTD_getFrameContentSize(const void *src, size_t /*! ZSTD_getDecompressedSize() : * NOTE: This function is now obsolete, in favor of ZSTD_getFrameContentSize(). - * Both functions work the same way, - * but ZSTD_getDecompressedSize() blends - * "empty", "unknown" and "error" results in the same return value (0), - * while ZSTD_getFrameContentSize() distinguishes them. - * - * 'src' is the start of a zstd compressed frame. - * @return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise. */ + * Both functions work the same way, but ZSTD_getDecompressedSize() blends + * "empty", "unknown" and "error" results to the same return value (0), + * while ZSTD_getFrameContentSize() gives them separate return values. + * `src` is the start of a zstd compressed frame. + * @return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise. */ ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); /*====== Helper functions ======*/ #define ZSTD_COMPRESSBOUND(srcSize) ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */ -ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case scenario */ +ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */ ZSTDLIB_API unsigned ZSTD_isError(size_t code); /*!< tells if a `size_t` function result is an error code */ ZSTDLIB_API const char* ZSTD_getErrorName(size_t code); /*!< provides readable string from an error code */ ZSTDLIB_API int ZSTD_maxCLevel(void); /*!< maximum compression level available */ /*************************************** -* Explicit memory management +* Explicit context ***************************************/ /*= Compression context * When compressing many times, @@ -345,7 +343,7 @@ ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output * *******************************************************************************/ typedef ZSTD_DCtx ZSTD_DStream; /**< DCtx and DStream are now effectively same object (>= v1.3.0) */ - /* Continue to distinguish them for compatibility with versions <= v1.2.0 */ + /* For compatibility with versions <= v1.2.0, continue to consider them separated. */ /*===== ZSTD_DStream management functions =====*/ ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream(void); ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds); @@ -375,7 +373,7 @@ ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output /* --- Constants ---*/ #define ZSTD_MAGICNUMBER 0xFD2FB528 /* >= v0.8.0 */ #define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50U -#define ZSTD_MAGIC_DICTIONARY 0xEC30A437 /* v0.7+ */ +#define ZSTD_MAGIC_DICTIONARY 0xEC30A437 /* >= v0.7.0 */ #define ZSTD_WINDOWLOG_MAX_32 30 #define ZSTD_WINDOWLOG_MAX_64 31 @@ -432,6 +430,18 @@ typedef struct { typedef struct ZSTD_CCtx_params_s ZSTD_CCtx_params; +typedef enum { + ZSTD_dm_auto=0, /* dictionary is "full" if it starts with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */ + ZSTD_dm_rawContent, /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */ + ZSTD_dm_fullDict /* refuses to load a dictionary if it does not respect Zstandard's specification */ +} ZSTD_dictMode_e; + +typedef enum { + ZSTD_dlm_byCopy = 0, /**< Copy dictionary content internally */ + ZSTD_dlm_byRef, /**< Reference dictionary content -- the dictionary buffer must outlive its users. */ +} ZSTD_dictLoadMethod_e; + + /*--- Custom memory allocation functions ---*/ typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size); typedef void (*ZSTD_freeFunction) (void* opaque, void* address); @@ -483,12 +493,12 @@ ZSTDLIB_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize); /*************************************** -* Context memory usage +* Memory management ***************************************/ /*! ZSTD_sizeof_*() : * These functions give the current memory usage of selected object. - * Object memory usage can evolve when re-used multiple times. */ + * Object memory usage can evolve when re-used. */ ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx); ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx); ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs); @@ -504,7 +514,7 @@ ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict); * If srcSize is known to always be small, ZSTD_estimateCCtxSize_usingCParams() can provide a tighter estimation. * ZSTD_estimateCCtxSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel. * ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is > 1. - * Note : CCtx estimation is only correct for single-threaded compression */ + * Note : CCtx size estimation is only correct for single-threaded compression. */ ZSTDLIB_API size_t ZSTD_estimateCCtxSize(int compressionLevel); ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams); ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params); @@ -516,7 +526,7 @@ ZSTDLIB_API size_t ZSTD_estimateDCtxSize(void); * If srcSize is known to always be small, ZSTD_estimateCStreamSize_usingCParams() can provide a tighter estimation. * ZSTD_estimateCStreamSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel. * ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is set to a value > 1. - * Note : CStream estimation is only correct for single-threaded compression. + * Note : CStream size estimation is only correct for single-threaded compression. * ZSTD_DStream memory budget depends on window Size. * This information can be passed manually, using ZSTD_estimateDStreamSize, * or deducted from a valid frame Header, using ZSTD_estimateDStreamSize_fromFrame(); @@ -529,20 +539,53 @@ ZSTDLIB_API size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_para ZSTDLIB_API size_t ZSTD_estimateDStreamSize(size_t windowSize); ZSTDLIB_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize); -typedef enum { - ZSTD_dlm_byCopy = 0, /**< Copy dictionary content internally */ - ZSTD_dlm_byRef, /**< Reference dictionary content -- the dictionary buffer must outlive its users. */ -} ZSTD_dictLoadMethod_e; - /*! ZSTD_estimate?DictSize() : * ZSTD_estimateCDictSize() will bet that src size is relatively "small", and content is copied, like ZSTD_createCDict(). - * ZSTD_estimateCStreamSize_advanced_usingCParams() makes it possible to control precisely compression parameters, like ZSTD_createCDict_advanced(). - * Note : dictionary created by reference using ZSTD_dlm_byRef are smaller + * ZSTD_estimateCDictSize_advanced() makes it possible to control compression parameters precisely, like ZSTD_createCDict_advanced(). + * Note : dictionaries created by reference (`ZSTD_dlm_byRef`) are logically smaller. */ ZSTDLIB_API size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel); ZSTDLIB_API size_t ZSTD_estimateCDictSize_advanced(size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod); ZSTDLIB_API size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod); +/*! ZSTD_initStatic*() : + * Initialize an object using a pre-allocated fixed-size buffer. + * workspace: The memory area to emplace the object into. + * Provided pointer *must be 8-bytes aligned*. + * Buffer must outlive object. + * workspaceSize: Use ZSTD_estimate*Size() to determine + * how large workspace must be to support target scenario. + * @return : pointer to object (same address as workspace, just different type), + * or NULL if error (size too small, incorrect alignment, etc.) + * Note : zstd will never resize nor malloc() when using a static buffer. + * If the object requires more memory than available, + * zstd will just error out (typically ZSTD_error_memory_allocation). + * Note 2 : there is no corresponding "free" function. + * Since workspace is allocated externally, it must be freed externally too. + * Note 3 : cParams : use ZSTD_getCParams() to convert a compression level + * into its associated cParams. + * Limitation 1 : currently not compatible with internal dictionary creation, triggered by + * ZSTD_CCtx_loadDictionary(), ZSTD_initCStream_usingDict() or ZSTD_initDStream_usingDict(). + * Limitation 2 : static cctx currently not compatible with multi-threading. + * Limitation 3 : static dctx is incompatible with legacy support. + */ +ZSTDLIB_API ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize); +ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticCCtx() */ + +ZSTDLIB_API ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize); +ZSTDLIB_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticDCtx() */ + +ZSTDLIB_API ZSTD_CDict* ZSTD_initStaticCDict(void* workspace, size_t workspaceSize, + const void* dict, size_t dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictMode_e dictMode, + ZSTD_compressionParameters cParams); + +ZSTDLIB_API ZSTD_DDict* ZSTD_initStaticDDict(void* workspace, size_t workspaceSize, + const void* dict, size_t dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod); + + /*************************************** * Advanced compression functions @@ -551,35 +594,12 @@ ZSTDLIB_API size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e * Create a ZSTD compression context using external alloc and free functions */ ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem); -/*! ZSTD_initStaticCCtx() : initialize a fixed-size zstd compression context - * workspace: The memory area to emplace the context into. - * Provided pointer must 8-bytes aligned. - * It must outlive context usage. - * workspaceSize: Use ZSTD_estimateCCtxSize() or ZSTD_estimateCStreamSize() - * to determine how large workspace must be to support scenario. - * @return : pointer to ZSTD_CCtx* (same address as workspace, but different type), - * or NULL if error (typically size too small) - * Note : zstd will never resize nor malloc() when using a static cctx. - * If it needs more memory than available, it will simply error out. - * Note 2 : there is no corresponding "free" function. - * Since workspace was allocated externally, it must be freed externally too. - * Limitation 1 : currently not compatible with internal CDict creation, such as - * ZSTD_CCtx_loadDictionary() or ZSTD_initCStream_usingDict(). - * Limitation 2 : currently not compatible with multi-threading - */ -ZSTDLIB_API ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize); - - /*! ZSTD_createCDict_byReference() : * Create a digested dictionary for compression * Dictionary content is simply referenced, and therefore stays in dictBuffer. * It is important that dictBuffer outlives CDict, it must remain read accessible throughout the lifetime of CDict */ ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel); -typedef enum { ZSTD_dm_auto=0, /* dictionary is "full" if it starts with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */ - ZSTD_dm_rawContent, /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */ - ZSTD_dm_fullDict /* refuses to load a dictionary if it does not respect Zstandard's specification */ -} ZSTD_dictMode_e; /*! ZSTD_createCDict_advanced() : * Create a ZSTD_CDict using external alloc and free, and customized compression parameters */ ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, @@ -588,26 +608,6 @@ ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictS ZSTD_compressionParameters cParams, ZSTD_customMem customMem); -/*! ZSTD_initStaticCDict() : - * Generate a digested dictionary in provided memory area. - * workspace: The memory area to emplace the dictionary into. - * Provided pointer must 8-bytes aligned. - * It must outlive dictionary usage. - * workspaceSize: Use ZSTD_estimateCDictSize() - * to determine how large workspace must be. - * cParams : use ZSTD_getCParams() to transform a compression level - * into its relevants cParams. - * @return : pointer to ZSTD_CDict* (same address as workspace, but different type), - * or NULL if error (typically, size too small). - * Note : there is no corresponding "free" function. - * Since workspace was allocated externally, it must be freed externally. - */ -ZSTDLIB_API ZSTD_CDict* ZSTD_initStaticCDict( - void* workspace, size_t workspaceSize, - const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode, - ZSTD_compressionParameters cParams); - /*! ZSTD_getCParams() : * @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize. * `estimatedSrcSize` value is optional, select 0 if not known */ @@ -656,24 +656,6 @@ ZSTDLIB_API unsigned ZSTD_isFrame(const void* buffer, size_t size); * Create a ZSTD decompression context using external alloc and free functions */ ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem); -/*! ZSTD_initStaticDCtx() : initialize a fixed-size zstd decompression context - * workspace: The memory area to emplace the context into. - * Provided pointer must 8-bytes aligned. - * It must outlive context usage. - * workspaceSize: Use ZSTD_estimateDCtxSize() or ZSTD_estimateDStreamSize() - * to determine how large workspace must be to support scenario. - * @return : pointer to ZSTD_DCtx* (same address as workspace, but different type), - * or NULL if error (typically size too small) - * Note : zstd will never resize nor malloc() when using a static dctx. - * If it needs more memory than available, it will simply error out. - * Note 2 : static dctx is incompatible with legacy support - * Note 3 : there is no corresponding "free" function. - * Since workspace was allocated externally, it must be freed externally. - * Limitation : currently not compatible with internal DDict creation, - * such as ZSTD_initDStream_usingDict(). - */ -ZSTDLIB_API ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize); - /*! ZSTD_createDDict_byReference() : * Create a digested dictionary, ready to start decompression operation without startup delay. * Dictionary content is referenced, and therefore stays in dictBuffer. @@ -687,21 +669,6 @@ ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictS ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_customMem customMem); -/*! ZSTD_initStaticDDict() : - * Generate a digested dictionary in provided memory area. - * workspace: The memory area to emplace the dictionary into. - * Provided pointer must 8-bytes aligned. - * It must outlive dictionary usage. - * workspaceSize: Use ZSTD_estimateDDictSize() - * to determine how large workspace must be. - * @return : pointer to ZSTD_DDict*, or NULL if error (size too small) - * Note : there is no corresponding "free" function. - * Since workspace was allocated externally, it must be freed externally. - */ -ZSTDLIB_API ZSTD_DDict* ZSTD_initStaticDDict(void* workspace, size_t workspaceSize, - const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod); - /*! ZSTD_getDictID_fromDict() : * Provides the dictID stored within dictionary. * if @return == 0, the dictionary is not conformant with Zstandard specification. @@ -733,7 +700,6 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); /*===== Advanced Streaming compression functions =====*/ ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem); -ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticCCtx() */ ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */ ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/ ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, @@ -755,7 +721,6 @@ ZSTDLIB_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledg /*===== Advanced Streaming decompression functions =====*/ ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem); -ZSTDLIB_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticDCtx() */ typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e; ZSTDLIB_API size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue); /* obsolete : this API will be removed in a future version */ ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: no dictionary will be used if dict == NULL or dictSize < 8 */ From 292eeb672f8c4bec47191b9e1929c5e06d107a10 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 10 Jan 2018 09:07:47 -0800 Subject: [PATCH 030/255] api doc : grouped all ZSTD_create*_advanced() functions together in a new "custom memory allocator" paragraph which is itself part of "memory management" category. This makes it simpler to see the relation between the type and its usages. --- lib/zstd.h | 61 +++++++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/lib/zstd.h b/lib/zstd.h index 9971d4762..c19496d02 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -442,13 +442,6 @@ typedef enum { } ZSTD_dictLoadMethod_e; -/*--- Custom memory allocation functions ---*/ -typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size); -typedef void (*ZSTD_freeFunction) (void* opaque, void* address); -typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem; -/* use this constant to defer to stdlib's functions */ -static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; - /*************************************** * Frame size functions @@ -585,14 +578,36 @@ ZSTDLIB_API ZSTD_DDict* ZSTD_initStaticDDict(void* workspace, size_t workspaceSi const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod); +/*! Custom memory allocation : + * These prototypes make it possible to pass your own allocation/free functions. + * ZSTD_customMem is provided at creation time, using ZSTD_create*_advanced() variants listed below. + * All allocation/free operations will be completed using these custom variants instead of regular ones. + */ +typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size); +typedef void (*ZSTD_freeFunction) (void* opaque, void* address); +typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem; +static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< this constant defers to stdlib's functions */ + +ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem); +ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem); +ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem); +ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem); + +ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictMode_e dictMode, + ZSTD_compressionParameters cParams, + ZSTD_customMem customMem); + +ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_customMem customMem); + /*************************************** * Advanced compression functions ***************************************/ -/*! ZSTD_createCCtx_advanced() : - * Create a ZSTD compression context using external alloc and free functions */ -ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem); /*! ZSTD_createCDict_byReference() : * Create a digested dictionary for compression @@ -600,14 +615,6 @@ ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem); * It is important that dictBuffer outlives CDict, it must remain read accessible throughout the lifetime of CDict */ ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel); -/*! ZSTD_createCDict_advanced() : - * Create a ZSTD_CDict using external alloc and free, and customized compression parameters */ -ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictMode, - ZSTD_compressionParameters cParams, - ZSTD_customMem customMem); - /*! ZSTD_getCParams() : * @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize. * `estimatedSrcSize` value is optional, select 0 if not known */ @@ -652,10 +659,6 @@ ZSTDLIB_API size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx, * Note 3 : Skippable Frame Identifiers are considered valid. */ ZSTDLIB_API unsigned ZSTD_isFrame(const void* buffer, size_t size); -/*! ZSTD_createDCtx_advanced() : - * Create a ZSTD decompression context using external alloc and free functions */ -ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem); - /*! ZSTD_createDDict_byReference() : * Create a digested dictionary, ready to start decompression operation without startup delay. * Dictionary content is referenced, and therefore stays in dictBuffer. @@ -663,11 +666,6 @@ ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem); * it must remain read accessible throughout the lifetime of DDict */ ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize); -/*! ZSTD_createDDict_advanced() : - * Create a ZSTD_DDict using external alloc and free, optionally by reference */ -ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_customMem customMem); /*! ZSTD_getDictID_fromDict() : * Provides the dictID stored within dictionary. @@ -699,7 +697,6 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); ********************************************************************/ /*===== Advanced Streaming compression functions =====*/ -ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem); ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */ ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/ ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, @@ -720,7 +717,6 @@ ZSTDLIB_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledg /*===== Advanced Streaming decompression functions =====*/ -ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem); typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e; ZSTDLIB_API size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue); /* obsolete : this API will be removed in a future version */ ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: no dictionary will be used if dict == NULL or dictSize < 8 */ @@ -891,8 +887,7 @@ ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); * * This API is intended to replace all others experimental API. * It can basically do all other use cases, and even new ones. - * In constrast with _advanced() variants, it stands a reasonable chance to become "stable", - * after a good testing period. + * It stands a reasonable chance to become "stable", after a good testing period. */ /* note on naming convention : @@ -1137,7 +1132,7 @@ ZSTDLIB_API size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, * All parameters are back to default values. * It's possible to modify compression parameters after a reset. */ -ZSTDLIB_API void ZSTD_CCtx_reset(ZSTD_CCtx* cctx); /* Not ready yet ! */ +ZSTDLIB_API void ZSTD_CCtx_reset(ZSTD_CCtx* cctx); /*! ZSTD_compress_generic_simpleArgs() : @@ -1354,7 +1349,7 @@ ZSTDLIB_API void ZSTD_DCtx_reset(ZSTD_DCtx* dctx); ZSTDLIB_API size_t ZSTD_getBlockSize (const ZSTD_CCtx* cctx); ZSTDLIB_API size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); ZSTDLIB_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -ZSTDLIB_API size_t ZSTD_insertBlock(ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression */ +ZSTDLIB_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */ #endif /* ZSTD_H_ZSTD_STATIC_LINKING_ONLY */ From 0e88f6e97b62bbb66f5ac917c7d560dfb7ca4dab Mon Sep 17 00:00:00 2001 From: conor42 Date: Thu, 11 Jan 2018 11:42:58 +1000 Subject: [PATCH 031/255] Fix break condition in decompression noise test The bug prevents noise being added --- tests/fuzzer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 141bf6548..0f82ee61e 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -1412,7 +1412,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD size_t const skipLength = FUZ_rand(&lseed) & mask; pos += skipLength; } - if (pos <= cSize) break; + if (pos >= cSize) break; /* add noise */ { U32 const nbBitsCodes = FUZ_rand(&lseed) % maxNbBits; U32 const nbBits = nbBitsCodes ? nbBitsCodes-1 : 0; From ff795580f2e9acb02d1a68b9e2f1d4a17a4e3343 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 10 Jan 2018 20:33:45 -0800 Subject: [PATCH 032/255] fixed bug #976, reported by @indygreg constants in zstd.h should not depend on MIN() macro which existence is not guaranteed. Added a test to check the specific constants. The test is a bit too specific. But I have found no way to control a more generic "are all macro already defined" condition, especially as this is a valid construction (the missing macro might be defined later, intentionnally). --- lib/zstd.h | 4 ++-- tests/fuzzer.c | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/zstd.h b/lib/zstd.h index c19496d02..a84490d99 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -379,9 +379,9 @@ ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output #define ZSTD_WINDOWLOG_MAX_64 31 #define ZSTD_WINDOWLOG_MAX ((unsigned)(sizeof(size_t) == 4 ? ZSTD_WINDOWLOG_MAX_32 : ZSTD_WINDOWLOG_MAX_64)) #define ZSTD_WINDOWLOG_MIN 10 -#define ZSTD_HASHLOG_MAX MIN(ZSTD_WINDOWLOG_MAX, 30) +#define ZSTD_HASHLOG_MAX ((ZSTD_WINDOWLOG_MAX < 30) ? ZSTD_WINDOWLOG_MAX : 30) #define ZSTD_HASHLOG_MIN 6 -#define ZSTD_CHAINLOG_MAX MIN(ZSTD_WINDOWLOG_MAX+1, 30) +#define ZSTD_CHAINLOG_MAX ((ZSTD_WINDOWLOG_MAX < 29) ? ZSTD_WINDOWLOG_MAX+1 : 30) #define ZSTD_CHAINLOG_MIN ZSTD_HASHLOG_MIN #define ZSTD_HASHLOG3_MAX 17 #define ZSTD_SEARCHLOG_MAX (ZSTD_WINDOWLOG_MAX-1) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 141bf6548..13e856be0 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -65,11 +65,18 @@ static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ if (g_displayLevel>=4) fflush(stderr); } } -/*-******************************************************* -* Fuzzer functions -*********************************************************/ + #undef MIN #undef MAX +void FUZ_bug976() +{ /* these constants shall not depend on MIN() macro */ + assert(ZSTD_HASHLOG_MAX < 31); + assert(ZSTD_CHAINLOG_MAX < 31); +} + +/*-******************************************************* +* Internal functions +*********************************************************/ #define MIN(a,b) ((a)<(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b)) From 2103a62b3d4b7c092021d7c21c0d2db7ad760841 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 11 Jan 2018 04:49:19 -0800 Subject: [PATCH 033/255] fixed minor warning on prototype definition --- tests/fuzzer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 13e856be0..e0817e903 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -68,7 +68,7 @@ static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; #undef MIN #undef MAX -void FUZ_bug976() +void FUZ_bug976(void) { /* these constants shall not depend on MIN() macro */ assert(ZSTD_HASHLOG_MAX < 31); assert(ZSTD_CHAINLOG_MAX < 31); From 218e9fe0fc25cc8bfd362d0086e23d070fbf9f5c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 11 Jan 2018 09:42:38 -0800 Subject: [PATCH 034/255] added a test case for dictBuilder failure cyclic data set makes the entropy stage fails now, onto a fix for #304 ... --- lib/compress/zstd_compress.c | 2 +- lib/dictBuilder/cover.c | 21 ++++---- lib/dictBuilder/zdict.c | 97 ++++++++++++++++++++---------------- lib/dictBuilder/zdict.h | 85 +++++++++++++++---------------- tests/fuzzer.c | 9 ++++ 5 files changed, 120 insertions(+), 94 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index ef7d3a343..b9e0ec44d 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2869,7 +2869,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, if (params.nbThreads > 1) { if (cctx->mtctx == NULL || (params.nbThreads != ZSTDMT_getNbThreads(cctx->mtctx))) { DEBUGLOG(4, "ZSTD_compress_generic: creating new mtctx for nbThreads=%u (previous: %u)", - params.nbThreads, ZSTDMT_getNbThreads(cctx->mtctx)); + params.nbThreads, (U32)ZSTDMT_getNbThreads(cctx->mtctx)); ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbThreads, cctx->customMem); if (cctx->mtctx == NULL) return ERROR(memory_allocation); diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c index efdffddbf..b68370671 100644 --- a/lib/dictBuilder/cover.c +++ b/lib/dictBuilder/cover.c @@ -537,8 +537,8 @@ static int COVER_ctx_init(COVER_ctx_t *ctx, const void *samplesBuffer, /* Checks */ if (totalSamplesSize < MAX(d, sizeof(U64)) || totalSamplesSize >= (size_t)COVER_MAX_SAMPLES_SIZE) { - DISPLAYLEVEL(1, "Total samples size is too large, maximum size is %u MB\n", - (COVER_MAX_SAMPLES_SIZE >> 20)); + DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n", + (U32)(totalSamplesSize>>20), (COVER_MAX_SAMPLES_SIZE >> 20)); return 0; } /* Zero the context */ @@ -651,12 +651,16 @@ static size_t COVER_buildDictionary(const COVER_ctx_t *ctx, U32 *freqs, } ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( - void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, - const size_t *samplesSizes, unsigned nbSamples, - ZDICT_cover_params_t parameters) { - BYTE *const dict = (BYTE *)dictBuffer; + void *dictBuffer, size_t dictBufferCapacity, + const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, + ZDICT_cover_params_t parameters) +{ + BYTE* const dict = (BYTE*)dictBuffer; COVER_ctx_t ctx; COVER_map_t activeDmers; + + /* Initialize global data */ + g_displayLevel = parameters.zParams.notificationLevel; /* Checks */ if (!COVER_checkParameters(parameters, dictBufferCapacity)) { DISPLAYLEVEL(1, "Cover parameters incorrect\n"); @@ -671,8 +675,6 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( ZDICT_DICTSIZE_MIN); return ERROR(dstSize_tooSmall); } - /* Initialize global data */ - g_displayLevel = parameters.zParams.notificationLevel; /* Initialize context and activeDmers */ if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, parameters.d)) { @@ -947,6 +949,7 @@ ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover( unsigned k; COVER_best_t best; POOL_ctx *pool = NULL; + /* Checks */ if (kMinK < kMaxD || kMaxK < kMinK) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n"); @@ -1004,7 +1007,7 @@ ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover( data->parameters.k = k; data->parameters.d = d; data->parameters.steps = kSteps; - data->parameters.zParams.notificationLevel = g_displayLevel; + data->parameters.zParams.notificationLevel = displayLevel; /* Check the parameters */ if (!COVER_checkParameters(data->parameters, dictBufferCapacity)) { DISPLAYLEVEL(1, "Cover parameters incorrect\n"); diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index e8f454045..3a1649419 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -207,7 +207,6 @@ static dictItem ZDICT_analyzePos( U32 cumulLength[LLIMIT] = {0}; U32 savings[LLIMIT] = {0}; const BYTE* b = (const BYTE*)buffer; - size_t length; size_t maxLength = LLIMIT; size_t pos = suffix[start]; U32 end = start; @@ -222,26 +221,30 @@ static dictItem ZDICT_analyzePos( ||(MEM_read16(b+pos+1) == MEM_read16(b+pos+3)) ||(MEM_read16(b+pos+2) == MEM_read16(b+pos+4)) ) { /* skip and mark segment */ - U16 u16 = MEM_read16(b+pos+4); - U32 u, e = 6; - while (MEM_read16(b+pos+e) == u16) e+=2 ; - if (b[pos+e] == b[pos+e-1]) e++; - for (u=1; u=MINMATCHLENGTH); + { size_t length; + do { + end++; + length = ZDICT_count(b + pos, b + suffix[end]); + } while (length >= MINMATCHLENGTH); + } /* look backward */ - do { - length = ZDICT_count(b + pos, b + *(suffix+start-1)); - if (length >=MINMATCHLENGTH) start--; - } while(length >= MINMATCHLENGTH); + { size_t length; + do { + length = ZDICT_count(b + pos, b + *(suffix+start-1)); + if (length >=MINMATCHLENGTH) start--; + } while(length >= MINMATCHLENGTH); + } /* exit if not found a minimum nb of repetitions */ if (end-start < minRatio) { @@ -268,7 +271,7 @@ static dictItem ZDICT_analyzePos( U32 selectedCount = 0; U32 selectedID = currentID; for (id =refinedStart; id < refinedEnd; id++) { - if (b[ suffix[id] + searchLength] != currentChar) { + if (b[suffix[id] + searchLength] != currentChar) { if (currentCount > selectedCount) { selectedCount = currentCount; selectedID = currentID; @@ -297,20 +300,23 @@ static dictItem ZDICT_analyzePos( memset(lengthList, 0, sizeof(lengthList)); /* look forward */ - do { - end++; - length = ZDICT_count(b + pos, b + suffix[end]); - if (length >= LLIMIT) length = LLIMIT-1; - lengthList[length]++; - } while (length >=MINMATCHLENGTH); + { size_t length; + do { + end++; + length = ZDICT_count(b + pos, b + suffix[end]); + if (length >= LLIMIT) length = LLIMIT-1; + lengthList[length]++; + } while (length >=MINMATCHLENGTH); + } /* look backward */ - length = MINMATCHLENGTH; - while ((length >= MINMATCHLENGTH) & (start > 0)) { - length = ZDICT_count(b + pos, b + suffix[start - 1]); - if (length >= LLIMIT) length = LLIMIT - 1; - lengthList[length]++; - if (length >= MINMATCHLENGTH) start--; + { size_t length = MINMATCHLENGTH; + while ((length >= MINMATCHLENGTH) & (start > 0)) { + length = ZDICT_count(b + pos, b + suffix[start - 1]); + if (length >= LLIMIT) length = LLIMIT - 1; + lengthList[length]++; + if (length >= MINMATCHLENGTH) start--; + } } /* largest useful length */ @@ -345,12 +351,12 @@ static dictItem ZDICT_analyzePos( /* mark positions done */ { U32 id; for (id=start; id solution.length) length = solution.length; } pEnd = (U32)(testedPos + length); @@ -575,29 +581,30 @@ static void ZDICT_fillNoise(void* buffer, size_t length) typedef struct { - ZSTD_CCtx* ref; - ZSTD_CCtx* zc; + ZSTD_CCtx* ref; /* contains reference to dictionary */ + ZSTD_CCtx* zc; /* working context */ void* workPlace; /* must be ZSTD_BLOCKSIZE_MAX allocated */ } EStats_ress_t; #define MAXREPOFFSET 1024 static void ZDICT_countEStats(EStats_ress_t esr, ZSTD_parameters params, - U32* countLit, U32* offsetcodeCount, U32* matchlengthCount, U32* litlengthCount, U32* repOffsets, - const void* src, size_t srcSize, U32 notificationLevel) + U32* countLit, U32* offsetcodeCount, U32* matchlengthCount, U32* litlengthCount, U32* repOffsets, + const void* src, size_t srcSize, + U32 notificationLevel) { size_t const blockSizeMax = MIN (ZSTD_BLOCKSIZE_MAX, 1 << params.cParams.windowLog); size_t cSize; if (srcSize > blockSizeMax) srcSize = blockSizeMax; /* protection vs large samples */ - { size_t const errorCode = ZSTD_copyCCtx(esr.zc, esr.ref, 0); - if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_copyCCtx failed \n"); return; } + { size_t const errorCode = ZSTD_copyCCtx(esr.zc, esr.ref, 0); + if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_copyCCtx failed \n"); return; } } cSize = ZSTD_compressBlock(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_MAX, src, srcSize); if (ZSTD_isError(cSize)) { DISPLAYLEVEL(3, "warning : could not compress sample size %u \n", (U32)srcSize); return; } if (cSize) { /* if == 0; block is not compressible */ - const seqStore_t* seqStorePtr = ZSTD_getSeqStore(esr.zc); + const seqStore_t* const seqStorePtr = ZSTD_getSeqStore(esr.zc); /* literals stats */ { const BYTE* bytePtr; @@ -688,6 +695,7 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, BYTE* dstPtr = (BYTE*)dstBuffer; /* init */ + DEBUGLOG(4, "ZDICT_analyzeEntropy"); esr.ref = ZSTD_createCCtx(); esr.zc = ZSTD_createCCtx(); esr.workPlace = malloc(ZSTD_BLOCKSIZE_MAX); @@ -713,7 +721,7 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, goto _cleanup; } } - /* collect stats on all files */ + /* collect stats on all samples */ for (u=0; u=1) + params.zParams.notificationLevel = ZSTD_DEBUG; +#endif return ZDICT_optimizeTrainFromBuffer_cover(dictBuffer, dictBufferCapacity, - samplesBuffer, samplesSizes, - nbSamples, ¶ms); + samplesBuffer, samplesSizes, nbSamples, + ¶ms); } size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, - const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples) + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples) { ZDICT_params_t params; memset(¶ms, 0, sizeof(params)); diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index 5f0000b1c..92f66415f 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -38,21 +38,21 @@ extern "C" { /*! ZDICT_trainFromBuffer(): - * Train a dictionary from an array of samples. - * Uses ZDICT_optimizeTrainFromBuffer_cover() single-threaded, with d=8 and steps=4. - * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, - * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. - * The resulting dictionary will be saved into `dictBuffer`. + * Train a dictionary from an array of samples. + * Redirect towards ZDICT_optimizeTrainFromBuffer_cover() single-threaded, with d=8 and steps=4. + * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, + * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. + * The resulting dictionary will be saved into `dictBuffer`. * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) - * or an error code, which can be tested with ZDICT_isError(). - * Note: ZDICT_trainFromBuffer() requires about 9 bytes of memory for each input byte. - * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. - * It's obviously possible to target smaller or larger ones, just by specifying different `dictBufferCapacity`. - * In general, it's recommended to provide a few thousands samples, but this can vary a lot. + * or an error code, which can be tested with ZDICT_isError(). + * Note: ZDICT_trainFromBuffer() requires about 9 bytes of memory for each input byte. + * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. + * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`. + * In general, it's recommended to provide a few thousands samples, though this can vary a lot. * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. */ ZDICTLIB_API size_t ZDICT_trainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, - const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); /*====== Helper functions ======*/ @@ -72,14 +72,14 @@ ZDICTLIB_API const char* ZDICT_getErrorName(size_t errorCode); * ==================================================================================== */ typedef struct { - int compressionLevel; /* 0 means default; target a specific zstd compression level */ - unsigned notificationLevel; /* Write to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ - unsigned dictID; /* 0 means auto mode (32-bits random value); other : force dictID value */ + int compressionLevel; /* optimize for a specific zstd compression level; 0 means default */ + unsigned notificationLevel; /* Write log to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ + unsigned dictID; /* force dictID value; 0 means auto mode (32-bits random value) */ } ZDICT_params_t; /*! ZDICT_cover_params_t: - * For all values 0 means default. * k and d are the only required parameters. + * For others, value 0 means default. */ typedef struct { unsigned k; /* Segment size : constraint: 0 < k : Reasonable range [16, 2048+] */ @@ -91,28 +91,28 @@ typedef struct { /*! ZDICT_trainFromBuffer_cover(): - * Train a dictionary from an array of samples using the COVER algorithm. - * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, - * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. - * The resulting dictionary will be saved into `dictBuffer`. + * Train a dictionary from an array of samples using the COVER algorithm. + * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, + * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. + * The resulting dictionary will be saved into `dictBuffer`. * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) - * or an error code, which can be tested with ZDICT_isError(). - * Note: ZDICT_trainFromBuffer_cover() requires about 9 bytes of memory for each input byte. - * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. - * It's obviously possible to target smaller or larger ones, just by specifying different `dictBufferCapacity`. - * In general, it's recommended to provide a few thousands samples, but this can vary a lot. + * or an error code, which can be tested with ZDICT_isError(). + * Note: ZDICT_trainFromBuffer_cover() requires about 9 bytes of memory for each input byte. + * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. + * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`. + * In general, it's recommended to provide a few thousands samples, though this can vary a lot. * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. */ ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( - void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, - const size_t *samplesSizes, unsigned nbSamples, + void *dictBuffer, size_t dictBufferCapacity, + const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, ZDICT_cover_params_t parameters); /*! ZDICT_optimizeTrainFromBuffer_cover(): * The same requirements as above hold for all the parameters except `parameters`. * This function tries many parameter combinations and picks the best parameters. - * `*parameters` is filled with the best parameters found, and the dictionary - * constructed with those parameters is stored in `dictBuffer`. + * `*parameters` is filled with the best parameters found, + * dictionary constructed with those parameters is stored in `dictBuffer`. * * All of the parameters d, k, steps are optional. * If d is non-zero then we don't check multiple values of d, otherwise we check d = {6, 8, 10, 12, 14, 16}. @@ -125,9 +125,9 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( * Note: ZDICT_optimizeTrainFromBuffer_cover() requires about 8 bytes of memory for each input byte and additionally another 5 bytes of memory for each byte of memory for each thread. */ ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover( - void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, - const size_t *samplesSizes, unsigned nbSamples, - ZDICT_cover_params_t *parameters); + void* dictBuffer, size_t dictBufferCapacity, + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, + ZDICT_cover_params_t* parameters); /*! ZDICT_finalizeDictionary(): * Given a custom content as a basis for dictionary, and a set of samples, @@ -157,22 +157,23 @@ typedef struct { } ZDICT_legacy_params_t; /*! ZDICT_trainFromBuffer_legacy(): - * Train a dictionary from an array of samples. - * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, - * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. - * The resulting dictionary will be saved into `dictBuffer`. + * Train a dictionary from an array of samples. + * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, + * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. + * The resulting dictionary will be saved into `dictBuffer`. * `parameters` is optional and can be provided with values set to 0 to mean "default". * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) - * or an error code, which can be tested with ZDICT_isError(). - * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. - * It's obviously possible to target smaller or larger ones, just by specifying different `dictBufferCapacity`. - * In general, it's recommended to provide a few thousands samples, but this can vary a lot. + * or an error code, which can be tested with ZDICT_isError(). + * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. + * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`. + * In general, it's recommended to provide a few thousands samples, though this can vary a lot. * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. - * Note: ZDICT_trainFromBuffer_legacy() will send notifications into stderr if instructed to, using notificationLevel>0. + * Note: ZDICT_trainFromBuffer_legacy() will send notifications into stderr if instructed to, using notificationLevel>0. */ ZDICTLIB_API size_t ZDICT_trainFromBuffer_legacy( - void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, - const size_t *samplesSizes, unsigned nbSamples, ZDICT_legacy_params_t parameters); + void *dictBuffer, size_t dictBufferCapacity, + const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, + ZDICT_legacy_params_t parameters); /* Deprecation warnings */ /* It is generally possible to disable deprecation warnings from compiler, diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 141bf6548..7198329a8 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -673,6 +673,15 @@ static int basicUnitTests(U32 seed, double compressibility) goto _output_error; } + DISPLAYLEVEL(4, "test%3i : dictBuilder on cyclic data : ", testNb++); + assert(compressedBufferSize >= totalSampleSize); + { U32 u; for (u=0; u Date: Thu, 11 Jan 2018 11:16:32 -0800 Subject: [PATCH 035/255] fixed #304 Pathological samples may result in literal section being incompressible. This case is now detected, and literal distribution is replaced by one that can be written into the dictionary. --- lib/common/huf.h | 4 ++-- lib/compress/huf_compress.c | 1 + lib/dictBuilder/zdict.c | 32 +++++++++++++++++++++++++------- tests/fuzzer.c | 18 ++++++++++-------- 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/lib/common/huf.h b/lib/common/huf.h index 522bf9b6c..1cead357a 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -206,10 +206,10 @@ The following API allows targeting specific sub-functions for advanced tasks. For example, it's possible to compress several blocks using the same 'CTable', or to save and regenerate 'CTable' using external methods. */ -/* FSE_count() : find it within "fse.h" */ +/* FSE_count() : exposed within "fse.h" */ unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); typedef struct HUF_CElt_s HUF_CElt; /* incomplete type */ -size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits); +size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits); /* @return : maxNbBits; CTable and count can overlap, in which case, CTable will overwrite count content */ size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 5692d56e0..cfc5a98bb 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -405,6 +405,7 @@ size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValu } /** HUF_buildCTable() : + * @return : maxNbBits * Note : count is used before tree is written, so they can safely overlap */ size_t HUF_buildCTable (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits) diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 3a1649419..2380599c9 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -666,6 +666,18 @@ static void ZDICT_insertSortCount(offsetCount_t table[ZSTD_REP_NUM+1], U32 val, } } +/* ZDICT_flatLit() : + * rewrite `countLit` to contain a mostly flat but still compressible distribution of literals. + * necessary to avoid generating a non-compressible distribution that HUF_writeCTable() cannot encode. + */ +static void ZDICT_flatLit(U32* countLit) +{ + int u; + for (u=1; u<256; u++) countLit[u] = 2; + countLit[0] = 4; + countLit[253] = 1; + countLit[254] = 1; +} #define OFFCODE_MAX 30 /* only applicable to first block */ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, @@ -730,14 +742,20 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, pos += fileSizes[u]; } - /* analyze */ - errorCode = HUF_buildCTable (hufTable, countLit, 255, huffLog); - if (HUF_isError(errorCode)) { - eSize = ERROR(GENERIC); - DISPLAYLEVEL(1, " HUF_buildCTable error \n"); - goto _cleanup; + /* analyze, build stats, starting with literals */ + { size_t maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog); + if (HUF_isError(maxNbBits)) { + eSize = ERROR(GENERIC); + DISPLAYLEVEL(1, " HUF_buildCTable error \n"); + goto _cleanup; + } + if (maxNbBits==8) { /* not compressible : will fail on HUF_writeCTable() */ + ZDICT_flatLit(countLit); /* replace distribution by a fake "mostly flat but still compressible" distribution, that HUF_writeCTable() can encode */ + maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog); + assert(maxNbBits==9); + } + huffLog = (U32)maxNbBits; } - huffLog = (U32)errorCode; /* looking for most common first offsets */ { U32 offset; diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 7198329a8..acb670b10 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -659,12 +659,13 @@ static int basicUnitTests(U32 seed, double compressibility) /* Dictionary and dictBuilder tests */ { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); - size_t dictSize = 16 KB; - void* dictBuffer = malloc(dictSize); + size_t const dictBufferCapacity = 16 KB; + void* dictBuffer = malloc(dictBufferCapacity); size_t const totalSampleSize = 1 MB; size_t const sampleUnitSize = 8 KB; U32 const nbSamples = (U32)(totalSampleSize / sampleUnitSize); size_t* const samplesSizes = (size_t*) malloc(nbSamples * sizeof(size_t)); + size_t dictSize; U32 dictID; if (dictBuffer==NULL || samplesSizes==NULL) { @@ -675,16 +676,17 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "test%3i : dictBuilder on cyclic data : ", testNb++); assert(compressedBufferSize >= totalSampleSize); - { U32 u; for (u=0; u Date: Thu, 11 Jan 2018 11:29:28 -0800 Subject: [PATCH 036/255] added warning message when pathological dataset is detected (note : cover_optimize needs -v to display the warning) --- lib/dictBuilder/cover.c | 2 +- lib/dictBuilder/zdict.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c index b68370671..b5a3957a9 100644 --- a/lib/dictBuilder/cover.c +++ b/lib/dictBuilder/cover.c @@ -1007,7 +1007,7 @@ ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover( data->parameters.k = k; data->parameters.d = d; data->parameters.steps = kSteps; - data->parameters.zParams.notificationLevel = displayLevel; + data->parameters.zParams.notificationLevel = g_displayLevel; /* Check the parameters */ if (!COVER_checkParameters(data->parameters, dictBufferCapacity)) { DISPLAYLEVEL(1, "Cover parameters incorrect\n"); diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 2380599c9..7d24e4991 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -750,6 +750,7 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, goto _cleanup; } if (maxNbBits==8) { /* not compressible : will fail on HUF_writeCTable() */ + DISPLAYLEVEL(2, "warning : pathological dataset : literals are not compressible : samples are noisy or too regular \n"); ZDICT_flatLit(countLit); /* replace distribution by a fake "mostly flat but still compressible" distribution, that HUF_writeCTable() can encode */ maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog); assert(maxNbBits==9); @@ -1052,8 +1053,9 @@ size_t ZDICT_trainFromBuffer_unsafe_legacy( } -/* issue : samplesBuffer need to be followed by a noisy guard band. -* work around : duplicate the buffer, and add the noise */ +/* ZDICT_trainFromBuffer_legacy() : + * issue : samplesBuffer need to be followed by a noisy guard band. + * work around : duplicate the buffer, and add the noise */ size_t ZDICT_trainFromBuffer_legacy(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_legacy_params_t params) From b9a14900ff95965868c5c21276cf59d509cd35a1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 11 Jan 2018 12:38:31 -0800 Subject: [PATCH 037/255] changed function name to ZSTD_DUBT_findBestMatch() --- lib/compress/zstd_lazy.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 4af843d1b..1d575804a 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -165,7 +165,7 @@ static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, } -static size_t ZSTD_insertBtAndFindBestMatch ( +static size_t ZSTD_DUBT_findBestMatch ( ZSTD_CCtx* zc, const BYTE* const ip, const BYTE* const iend, size_t* offsetPtr, @@ -192,14 +192,14 @@ static size_t ZSTD_insertBtAndFindBestMatch ( U32 nbCandidates = nbCompares; U32 previousCandidate = 0; - DEBUGLOG(7, "ZSTD_insertBtAndFindBestMatch (%u) ", current); + DEBUGLOG(7, "ZSTD_DUBT_findBestMatch (%u) ", current); assert(ip <= iend-8); /* required for h calculation */ /* reach end of unsorted candidates list */ while ( (matchIndex > unsortLimit) && (*unsortedMark == ZSTD_DUBT_UNSORTED_MARK) && (nbCandidates > 1) ) { - DEBUGLOG(8, "ZSTD_insertBtAndFindBestMatch: candidate %u is unsorted", + DEBUGLOG(8, "ZSTD_DUBT_findBestMatch: candidate %u is unsorted", matchIndex); *unsortedMark = previousCandidate; previousCandidate = matchIndex; @@ -211,7 +211,7 @@ static size_t ZSTD_insertBtAndFindBestMatch ( if ( (matchIndex > unsortLimit) && (*unsortedMark==ZSTD_DUBT_UNSORTED_MARK) ) { - DEBUGLOG(7, "ZSTD_insertBtAndFindBestMatch: nullify last unsorted candidate %u", + DEBUGLOG(7, "ZSTD_DUBT_findBestMatch: nullify last unsorted candidate %u", matchIndex); *nextCandidate = *unsortedMark = 0; /* nullify next candidate if it's still unsorted (note : simplification, detrimental to compression ratio, beneficial for speed) */ } @@ -289,7 +289,7 @@ static size_t ZSTD_insertBtAndFindBestMatch ( zc->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ if (bestLength >= MINMATCH) { U32 const mIndex = current - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex; - DEBUGLOG(8, "ZSTD_insertBtAndFindBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)", + DEBUGLOG(8, "ZSTD_DUBT_findBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)", current, (U32)bestLength, (U32)*offsetPtr, mIndex); } return bestLength; @@ -307,7 +307,7 @@ static size_t ZSTD_BtFindBestMatch ( DEBUGLOG(7, "ZSTD_BtFindBestMatch"); if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */ ZSTD_updateDUBT(zc, ip, iLimit, mls); - return ZSTD_insertBtAndFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 0); + return ZSTD_DUBT_findBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 0); } @@ -338,7 +338,7 @@ static size_t ZSTD_BtFindBestMatch_extDict ( DEBUGLOG(7, "ZSTD_BtFindBestMatch_extDict"); if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */ ZSTD_updateDUBT(zc, ip, iLimit, mls); - return ZSTD_insertBtAndFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 1); + return ZSTD_DUBT_findBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 1); } From b610b777d39c9eee139e7b73f32db5e2eafbd2e9 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 11 Jan 2018 14:35:04 -0800 Subject: [PATCH 038/255] Increase windowLog from CDict based on the srcSize when known --- lib/compress/zstd_compress.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b9e0ec44d..d11fbeb3c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2453,6 +2453,15 @@ size_t ZSTD_compressBegin_usingCDict_advanced( if (cdict==NULL) return ERROR(dictionary_wrong); { ZSTD_CCtx_params params = cctx->requestedParams; params.cParams = ZSTD_getCParamsFromCDict(cdict); + /* Increase window log to fit the entire dictionary and source if the + * source size is known. Limit the increase to 19, which is the + * window log for compression level 1 with the largest source size. + */ + if (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN) { + U32 const limitedSrcSize = (U32)MIN(pledgedSrcSize, 1U << 19); + U32 const limitedSrcLog = limitedSrcSize > 1 ? ZSTD_highbit32(limitedSrcSize - 1) + 1 : 1; + params.cParams.windowLog = MAX(params.cParams.windowLog, limitedSrcLog); + } params.fParams = fParams; return ZSTD_compressBegin_internal(cctx, NULL, 0, ZSTD_dm_auto, From 4b7c4e5f41cdb3e6c670f1fcc7932e4028b52af3 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 11 Jan 2018 16:45:16 -0800 Subject: [PATCH 039/255] Add test for cdict window log adjustment --- tests/fuzzer.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 024a583ba..21d7d21a3 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -1206,6 +1206,28 @@ static int basicUnitTests(U32 seed, double compressibility) if (strcmp("No error detected", ZSTD_getErrorName(ZSTD_error_GENERIC)) != 0) goto _output_error; DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(4, "test%3i : testing ZSTD dictionary sizes : ", testNb++); + RDG_genBuffer(CNBuffer, CNBuffSize, compressibility, 0., seed); + { + size_t const size = MIN(128 KB, CNBuffSize); + ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + ZSTD_CDict* const lgCDict = ZSTD_createCDict(CNBuffer, size, 1); + ZSTD_CDict* const smCDict = ZSTD_createCDict(CNBuffer, 1 KB, 1); + ZSTD_frameHeader lgHeader; + ZSTD_frameHeader smHeader; + + CHECK_Z(ZSTD_compress_usingCDict(cctx, compressedBuffer, compressedBufferSize, CNBuffer, size, lgCDict)); + CHECK_Z(ZSTD_getFrameHeader(&lgHeader, compressedBuffer, compressedBufferSize)); + CHECK_Z(ZSTD_compress_usingCDict(cctx, compressedBuffer, compressedBufferSize, CNBuffer, size, smCDict)); + CHECK_Z(ZSTD_getFrameHeader(&smHeader, compressedBuffer, compressedBufferSize)); + + if (lgHeader.windowSize != smHeader.windowSize) goto _output_error; + + ZSTD_freeCDict(smCDict); + ZSTD_freeCDict(lgCDict); + ZSTD_freeCCtx(cctx); + } + _end: free(CNBuffer); free(compressedBuffer); From 9a211d1f05bd8cb6cb80780bf1880c23204b7b68 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 12 Jan 2018 12:26:47 -0800 Subject: [PATCH 040/255] Load more dictionary positions into table if empty If the hash table is empty load positions into the hash table that we would otherwise skip. | Level | Data Set | Improvement | |-------|--------------|-------------| | 1 | github | 0.44% | | 1 | hg-changelog | 0.13% | | 1 | hg-commands | 1.28% | | 1 | hg-manifest | 0.70% | | 3 | github | 0.74% | | 3 | hg-changelog | 0.87% | | 3 | hg-commands | 1.74% | | 3 | hg-manifest | 0.23% | --- lib/compress/zstd_double_fast.c | 21 ++++++++++++++++----- lib/compress/zstd_fast.c | 16 ++++++++++++---- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index fee5127c3..a6d8ad889 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -21,12 +21,23 @@ void ZSTD_fillDoubleHashTable(ZSTD_CCtx* cctx, const void* end, const U32 mls) const BYTE* const base = cctx->base; const BYTE* ip = base + cctx->nextToUpdate; const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; - const size_t fastHashFillStep = 3; + const U32 fastHashFillStep = 3; - while(ip <= iend) { - hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip - base); - hashLarge[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip - base); - ip += fastHashFillStep; + /* Always insert every fastHashFillStep position into the hash tables. + * Insert the other positions into the large hash table if their entry + * is empty. + */ + for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) { + U32 const current = (U32)(ip - base); + U32 i; + for (i = 0; i < fastHashFillStep; ++i) { + size_t const smHash = ZSTD_hashPtr(ip + i, hBitsS, mls); + size_t const lgHash = ZSTD_hashPtr(ip + i, hBitsL, 8); + if (i == 0) + hashSmall[smHash] = current + i; + if (i == 0 || hashLarge[lgHash] == 0) + hashLarge[lgHash] = current + i; + } } } diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 7b56c3d6a..6497a2d8b 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -19,11 +19,19 @@ void ZSTD_fillHashTable (ZSTD_CCtx* zc, const void* end, const U32 mls) const BYTE* const base = zc->base; const BYTE* ip = base + zc->nextToUpdate; const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; - const size_t fastHashFillStep = 3; + const U32 fastHashFillStep = 3; - while(ip <= iend) { - hashTable[ZSTD_hashPtr(ip, hBits, mls)] = (U32)(ip - base); - ip += fastHashFillStep; + /* Always insert every fastHashFillStep position into the hash table. + * Insert the other positions if their hash entry is empty. + */ + for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) { + U32 const current = (U32)(ip - base); + U32 i; + for (i = 0; i < fastHashFillStep; ++i) { + size_t const hash = ZSTD_hashPtr(ip + i, hBits, mls); + if (i == 0 || hashTable[hash] == 0) + hashTable[hash] = current + i; + } } } From 58ecf13e025c3601391d0c001ef2369eaa0d747c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 13 Jan 2018 13:18:57 -0800 Subject: [PATCH 041/255] zstdmt : can compress at block granularity offering perspective of more accurate progression report. --- lib/compress/zstdmt_compress.c | 101 +++++++++++++++++++++------------ programs/fileio.c | 2 +- 2 files changed, 67 insertions(+), 36 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index e51edf124..3a5b58a72 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -22,6 +22,7 @@ /* ====== Dependencies ====== */ #include /* memcpy, memset */ +#include /* INT_MAX */ #include "pool.h" /* threadpool */ #include "threading.h" /* mutex */ #include "zstd_compress_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */ @@ -129,7 +130,7 @@ static void ZSTDMT_freeBufferPool(ZSTDMT_bufferPool* bufPool) static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool) { size_t const poolSize = sizeof(*bufPool) - + (bufPool->totalBuffers - 1) * sizeof(buffer_t); + + (bufPool->totalBuffers - 1) * sizeof(buffer_t); unsigned u; size_t totalBufferSize = 0; ZSTD_pthread_mutex_lock(&bufPool->poolMutex); @@ -201,20 +202,6 @@ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf) ZSTD_free(buf.start, bufPool->cMem); } -/* Sets parameters relevant to the compression job, initializing others to - * default values. Notably, nbThreads should probably be zero. */ -static ZSTD_CCtx_params ZSTDMT_makeJobCCtxParams(ZSTD_CCtx_params const params) -{ - ZSTD_CCtx_params jobParams; - memset(&jobParams, 0, sizeof(jobParams)); - - jobParams.cParams = params.cParams; - jobParams.fParams = params.fParams; - jobParams.compressionLevel = params.compressionLevel; - - jobParams.ldmParams = params.ldmParams; - return jobParams; -} /* ===== CCtx Pool ===== */ /* a single CCtx Pool can be invoked from multiple threads in parallel */ @@ -305,13 +292,16 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) } -/* ===== Thread worker ===== */ +/* ------------------------------------------ */ +/* ===== Thread worker ===== */ +/* ------------------------------------------ */ typedef struct { buffer_t src; const void* srcStart; size_t prefixSize; size_t srcSize; + size_t readSize; buffer_t dstBuff; size_t cSize; size_t dstFlushed; @@ -328,21 +318,19 @@ typedef struct { unsigned long long fullFrameSize; } ZSTDMT_jobDescription; -/* ZSTDMT_compressChunk() : POOL_function type */ +/* ZSTDMT_compressChunk() is a POOL_function type */ void ZSTDMT_compressChunk(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool); const void* const src = (const char*)job->srcStart + job->prefixSize; buffer_t dstBuff = job->dstBuff; - DEBUGLOG(5, "ZSTDMT_compressChunk: job (first:%u) (last:%u) : prefixSize %u, srcSize %u ", - job->firstChunk, job->lastChunk, (U32)job->prefixSize, (U32)job->srcSize); + /* ressources */ if (cctx==NULL) { job->cSize = ERROR(memory_allocation); goto _endJob; } - if (dstBuff.start == NULL) { dstBuff = ZSTDMT_getBuffer(job->bufPool); if (dstBuff.start==NULL) { @@ -350,30 +338,26 @@ void ZSTDMT_compressChunk(void* jobDescription) goto _endJob; } job->dstBuff = dstBuff; - DEBUGLOG(5, "ZSTDMT_compressChunk: received dstBuff of size %u", (U32)dstBuff.size); } + /* init */ if (job->cdict) { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dm_auto, job->cdict, job->params, job->fullFrameSize); - DEBUGLOG(4, "ZSTDMT_compressChunk: init using CDict (windowLog=%u)", job->params.cParams.windowLog); assert(job->firstChunk); /* only allowed for first job */ if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : ZSTD_CONTENTSIZE_UNKNOWN; ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */ - size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk); - if (ZSTD_isError(forceWindowError)) { - DEBUGLOG(5, "ZSTD_CCtxParam_setParameter error : %s ", ZSTD_getErrorName(forceWindowError)); - job->cSize = forceWindowError; - goto _endJob; - } - DEBUGLOG(5, "ZSTDMT_compressChunk: invoking ZSTD_compressBegin_advanced_internal with windowLog = %u ", jobParams.cParams.windowLog); + { size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk); + if (ZSTD_isError(forceWindowError)) { + job->cSize = forceWindowError; + goto _endJob; + } } { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->prefixSize, ZSTD_dm_rawContent, /* load dictionary in "content-only" mode (no header analysis) */ NULL, jobParams, pledgedSrcSize); if (ZSTD_isError(initError)) { - DEBUGLOG(5, "ZSTD_compressBegin_advanced_internal error : %s ", ZSTD_getErrorName(initError)); job->cSize = initError; goto _endJob; } } @@ -384,19 +368,50 @@ void ZSTDMT_compressChunk(void* jobDescription) ZSTD_invalidateRepCodes(cctx); } - DEBUGLOG(5, "Compressing into dstBuff of size %u", (U32)dstBuff.size); - DEBUG_PRINTHEX(6, job->srcStart, 12); + /* compress */ +#if 1 job->cSize = (job->lastChunk) ? ZSTD_compressEnd (cctx, dstBuff.start, dstBuff.size, src, job->srcSize) : ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, job->srcSize); - DEBUGLOG(5, "compressed %u bytes into %u bytes (first:%u) (last:%u) ", - (unsigned)job->srcSize, (unsigned)job->cSize, job->firstChunk, job->lastChunk); - DEBUGLOG(5, "dstBuff.size : %u ; => %s ", (U32)dstBuff.size, ZSTD_getErrorName(job->cSize)); +#else + if (sizeof(size_t) > sizeof(int)) + assert(job->srcSize < ((size_t)INT_MAX) * ZSTD_BLOCKSIZE_MAX); /* check overflow */ + { int const nbBlocks = (int)((job->srcSize + (ZSTD_BLOCKSIZE_MAX-1)) / ZSTD_BLOCKSIZE_MAX); + const BYTE* ip = (const BYTE*) src; + BYTE* const ostart = (BYTE*)dstBuff.start; + BYTE* op = ostart; + BYTE* oend = op + dstBuff.size; + int blockNb; + job->cSize = 0; + for (blockNb = 0; blockNb < nbBlocks-1; blockNb++) { + size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, ZSTD_BLOCKSIZE_MAX); + if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; } + ip += ZSTD_BLOCKSIZE_MAX; + op += cSize; assert(op < oend); + /* stats */ + job->cSize += cSize; + job->readSize = ZSTD_BLOCKSIZE_MAX * (blockNb+1); + } + /* last block */ + { size_t const lastBlockSize1 = job->srcSize & (ZSTD_BLOCKSIZE_MAX-1); + size_t const lastBlockSize = (lastBlockSize1==0) ? ZSTD_BLOCKSIZE_MAX : lastBlockSize1; + size_t const cSize = (job->lastChunk) ? + ZSTD_compressEnd (cctx, op, oend-op, ip, lastBlockSize) : + ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize); + if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; } + /* stats */ + job->cSize += cSize; + job->readSize = ZSTD_BLOCKSIZE_MAX * (blockNb+1); + } + } +#endif _endJob: + /* release */ ZSTDMT_releaseCCtx(job->cctxPool, cctx); ZSTDMT_releaseBuffer(job->bufPool, job->src); job->src = g_nullBuffer; job->srcStart = NULL; + /* report */ ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); job->jobCompleted = 1; job->jobScanned = 0; @@ -440,6 +455,21 @@ struct ZSTDMT_CCtx_s { const ZSTD_CDict* cdict; }; +/* Sets parameters relevant to the compression job, initializing others to + * default values. Notably, nbThreads should probably be zero. */ +static ZSTD_CCtx_params ZSTDMT_makeJobCCtxParams(ZSTD_CCtx_params const params) +{ + ZSTD_CCtx_params jobParams; + memset(&jobParams, 0, sizeof(jobParams)); + + jobParams.cParams = params.cParams; + jobParams.fParams = params.fParams; + jobParams.compressionLevel = params.compressionLevel; + + jobParams.ldmParams = params.ldmParams; + return jobParams; +} + static ZSTDMT_jobDescription* ZSTDMT_allocJobsTable(U32* nbJobsPtr, ZSTD_customMem cMem) { U32 const nbJobsLog2 = ZSTD_highbit32(*nbJobsPtr) + 1; @@ -908,6 +938,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; + zcs->jobs[jobID].readSize = 0; zcs->jobs[jobID].prefixSize = zcs->dictSize; assert(zcs->inBuff.filled >= srcSize + zcs->dictSize); zcs->jobs[jobID].params = zcs->params; diff --git a/programs/fileio.c b/programs/fileio.c index 887cbeb37..3ae2d4057 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -828,7 +828,7 @@ finish: /* Status */ DISPLAYLEVEL(2, "\r%79s\r", ""); DISPLAYLEVEL(2,"%-20s :%6.2f%% (%6llu => %6llu bytes, %s) \n", srcFileName, - (double)compressedfilesize/(readsize+(!readsize) /* avoid div by zero */ )*100, + (double)compressedfilesize / (readsize+(!readsize)/*avoid div by zero*/) * 100, (unsigned long long)readsize, (unsigned long long) compressedfilesize, dstFileName); From f8a05932b9bfd504614b15b8898eaaa3e3b8520b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 13 Jan 2018 21:54:21 -0800 Subject: [PATCH 042/255] added `make list` capability to `tests/Makefile` --- tests/Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/Makefile b/tests/Makefile index 48ae17538..852ea7379 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -292,15 +292,23 @@ ifneq (,$(filter $(shell uname),SunOS)) DIFF:=gdiff endif +.PHONY: list +list: + @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | xargs + +.PHONY: zstd-playTests zstd-playTests: datagen file $(ZSTD) ZSTD="$(QEMU_SYS) $(ZSTD)" ./playTests.sh $(ZSTDRTTEST) +.PHONY: shortest shortest: ZSTDRTTEST= shortest: test-zstd +.PHONY: fuzztest fuzztest: test-fuzzer test-zstream test-decodecorpus +.PHONY: test test: test-zstd test-fullbench test-fuzzer test-zstream test-invalidDictionaries test-legacy test-decodecorpus ifeq ($(QEMU_SYS),) test: test-pool From 99a0a8bbd2c2faa7f90dd755f4523f669086cb99 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 13 Jan 2018 22:00:05 -0800 Subject: [PATCH 043/255] tests/Makefile : fixed target allnothread ensures MT is disabled --- tests/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Makefile b/tests/Makefile index 852ea7379..4acc7962f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -82,6 +82,8 @@ all: fullbench fuzzer zstreamtest paramgrill datagen decodecorpus roundTripCrash all32: fullbench32 fuzzer32 zstreamtest32 +allnothread: MULTITHREAD_CPP= +allnothread: MULTITHREAD_LD= allnothread: fullbench fuzzer paramgrill datagen decodecorpus dll: fuzzer-dll zstreamtest-dll From 4792ac6689d8e27d2f9aab4535d7efc8964180f0 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 14 Jan 2018 14:09:17 -0800 Subject: [PATCH 044/255] make -C tests legacy : minor flag alteration ZSTD_LEGACY_SUPPORT is a macro constant, so it should be part of CPPFLAGS, instead of CFLAGS --- tests/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index 4acc7962f..95d0bf549 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -218,8 +218,7 @@ longmatch : $(ZSTD_OBJECTS) longmatch.c invalidDictionaries : $(ZSTD_OBJECTS) invalidDictionaries.c $(CC) $(FLAGS) $^ -o $@$(EXT) -legacy : CFLAGS+= -DZSTD_LEGACY_SUPPORT=4 -legacy : CPPFLAGS+= -I$(ZSTDDIR)/legacy +legacy : CPPFLAGS += -I$(ZSTDDIR)/legacy -DZSTD_LEGACY_SUPPORT=4 legacy : $(ZSTD_FILES) $(wildcard $(ZSTDDIR)/legacy/*.c) legacy.c $(CC) $(FLAGS) $^ -o $@$(EXT) From f6e17b8c7a27ffc5e51209ba698bc3462790f823 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 14 Jan 2018 17:03:45 -0800 Subject: [PATCH 045/255] added tests/checkTag compared provided tag with current libzstd version --- tests/.gitignore | 1 + tests/Makefile | 6 ++++- tests/checkTag.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 tests/checkTag.c diff --git a/tests/.gitignore b/tests/.gitignore index b16e26e3d..a06fa20be 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -23,6 +23,7 @@ decodecorpus pool poolTests invalidDictionaries +checkTag # Tmp test directory zstdtest diff --git a/tests/Makefile b/tests/Makefile index 95d0bf549..a1289440b 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -236,9 +236,13 @@ endif poolTests : poolTests.c $(ZSTDDIR)/common/pool.c $(ZSTDDIR)/common/threading.c $(ZSTDDIR)/common/zstd_common.c $(ZSTDDIR)/common/error_private.c $(CC) $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT) +.PHONY: versionsTest versionsTest: clean $(PYTHON) test-zstd-versions.py +checkTag: checkTag.c $(ZSTDDIR)/zstd.h + $(CC) $(FLAGS) $< -o $@$(EXT) + clean: $(MAKE) -C $(ZSTDDIR) clean @$(RM) -fR $(TESTARTEFACT) @@ -251,7 +255,7 @@ clean: zstreamtest$(EXT) zstreamtest32$(EXT) \ datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) \ symbols$(EXT) invalidDictionaries$(EXT) legacy$(EXT) poolTests$(EXT) \ - decodecorpus$(EXT) + decodecorpus$(EXT) checkTag$(EXT) @echo Cleaning completed diff --git a/tests/checkTag.c b/tests/checkTag.c new file mode 100644 index 000000000..fda3fd1f9 --- /dev/null +++ b/tests/checkTag.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018-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. + */ + +/* checkTag : validation tool for libzstd + * command : + * $ ./checkTag tag + * checkTag validates tags of following format : v[0-9].[0-9].[0-9]{any} + * The tag is then compared to zstd version number. + * They are compatible if first 3 digits are identical. + * Anything beyond that is free, and doesn't impact validation. + * Example : tag v1.8.1.2 is compatible with version 1.8.1 + * When tag and version are not compatible, program exits with error code 1. + * When they are compatible, it exists with a code 0. + * checkTag is intended to be used in automated testing environment. + */ + +#include /* printf */ +#include /* strlen, strncmp */ +#include "zstd.h" /* ZSTD_VERSION_STRING */ + + +/* validate() : + * @return 1 if tag is compatible, 0 if not. + */ +static int validate(const char* const tag) +{ + size_t const tagLength = strlen(tag); + size_t const verLength = strlen(ZSTD_VERSION_STRING); + + if (tagLength < 2) return 0; + if (tag[0] != 'v') return 0; + if (tagLength <= verLength) return 0; + + if (strncmp(ZSTD_VERSION_STRING, tag+1, verLength)) return 0; + + return 1; +} + +int main(int argc, const char** argv) +{ + const char* const exeName = argv[0]; + const char* const tag = argv[1]; + if (argc!=2) { + printf("incorrect usage : %s tag \n", exeName); + return 2; + } + + printf("Version : %s \n", ZSTD_VERSION_STRING); + printf("Tag : %s \n", tag); + + if (validate(tag)) { + printf("OK : tag is compatible with zstd version \n"); + return 0; + } + + printf("!! error : tag and versions are not compatible !! \n"); + return 1; +} From 815eddeda4cb0d428a83a889fbfddfbf377e5fb6 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 14 Jan 2018 17:06:21 -0800 Subject: [PATCH 046/255] added tag-triggered test ensure tag and libzstd version are compatible --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 48d9ac3a6..e6fe49cac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,10 @@ matrix: - env: Cmd='make lz4install && make -C tests test-lz4' + # tag-specific test + - if: tag =~ ^v[0-9]\.[0-9] + env: Cmd='make -C tests checkTag && tests/checkTag $TRAVIS_BRANCH' + git: depth: 1 From 403e2db139d81a00a5b2da10d22dec56e41225c4 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Mon, 15 Jan 2018 22:48:46 +0300 Subject: [PATCH 047/255] CMake: use GNUInstallDirs for library install dir Libraries now will be installed in the correct directory on x86_64 linux systems, and can be changed with `-DCMAKE_INSTALL_LIBDIR=` option. --- build/cmake/CMakeLists.txt | 7 +++++++ build/cmake/lib/CMakeLists.txt | 10 ++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 8e8824e7c..7361bc8a6 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -11,6 +11,7 @@ PROJECT(zstd) CMAKE_MINIMUM_REQUIRED(VERSION 2.8.9) SET(ZSTD_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../..") LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") +INCLUDE(GNUInstallDirs) #----------------------------------------------------------------------------- # Add extra compilation flags @@ -21,6 +22,12 @@ ADD_ZSTD_COMPILATION_FLAGS() # Always hide XXHash symbols ADD_DEFINITIONS(-DXXH_NAMESPACE=ZSTD_) +#----------------------------------------------------------------------------- +# Installation variables +#----------------------------------------------------------------------------- +MESSAGE(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") +MESSAGE(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_LIBDIR}") + #----------------------------------------------------------------------------- # Options #----------------------------------------------------------------------------- diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index fd20bdb87..3435e1bb3 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -146,7 +146,7 @@ ENDIF (ZSTD_BUILD_STATIC) IF (UNIX) # pkg-config SET(PREFIX "${CMAKE_INSTALL_PREFIX}") - SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib") + SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") SET(INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include") SET(VERSION "${LIBVER_MAJOR}.${LIBVER_MINOR}.${LIBVER_RELEASE}") ADD_CUSTOM_TARGET(libzstd.pc ALL @@ -155,7 +155,7 @@ IF (UNIX) -P "${CMAKE_CURRENT_SOURCE_DIR}/pkgconfig.cmake" COMMENT "Creating pkg-config file") - INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/libzstd.pc" DESTINATION "share/pkgconfig") + INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/libzstd.pc" DESTINATION "${LIBDIR}/pkgconfig") ENDIF (UNIX) # install target @@ -167,10 +167,12 @@ INSTALL(FILES DESTINATION "include") IF (ZSTD_BUILD_SHARED) - INSTALL(TARGETS libzstd_shared RUNTIME DESTINATION "bin" LIBRARY DESTINATION "lib" ARCHIVE DESTINATION "lib") + INSTALL(TARGETS libzstd_shared RUNTIME DESTINATION "bin" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") ENDIF() IF (ZSTD_BUILD_STATIC) - INSTALL(TARGETS libzstd_static ARCHIVE DESTINATION "lib") + INSTALL(TARGETS libzstd_static ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") ENDIF (ZSTD_BUILD_STATIC) # uninstall target From 887cd4e35eda262c13a2751e31aa751fc787b067 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 12 Dec 2017 16:51:00 -0800 Subject: [PATCH 048/255] Split ZSTD_CCtx into smaller sub-structures --- lib/common/zstd_internal.h | 2 - lib/compress/zstd_compress.c | 515 +++++++++++++++----------- lib/compress/zstd_compress_internal.h | 46 ++- lib/compress/zstd_double_fast.c | 115 +++--- lib/compress/zstd_double_fast.h | 15 +- lib/compress/zstd_fast.c | 119 +++--- lib/compress/zstd_fast.h | 16 +- lib/compress/zstd_lazy.c | 316 ++++++++-------- lib/compress/zstd_lazy.h | 41 +- lib/compress/zstd_ldm.c | 183 ++++----- lib/compress/zstd_ldm.h | 10 +- lib/compress/zstd_opt.c | 163 ++++---- lib/compress/zstd_opt.h | 23 +- 13 files changed, 844 insertions(+), 720 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 5d2900eb7..49f19a010 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -228,8 +228,6 @@ typedef struct { BYTE* ofCode; U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */ U32 longLengthPos; - U32 rep[ZSTD_REP_NUM]; - U32 repToConfirm[ZSTD_REP_NUM]; } seqStore_t; const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); /* compress & dictBuilder */ diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 101c689a9..eba0baad0 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -81,11 +81,15 @@ ZSTD_CCtx* ZSTD_initStaticCCtx(void *workspace, size_t workspaceSize) cctx->workSpace = (void*)(cctx+1); cctx->workSpaceSize = workspaceSize - sizeof(ZSTD_CCtx); - /* entropy space (never moves) */ - if (cctx->workSpaceSize < sizeof(ZSTD_entropyCTables_t)) return NULL; + /* statically sized space. entropyWorkspace never moves (but prev/next block swap places) */ + if (cctx->workSpaceSize < HUF_WORKSPACE_SIZE + 2 * sizeof(ZSTD_blockState_t)) return NULL; assert(((size_t)cctx->workSpace & (sizeof(void*)-1)) == 0); /* ensure correct alignment */ - cctx->entropy = (ZSTD_entropyCTables_t*)cctx->workSpace; - + cctx->prevBlock = (ZSTD_blockState_t*)cctx->workSpace; + cctx->nextBlock = cctx->prevBlock + 1; + { + void* const ptr = cctx->nextBlock + 1; + cctx->entropyWorkspace = (U32*)ptr; + } return cctx; } @@ -660,7 +664,8 @@ size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params) U32 const hashLog3 = (cParams.searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, cParams.windowLog); size_t const h3Size = ((size_t)1) << hashLog3; - size_t const entropySpace = sizeof(ZSTD_entropyCTables_t); + size_t const entropySpace = HUF_WORKSPACE_SIZE; + size_t const blockStateSpace = 2 * sizeof(ZSTD_blockState_t); size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); size_t const optBudget = @@ -672,7 +677,7 @@ size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params) ZSTD_ldm_getTableSize(params->ldmParams.hashLog, params->ldmParams.bucketSizeLog) : 0; - size_t const neededSpace = entropySpace + tableSpace + tokenSpace + + size_t const neededSpace = entropySpace + blockStateSpace + tableSpace + tokenSpace + optSpace + ldmSpace; DEBUGLOG(5, "sizeof(ZSTD_CCtx) : %u", (U32)sizeof(ZSTD_CCtx)); @@ -794,16 +799,41 @@ static U32 ZSTD_equivalentParams(ZSTD_CCtx_params params1, ZSTD_sufficientBuff(buffSize1, blockSize1, buffPol2, params2.cParams, pledgedSrcSize); } +static void ZSTD_resetBlockState(ZSTD_blockState_t* bs) +{ + int i; + for (i = 0; i < ZSTD_REP_NUM; ++i) + bs->rep[i] = repStartValue[i]; + bs->entropy.hufCTable_repeatMode = HUF_repeat_none; + bs->entropy.offcode_repeatMode = FSE_repeat_none; + bs->entropy.matchlength_repeatMode = FSE_repeat_none; + bs->entropy.litlength_repeatMode = FSE_repeat_none; +} + +/*! ZSTD_invalidateMatchState() + * Invalidate all the matches in the match finder tables. + * Requires nextSrc and base to be set (can be NULL). + */ +static void ZSTD_invalidateMatchState(ZSTD_matchState_t* ms) +{ + size_t const endT = (size_t)(ms->nextSrc - ms->base); + U32 const end = (U32)endT; + assert(endT < (3U<<30)); + + ms->lowLimit = end; + ms->dictLimit = end; + ms->nextToUpdate = end + 1; + ms->loadedDictEnd = 0; + ms->opt.litLengthSum = 0; /* force reset of btopt stats */ +} + /*! ZSTD_continueCCtx() : * reuse CCtx without reset (note : requires no dictionary) */ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pledgedSrcSize) { - size_t const endT = (size_t)(cctx->nextSrc - cctx->base); - U32 const end = (U32)endT; size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize)); size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); DEBUGLOG(4, "ZSTD_continueCCtx: re-use context in place"); - assert(endT < (3U<<30)); cctx->blockSize = blockSize; /* previous block size could be different even for same windowLog, due to pledgedSrcSize */ cctx->appliedParams = params; @@ -813,14 +843,10 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pl cctx->appliedParams.fParams.contentSizeFlag = 0; DEBUGLOG(4, "pledged content size : %u ; flag : %u", (U32)pledgedSrcSize, cctx->appliedParams.fParams.contentSizeFlag); - cctx->lowLimit = end; - cctx->dictLimit = end; - cctx->nextToUpdate = end+1; cctx->stage = ZSTDcs_init; cctx->dictID = 0; - cctx->loadedDictEnd = 0; - { int i; for (i=0; iseqStore.rep[i] = repStartValue[i]; } - cctx->optState.litLengthSum = 0; /* force reset of btopt stats */ + ZSTD_invalidateMatchState(&cctx->matchState); + ZSTD_resetBlockState(cctx->prevBlock); XXH64_reset(&cctx->xxhState, 0); return 0; } @@ -846,10 +872,6 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->appliedParams.cParams.windowLog, (U32)zc->blockSize); assert(!(params.ldmParams.enableLdm && params.ldmParams.hashEveryLog == ZSTD_LDM_HASHEVERYLOG_NOTSET)); - zc->entropy->hufCTable_repeatMode = HUF_repeat_none; - zc->entropy->offcode_repeatMode = FSE_repeat_none; - zc->entropy->matchlength_repeatMode = FSE_repeat_none; - zc->entropy->litlength_repeatMode = FSE_repeat_none; return ZSTD_continueCCtx(zc, params, pledgedSrcSize); } } DEBUGLOG(4, "ZSTD_equivalentParams()==0 -> reset CCtx"); @@ -880,7 +902,8 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, void* ptr; /* Check if workSpace is large enough, alloc a new one if needed */ - { size_t const entropySpace = sizeof(ZSTD_entropyCTables_t); + { size_t const entropySpace = HUF_WORKSPACE_SIZE; + size_t const blockStateSpace = 2 * sizeof(ZSTD_blockState_t); size_t const optPotentialSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<>10), (U32)(tableSpace>>10), (U32)(bufferSpace>>10)); @@ -911,10 +934,13 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->workSpaceSize = neededSpace; ptr = zc->workSpace; - /* entropy space */ + /* Statically sized space. entropyWorkspace never moves (but prev/next block swap places) */ assert(((size_t)zc->workSpace & 3) == 0); /* ensure correct alignment */ - assert(zc->workSpaceSize >= sizeof(ZSTD_entropyCTables_t)); - zc->entropy = (ZSTD_entropyCTables_t*)zc->workSpace; + assert(zc->workSpaceSize >= 2 * sizeof(ZSTD_blockState_t)); + zc->prevBlock = (ZSTD_blockState_t*)zc->workSpace; + zc->nextBlock = zc->prevBlock + 1; + ptr = zc->nextBlock + 1; + zc->entropyWorkspace = (U32*)ptr; } } /* init params */ @@ -930,36 +956,31 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, XXH64_reset(&zc->xxhState, 0); zc->stage = ZSTDcs_init; zc->dictID = 0; - zc->loadedDictEnd = 0; - zc->entropy->hufCTable_repeatMode = HUF_repeat_none; - zc->entropy->offcode_repeatMode = FSE_repeat_none; - zc->entropy->matchlength_repeatMode = FSE_repeat_none; - zc->entropy->litlength_repeatMode = FSE_repeat_none; - zc->base = NULL; - zc->dictBase = NULL; - zc->lowLimit = 0; - zc->dictLimit = 0; - zc->nextToUpdate = 1; - zc->nextSrc = NULL; - { int i; for (i=0; iseqStore.rep[i] = repStartValue[i]; } - zc->hashLog3 = hashLog3; - zc->optState.litLengthSum = 0; - ptr = zc->entropy + 1; + zc->matchState.nextSrc = NULL; + zc->matchState.base = NULL; + zc->matchState.dictBase = NULL; + zc->matchState.hashLog3 = hashLog3; + ZSTD_invalidateMatchState(&zc->matchState); + + ZSTD_resetBlockState(zc->prevBlock); + + ptr = zc->entropyWorkspace + HUF_WORKSPACE_SIZE_U32; /* opt parser space */ if ((params.cParams.strategy == ZSTD_btopt) | (params.cParams.strategy == ZSTD_btultra)) { + optState_t* opt = &zc->matchState.opt; DEBUGLOG(4, "reserving optimal parser space"); assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ - zc->optState.litFreq = (U32*)ptr; - zc->optState.litLengthFreq = zc->optState.litFreq + (1<optState.matchLengthFreq = zc->optState.litLengthFreq + (MaxLL+1); - zc->optState.offCodeFreq = zc->optState.matchLengthFreq + (MaxML+1); - ptr = zc->optState.offCodeFreq + (MaxOff+1); - zc->optState.matchTable = (ZSTD_match_t*)ptr; - ptr = zc->optState.matchTable + ZSTD_OPT_NUM+1; - zc->optState.priceTable = (ZSTD_optimal_t*)ptr; - ptr = zc->optState.priceTable + ZSTD_OPT_NUM+1; + opt->litFreq = (U32*)ptr; + opt->litLengthFreq = opt->litFreq + (1<matchLengthFreq = opt->litLengthFreq + (MaxLL+1); + opt->offCodeFreq = opt->matchLengthFreq + (MaxML+1); + ptr = opt->offCodeFreq + (MaxOff+1); + opt->matchTable = (ZSTD_match_t*)ptr; + ptr = opt->matchTable + ZSTD_OPT_NUM+1; + opt->priceTable = (ZSTD_optimal_t*)ptr; + ptr = opt->priceTable + ZSTD_OPT_NUM+1; } /* ldm hash table */ @@ -976,10 +997,10 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, DEBUGLOG(4, "reset table : %u", crp!=ZSTDcrp_noMemset); if (crp!=ZSTDcrp_noMemset) memset(ptr, 0, tableSpace); /* reset tables only */ assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ - zc->hashTable = (U32*)(ptr); - zc->chainTable = zc->hashTable + hSize; - zc->hashTable3 = zc->chainTable + chainSize; - ptr = zc->hashTable3 + h3Size; + zc->matchState.hashTable = (U32*)(ptr); + zc->matchState.chainTable = zc->matchState.hashTable + hSize; + zc->matchState.hashTable3 = zc->matchState.chainTable + chainSize; + ptr = zc->matchState.hashTable3 + h3Size; /* sequences storage */ zc->seqStore.sequencesStart = (seqDef*)ptr; @@ -1016,7 +1037,8 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, * do not use with extDict variant ! */ void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) { int i; - for (i=0; iseqStore.rep[i] = 0; + for (i=0; iprevBlock->rep[i] = 0; + assert(/* !extDict */ cctx->matchState.lowLimit == cctx->matchState.dictLimit); } @@ -1050,32 +1072,30 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, /* copy tables */ { size_t const chainSize = (srcCCtx->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << srcCCtx->appliedParams.cParams.chainLog); size_t const hSize = (size_t)1 << srcCCtx->appliedParams.cParams.hashLog; - size_t const h3Size = (size_t)1 << srcCCtx->hashLog3; + size_t const h3Size = (size_t)1 << srcCCtx->matchState.hashLog3; size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); - assert((U32*)dstCCtx->chainTable == (U32*)dstCCtx->hashTable + hSize); /* chainTable must follow hashTable */ - assert((U32*)dstCCtx->hashTable3 == (U32*)dstCCtx->chainTable + chainSize); - memcpy(dstCCtx->hashTable, srcCCtx->hashTable, tableSpace); /* presumes all tables follow each other */ + assert((U32*)dstCCtx->matchState.chainTable == (U32*)dstCCtx->matchState.hashTable + hSize); /* chainTable must follow hashTable */ + assert((U32*)dstCCtx->matchState.hashTable3 == (U32*)dstCCtx->matchState.chainTable + chainSize); + memcpy(dstCCtx->matchState.hashTable, srcCCtx->matchState.hashTable, tableSpace); /* presumes all tables follow each other */ } /* copy dictionary offsets */ - dstCCtx->nextToUpdate = srcCCtx->nextToUpdate; - dstCCtx->nextToUpdate3= srcCCtx->nextToUpdate3; - dstCCtx->nextSrc = srcCCtx->nextSrc; - dstCCtx->base = srcCCtx->base; - dstCCtx->dictBase = srcCCtx->dictBase; - dstCCtx->dictLimit = srcCCtx->dictLimit; - dstCCtx->lowLimit = srcCCtx->lowLimit; - dstCCtx->loadedDictEnd= srcCCtx->loadedDictEnd; - dstCCtx->dictID = srcCCtx->dictID; - - /* copy entropy tables */ - memcpy(dstCCtx->entropy, srcCCtx->entropy, sizeof(ZSTD_entropyCTables_t)); - /* copy repcodes */ { - int i; - for (i = 0; i < ZSTD_REP_NUM; ++i) - dstCCtx->seqStore.rep[i] = srcCCtx->seqStore.rep[i]; + ZSTD_matchState_t const* srcMatchState = &srcCCtx->matchState; + ZSTD_matchState_t* dstMatchState = &dstCCtx->matchState; + dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; + dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3; + dstMatchState->nextSrc = srcMatchState->nextSrc; + dstMatchState->base = srcMatchState->base; + dstMatchState->dictBase = srcMatchState->dictBase; + dstMatchState->dictLimit = srcMatchState->dictLimit; + dstMatchState->lowLimit = srcMatchState->lowLimit; + dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd; } + dstCCtx->dictID = srcCCtx->dictID; + + /* copy block state */ + memcpy(dstCCtx->prevBlock, srcCCtx->prevBlock, sizeof(*srcCCtx->prevBlock)); return 0; } @@ -1142,20 +1162,21 @@ static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size, * rescale all indexes to avoid future overflow (indexes are U32) */ static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue) { + ZSTD_matchState_t* const ms = &zc->matchState; { U32 const hSize = (U32)1 << zc->appliedParams.cParams.hashLog; - ZSTD_reduceTable(zc->hashTable, hSize, reducerValue); + ZSTD_reduceTable(ms->hashTable, hSize, reducerValue); } if (zc->appliedParams.cParams.strategy != ZSTD_fast) { U32 const chainSize = (U32)1 << zc->appliedParams.cParams.chainLog; if (zc->appliedParams.cParams.strategy == ZSTD_btlazy2) - ZSTD_preserveUnsortedMark(zc->chainTable, chainSize, reducerValue); - ZSTD_reduceTable(zc->chainTable, chainSize, reducerValue); + ZSTD_preserveUnsortedMark(ms->chainTable, chainSize, reducerValue); + ZSTD_reduceTable(ms->chainTable, chainSize, reducerValue); } - if (zc->hashLog3) { - U32 const h3Size = (U32)1 << zc->hashLog3; - ZSTD_reduceTable(zc->hashTable3, h3Size, reducerValue); + if (ms->hashLog3) { + U32 const h3Size = (U32)1 << ms->hashLog3; + ZSTD_reduceTable(ms->hashTable3, h3Size, reducerValue); } if (zc->appliedParams.ldmParams.enableLdm) { @@ -1235,10 +1256,12 @@ static size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, cons static size_t ZSTD_minGain(size_t srcSize) { return (srcSize >> 6) + 2; } -static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t * entropy, +static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, ZSTD_strategy strategy, void* dst, size_t dstCapacity, - const void* src, size_t srcSize) + const void* src, size_t srcSize, + U32* workspace) { size_t const minGain = ZSTD_minGain(srcSize); size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB); @@ -1247,34 +1270,45 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t * entropy, symbolEncodingType_e hType = set_compressed; size_t cLitSize; + /* Prepare nextEntropy assuming reusing the existing table */ + nextEntropy->hufCTable_repeatMode = prevEntropy->hufCTable_repeatMode; + memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, + sizeof(prevEntropy->hufCTable)); /* small ? don't even attempt compression (speed opt) */ # define LITERAL_NOENTROPY 63 - { size_t const minLitSize = entropy->hufCTable_repeatMode == HUF_repeat_valid ? 6 : LITERAL_NOENTROPY; + { size_t const minLitSize = prevEntropy->hufCTable_repeatMode == HUF_repeat_valid ? 6 : LITERAL_NOENTROPY; if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } if (dstCapacity < lhSize+1) return ERROR(dstSize_tooSmall); /* not enough space for compression */ - { HUF_repeat repeat = entropy->hufCTable_repeatMode; + { HUF_repeat repeat = prevEntropy->hufCTable_repeatMode; int const preferRepeat = strategy < ZSTD_lazy ? srcSize <= 1024 : 0; if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1; cLitSize = singleStream ? HUF_compress1X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, - entropy->workspace, sizeof(entropy->workspace), (HUF_CElt*)entropy->hufCTable, &repeat, preferRepeat) + workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat) : HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, - entropy->workspace, sizeof(entropy->workspace), (HUF_CElt*)entropy->hufCTable, &repeat, preferRepeat); - if (repeat != HUF_repeat_none) { hType = set_repeat; } /* reused the existing table */ - else { entropy->hufCTable_repeatMode = HUF_repeat_check; } /* now have a table to reuse */ + workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat); + if (repeat != HUF_repeat_none) { + /* reused the existing table */ + hType = set_repeat; + } } if ((cLitSize==0) | (cLitSize >= srcSize - minGain) | ERR_isError(cLitSize)) { - entropy->hufCTable_repeatMode = HUF_repeat_none; + memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, sizeof(prevEntropy->hufCTable)); return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } if (cLitSize==1) { - entropy->hufCTable_repeatMode = HUF_repeat_none; + memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, sizeof(prevEntropy->hufCTable)); return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); } + if (hType == set_compressed) { + /* using a newly constructed table */ + nextEntropy->hufCTable_repeatMode = HUF_repeat_check; + } + /* Build header */ switch(lhSize) { @@ -1368,10 +1402,11 @@ symbolEncodingType_e ZSTD_selectEncodingType( MEM_STATIC size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, - FSE_CTable* CTable, U32 FSELog, symbolEncodingType_e type, + FSE_CTable* nextCTable, U32 FSELog, symbolEncodingType_e type, U32* count, U32 max, BYTE const* codeTable, size_t nbSeq, S16 const* defaultNorm, U32 defaultNormLog, U32 defaultMax, + FSE_CTable const* prevCTable, size_t prevCTableSize, void* workspace, size_t workspaceSize) { BYTE* op = (BYTE*)dst; @@ -1380,12 +1415,13 @@ size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, switch (type) { case set_rle: *op = codeTable[0]; - CHECK_F(FSE_buildCTable_rle(CTable, (BYTE)max)); + CHECK_F(FSE_buildCTable_rle(nextCTable, (BYTE)max)); return 1; case set_repeat: + memcpy(nextCTable, prevCTable, prevCTableSize); return 0; case set_basic: - CHECK_F(FSE_buildCTable_wksp(CTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize)); /* note : could be pre-calculated */ + CHECK_F(FSE_buildCTable_wksp(nextCTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize)); /* note : could be pre-calculated */ return 0; case set_compressed: { S16 norm[MaxSeq + 1]; @@ -1399,7 +1435,7 @@ size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, CHECK_F(FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max)); { size_t const NCountSize = FSE_writeNCount(op, oend - op, norm, max, tableLog); /* overflow protected */ if (FSE_isError(NCountSize)) return NCountSize; - CHECK_F(FSE_buildCTable_wksp(CTable, norm, max, tableLog, workspace, workspaceSize)); + CHECK_F(FSE_buildCTable_wksp(nextCTable, norm, max, tableLog, workspace, workspaceSize)); return NCountSize; } } @@ -1492,15 +1528,16 @@ size_t ZSTD_encodeSequences( } MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, - ZSTD_entropyCTables_t* entropy, + ZSTD_entropyCTables_t const* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, ZSTD_compressionParameters const* cParams, - void* dst, size_t dstCapacity) + void* dst, size_t dstCapacity, U32* workspace) { const int longOffsets = cParams->windowLog > STREAM_ACCUMULATOR_MIN; U32 count[MaxSeq+1]; - FSE_CTable* CTable_LitLength = entropy->litlengthCTable; - FSE_CTable* CTable_OffsetBits = entropy->offcodeCTable; - FSE_CTable* CTable_MatchLength = entropy->matchlengthCTable; + FSE_CTable* CTable_LitLength = nextEntropy->litlengthCTable; + FSE_CTable* CTable_OffsetBits = nextEntropy->offcodeCTable; + FSE_CTable* CTable_MatchLength = nextEntropy->matchlengthCTable; U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */ const seqDef* const sequences = seqStorePtr->sequencesStart; const BYTE* const ofCodeTable = seqStorePtr->ofCode; @@ -1512,13 +1549,14 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; BYTE* seqHead; - ZSTD_STATIC_ASSERT(sizeof(entropy->workspace) >= (1<= (1<litStart; size_t const litSize = seqStorePtr->lit - literals; - size_t const cSize = ZSTD_compressLiterals( - entropy, cParams->strategy, op, dstCapacity, literals, litSize); + size_t const cSize = ZSTD_compressLiterals(prevEntropy, nextEntropy, + cParams->strategy, op, dstCapacity, literals, litSize, + workspace); if (ZSTD_isError(cSize)) return cSize; assert(cSize <= dstCapacity); @@ -1533,7 +1571,15 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2; else op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3; - if (nbSeq==0) return op - ostart; + if (nbSeq==0) { + memcpy(nextEntropy->litlengthCTable, prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable)); + nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode; + memcpy(nextEntropy->offcodeCTable, prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable)); + nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode; + memcpy(nextEntropy->matchlengthCTable, prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable)); + nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode; + return op - ostart; + } /* seqHead : flags for FSE encoding type */ seqHead = op++; @@ -1542,36 +1588,42 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, ZSTD_seqToCodes(seqStorePtr); /* build CTable for Literal Lengths */ { U32 max = MaxLL; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, entropy->workspace); + size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, workspace); DEBUGLOG(5, "Building LL table"); - LLtype = ZSTD_selectEncodingType(&entropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog, ZSTD_defaultAllowed); + nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode; + LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog, ZSTD_defaultAllowed); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype, count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, - entropy->workspace, sizeof(entropy->workspace)); + prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable), + workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; } } /* build CTable for Offsets */ { U32 max = MaxOff; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, entropy->workspace); + size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, workspace); /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */ ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed; DEBUGLOG(5, "Building OF table"); - Offtype = ZSTD_selectEncodingType(&entropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog, defaultPolicy); + nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode; + Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog, defaultPolicy); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, - entropy->workspace, sizeof(entropy->workspace)); + prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable), + workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; } } /* build CTable for MatchLengths */ { U32 max = MaxML; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, entropy->workspace); + size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, workspace); DEBUGLOG(5, "Building ML table"); - MLtype = ZSTD_selectEncodingType(&entropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog, ZSTD_defaultAllowed); + nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode; + MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog, ZSTD_defaultAllowed); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype, count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, - entropy->workspace, sizeof(entropy->workspace)); + prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable), + workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; } } @@ -1593,13 +1645,15 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, } MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, - ZSTD_entropyCTables_t* entropy, + ZSTD_entropyCTables_t const* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, ZSTD_compressionParameters const* cParams, void* dst, size_t dstCapacity, - size_t srcSize) + size_t srcSize, U32* workspace) { - size_t const cSize = ZSTD_compressSequences_internal(seqStorePtr, entropy, cParams, - dst, dstCapacity); + size_t const cSize = ZSTD_compressSequences_internal( + seqStorePtr, prevEntropy, nextEntropy, cParams, dst, dstCapacity, + workspace); /* If the srcSize <= dstCapacity, then there is enough space to write a * raw uncompressed block. Since we ran out of space, the block must not * be compressible, so fall back to a raw uncompressed block. @@ -1607,34 +1661,28 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, int const uncompressibleError = (cSize == ERROR(dstSize_tooSmall)) && (srcSize <= dstCapacity); if (ZSTD_isError(cSize) && !uncompressibleError) return cSize; - /* We check that dictionaries have offset codes available for the first - * block. After the first block, the offcode table might not have large - * enough codes to represent the offsets in the data. - */ - if (entropy->offcode_repeatMode == FSE_repeat_valid) - entropy->offcode_repeatMode = FSE_repeat_check; /* Check compressibility */ { size_t const minGain = ZSTD_minGain(srcSize); /* note : fixed formula, maybe should depend on compression level, or strategy */ size_t const maxCSize = srcSize - minGain; if (cSize >= maxCSize || uncompressibleError) { - entropy->hufCTable_repeatMode = HUF_repeat_none; - entropy->offcode_repeatMode = FSE_repeat_none; - entropy->matchlength_repeatMode = FSE_repeat_none; - entropy->litlength_repeatMode = FSE_repeat_none; return 0; /* block not compressed */ } } assert(!ZSTD_isError(cSize)); - /* block is compressed => confirm repcodes in history */ - { int i; for (i=0; irep[i] = seqStorePtr->repToConfirm[i]; } + /* We check that dictionaries have offset codes available for the first + * block. After the first block, the offcode table might not have large + * enough codes to represent the offsets in the data. + */ + if (nextEntropy->offcode_repeatMode == FSE_repeat_valid) + nextEntropy->offcode_repeatMode = FSE_repeat_check; + return cSize; } /* ZSTD_selectBlockCompressor() : * Not static, but internal use only (used by long distance matcher) * assumption : strat is a valid strategy */ -typedef size_t (*ZSTD_blockCompressor) (ZSTD_CCtx* ctx, const void* src, size_t srcSize); ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict) { static const ZSTD_blockCompressor blockCompressor[2][(unsigned)ZSTD_btultra+1] = { @@ -1673,30 +1721,54 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u) (dictLimit=%u, nextToUpdate=%u)", - (U32)dstCapacity, zc->dictLimit, zc->nextToUpdate); + (U32)dstCapacity, zc->matchState.dictLimit, zc->matchState.nextToUpdate); if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) return 0; /* don't even attempt compression below a certain srcSize */ ZSTD_resetSeqStore(&(zc->seqStore)); /* limited update after a very long match */ - { const BYTE* const base = zc->base; + { const BYTE* const base = zc->matchState.base; const BYTE* const istart = (const BYTE*)src; const U32 current = (U32)(istart-base); - if (current > zc->nextToUpdate + 384) - zc->nextToUpdate = current - MIN(192, (U32)(current - zc->nextToUpdate - 384)); + if (current > zc->matchState.nextToUpdate + 384) + zc->matchState.nextToUpdate = current - MIN(192, (U32)(current - zc->matchState.nextToUpdate - 384)); } /* find and store sequences */ - { U32 const extDict = zc->lowLimit < zc->dictLimit; - const ZSTD_blockCompressor blockCompressor = - zc->appliedParams.ldmParams.enableLdm - ? (extDict ? ZSTD_compressBlock_ldm_extDict : ZSTD_compressBlock_ldm) - : ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict); - size_t const lastLLSize = blockCompressor(zc, src, srcSize); - const BYTE* const anchor = (const BYTE*)src + srcSize - lastLLSize; - ZSTD_storeLastLiterals(&zc->seqStore, anchor, lastLLSize); + { + U32 const extDict = zc->matchState.lowLimit < zc->matchState.dictLimit; + size_t lastLLSize; + { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) zc->nextBlock->rep[i] = zc->prevBlock->rep[i]; } + if (zc->appliedParams.ldmParams.enableLdm) { + typedef size_t (*ZSTD_ldmBlockCompressor)( + ldmState_t* ldms, ZSTD_matchState_t* ms, seqStore_t* seqStore, + U32 rep[ZSTD_REP_NUM], ZSTD_CCtx_params const* params, + void const* src, size_t srcSize); + ZSTD_ldmBlockCompressor const ldmBlockCompressor = extDict ? ZSTD_compressBlock_ldm_extDict : ZSTD_compressBlock_ldm; + + lastLLSize = ldmBlockCompressor(&zc->ldmState, &zc->matchState, &zc->seqStore, zc->nextBlock->rep, &zc->appliedParams, src, srcSize); + } else { + ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict); + + lastLLSize = blockCompressor(&zc->matchState, &zc->seqStore, zc->nextBlock->rep, &zc->appliedParams.cParams, src, srcSize); + } + { + const BYTE* const anchor = (const BYTE*)src + srcSize - lastLLSize; + ZSTD_storeLastLiterals(&zc->seqStore, anchor, lastLLSize); + } } /* encode */ - return ZSTD_compressSequences(&zc->seqStore, zc->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize); + { + size_t const cSize = ZSTD_compressSequences(&zc->seqStore, &zc->prevBlock->entropy, &zc->nextBlock->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize, zc->entropyWorkspace); + if (ZSTD_isError(cSize) || cSize == 0) + return cSize; + /* confirm repcodes and entropy tables */ + { + ZSTD_blockState_t* const tmp = zc->prevBlock; + zc->prevBlock = zc->nextBlock; + zc->nextBlock = tmp; + } + return cSize; + } } @@ -1725,6 +1797,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, XXH64_update(&cctx->xxhState, src, srcSize); while (remaining) { + ZSTD_matchState_t* const ms = &cctx->matchState; U32 const lastBlock = lastFrameChunk & (blockSize >= remaining); if (dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE) @@ -1748,9 +1821,9 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, * 3. cctx->lowLimit < 1<<32: * windowLog <= 31 ==> 3<<29 + 1<lowLimit > (3U<<29)) { + if (ms->lowLimit > (3U<<29)) { U32 const cycleMask = ((U32)1 << ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy)) - 1; - U32 const current = (U32)(ip - cctx->base); + U32 const current = (U32)(ip - cctx->matchState.base); U32 const newCurrent = (current & cycleMask) + ((U32)1 << cctx->appliedParams.cParams.windowLog); U32 const correction = current - newCurrent; ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30); @@ -1759,23 +1832,23 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, assert(current > newCurrent); assert(correction > 1<<28); /* Loose bound, should be about 1<<29 */ ZSTD_reduceIndex(cctx, correction); - cctx->base += correction; - cctx->dictBase += correction; - cctx->lowLimit -= correction; - cctx->dictLimit -= correction; - if (cctx->nextToUpdate < correction) cctx->nextToUpdate = 0; - else cctx->nextToUpdate -= correction; - DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x", correction, cctx->lowLimit); + ms->base += correction; + ms->dictBase += correction; + ms->lowLimit -= correction; + ms->dictLimit -= correction; + if (ms->nextToUpdate < correction) ms->nextToUpdate = 0; + else ms->nextToUpdate -= correction; + DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x", correction, ms->lowLimit); } /* enforce maxDist */ - if ((U32)(ip+blockSize - cctx->base) > cctx->loadedDictEnd + maxDist) { - U32 const newLowLimit = (U32)(ip+blockSize - cctx->base) - maxDist; - if (cctx->lowLimit < newLowLimit) cctx->lowLimit = newLowLimit; - if (cctx->dictLimit < cctx->lowLimit) + if ((U32)(ip+blockSize - ms->base) > ms->loadedDictEnd + maxDist) { + U32 const newLowLimit = (U32)(ip+blockSize - ms->base) - maxDist; + if (ms->lowLimit < newLowLimit) ms->lowLimit = newLowLimit; + if (ms->dictLimit < ms->lowLimit) DEBUGLOG(5, "ZSTD_compress_frameChunk : update dictLimit from %u to %u ", - cctx->dictLimit, cctx->lowLimit); - if (cctx->dictLimit < cctx->lowLimit) cctx->dictLimit = cctx->lowLimit; - if (cctx->nextToUpdate < cctx->lowLimit) cctx->nextToUpdate = cctx->lowLimit; + ms->dictLimit, ms->lowLimit); + if (ms->dictLimit < ms->lowLimit) ms->dictLimit = ms->lowLimit; + if (ms->nextToUpdate < ms->lowLimit) ms->nextToUpdate = ms->lowLimit; } { size_t cSize = ZSTD_compressBlock_internal(cctx, @@ -1854,12 +1927,38 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, } +static void ZSTD_manageWindowContinuity(ZSTD_matchState_t* ms, void const* src, size_t srcSize) +{ + const BYTE* const ip = (const BYTE*) src; + + /* Check if blocks follow each other */ + if (src != ms->nextSrc) { + /* not contiguous */ + size_t const distanceFromBase = (size_t)(ms->nextSrc - ms->base); + DEBUGLOG(5, "ZSTD_manageWindowContinuity: non contiguous blocks, new segment starts at %u", ms->dictLimit); + ms->lowLimit = ms->dictLimit; + assert(distanceFromBase == (size_t)(U32)distanceFromBase); /* should never overflow */ + ms->dictLimit = (U32)distanceFromBase; + ms->dictBase = ms->base; + ms->base = ip - distanceFromBase; + ms->nextToUpdate = ms->dictLimit; + if (ms->dictLimit - ms->lowLimit < HASH_READ_SIZE) ms->lowLimit = ms->dictLimit; /* too small extDict */ + } + ms->nextSrc = ip + srcSize; + /* if input and dictionary overlap : reduce dictionary (area presumed modified by input) */ + if ((ip+srcSize > ms->dictBase + ms->lowLimit) & (ip < ms->dictBase + ms->dictLimit)) { + ptrdiff_t const highInputIdx = (ip + srcSize) - ms->dictBase; + U32 const lowLimitMax = (highInputIdx > (ptrdiff_t)ms->dictLimit) ? ms->dictLimit : (U32)highInputIdx; + ms->lowLimit = lowLimitMax; + } +} + + static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 frame, U32 lastFrameChunk) { - const BYTE* const ip = (const BYTE*) src; size_t fhSize = 0; DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u", cctx->stage); @@ -1876,27 +1975,7 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, if (!srcSize) return fhSize; /* do not generate an empty block if no input */ - /* Check if blocks follow each other */ - if (src != cctx->nextSrc) { - size_t const distanceFromBase = (size_t)(cctx->nextSrc - cctx->base); - DEBUGLOG(5, "ZSTD_compressContinue_internal: non contiguous blocks, new segment starts at %u", - cctx->dictLimit); - cctx->lowLimit = cctx->dictLimit; - assert(distanceFromBase == (size_t)(U32)distanceFromBase); /* should never overflow */ - cctx->dictLimit = (U32)distanceFromBase; - cctx->dictBase = cctx->base; - cctx->base = ip - distanceFromBase; - cctx->nextToUpdate = cctx->dictLimit; - if (cctx->dictLimit - cctx->lowLimit < HASH_READ_SIZE) cctx->lowLimit = cctx->dictLimit; /* too small extDict */ - } - cctx->nextSrc = ip + srcSize; - - /* if input and dictionary overlap : reduce dictionary (area presumed modified by input) */ - if ((ip+srcSize > cctx->dictBase + cctx->lowLimit) & (ip < cctx->dictBase + cctx->dictLimit)) { - ptrdiff_t const highInputIdx = (ip + srcSize) - cctx->dictBase; - U32 const lowLimitMax = (highInputIdx > (ptrdiff_t)cctx->dictLimit) ? cctx->dictLimit : (U32)highInputIdx; - cctx->lowLimit = lowLimitMax; - } + ZSTD_manageWindowContinuity(&cctx->matchState, src, srcSize); DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (U32)cctx->blockSize); { size_t const cSize = frame ? @@ -1934,50 +2013,51 @@ size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const /*! ZSTD_loadDictionaryContent() : * @return : 0, or an error code */ -static size_t ZSTD_loadDictionaryContent(ZSTD_CCtx* zc, const void* src, size_t srcSize) +static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, ZSTD_CCtx_params const* params, const void* src, size_t srcSize) { const BYTE* const ip = (const BYTE*) src; const BYTE* const iend = ip + srcSize; + ZSTD_compressionParameters const* cParams = ¶ms->cParams; /* input becomes current prefix */ - zc->lowLimit = zc->dictLimit; - zc->dictLimit = (U32)(zc->nextSrc - zc->base); - zc->dictBase = zc->base; - zc->base = ip - zc->dictLimit; - zc->nextToUpdate = zc->dictLimit; - zc->loadedDictEnd = zc->appliedParams.forceWindow ? 0 : (U32)(iend - zc->base); + ms->lowLimit = ms->dictLimit; + ms->dictLimit = (U32)(ms->nextSrc - ms->base); + ms->dictBase = ms->base; + ms->base = ip - ms->dictLimit; + ms->nextToUpdate = ms->dictLimit; + ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->base); - zc->nextSrc = iend; + ms->nextSrc = iend; if (srcSize <= HASH_READ_SIZE) return 0; - switch(zc->appliedParams.cParams.strategy) + switch(params->cParams.strategy) { case ZSTD_fast: - ZSTD_fillHashTable (zc, iend, zc->appliedParams.cParams.searchLength); + ZSTD_fillHashTable(ms, cParams, iend); break; case ZSTD_dfast: - ZSTD_fillDoubleHashTable (zc, iend, zc->appliedParams.cParams.searchLength); + ZSTD_fillDoubleHashTable(ms, cParams, iend); break; case ZSTD_greedy: case ZSTD_lazy: case ZSTD_lazy2: if (srcSize >= HASH_READ_SIZE) - ZSTD_insertAndFindFirstIndex(zc, iend-HASH_READ_SIZE, zc->appliedParams.cParams.searchLength); + ZSTD_insertAndFindFirstIndex(ms, cParams, iend-HASH_READ_SIZE); break; case ZSTD_btlazy2: /* we want the dictionary table fully sorted */ case ZSTD_btopt: case ZSTD_btultra: if (srcSize >= HASH_READ_SIZE) - ZSTD_updateTree(zc, iend-HASH_READ_SIZE, iend, (U32)1 << zc->appliedParams.cParams.searchLog, zc->appliedParams.cParams.searchLength); + ZSTD_updateTree(ms, cParams, iend-HASH_READ_SIZE, iend); break; default: assert(0); /* not possible : not a valid strategy id */ } - zc->nextToUpdate = (U32)(iend - zc->base); + ms->nextToUpdate = (U32)(iend - ms->base); return 0; } @@ -2001,25 +2081,26 @@ static size_t ZSTD_checkDictNCount(short* normalizedCounter, unsigned dictMaxSym * https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format */ /*! ZSTD_loadZstdDictionary() : - * @return : 0, or an error code + * @return : dictID, or an error code * assumptions : magic number supposed already checked * dictSize supposed > 8 */ -static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize) +static size_t ZSTD_loadZstdDictionary(ZSTD_blockState_t* bs, ZSTD_matchState_t* ms, ZSTD_CCtx_params const* params, const void* dict, size_t dictSize, void* workspace) { const BYTE* dictPtr = (const BYTE*)dict; const BYTE* const dictEnd = dictPtr + dictSize; short offcodeNCount[MaxOff+1]; unsigned offcodeMaxValue = MaxOff; + size_t dictID; - ZSTD_STATIC_ASSERT(sizeof(cctx->entropy->workspace) >= (1<= (1<dictID = cctx->appliedParams.fParams.noDictIDFlag ? 0 : MEM_readLE32(dictPtr); + dictID = params->fParams.noDictIDFlag ? 0 : MEM_readLE32(dictPtr); dictPtr += 4; { unsigned maxSymbolValue = 255; - size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)cctx->entropy->hufCTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr); + size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.hufCTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr); if (HUF_isError(hufHeaderSize)) return ERROR(dictionary_corrupted); if (maxSymbolValue < 255) return ERROR(dictionary_corrupted); dictPtr += hufHeaderSize; @@ -2030,7 +2111,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted); if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted); /* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */ - CHECK_E( FSE_buildCTable_wksp(cctx->entropy->offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)), + CHECK_E( FSE_buildCTable_wksp(bs->entropy.offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog, workspace, HUF_WORKSPACE_SIZE), dictionary_corrupted); dictPtr += offcodeHeaderSize; } @@ -2042,7 +2123,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t if (matchlengthLog > MLFSELog) return ERROR(dictionary_corrupted); /* Every match length code must have non-zero probability */ CHECK_F( ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML)); - CHECK_E( FSE_buildCTable_wksp(cctx->entropy->matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)), + CHECK_E( FSE_buildCTable_wksp(bs->entropy.matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, workspace, HUF_WORKSPACE_SIZE), dictionary_corrupted); dictPtr += matchlengthHeaderSize; } @@ -2054,15 +2135,15 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t if (litlengthLog > LLFSELog) return ERROR(dictionary_corrupted); /* Every literal length code must have non-zero probability */ CHECK_F( ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL)); - CHECK_E( FSE_buildCTable_wksp(cctx->entropy->litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)), + CHECK_E( FSE_buildCTable_wksp(bs->entropy.litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, workspace, HUF_WORKSPACE_SIZE), dictionary_corrupted); dictPtr += litlengthHeaderSize; } if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted); - cctx->seqStore.rep[0] = MEM_readLE32(dictPtr+0); - cctx->seqStore.rep[1] = MEM_readLE32(dictPtr+4); - cctx->seqStore.rep[2] = MEM_readLE32(dictPtr+8); + bs->rep[0] = MEM_readLE32(dictPtr+0); + bs->rep[1] = MEM_readLE32(dictPtr+4); + bs->rep[2] = MEM_readLE32(dictPtr+8); dictPtr += 12; { size_t const dictContentSize = (size_t)(dictEnd - dictPtr); @@ -2076,15 +2157,16 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t /* All repCodes must be <= dictContentSize and != 0*/ { U32 u; for (u=0; u<3; u++) { - if (cctx->seqStore.rep[u] == 0) return ERROR(dictionary_corrupted); - if (cctx->seqStore.rep[u] > dictContentSize) return ERROR(dictionary_corrupted); + if (bs->rep[u] == 0) return ERROR(dictionary_corrupted); + if (bs->rep[u] > dictContentSize) return ERROR(dictionary_corrupted); } } - cctx->entropy->hufCTable_repeatMode = HUF_repeat_valid; - cctx->entropy->offcode_repeatMode = FSE_repeat_valid; - cctx->entropy->matchlength_repeatMode = FSE_repeat_valid; - cctx->entropy->litlength_repeatMode = FSE_repeat_valid; - return ZSTD_loadDictionaryContent(cctx, dictPtr, dictContentSize); + bs->entropy.hufCTable_repeatMode = HUF_repeat_valid; + bs->entropy.offcode_repeatMode = FSE_repeat_valid; + bs->entropy.matchlength_repeatMode = FSE_repeat_valid; + bs->entropy.litlength_repeatMode = FSE_repeat_valid; + CHECK_F(ZSTD_loadDictionaryContent(ms, params, dictPtr, dictContentSize)); + return dictID; } } @@ -2099,12 +2181,12 @@ static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* cctx, /* dict restricted modes */ if (dictMode==ZSTD_dm_rawContent) - return ZSTD_loadDictionaryContent(cctx, dict, dictSize); + return ZSTD_loadDictionaryContent(&cctx->matchState, &cctx->appliedParams, dict, dictSize); if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) { if (dictMode == ZSTD_dm_auto) { DEBUGLOG(4, "raw content dictionary detected"); - return ZSTD_loadDictionaryContent(cctx, dict, dictSize); + return ZSTD_loadDictionaryContent(&cctx->matchState, &cctx->appliedParams, dict, dictSize); } if (dictMode == ZSTD_dm_fullDict) return ERROR(dictionary_wrong); @@ -2112,7 +2194,12 @@ static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* cctx, } /* dict as full zstd dictionary */ - return ZSTD_loadZstdDictionary(cctx, dict, dictSize); + { + size_t const dictID = ZSTD_loadZstdDictionary(cctx->prevBlock, &cctx->matchState, &cctx->appliedParams, dict, dictSize, cctx->entropyWorkspace); + if (ZSTD_isError(dictID)) return dictID; + cctx->dictID = (U32)dictID; + } + return 0; } /*! ZSTD_compressBegin_internal() : diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index f104fe981..7a7b1db1d 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -51,13 +51,17 @@ typedef struct { FSE_CTable offcodeCTable[FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)]; FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)]; FSE_CTable litlengthCTable[FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)]; - U32 workspace[HUF_WORKSPACE_SIZE_U32]; HUF_repeat hufCTable_repeatMode; FSE_repeat offcode_repeatMode; FSE_repeat matchlength_repeatMode; FSE_repeat litlength_repeatMode; } ZSTD_entropyCTables_t; +typedef struct { + ZSTD_entropyCTables_t entropy; + U32 rep[ZSTD_REP_NUM]; +} ZSTD_blockState_t; + typedef struct { U32 off; U32 len; @@ -93,6 +97,22 @@ typedef struct { U32 staticPrices; /* prices follow a pre-defined cost structure, statistics are irrelevant */ } optState_t; +typedef struct { + BYTE const* nextSrc; /* next block here to continue on current prefix */ /* TODO: Does this belong here? */ + BYTE const* base; /* All regular indexes relative to this position */ + BYTE const* dictBase; /* extDict indexes relative to this position */ + U32 dictLimit; /* below that point, need extDict */ + U32 lowLimit; /* below that point, no more data */ + U32 nextToUpdate; /* index from which to continue table update */ + U32 nextToUpdate3; /* index from which to continue table update */ + U32 hashLog3; /* dispatch table : larger == faster, more memory */ + U32 loadedDictEnd; /* index of end of dictionary */ /* TODO: Does this belong here? */ + U32* hashTable; + U32* hashTable3; + U32* chainTable; + optState_t opt; /* optimal parser state */ +} ZSTD_matchState_t; + typedef struct { U32 offset; U32 checksum; @@ -136,15 +156,6 @@ struct ZSTD_CCtx_params_s { }; /* typedef'd to ZSTD_CCtx_params within "zstd.h" */ struct ZSTD_CCtx_s { - const BYTE* nextSrc; /* next block here to continue on current prefix */ - const BYTE* base; /* All regular indexes relative to this position */ - const BYTE* dictBase; /* extDict indexes relative to this position */ - U32 dictLimit; /* below that point, need extDict */ - U32 lowLimit; /* below that point, no more data */ - U32 nextToUpdate; /* index from which to continue dictionary update */ - U32 nextToUpdate3; /* index from which to continue dictionary update */ - U32 hashLog3; /* dispatch table : larger == faster, more memory */ - U32 loadedDictEnd; /* index of end of dictionary */ ZSTD_compressionStage_e stage; U32 dictID; ZSTD_CCtx_params requestedParams; @@ -159,12 +170,11 @@ struct ZSTD_CCtx_s { size_t staticSize; seqStore_t seqStore; /* sequences storage ptrs */ - optState_t optState; ldmState_t ldmState; /* long distance matching state */ - U32* hashTable; - U32* hashTable3; - U32* chainTable; - ZSTD_entropyCTables_t* entropy; + ZSTD_matchState_t matchState; + ZSTD_blockState_t* prevBlock; + ZSTD_blockState_t* nextBlock; + U32* entropyWorkspace; /* entropy workspace of HUF_WORKSPACE_SIZE bytes */ /* streaming */ char* inBuff; @@ -191,6 +201,12 @@ struct ZSTD_CCtx_s { }; +typedef size_t (*ZSTD_blockCompressor) ( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict); + + MEM_STATIC U32 ZSTD_LLcode(U32 litLength) { static const BYTE LL_Code[64] = { 0, 1, 2, 3, 4, 5, 6, 7, diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index a6d8ad889..834a64b00 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -12,14 +12,17 @@ #include "zstd_double_fast.h" -void ZSTD_fillDoubleHashTable(ZSTD_CCtx* cctx, const void* end, const U32 mls) +void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, + ZSTD_compressionParameters const* cParams, + void const* end) { - U32* const hashLarge = cctx->hashTable; - U32 const hBitsL = cctx->appliedParams.cParams.hashLog; - U32* const hashSmall = cctx->chainTable; - U32 const hBitsS = cctx->appliedParams.cParams.chainLog; - const BYTE* const base = cctx->base; - const BYTE* ip = base + cctx->nextToUpdate; + U32* const hashLarge = ms->hashTable; + U32 const hBitsL = cParams->hashLog; + U32 const mls = cParams->searchLength; + U32* const hashSmall = ms->chainTable; + U32 const hBitsS = cParams->chainLog; + const BYTE* const base = ms->base; + const BYTE* ip = base + ms->nextToUpdate; const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; const U32 fastHashFillStep = 3; @@ -43,24 +46,24 @@ void ZSTD_fillDoubleHashTable(ZSTD_CCtx* cctx, const void* end, const U32 mls) FORCE_INLINE_TEMPLATE -size_t ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx, - const void* src, size_t srcSize, - const U32 mls) +size_t ZSTD_compressBlock_doubleFast_generic( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize, + U32 const mls /* template */) { - U32* const hashLong = cctx->hashTable; - const U32 hBitsL = cctx->appliedParams.cParams.hashLog; - U32* const hashSmall = cctx->chainTable; - const U32 hBitsS = cctx->appliedParams.cParams.chainLog; - seqStore_t* seqStorePtr = &(cctx->seqStore); - const BYTE* const base = cctx->base; + U32* const hashLong = ms->hashTable; + const U32 hBitsL = cParams->hashLog; + U32* const hashSmall = ms->chainTable; + const U32 hBitsS = cParams->chainLog; + const BYTE* const base = ms->base; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = cctx->dictLimit; + const U32 lowestIndex = ms->dictLimit; const BYTE* const lowest = base + lowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; - U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1]; + U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; /* init */ @@ -87,7 +90,7 @@ size_t ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx, /* favor repcode */ mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; - ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else { U32 offset; if ( (matchIndexL > lowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { @@ -117,7 +120,7 @@ size_t ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx, offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } /* match found */ @@ -140,61 +143,63 @@ size_t ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx, { U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */ hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base); hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base); - ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, rLength-MINMATCH); + ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); ip += rLength; anchor = ip; continue; /* faster when present ... (?) */ } } } /* save reps for next block */ - seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved; - seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved; + rep[0] = offset_1 ? offset_1 : offsetSaved; + rep[1] = offset_2 ? offset_2 : offsetSaved; /* Return the last literals size */ return iend - anchor; } -size_t ZSTD_compressBlock_doubleFast(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_doubleFast( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - const U32 mls = ctx->appliedParams.cParams.searchLength; + const U32 mls = cParams->searchLength; switch(mls) { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 4); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 4); case 5 : - return ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 5); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 5); case 6 : - return ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 6); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 6); case 7 : - return ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 7); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 7); } } -static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, - const void* src, size_t srcSize, - const U32 mls) +static size_t ZSTD_compressBlock_doubleFast_extDict_generic( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize, + U32 const mls /* template */) { - U32* const hashLong = ctx->hashTable; - U32 const hBitsL = ctx->appliedParams.cParams.hashLog; - U32* const hashSmall = ctx->chainTable; - U32 const hBitsS = ctx->appliedParams.cParams.chainLog; - seqStore_t* seqStorePtr = &(ctx->seqStore); - const BYTE* const base = ctx->base; - const BYTE* const dictBase = ctx->dictBase; + U32* const hashLong = ms->hashTable; + U32 const hBitsL = cParams->hashLog; + U32* const hashSmall = ms->chainTable; + U32 const hBitsS = cParams->chainLog; + const BYTE* const base = ms->base; + const BYTE* const dictBase = ms->dictBase; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ctx->lowLimit; + const U32 lowestIndex = ms->lowLimit; const BYTE* const dictStart = dictBase + lowestIndex; - const U32 dictLimit = ctx->dictLimit; + const U32 dictLimit = ms->dictLimit; const BYTE* const lowPrefixPtr = base + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; - U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1]; + U32 offset_1=rep[0], offset_2=rep[1]; /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ @@ -220,7 +225,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, const BYTE* repMatchEnd = repIndex < dictLimit ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, lowPrefixPtr) + 4; ip++; - ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else { if ((matchLongIndex > lowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) { const BYTE* matchEnd = matchLongIndex < dictLimit ? dictEnd : iend; @@ -231,7 +236,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } else if ((matchIndex > lowestIndex) && (MEM_read32(match) == MEM_read32(ip))) { size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8); @@ -256,7 +261,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, } offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } else { ip += ((ip-anchor) >> g_searchStrength) + 1; @@ -283,7 +288,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, const BYTE* const repEnd2 = repIndex2 < dictLimit ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, lowPrefixPtr) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, repLength2-MINMATCH); + ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; ip += repLength2; @@ -294,27 +299,29 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, } } } /* save reps for next block */ - seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2; + rep[0] = offset_1; + rep[1] = offset_2; /* Return the last literals size */ return iend - anchor; } -size_t ZSTD_compressBlock_doubleFast_extDict(ZSTD_CCtx* ctx, - const void* src, size_t srcSize) +size_t ZSTD_compressBlock_doubleFast_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - U32 const mls = ctx->appliedParams.cParams.searchLength; + U32 const mls = cParams->searchLength; switch(mls) { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 4); + return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 4); case 5 : - return ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 5); + return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 5); case 6 : - return ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 6); + return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 6); case 7 : - return ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 7); + return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 7); } } diff --git a/lib/compress/zstd_double_fast.h b/lib/compress/zstd_double_fast.h index 75e041580..6d80b2774 100644 --- a/lib/compress/zstd_double_fast.h +++ b/lib/compress/zstd_double_fast.h @@ -16,11 +16,18 @@ extern "C" { #endif #include "mem.h" /* U32 */ -#include "zstd.h" /* ZSTD_CCtx, size_t */ +#include "zstd_compress_internal.h" /* ZSTD_CCtx, size_t */ + +void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, + ZSTD_compressionParameters const* cParams, + void const* end); +size_t ZSTD_compressBlock_doubleFast( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_doubleFast_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); -void ZSTD_fillDoubleHashTable(ZSTD_CCtx* cctx, const void* end, const U32 mls); -size_t ZSTD_compressBlock_doubleFast(ZSTD_CCtx* ctx, const void* src, size_t srcSize); -size_t ZSTD_compressBlock_doubleFast_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize); #if defined (__cplusplus) } diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 6497a2d8b..8941326e1 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -12,12 +12,15 @@ #include "zstd_fast.h" -void ZSTD_fillHashTable (ZSTD_CCtx* zc, const void* end, const U32 mls) +void ZSTD_fillHashTable(ZSTD_matchState_t* ms, + ZSTD_compressionParameters const* cParams, + void const* end) { - U32* const hashTable = zc->hashTable; - U32 const hBits = zc->appliedParams.cParams.hashLog; - const BYTE* const base = zc->base; - const BYTE* ip = base + zc->nextToUpdate; + U32* const hashTable = ms->hashTable; + U32 const hBits = cParams->hashLog; + U32 const mls = cParams->searchLength; + const BYTE* const base = ms->base; + const BYTE* ip = base + ms->nextToUpdate; const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; const U32 fastHashFillStep = 3; @@ -35,24 +38,22 @@ void ZSTD_fillHashTable (ZSTD_CCtx* zc, const void* end, const U32 mls) } } - FORCE_INLINE_TEMPLATE -size_t ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, - const void* src, size_t srcSize, - const U32 mls) +size_t ZSTD_compressBlock_fast_generic( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + void const* src, size_t srcSize, + U32 const hlog, U32 const mls) { - U32* const hashTable = cctx->hashTable; - U32 const hBits = cctx->appliedParams.cParams.hashLog; - seqStore_t* seqStorePtr = &(cctx->seqStore); - const BYTE* const base = cctx->base; + U32* const hashTable = ms->hashTable; + const BYTE* const base = ms->base; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = cctx->dictLimit; + const U32 lowestIndex = ms->dictLimit; const BYTE* const lowest = base + lowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; - U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1]; + U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; /* init */ @@ -65,7 +66,7 @@ size_t ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, /* Main Search Loop */ while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ size_t mLength; - size_t const h = ZSTD_hashPtr(ip, hBits, mls); + size_t const h = ZSTD_hashPtr(ip, hlog, mls); U32 const current = (U32)(ip-base); U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; @@ -74,7 +75,7 @@ size_t ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; - ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else { U32 offset; if ( (matchIndex <= lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { @@ -87,7 +88,7 @@ size_t ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } /* match found */ @@ -96,8 +97,8 @@ size_t ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, if (ip <= ilimit) { /* Fill Table */ - hashTable[ZSTD_hashPtr(base+current+2, hBits, mls)] = current+2; /* here because current+2 could be > iend-8 */ - hashTable[ZSTD_hashPtr(ip-2, hBits, mls)] = (U32)(ip-2-base); + hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2; /* here because current+2 could be > iend-8 */ + hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ while ( (ip <= ilimit) && ( (offset_2>0) @@ -105,65 +106,66 @@ size_t ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, /* store sequence */ size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; { U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */ - hashTable[ZSTD_hashPtr(ip, hBits, mls)] = (U32)(ip-base); - ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, rLength-MINMATCH); + hashTable[ZSTD_hashPtr(ip, hlog, mls)] = (U32)(ip-base); + ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); ip += rLength; anchor = ip; continue; /* faster when present ... (?) */ } } } /* save reps for next block */ - seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved; - seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved; + rep[0] = offset_1 ? offset_1 : offsetSaved; + rep[1] = offset_2 ? offset_2 : offsetSaved; /* Return the last literals size */ return iend - anchor; } -size_t ZSTD_compressBlock_fast(ZSTD_CCtx* ctx, - const void* src, size_t srcSize) +size_t ZSTD_compressBlock_fast( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - const U32 mls = ctx->appliedParams.cParams.searchLength; + U32 const hlog = cParams->hashLog; + U32 const mls = cParams->searchLength; switch(mls) { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 4); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, 4); case 5 : - return ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 5); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, 5); case 6 : - return ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 6); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, 6); case 7 : - return ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 7); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, 7); } } -static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx, - const void* src, size_t srcSize, - const U32 mls) +static size_t ZSTD_compressBlock_fast_extDict_generic( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + void const* src, size_t srcSize, + U32 const hlog, U32 const mls) { - U32* hashTable = ctx->hashTable; - const U32 hBits = ctx->appliedParams.cParams.hashLog; - seqStore_t* seqStorePtr = &(ctx->seqStore); - const BYTE* const base = ctx->base; - const BYTE* const dictBase = ctx->dictBase; + U32* hashTable = ms->hashTable; + const BYTE* const base = ms->base; + const BYTE* const dictBase = ms->dictBase; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ctx->lowLimit; + const U32 lowestIndex = ms->lowLimit; const BYTE* const dictStart = dictBase + lowestIndex; - const U32 dictLimit = ctx->dictLimit; + const U32 dictLimit = ms->dictLimit; const BYTE* const lowPrefixPtr = base + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; - U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1]; + U32 offset_1=rep[0], offset_2=rep[1]; /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ - const size_t h = ZSTD_hashPtr(ip, hBits, mls); + const size_t h = ZSTD_hashPtr(ip, hlog, mls); const U32 matchIndex = hashTable[h]; const BYTE* matchBase = matchIndex < dictLimit ? dictBase : base; const BYTE* match = matchBase + matchIndex; @@ -179,7 +181,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx, const BYTE* repMatchEnd = repIndex < dictLimit ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, lowPrefixPtr) + 4; ip++; - ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else { if ( (matchIndex < lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { @@ -194,7 +196,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx, offset = current - matchIndex; offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } } /* found a match : store it */ @@ -203,8 +205,8 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx, if (ip <= ilimit) { /* Fill Table */ - hashTable[ZSTD_hashPtr(base+current+2, hBits, mls)] = current+2; - hashTable[ZSTD_hashPtr(ip-2, hBits, mls)] = (U32)(ip-2-base); + hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2; + hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); @@ -215,8 +217,8 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx, const BYTE* const repEnd2 = repIndex2 < dictLimit ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, lowPrefixPtr) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, repLength2-MINMATCH); - hashTable[ZSTD_hashPtr(ip, hBits, mls)] = current2; + ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); + hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; ip += repLength2; anchor = ip; continue; @@ -225,27 +227,30 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx, } } } /* save reps for next block */ - seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2; + rep[0] = offset_1; + rep[1] = offset_2; /* Return the last literals size */ return iend - anchor; } -size_t ZSTD_compressBlock_fast_extDict(ZSTD_CCtx* ctx, - const void* src, size_t srcSize) +size_t ZSTD_compressBlock_fast_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - U32 const mls = ctx->appliedParams.cParams.searchLength; + U32 const hlog = cParams->hashLog; + U32 const mls = cParams->searchLength; switch(mls) { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 4); + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, 4); case 5 : - return ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 5); + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, 5); case 6 : - return ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 6); + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, 6); case 7 : - return ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 7); + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, 7); } } diff --git a/lib/compress/zstd_fast.h b/lib/compress/zstd_fast.h index d8b777195..f0438ad5b 100644 --- a/lib/compress/zstd_fast.h +++ b/lib/compress/zstd_fast.h @@ -16,13 +16,17 @@ extern "C" { #endif #include "mem.h" /* U32 */ -#include "zstd.h" /* ZSTD_CCtx, size_t */ +#include "zstd_compress_internal.h" -void ZSTD_fillHashTable(ZSTD_CCtx* zc, const void* end, const U32 mls); -size_t ZSTD_compressBlock_fast(ZSTD_CCtx* ctx, - const void* src, size_t srcSize); -size_t ZSTD_compressBlock_fast_extDict(ZSTD_CCtx* ctx, - const void* src, size_t srcSize); +void ZSTD_fillHashTable(ZSTD_matchState_t* ms, + ZSTD_compressionParameters const* cParams, + void const* end); +size_t ZSTD_compressBlock_fast( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_fast_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); #if defined (__cplusplus) } diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 1d575804a..7f59eb34a 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -45,28 +45,29 @@ void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const redu table[u] = ZSTD_DUBT_UNSORTED_MARK + reducerValue; } -void ZSTD_updateDUBT(ZSTD_CCtx* zc, +void ZSTD_updateDUBT( + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* ip, const BYTE* iend, U32 mls) { - U32* const hashTable = zc->hashTable; - U32 const hashLog = zc->appliedParams.cParams.hashLog; + U32* const hashTable = ms->hashTable; + U32 const hashLog = cParams->hashLog; - U32* const bt = zc->chainTable; - U32 const btLog = zc->appliedParams.cParams.chainLog - 1; + U32* const bt = ms->chainTable; + U32 const btLog = cParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; - const BYTE* const base = zc->base; + const BYTE* const base = ms->base; U32 const target = (U32)(ip - base); - U32 idx = zc->nextToUpdate; + U32 idx = ms->nextToUpdate; if (idx != target) DEBUGLOG(7, "ZSTD_updateDUBT, from %u to %u (dictLimit:%u)", - idx, target, zc->dictLimit); + idx, target, ms->dictLimit); assert(ip + 8 <= iend); /* condition for ZSTD_hashPtr */ (void)iend; - assert(idx >= zc->dictLimit); /* condition for valid base+idx */ + assert(idx >= ms->dictLimit); /* condition for valid base+idx */ for ( ; idx < target ; idx++) { size_t const h = ZSTD_hashPtr(base + idx, hashLog, mls); /* assumption : ip + 8 <= iend */ U32 const matchIndex = hashTable[h]; @@ -79,7 +80,7 @@ void ZSTD_updateDUBT(ZSTD_CCtx* zc, *nextCandidatePtr = matchIndex; /* update BT like a chain */ *sortMarkPtr = ZSTD_DUBT_UNSORTED_MARK; } - zc->nextToUpdate = target; + ms->nextToUpdate = target; } @@ -87,17 +88,18 @@ void ZSTD_updateDUBT(ZSTD_CCtx* zc, * sort one already inserted but unsorted position * assumption : current >= btlow == (current - btmask) * doesn't fail */ -static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, +static void ZSTD_insertDUBT1( + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, U32 current, const BYTE* inputEnd, U32 nbCompares, U32 btLow, int extDict) { - U32* const bt = zc->chainTable; - U32 const btLog = zc->appliedParams.cParams.chainLog - 1; + U32* const bt = ms->chainTable; + U32 const btLog = cParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; size_t commonLengthSmaller=0, commonLengthLarger=0; - const BYTE* const base = zc->base; - const BYTE* const dictBase = zc->dictBase; - const U32 dictLimit = zc->dictLimit; + const BYTE* const base = ms->base; + const BYTE* const dictBase = ms->dictBase; + const U32 dictLimit = ms->dictLimit; const BYTE* const ip = (current>=dictLimit) ? base + current : dictBase + current; const BYTE* const iend = (current>=dictLimit) ? inputEnd : dictBase + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; @@ -107,7 +109,7 @@ static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, U32* largerPtr = smallerPtr + 1; U32 matchIndex = *smallerPtr; U32 dummy32; /* to be nullified at the end */ - U32 const windowLow = zc->lowLimit; + U32 const windowLow = ms->lowLimit; DEBUGLOG(8, "ZSTD_insertDUBT1(%u) (dictLimit=%u, lowLimit=%u)", current, dictLimit, windowLow); @@ -166,29 +168,30 @@ static void ZSTD_insertDUBT1(ZSTD_CCtx* zc, static size_t ZSTD_DUBT_findBestMatch ( - ZSTD_CCtx* zc, + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iend, size_t* offsetPtr, - U32 nbCompares, const U32 mls, - U32 extDict) + U32 const mls, + U32 const extDict) { - U32* const hashTable = zc->hashTable; - U32 const hashLog = zc->appliedParams.cParams.hashLog; + U32* const hashTable = ms->hashTable; + U32 const hashLog = cParams->hashLog; size_t const h = ZSTD_hashPtr(ip, hashLog, mls); U32 matchIndex = hashTable[h]; - const BYTE* const base = zc->base; + const BYTE* const base = ms->base; U32 const current = (U32)(ip-base); - U32 const windowLow = zc->lowLimit; + U32 const windowLow = ms->lowLimit; - U32* const bt = zc->chainTable; - U32 const btLog = zc->appliedParams.cParams.chainLog - 1; + U32* const bt = ms->chainTable; + U32 const btLog = cParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; U32 const btLow = (btMask >= current) ? 0 : current - btMask; U32 const unsortLimit = MAX(btLow, windowLow); U32* nextCandidate = bt + 2*(matchIndex&btMask); U32* unsortedMark = bt + 2*(matchIndex&btMask) + 1; + U32 nbCompares = 1U << cParams->searchLog; U32 nbCandidates = nbCompares; U32 previousCandidate = 0; @@ -221,7 +224,7 @@ static size_t ZSTD_DUBT_findBestMatch ( while (matchIndex) { /* will end on matchIndex == 0 */ U32* const nextCandidateIdxPtr = bt + 2*(matchIndex&btMask) + 1; U32 const nextCandidateIdx = *nextCandidateIdxPtr; - ZSTD_insertDUBT1(zc, matchIndex, iend, + ZSTD_insertDUBT1(ms, cParams, matchIndex, iend, nbCandidates, unsortLimit, extDict); matchIndex = nextCandidateIdx; nbCandidates++; @@ -229,8 +232,8 @@ static size_t ZSTD_DUBT_findBestMatch ( /* find longest match */ { size_t commonLengthSmaller=0, commonLengthLarger=0; - const BYTE* const dictBase = zc->dictBase; - const U32 dictLimit = zc->dictLimit; + const BYTE* const dictBase = ms->dictBase; + const U32 dictLimit = ms->dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; U32* smallerPtr = bt + 2*(current&btMask); @@ -286,7 +289,7 @@ static size_t ZSTD_DUBT_findBestMatch ( *smallerPtr = *largerPtr = 0; assert(matchEndIdx > current+8); /* ensure nextToUpdate is increased */ - zc->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ + ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ if (bestLength >= MINMATCH) { U32 const mIndex = current - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex; DEBUGLOG(8, "ZSTD_DUBT_findBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)", @@ -299,62 +302,60 @@ static size_t ZSTD_DUBT_findBestMatch ( /** ZSTD_BtFindBestMatch() : Tree updater, providing best match */ static size_t ZSTD_BtFindBestMatch ( - ZSTD_CCtx* zc, + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iLimit, size_t* offsetPtr, - const U32 maxNbAttempts, const U32 mls) + const U32 mls /* template */) { DEBUGLOG(7, "ZSTD_BtFindBestMatch"); - if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */ - ZSTD_updateDUBT(zc, ip, iLimit, mls); - return ZSTD_DUBT_findBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 0); + if (ip < ms->base + ms->nextToUpdate) return 0; /* skipped area */ + ZSTD_updateDUBT(ms, cParams, ip, iLimit, mls); + return ZSTD_DUBT_findBestMatch(ms, cParams, ip, iLimit, offsetPtr, mls, 0); } static size_t ZSTD_BtFindBestMatch_selectMLS ( - ZSTD_CCtx* zc, /* Index table will be updated */ + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr, - const U32 maxNbAttempts, const U32 matchLengthSearch) + size_t* offsetPtr) { - switch(matchLengthSearch) + switch(cParams->searchLength) { default : /* includes case 3 */ - case 4 : return ZSTD_BtFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4); - case 5 : return ZSTD_BtFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5); + case 4 : return ZSTD_BtFindBestMatch(ms, cParams, ip, iLimit, offsetPtr, 4); + case 5 : return ZSTD_BtFindBestMatch(ms, cParams, ip, iLimit, offsetPtr, 5); case 7 : - case 6 : return ZSTD_BtFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6); + case 6 : return ZSTD_BtFindBestMatch(ms, cParams, ip, iLimit, offsetPtr, 6); } } /** Tree updater, providing best match */ static size_t ZSTD_BtFindBestMatch_extDict ( - ZSTD_CCtx* zc, + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iLimit, size_t* offsetPtr, - const U32 maxNbAttempts, const U32 mls) + const U32 mls) { DEBUGLOG(7, "ZSTD_BtFindBestMatch_extDict"); - if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */ - ZSTD_updateDUBT(zc, ip, iLimit, mls); - return ZSTD_DUBT_findBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 1); + if (ip < ms->base + ms->nextToUpdate) return 0; /* skipped area */ + ZSTD_updateDUBT(ms, cParams, ip, iLimit, mls); + return ZSTD_DUBT_findBestMatch(ms, cParams, ip, iLimit, offsetPtr, mls, 1); } static size_t ZSTD_BtFindBestMatch_selectMLS_extDict ( - ZSTD_CCtx* zc, /* Index table will be updated */ + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr, - const U32 maxNbAttempts, const U32 matchLengthSearch) + size_t* offsetPtr) { - switch(matchLengthSearch) + switch(cParams->searchLength) { default : /* includes case 3 */ - case 4 : return ZSTD_BtFindBestMatch_extDict(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4); - case 5 : return ZSTD_BtFindBestMatch_extDict(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5); + case 4 : return ZSTD_BtFindBestMatch_extDict(ms, cParams, ip, iLimit, offsetPtr, 4); + case 5 : return ZSTD_BtFindBestMatch_extDict(ms, cParams, ip, iLimit, offsetPtr, 5); case 7 : - case 6 : return ZSTD_BtFindBestMatch_extDict(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6); + case 6 : return ZSTD_BtFindBestMatch_extDict(ms, cParams, ip, iLimit, offsetPtr, 6); } } @@ -367,15 +368,17 @@ static size_t ZSTD_BtFindBestMatch_selectMLS_extDict ( /* Update chains up to ip (excluded) Assumption : always within prefix (i.e. not within extDict) */ -U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls) +static U32 ZSTD_insertAndFindFirstIndex_internal( + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, + const BYTE* ip, U32 const mls) { - U32* const hashTable = zc->hashTable; - const U32 hashLog = zc->appliedParams.cParams.hashLog; - U32* const chainTable = zc->chainTable; - const U32 chainMask = (1 << zc->appliedParams.cParams.chainLog) - 1; - const BYTE* const base = zc->base; + U32* const hashTable = ms->hashTable; + const U32 hashLog = cParams->hashLog; + U32* const chainTable = ms->chainTable; + const U32 chainMask = (1 << cParams->chainLog) - 1; + const BYTE* const base = ms->base; const U32 target = (U32)(ip - base); - U32 idx = zc->nextToUpdate; + U32 idx = ms->nextToUpdate; while(idx < target) { /* catch up */ size_t const h = ZSTD_hashPtr(base+idx, hashLog, mls); @@ -384,35 +387,42 @@ U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls) idx++; } - zc->nextToUpdate = target; + ms->nextToUpdate = target; return hashTable[ZSTD_hashPtr(ip, hashLog, mls)]; } +U32 ZSTD_insertAndFindFirstIndex( + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, + const BYTE* ip) +{ + return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, cParams->searchLength); +} + /* inlining is important to hardwire a hot branch (template emulation) */ FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_generic ( - ZSTD_CCtx* zc, /* Index table will be updated */ + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iLimit, size_t* offsetPtr, - const U32 maxNbAttempts, const U32 mls, const U32 extDict) + const U32 mls, const U32 extDict) { - U32* const chainTable = zc->chainTable; - const U32 chainSize = (1 << zc->appliedParams.cParams.chainLog); + U32* const chainTable = ms->chainTable; + const U32 chainSize = (1 << cParams->chainLog); const U32 chainMask = chainSize-1; - const BYTE* const base = zc->base; - const BYTE* const dictBase = zc->dictBase; - const U32 dictLimit = zc->dictLimit; + const BYTE* const base = ms->base; + const BYTE* const dictBase = ms->dictBase; + const U32 dictLimit = ms->dictLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; - const U32 lowLimit = zc->lowLimit; + const U32 lowLimit = ms->lowLimit; const U32 current = (U32)(ip-base); const U32 minChain = current > chainSize ? current - chainSize : 0; - int nbAttempts=maxNbAttempts; + U32 nbAttempts = 1U << cParams->searchLog; size_t ml=4-1; /* HC4 match finder */ - U32 matchIndex = ZSTD_insertAndFindFirstIndex (zc, ip, mls); + U32 matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls); for ( ; (matchIndex>lowLimit) & (nbAttempts>0) ; nbAttempts--) { size_t currentMl=0; @@ -443,35 +453,33 @@ size_t ZSTD_HcFindBestMatch_generic ( FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_selectMLS ( - ZSTD_CCtx* zc, + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr, - const U32 maxNbAttempts, const U32 matchLengthSearch) + size_t* offsetPtr) { - switch(matchLengthSearch) + switch(cParams->searchLength) { default : /* includes case 3 */ - case 4 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4, 0); - case 5 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5, 0); + case 4 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 4, 0); + case 5 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 5, 0); case 7 : - case 6 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6, 0); + case 6 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 6, 0); } } FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_extDict_selectMLS ( - ZSTD_CCtx* const zc, + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* ip, const BYTE* const iLimit, - size_t* const offsetPtr, - U32 const maxNbAttempts, U32 const matchLengthSearch) + size_t* const offsetPtr) { - switch(matchLengthSearch) + switch(cParams->searchLength) { default : /* includes case 3 */ - case 4 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4, 1); - case 5 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5, 1); + case 4 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 4, 1); + case 5 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 5, 1); case 7 : - case 6 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6, 1); + case 6 : return ZSTD_HcFindBestMatch_generic(ms, cParams, ip, iLimit, offsetPtr, 6, 1); } } @@ -480,30 +488,29 @@ FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_extDict_selectMLS ( * Common parser - lazy strategy *********************************/ FORCE_INLINE_TEMPLATE -size_t ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx, - const void* src, size_t srcSize, - const U32 searchMethod, const U32 depth) +size_t ZSTD_compressBlock_lazy_generic( + ZSTD_matchState_t* ms, seqStore_t* seqStore, + U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, + const void* src, size_t srcSize, + const U32 searchMethod, const U32 depth) { - seqStore_t* seqStorePtr = &(ctx->seqStore); const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; - const BYTE* const base = ctx->base + ctx->dictLimit; + const BYTE* const base = ms->base + ms->dictLimit; - U32 const maxSearches = 1 << ctx->appliedParams.cParams.searchLog; - U32 const mls = ctx->appliedParams.cParams.searchLength; - - typedef size_t (*searchMax_f)(ZSTD_CCtx* zc, const BYTE* ip, const BYTE* iLimit, - size_t* offsetPtr, - U32 maxNbAttempts, U32 matchLengthSearch); + typedef size_t (*searchMax_f)( + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, + const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr); searchMax_f const searchMax = searchMethod ? ZSTD_BtFindBestMatch_selectMLS : ZSTD_HcFindBestMatch_selectMLS; - U32 offset_1 = seqStorePtr->rep[0], offset_2 = seqStorePtr->rep[1], savedOffset=0; + U32 offset_1 = rep[0], offset_2 = rep[1], savedOffset=0; /* init */ ip += (ip==base); - ctx->nextToUpdate3 = ctx->nextToUpdate; + ms->nextToUpdate3 = ms->nextToUpdate; { U32 const maxRep = (U32)(ip-base); if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0; if (offset_1 > maxRep) savedOffset = offset_1, offset_1 = 0; @@ -524,7 +531,7 @@ size_t ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx, /* first search (depth 0) */ { size_t offsetFound = 99999999; - size_t const ml2 = searchMax(ctx, ip, iend, &offsetFound, maxSearches, mls); + size_t const ml2 = searchMax(ms, cParams, ip, iend, &offsetFound); if (ml2 > matchLength) matchLength = ml2, start = ip, offset=offsetFound; } @@ -546,7 +553,7 @@ size_t ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx, matchLength = mlRep, offset = 0, start = ip; } { size_t offset2=99999999; - size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls); + size_t const ml2 = searchMax(ms, cParams, ip, iend, &offset2); int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4); if ((ml2 >= 4) && (gain2 > gain1)) { @@ -565,7 +572,7 @@ size_t ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx, matchLength = ml2, offset = 0, start = ip; } { size_t offset2=99999999; - size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls); + size_t const ml2 = searchMax(ms, cParams, ip, iend, &offset2); int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7); if ((ml2 >= 4) && (gain2 > gain1)) { @@ -590,7 +597,7 @@ size_t ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx, /* store sequence */ _storeSequence: { size_t const litLength = start - anchor; - ZSTD_storeSeq(seqStorePtr, litLength, anchor, (U32)offset, matchLength-MINMATCH); + ZSTD_storeSeq(seqStore, litLength, anchor, (U32)offset, matchLength-MINMATCH); anchor = ip = start + matchLength; } @@ -600,73 +607,80 @@ _storeSequence: /* store sequence */ matchLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap repcodes */ - ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, matchLength-MINMATCH); + ZSTD_storeSeq(seqStore, 0, anchor, 0, matchLength-MINMATCH); ip += matchLength; anchor = ip; continue; /* faster when present ... (?) */ } } /* Save reps for next block */ - seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : savedOffset; - seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : savedOffset; + rep[0] = offset_1 ? offset_1 : savedOffset; + rep[1] = offset_2 ? offset_2 : savedOffset; /* Return the last literals size */ return iend - anchor; } -size_t ZSTD_compressBlock_btlazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_btlazy2( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - return ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 1, 2); + return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, cParams, src, srcSize, 1, 2); } -size_t ZSTD_compressBlock_lazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_lazy2( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - return ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 0, 2); + return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 2); } -size_t ZSTD_compressBlock_lazy(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_lazy( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - return ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 0, 1); + return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 1); } -size_t ZSTD_compressBlock_greedy(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_greedy( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - return ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 0, 0); + return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 0); } FORCE_INLINE_TEMPLATE -size_t ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx, - const void* src, size_t srcSize, - const U32 searchMethod, const U32 depth) +size_t ZSTD_compressBlock_lazy_extDict_generic( + ZSTD_matchState_t* ms, seqStore_t* seqStore, + U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, + const void* src, size_t srcSize, + const U32 searchMethod, const U32 depth) { - seqStore_t* seqStorePtr = &(ctx->seqStore); const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; - const BYTE* const base = ctx->base; - const U32 dictLimit = ctx->dictLimit; - const U32 lowestIndex = ctx->lowLimit; + const BYTE* const base = ms->base; + const U32 dictLimit = ms->dictLimit; + const U32 lowestIndex = ms->lowLimit; const BYTE* const prefixStart = base + dictLimit; - const BYTE* const dictBase = ctx->dictBase; + const BYTE* const dictBase = ms->dictBase; const BYTE* const dictEnd = dictBase + dictLimit; - const BYTE* const dictStart = dictBase + ctx->lowLimit; + const BYTE* const dictStart = dictBase + ms->lowLimit; - const U32 maxSearches = 1 << ctx->appliedParams.cParams.searchLog; - const U32 mls = ctx->appliedParams.cParams.searchLength; - - typedef size_t (*searchMax_f)(ZSTD_CCtx* zc, const BYTE* ip, const BYTE* iLimit, - size_t* offsetPtr, - U32 maxNbAttempts, U32 matchLengthSearch); + typedef size_t (*searchMax_f)( + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, + const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr); searchMax_f searchMax = searchMethod ? ZSTD_BtFindBestMatch_selectMLS_extDict : ZSTD_HcFindBestMatch_extDict_selectMLS; - U32 offset_1 = seqStorePtr->rep[0], offset_2 = seqStorePtr->rep[1]; + U32 offset_1 = rep[0], offset_2 = rep[1]; /* init */ - ctx->nextToUpdate3 = ctx->nextToUpdate; + ms->nextToUpdate3 = ms->nextToUpdate; ip += (ip == prefixStart); /* Match Loop */ @@ -690,7 +704,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx, /* first search (depth 0) */ { size_t offsetFound = 99999999; - size_t const ml2 = searchMax(ctx, ip, iend, &offsetFound, maxSearches, mls); + size_t const ml2 = searchMax(ms, cParams, ip, iend, &offsetFound); if (ml2 > matchLength) matchLength = ml2, start = ip, offset=offsetFound; } @@ -723,7 +737,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx, /* search match, depth 1 */ { size_t offset2=99999999; - size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls); + size_t const ml2 = searchMax(ms, cParams, ip, iend, &offset2); int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4); if ((ml2 >= 4) && (gain2 > gain1)) { @@ -753,7 +767,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx, /* search match, depth 2 */ { size_t offset2=99999999; - size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls); + size_t const ml2 = searchMax(ms, cParams, ip, iend, &offset2); int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7); if ((ml2 >= 4) && (gain2 > gain1)) { @@ -775,7 +789,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx, /* store sequence */ _storeSequence: { size_t const litLength = start - anchor; - ZSTD_storeSeq(seqStorePtr, litLength, anchor, (U32)offset, matchLength-MINMATCH); + ZSTD_storeSeq(seqStore, litLength, anchor, (U32)offset, matchLength-MINMATCH); anchor = ip = start + matchLength; } @@ -790,7 +804,7 @@ _storeSequence: const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4; offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap offset history */ - ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, matchLength-MINMATCH); + ZSTD_storeSeq(seqStore, 0, anchor, 0, matchLength-MINMATCH); ip += matchLength; anchor = ip; continue; /* faster when present ... (?) */ @@ -799,29 +813,41 @@ _storeSequence: } } /* Save reps for next block */ - seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2; + rep[0] = offset_1; + rep[1] = offset_2; /* Return the last literals size */ return iend - anchor; } -size_t ZSTD_compressBlock_greedy_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_greedy_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - return ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 0, 0); + return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 0); } -size_t ZSTD_compressBlock_lazy_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_lazy_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) + { - return ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 0, 1); + return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 1); } -size_t ZSTD_compressBlock_lazy2_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_lazy2_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) + { - return ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 0, 2); + return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 0, 2); } -size_t ZSTD_compressBlock_btlazy2_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_btlazy2_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) + { - return ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 1, 2); + return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, cParams, src, srcSize, 1, 2); } diff --git a/lib/compress/zstd_lazy.h b/lib/compress/zstd_lazy.h index 5c02f1661..bda064f19 100644 --- a/lib/compress/zstd_lazy.h +++ b/lib/compress/zstd_lazy.h @@ -15,22 +15,39 @@ extern "C" { #endif -#include "mem.h" /* U32 */ -#include "zstd.h" /* ZSTD_CCtx, size_t */ +#include "zstd_compress_internal.h" + +U32 ZSTD_insertAndFindFirstIndex( + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, + const BYTE* ip); -U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls); /* used in ZSTD_loadDictionaryContent() */ -void ZSTD_updateDUBT(ZSTD_CCtx* zc, const BYTE* ip, const BYTE* iend, U32 mls); /* used in ZSTD_loadDictionaryContent() */ void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const reducerValue); /*! used in ZSTD_reduceIndex(). pre-emptively increase value of ZSTD_DUBT_UNSORTED_MARK */ -size_t ZSTD_compressBlock_btlazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize); -size_t ZSTD_compressBlock_lazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize); -size_t ZSTD_compressBlock_lazy(ZSTD_CCtx* ctx, const void* src, size_t srcSize); -size_t ZSTD_compressBlock_greedy(ZSTD_CCtx* ctx, const void* src, size_t srcSize); +size_t ZSTD_compressBlock_btlazy2( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_lazy2( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_lazy( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_greedy( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); -size_t ZSTD_compressBlock_greedy_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize); -size_t ZSTD_compressBlock_lazy_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize); -size_t ZSTD_compressBlock_lazy2_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize); -size_t ZSTD_compressBlock_btlazy2_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize); +size_t ZSTD_compressBlock_greedy_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_lazy_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_lazy2_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_btlazy2_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); #if defined (__cplusplus) } diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index be50872cf..cc71de2e0 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -205,21 +205,22 @@ static size_t ZSTD_ldm_countBackwardsMatch( * * The tables for the other strategies are filled within their * block compressors. */ -static size_t ZSTD_ldm_fillFastTables(ZSTD_CCtx* zc, const void* end) +static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms, + ZSTD_compressionParameters const* cParams, + void const* end) { const BYTE* const iend = (const BYTE*)end; - const U32 mls = zc->appliedParams.cParams.searchLength; - switch(zc->appliedParams.cParams.strategy) + switch(cParams->strategy) { case ZSTD_fast: - ZSTD_fillHashTable(zc, iend, mls); - zc->nextToUpdate = (U32)(iend - zc->base); + ZSTD_fillHashTable(ms, cParams, iend); + ms->nextToUpdate = (U32)(iend - ms->base); break; case ZSTD_dfast: - ZSTD_fillDoubleHashTable(zc, iend, mls); - zc->nextToUpdate = (U32)(iend - zc->base); + ZSTD_fillDoubleHashTable(ms, cParams, iend); + ms->nextToUpdate = (U32)(iend - ms->base); break; case ZSTD_greedy: @@ -268,51 +269,41 @@ static U64 ZSTD_ldm_fillLdmHashTable(ldmState_t* state, * Sets cctx->nextToUpdate to a position corresponding closer to anchor * if it is far way * (after a long match, only update tables a limited amount). */ -static void ZSTD_ldm_limitTableUpdate(ZSTD_CCtx* cctx, const BYTE* anchor) +static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor) { - U32 const current = (U32)(anchor - cctx->base); - if (current > cctx->nextToUpdate + 1024) { - cctx->nextToUpdate = - current - MIN(512, current - cctx->nextToUpdate - 1024); + U32 const current = (U32)(anchor - ms->base); + if (current > ms->nextToUpdate + 1024) { + ms->nextToUpdate = + current - MIN(512, current - ms->nextToUpdate - 1024); } } -typedef size_t (*ZSTD_blockCompressor) (ZSTD_CCtx* ctx, const void* src, size_t srcSize); -/* defined in zstd_compress.c */ -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict); +size_t ZSTD_compressBlock_ldm( + ldmState_t* ldmState, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_CCtx_params const* params, void const* src, size_t srcSize) -FORCE_INLINE_TEMPLATE -size_t ZSTD_compressBlock_ldm_generic(ZSTD_CCtx* cctx, - const void* src, size_t srcSize) { - ldmState_t* const ldmState = &(cctx->ldmState); - const ldmParams_t ldmParams = cctx->appliedParams.ldmParams; + ZSTD_compressionParameters const* cParams = ¶ms->cParams; + const ldmParams_t ldmParams = params->ldmParams; const U64 hashPower = ldmState->hashPower; const U32 hBits = ldmParams.hashLog - ldmParams.bucketSizeLog; const U32 ldmBucketSize = ((U32)1 << ldmParams.bucketSizeLog); const U32 ldmTagMask = ((U32)1 << ldmParams.hashEveryLog) - 1; - seqStore_t* const seqStorePtr = &(cctx->seqStore); - const BYTE* const base = cctx->base; + const BYTE* const base = ms->base; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = cctx->dictLimit; + const U32 lowestIndex = ms->dictLimit; const BYTE* const lowest = base + lowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - MAX(ldmParams.minMatchLength, HASH_READ_SIZE); const ZSTD_blockCompressor blockCompressor = - ZSTD_selectBlockCompressor(cctx->appliedParams.cParams.strategy, 0); - U32* const repToConfirm = seqStorePtr->repToConfirm; - U32 savedRep[ZSTD_REP_NUM]; + ZSTD_selectBlockCompressor(cParams->strategy, 0); U64 rollingHash = 0; const BYTE* lastHashed = NULL; size_t i, lastLiterals; - /* Save seqStorePtr->rep and copy repToConfirm */ - for (i = 0; i < ZSTD_REP_NUM; i++) - savedRep[i] = repToConfirm[i] = seqStorePtr->rep[i]; - /* Main Search Loop */ while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ size_t mLength; @@ -390,25 +381,21 @@ size_t ZSTD_compressBlock_ldm_generic(ZSTD_CCtx* cctx, const BYTE* const match = base + matchIndex - backwardMatchLength; U32 const offset = (U32)(ip - match); - /* Overwrite rep codes */ - for (i = 0; i < ZSTD_REP_NUM; i++) - seqStorePtr->rep[i] = repToConfirm[i]; - /* Fill tables for block compressor */ - ZSTD_ldm_limitTableUpdate(cctx, anchor); - ZSTD_ldm_fillFastTables(cctx, anchor); + ZSTD_ldm_limitTableUpdate(ms, anchor); + ZSTD_ldm_fillFastTables(ms, cParams, anchor); /* Call block compressor and get remaining literals */ - lastLiterals = blockCompressor(cctx, anchor, ip - anchor); - cctx->nextToUpdate = (U32)(ip - base); + lastLiterals = blockCompressor(ms, seqStore, rep, cParams, anchor, ip - anchor); + ms->nextToUpdate = (U32)(ip - base); /* Update repToConfirm with the new offset */ for (i = ZSTD_REP_NUM - 1; i > 0; i--) - repToConfirm[i] = repToConfirm[i-1]; - repToConfirm[0] = offset; + rep[i] = rep[i-1]; + rep[0] = offset; /* Store the sequence with the leftover literals */ - ZSTD_storeSeq(seqStorePtr, lastLiterals, ip - lastLiterals, + ZSTD_storeSeq(seqStore, lastLiterals, ip - lastLiterals, offset + ZSTD_REP_MOVE, mLength - MINMATCH); } @@ -431,19 +418,19 @@ size_t ZSTD_compressBlock_ldm_generic(ZSTD_CCtx* cctx, anchor = ip; /* Check immediate repcode */ while ( (ip < ilimit) - && ( (repToConfirm[1] > 0) && (repToConfirm[1] <= (U32)(ip-lowest)) - && (MEM_read32(ip) == MEM_read32(ip - repToConfirm[1])) )) { + && ( (rep[1] > 0) && (rep[1] <= (U32)(ip-lowest)) + && (MEM_read32(ip) == MEM_read32(ip - rep[1])) )) { - size_t const rLength = ZSTD_count(ip+4, ip+4-repToConfirm[1], + size_t const rLength = ZSTD_count(ip+4, ip+4-rep[1], iend) + 4; /* Swap repToConfirm[1] <=> repToConfirm[0] */ { - U32 const tmpOff = repToConfirm[1]; - repToConfirm[1] = repToConfirm[0]; - repToConfirm[0] = tmpOff; + U32 const tmpOff = rep[1]; + rep[1] = rep[0]; + rep[0] = tmpOff; } - ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, rLength-MINMATCH); + ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); /* Fill the hash table from lastHashed+1 to ip+rLength*/ if (ip + rLength < ilimit) { @@ -457,67 +444,45 @@ size_t ZSTD_compressBlock_ldm_generic(ZSTD_CCtx* cctx, } } - /* Overwrite rep */ - for (i = 0; i < ZSTD_REP_NUM; i++) - seqStorePtr->rep[i] = repToConfirm[i]; + ZSTD_ldm_limitTableUpdate(ms, anchor); + ZSTD_ldm_fillFastTables(ms, cParams, anchor); - ZSTD_ldm_limitTableUpdate(cctx, anchor); - ZSTD_ldm_fillFastTables(cctx, anchor); - - lastLiterals = blockCompressor(cctx, anchor, iend - anchor); - cctx->nextToUpdate = (U32)(iend - base); - - /* Restore seqStorePtr->rep */ - for (i = 0; i < ZSTD_REP_NUM; i++) - seqStorePtr->rep[i] = savedRep[i]; + lastLiterals = blockCompressor(ms, seqStore, rep, cParams, anchor, iend - anchor); + ms->nextToUpdate = (U32)(iend - base); /* Return the last literals size */ return lastLiterals; } -size_t ZSTD_compressBlock_ldm(ZSTD_CCtx* ctx, - const void* src, size_t srcSize) +size_t ZSTD_compressBlock_ldm_extDict( + ldmState_t* ldmState, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_CCtx_params const* params, void const* src, size_t srcSize) { - return ZSTD_compressBlock_ldm_generic(ctx, src, srcSize); -} - -static size_t ZSTD_compressBlock_ldm_extDict_generic( - ZSTD_CCtx* ctx, - const void* src, size_t srcSize) -{ - ldmState_t* const ldmState = &(ctx->ldmState); - const ldmParams_t ldmParams = ctx->appliedParams.ldmParams; + const ldmParams_t ldmParams = params->ldmParams; + ZSTD_compressionParameters const* cParams = ¶ms->cParams; const U64 hashPower = ldmState->hashPower; const U32 hBits = ldmParams.hashLog - ldmParams.bucketSizeLog; const U32 ldmBucketSize = ((U32)1 << ldmParams.bucketSizeLog); const U32 ldmTagMask = ((U32)1 << ldmParams.hashEveryLog) - 1; - seqStore_t* const seqStorePtr = &(ctx->seqStore); - const BYTE* const base = ctx->base; - const BYTE* const dictBase = ctx->dictBase; + const BYTE* const base = ms->base; + const BYTE* const dictBase = ms->dictBase; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ctx->lowLimit; + const U32 lowestIndex = ms->lowLimit; const BYTE* const dictStart = dictBase + lowestIndex; - const U32 dictLimit = ctx->dictLimit; + const U32 dictLimit = ms->dictLimit; const BYTE* const lowPrefixPtr = base + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - MAX(ldmParams.minMatchLength, HASH_READ_SIZE); const ZSTD_blockCompressor blockCompressor = - ZSTD_selectBlockCompressor(ctx->appliedParams.cParams.strategy, 1); - U32* const repToConfirm = seqStorePtr->repToConfirm; - U32 savedRep[ZSTD_REP_NUM]; + ZSTD_selectBlockCompressor(cParams->strategy, 1); U64 rollingHash = 0; const BYTE* lastHashed = NULL; size_t i, lastLiterals; - /* Save seqStorePtr->rep and copy repToConfirm */ - for (i = 0; i < ZSTD_REP_NUM; i++) { - savedRep[i] = repToConfirm[i] = seqStorePtr->rep[i]; - } - /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ size_t mLength; @@ -605,25 +570,21 @@ static size_t ZSTD_compressBlock_ldm_extDict_generic( U32 const matchIndex = bestEntry->offset; U32 const offset = current - matchIndex; - /* Overwrite rep codes */ - for (i = 0; i < ZSTD_REP_NUM; i++) - seqStorePtr->rep[i] = repToConfirm[i]; - /* Fill the hash table for the block compressor */ - ZSTD_ldm_limitTableUpdate(ctx, anchor); - ZSTD_ldm_fillFastTables(ctx, anchor); + ZSTD_ldm_limitTableUpdate(ms, anchor); + ZSTD_ldm_fillFastTables(ms, cParams, anchor); /* Call block compressor and get remaining literals */ - lastLiterals = blockCompressor(ctx, anchor, ip - anchor); - ctx->nextToUpdate = (U32)(ip - base); + lastLiterals = blockCompressor(ms, seqStore, rep, cParams, anchor, ip - anchor); + ms->nextToUpdate = (U32)(ip - base); /* Update repToConfirm with the new offset */ for (i = ZSTD_REP_NUM - 1; i > 0; i--) - repToConfirm[i] = repToConfirm[i-1]; - repToConfirm[0] = offset; + rep[i] = rep[i-1]; + rep[0] = offset; /* Store the sequence with the leftover literals */ - ZSTD_storeSeq(seqStorePtr, lastLiterals, ip - lastLiterals, + ZSTD_storeSeq(seqStore, lastLiterals, ip - lastLiterals, offset + ZSTD_REP_MOVE, mLength - MINMATCH); } @@ -647,7 +608,7 @@ static size_t ZSTD_compressBlock_ldm_extDict_generic( /* check immediate repcode */ while (ip < ilimit) { U32 const current2 = (U32)(ip-base); - U32 const repIndex2 = current2 - repToConfirm[1]; + U32 const repIndex2 = current2 - rep[1]; const BYTE* repMatch2 = repIndex2 < dictLimit ? dictBase + repIndex2 : base + repIndex2; if ( (((U32)((dictLimit-1) - repIndex2) >= 3) & @@ -659,11 +620,11 @@ static size_t ZSTD_compressBlock_ldm_extDict_generic( ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, lowPrefixPtr) + 4; - U32 tmpOffset = repToConfirm[1]; - repToConfirm[1] = repToConfirm[0]; - repToConfirm[0] = tmpOffset; + U32 tmpOffset = rep[1]; + rep[1] = rep[0]; + rep[0] = tmpOffset; - ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, repLength2-MINMATCH); + ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); /* Fill the hash table from lastHashed+1 to ip+repLength2*/ if (ip + repLength2 < ilimit) { @@ -681,27 +642,13 @@ static size_t ZSTD_compressBlock_ldm_extDict_generic( } } - /* Overwrite rep */ - for (i = 0; i < ZSTD_REP_NUM; i++) - seqStorePtr->rep[i] = repToConfirm[i]; - - ZSTD_ldm_limitTableUpdate(ctx, anchor); - ZSTD_ldm_fillFastTables(ctx, anchor); + ZSTD_ldm_limitTableUpdate(ms, anchor); + ZSTD_ldm_fillFastTables(ms, cParams, anchor); /* Call the block compressor one last time on the last literals */ - lastLiterals = blockCompressor(ctx, anchor, iend - anchor); - ctx->nextToUpdate = (U32)(iend - base); - - /* Restore seqStorePtr->rep */ - for (i = 0; i < ZSTD_REP_NUM; i++) - seqStorePtr->rep[i] = savedRep[i]; + lastLiterals = blockCompressor(ms, seqStore, rep, cParams, anchor, iend - anchor); + ms->nextToUpdate = (U32)(iend - base); /* Return the last literals size */ return lastLiterals; } - -size_t ZSTD_compressBlock_ldm_extDict(ZSTD_CCtx* ctx, - const void* src, size_t srcSize) -{ - return ZSTD_compressBlock_ldm_extDict_generic(ctx, src, srcSize); -} diff --git a/lib/compress/zstd_ldm.h b/lib/compress/zstd_ldm.h index 8f12c677a..7041c65a0 100644 --- a/lib/compress/zstd_ldm.h +++ b/lib/compress/zstd_ldm.h @@ -37,9 +37,13 @@ extern "C" { * parameters), which stores the matched sequences. The "long distance" * match is then stored with the remaining literals from the * ZSTD_blockCompressor. */ -size_t ZSTD_compressBlock_ldm(ZSTD_CCtx* cctx, const void* src, size_t srcSize); -size_t ZSTD_compressBlock_ldm_extDict(ZSTD_CCtx* ctx, - const void* src, size_t srcSize); +size_t ZSTD_compressBlock_ldm( + ldmState_t* ldms, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_CCtx_params const* params, void const* src, size_t srcSize); + +size_t ZSTD_compressBlock_ldm_extDict( + ldmState_t* ldms, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_CCtx_params const* params, void const* src, size_t srcSize); /** ZSTD_ldm_initializeParameters() : * Initialize the long distance matching parameters to their default values. */ diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 04824ccf2..1c56ff32d 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -243,13 +243,13 @@ MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length) /* Update hashTable3 up to ip (excluded) Assumption : always within prefix (i.e. not within extDict) */ -static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_CCtx* const cctx, const BYTE* const ip) +static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE* const ip) { - U32* const hashTable3 = cctx->hashTable3; - U32 const hashLog3 = cctx->hashLog3; - const BYTE* const base = cctx->base; - U32 idx = cctx->nextToUpdate3; - U32 const target = cctx->nextToUpdate3 = (U32)(ip - base); + U32* const hashTable3 = ms->hashTable3; + U32 const hashLog3 = ms->hashLog3; + const BYTE* const base = ms->base; + U32 idx = ms->nextToUpdate3; + U32 const target = ms->nextToUpdate3 = (U32)(ip - base); size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3); while(idx < target) { @@ -267,21 +267,22 @@ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_CCtx* const cctx, const BYTE* /** ZSTD_insertBt1() : add one or multiple positions to tree. * ip : assumed <= iend-8 . * @return : nb of positions added */ -static U32 ZSTD_insertBt1(ZSTD_CCtx* zc, +static U32 ZSTD_insertBt1( + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iend, - U32 nbCompares, U32 const mls, U32 const extDict) + U32 const mls, U32 const extDict) { - U32* const hashTable = zc->hashTable; - U32 const hashLog = zc->appliedParams.cParams.hashLog; + U32* const hashTable = ms->hashTable; + U32 const hashLog = cParams->hashLog; size_t const h = ZSTD_hashPtr(ip, hashLog, mls); - U32* const bt = zc->chainTable; - U32 const btLog = zc->appliedParams.cParams.chainLog - 1; + U32* const bt = ms->chainTable; + U32 const btLog = cParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; U32 matchIndex = hashTable[h]; size_t commonLengthSmaller=0, commonLengthLarger=0; - const BYTE* const base = zc->base; - const BYTE* const dictBase = zc->dictBase; - const U32 dictLimit = zc->dictLimit; + const BYTE* const base = ms->base; + const BYTE* const dictBase = ms->dictBase; + const U32 dictLimit = ms->dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* match; @@ -290,9 +291,10 @@ static U32 ZSTD_insertBt1(ZSTD_CCtx* zc, U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = smallerPtr + 1; U32 dummy32; /* to be nullified at the end */ - U32 const windowLow = zc->lowLimit; + U32 const windowLow = ms->lowLimit; U32 matchEndIdx = current+8+1; size_t bestLength = 8; + U32 nbCompares = 1U << cParams->searchLog; #ifdef ZSTD_C_PREDICT U32 predictedSmall = *(bt + 2*((current-1)&btMask) + 0); U32 predictedLarge = *(bt + 2*((current-1)&btMask) + 1); @@ -375,66 +377,60 @@ static U32 ZSTD_insertBt1(ZSTD_CCtx* zc, } FORCE_INLINE_TEMPLATE -void ZSTD_updateTree_internal(ZSTD_CCtx* zc, +void ZSTD_updateTree_internal( + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iend, - const U32 nbCompares, const U32 mls, const U32 extDict) + const U32 mls, const U32 extDict) { - const BYTE* const base = zc->base; + const BYTE* const base = ms->base; U32 const target = (U32)(ip - base); - U32 idx = zc->nextToUpdate; + U32 idx = ms->nextToUpdate; DEBUGLOG(7, "ZSTD_updateTree_internal, from %u to %u (extDict:%u)", idx, target, extDict); while(idx < target) - idx += ZSTD_insertBt1(zc, base+idx, iend, nbCompares, mls, extDict); - zc->nextToUpdate = target; + idx += ZSTD_insertBt1(ms, cParams, base+idx, iend, mls, extDict); + ms->nextToUpdate = target; } -void ZSTD_updateTree(ZSTD_CCtx* zc, - const BYTE* ip, const BYTE* iend, - U32 nbCompares, U32 mls) +void ZSTD_updateTree( + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, + const BYTE* ip, const BYTE* iend) { - ZSTD_updateTree_internal(zc, ip, iend, nbCompares, mls, 0 /*extDict*/); + ZSTD_updateTree_internal(ms, cParams, ip, iend, cParams->searchLength, 0 /*extDict*/); } -static void ZSTD_updateTree_extDict(ZSTD_CCtx* zc, - const BYTE* const ip, const BYTE* const iend, - const U32 nbCompares, const U32 mls) -{ - ZSTD_updateTree_internal(zc, ip, iend, nbCompares, mls, 1 /*extDict*/); -} - - FORCE_INLINE_TEMPLATE U32 ZSTD_insertBtAndGetAllMatches ( - ZSTD_CCtx* zc, + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iLimit, int const extDict, - U32 nbCompares, U32 const mls, U32 const sufficient_len, U32 rep[ZSTD_REP_NUM], U32 const ll0, - ZSTD_match_t* matches, const U32 lengthToBeat) + ZSTD_match_t* matches, const U32 lengthToBeat, U32 const mls /* template */) { - const BYTE* const base = zc->base; + U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1); + const BYTE* const base = ms->base; U32 const current = (U32)(ip-base); - U32 const hashLog = zc->appliedParams.cParams.hashLog; + U32 const hashLog = cParams->hashLog; U32 const minMatch = (mls==3) ? 3 : 4; - U32* const hashTable = zc->hashTable; + U32* const hashTable = ms->hashTable; size_t const h = ZSTD_hashPtr(ip, hashLog, mls); U32 matchIndex = hashTable[h]; - U32* const bt = zc->chainTable; - U32 const btLog = zc->appliedParams.cParams.chainLog - 1; + U32* const bt = ms->chainTable; + U32 const btLog = cParams->chainLog - 1; U32 const btMask= (1U << btLog) - 1; size_t commonLengthSmaller=0, commonLengthLarger=0; - const BYTE* const dictBase = zc->dictBase; - U32 const dictLimit = zc->dictLimit; + const BYTE* const dictBase = ms->dictBase; + U32 const dictLimit = ms->dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; U32 const btLow = btMask >= current ? 0 : current - btMask; - U32 const windowLow = zc->lowLimit; + U32 const windowLow = ms->lowLimit; U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = bt + 2*(current&btMask) + 1; U32 matchEndIdx = current+8+1; /* farthest referenced position of any match => detects repetitive patterns */ U32 dummy32; /* to be nullified at the end */ U32 mnum = 0; + U32 nbCompares = 1U << cParams->searchLog; size_t bestLength = lengthToBeat-1; DEBUGLOG(7, "ZSTD_insertBtAndGetAllMatches"); @@ -475,7 +471,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( /* HC3 match finder */ if ((mls == 3) /*static*/ && (bestLength < mls)) { - U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3 (zc, ip); + U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, ip); if ((matchIndex3 > windowLow) & (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) { size_t mlen; @@ -499,7 +495,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( mnum = 1; if ( (mlen > sufficient_len) | (ip+mlen == iLimit) ) { /* best possible length */ - zc->nextToUpdate = current+1; /* skip insertion */ + ms->nextToUpdate = current+1; /* skip insertion */ return 1; } } } } @@ -556,30 +552,29 @@ U32 ZSTD_insertBtAndGetAllMatches ( *smallerPtr = *largerPtr = 0; assert(matchEndIdx > current+8); - zc->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ + ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ return mnum; } FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches ( - ZSTD_CCtx* zc, /* Index table will be updated */ + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* ip, const BYTE* const iHighLimit, int const extDict, - U32 const maxNbAttempts, U32 const matchLengthSearch, U32 const sufficient_len, U32 rep[ZSTD_REP_NUM], U32 const ll0, ZSTD_match_t* matches, U32 const lengthToBeat) { + U32 const matchLengthSearch = cParams->searchLength; DEBUGLOG(7, "ZSTD_BtGetAllMatches"); - if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */ - if (extDict) ZSTD_updateTree_extDict(zc, ip, iHighLimit, maxNbAttempts, matchLengthSearch); - else ZSTD_updateTree(zc, ip, iHighLimit, maxNbAttempts, matchLengthSearch); + if (ip < ms->base + ms->nextToUpdate) return 0; /* skipped area */ + ZSTD_updateTree_internal(ms, cParams, ip, iHighLimit, matchLengthSearch, extDict); switch(matchLengthSearch) { - case 3 : return ZSTD_insertBtAndGetAllMatches(zc, ip, iHighLimit, extDict, maxNbAttempts, 3, sufficient_len, rep, ll0, matches, lengthToBeat); + case 3 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 3); default : - case 4 : return ZSTD_insertBtAndGetAllMatches(zc, ip, iHighLimit, extDict, maxNbAttempts, 4, sufficient_len, rep, ll0, matches, lengthToBeat); - case 5 : return ZSTD_insertBtAndGetAllMatches(zc, ip, iHighLimit, extDict, maxNbAttempts, 5, sufficient_len, rep, ll0, matches, lengthToBeat); + case 4 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 4); + case 5 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 5); case 7 : - case 6 : return ZSTD_insertBtAndGetAllMatches(zc, ip, iHighLimit, extDict, maxNbAttempts, 6, sufficient_len, rep, ll0, matches, lengthToBeat); + case 6 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 6); } } @@ -667,36 +662,33 @@ static int ZSTD_literalsContribution_cached( } FORCE_INLINE_TEMPLATE -size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, +size_t ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,seqStore_t* seqStore, + U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, const void* src, size_t srcSize, const int optLevel, const int extDict) { - seqStore_t* const seqStorePtr = &(ctx->seqStore); - optState_t* const optStatePtr = &(ctx->optState); + optState_t* const optStatePtr = &ms->opt; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; - const BYTE* const base = ctx->base; - const BYTE* const prefixStart = base + ctx->dictLimit; + const BYTE* const base = ms->base; + const BYTE* const prefixStart = base + ms->dictLimit; - U32 const maxSearches = 1U << ctx->appliedParams.cParams.searchLog; - U32 const sufficient_len = MIN(ctx->appliedParams.cParams.targetLength, ZSTD_OPT_NUM -1); - U32 const mls = ctx->appliedParams.cParams.searchLength; - U32 const minMatch = (ctx->appliedParams.cParams.searchLength == 3) ? 3 : 4; + U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1); + U32 const minMatch = (cParams->searchLength == 3) ? 3 : 4; ZSTD_optimal_t* const opt = optStatePtr->priceTable; ZSTD_match_t* const matches = optStatePtr->matchTable; cachedLiteralPrice_t cachedLitPrice; - U32 rep[ZSTD_REP_NUM]; /* init */ DEBUGLOG(5, "ZSTD_compressBlock_opt_generic"); - ctx->nextToUpdate3 = ctx->nextToUpdate; + ms->nextToUpdate3 = ms->nextToUpdate; ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize); ip += (ip==prefixStart); - { int i; for (i=0; irep[i]; } memset(&cachedLitPrice, 0, sizeof(cachedLitPrice)); /* Match Loop */ @@ -707,7 +699,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, /* find first match */ { U32 const litlen = (U32)(ip - anchor); U32 const ll0 = !litlen; - U32 const nbMatches = ZSTD_BtGetAllMatches(ctx, ip, iend, extDict, maxSearches, mls, sufficient_len, rep, ll0, matches, minMatch); + U32 const nbMatches = ZSTD_BtGetAllMatches(ms, cParams, ip, iend, extDict, rep, ll0, matches, minMatch); if (!nbMatches) { ip++; continue; } /* initialize opt[0] */ @@ -793,7 +785,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, U32 const litlen = (opt[cur].mlen == 1) ? opt[cur].litlen : 0; U32 const previousPrice = (cur > litlen) ? opt[cur-litlen].price : 0; U32 const basePrice = previousPrice + ZSTD_fullLiteralsCost(inr-litlen, litlen, optStatePtr); - U32 const nbMatches = ZSTD_BtGetAllMatches(ctx, inr, iend, extDict, maxSearches, mls, sufficient_len, opt[cur].rep, ll0, matches, minMatch); + U32 const nbMatches = ZSTD_BtGetAllMatches(ms, cParams, inr, iend, extDict, opt[cur].rep, ll0, matches, minMatch); U32 matchNb; if (!nbMatches) continue; @@ -889,37 +881,42 @@ _shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */ } ZSTD_updateStats(optStatePtr, llen, anchor, offset, mlen); - ZSTD_storeSeq(seqStorePtr, llen, anchor, offset, mlen-MINMATCH); + ZSTD_storeSeq(seqStore, llen, anchor, offset, mlen-MINMATCH); anchor = ip; } } ZSTD_setLog2Prices(optStatePtr); } /* while (ip < ilimit) */ - /* Save reps for next block */ - { int i; for (i=0; irepToConfirm[i] = rep[i]; } - /* Return the last literals size */ return iend - anchor; } -size_t ZSTD_compressBlock_btopt(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_btopt( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock_btopt"); - return ZSTD_compressBlock_opt_generic(ctx, src, srcSize, 0 /*optLevel*/, 0 /*extDict*/); + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, 0 /*extDict*/); } -size_t ZSTD_compressBlock_btultra(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_btultra( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - return ZSTD_compressBlock_opt_generic(ctx, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/); + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/); } -size_t ZSTD_compressBlock_btopt_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_btopt_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - return ZSTD_compressBlock_opt_generic(ctx, src, srcSize, 0 /*optLevel*/, 1 /*extDict*/); + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, 1 /*extDict*/); } -size_t ZSTD_compressBlock_btultra_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) +size_t ZSTD_compressBlock_btultra_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { - return ZSTD_compressBlock_opt_generic(ctx, src, srcSize, 2 /*optLevel*/, 1 /*extDict*/); + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, 1 /*extDict*/); } diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index b803a0f2e..b8dc389f3 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -15,16 +15,25 @@ extern "C" { #endif -#include "mem.h" /* U32 */ -#include "zstd.h" /* ZSTD_CCtx, size_t */ +#include "zstd_compress_internal.h" -void ZSTD_updateTree(ZSTD_CCtx* ctx, const BYTE* ip, const BYTE* iend, U32 nbCompares, U32 mls); /* used in ZSTD_loadDictionaryContent() */ +void ZSTD_updateTree( + ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, + const BYTE* ip, const BYTE* iend); /* used in ZSTD_loadDictionaryContent() */ -size_t ZSTD_compressBlock_btopt(ZSTD_CCtx* ctx, const void* src, size_t srcSize); -size_t ZSTD_compressBlock_btultra(ZSTD_CCtx* ctx, const void* src, size_t srcSize); +size_t ZSTD_compressBlock_btopt( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_btultra( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); -size_t ZSTD_compressBlock_btopt_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize); -size_t ZSTD_compressBlock_btultra_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize); +size_t ZSTD_compressBlock_btopt_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_btultra_extDict( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); #if defined (__cplusplus) } From aae267a2e1687b6eaebb3ada9c08ddbc5d5d74dc Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 12 Jan 2018 12:06:10 -0800 Subject: [PATCH 049/255] Reorganize block state --- lib/compress/zstd_compress.c | 107 +++++++++++++------------- lib/compress/zstd_compress_internal.h | 26 ++++--- 2 files changed, 69 insertions(+), 64 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index eba0baad0..49f73dc53 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -82,12 +82,12 @@ ZSTD_CCtx* ZSTD_initStaticCCtx(void *workspace, size_t workspaceSize) cctx->workSpaceSize = workspaceSize - sizeof(ZSTD_CCtx); /* statically sized space. entropyWorkspace never moves (but prev/next block swap places) */ - if (cctx->workSpaceSize < HUF_WORKSPACE_SIZE + 2 * sizeof(ZSTD_blockState_t)) return NULL; + if (cctx->workSpaceSize < HUF_WORKSPACE_SIZE + 2 * sizeof(ZSTD_compressedBlockState_t)) return NULL; assert(((size_t)cctx->workSpace & (sizeof(void*)-1)) == 0); /* ensure correct alignment */ - cctx->prevBlock = (ZSTD_blockState_t*)cctx->workSpace; - cctx->nextBlock = cctx->prevBlock + 1; + cctx->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)cctx->workSpace; + cctx->blockState.nextCBlock = cctx->blockState.prevCBlock + 1; { - void* const ptr = cctx->nextBlock + 1; + void* const ptr = cctx->blockState.nextCBlock + 1; cctx->entropyWorkspace = (U32*)ptr; } return cctx; @@ -665,7 +665,7 @@ size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params) 0 : MIN(ZSTD_HASHLOG3_MAX, cParams.windowLog); size_t const h3Size = ((size_t)1) << hashLog3; size_t const entropySpace = HUF_WORKSPACE_SIZE; - size_t const blockStateSpace = 2 * sizeof(ZSTD_blockState_t); + size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t); size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); size_t const optBudget = @@ -799,7 +799,7 @@ static U32 ZSTD_equivalentParams(ZSTD_CCtx_params params1, ZSTD_sufficientBuff(buffSize1, blockSize1, buffPol2, params2.cParams, pledgedSrcSize); } -static void ZSTD_resetBlockState(ZSTD_blockState_t* bs) +static void ZSTD_resetBlockState(ZSTD_compressedBlockState_t* bs) { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) @@ -845,8 +845,8 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pl (U32)pledgedSrcSize, cctx->appliedParams.fParams.contentSizeFlag); cctx->stage = ZSTDcs_init; cctx->dictID = 0; - ZSTD_invalidateMatchState(&cctx->matchState); - ZSTD_resetBlockState(cctx->prevBlock); + ZSTD_invalidateMatchState(&cctx->blockState.matchState); + ZSTD_resetBlockState(cctx->blockState.prevCBlock); XXH64_reset(&cctx->xxhState, 0); return 0; } @@ -860,6 +860,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ZSTD_compResetPolicy_e const crp, ZSTD_buffered_policy_e const zbuff) { + ZSTD_matchState_t* const ms = &zc->blockState.matchState; DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u", (U32)pledgedSrcSize, params.cParams.windowLog); assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); @@ -903,7 +904,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, /* Check if workSpace is large enough, alloc a new one if needed */ { size_t const entropySpace = HUF_WORKSPACE_SIZE; - size_t const blockStateSpace = 2 * sizeof(ZSTD_blockState_t); + size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t); size_t const optPotentialSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<workSpace & 3) == 0); /* ensure correct alignment */ - assert(zc->workSpaceSize >= 2 * sizeof(ZSTD_blockState_t)); - zc->prevBlock = (ZSTD_blockState_t*)zc->workSpace; - zc->nextBlock = zc->prevBlock + 1; - ptr = zc->nextBlock + 1; + assert(zc->workSpaceSize >= 2 * sizeof(ZSTD_compressedBlockState_t)); + zc->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)zc->workSpace; + zc->blockState.nextCBlock = zc->blockState.prevCBlock + 1; + ptr = zc->blockState.nextCBlock + 1; zc->entropyWorkspace = (U32*)ptr; } } @@ -957,19 +958,19 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->stage = ZSTDcs_init; zc->dictID = 0; - zc->matchState.nextSrc = NULL; - zc->matchState.base = NULL; - zc->matchState.dictBase = NULL; - zc->matchState.hashLog3 = hashLog3; - ZSTD_invalidateMatchState(&zc->matchState); + ms->nextSrc = NULL; + ms->base = NULL; + ms->dictBase = NULL; + ms->hashLog3 = hashLog3; + ZSTD_invalidateMatchState(ms); - ZSTD_resetBlockState(zc->prevBlock); + ZSTD_resetBlockState(zc->blockState.prevCBlock); ptr = zc->entropyWorkspace + HUF_WORKSPACE_SIZE_U32; /* opt parser space */ if ((params.cParams.strategy == ZSTD_btopt) | (params.cParams.strategy == ZSTD_btultra)) { - optState_t* opt = &zc->matchState.opt; + optState_t* opt = &zc->blockState.matchState.opt; DEBUGLOG(4, "reserving optimal parser space"); assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ opt->litFreq = (U32*)ptr; @@ -997,10 +998,10 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, DEBUGLOG(4, "reset table : %u", crp!=ZSTDcrp_noMemset); if (crp!=ZSTDcrp_noMemset) memset(ptr, 0, tableSpace); /* reset tables only */ assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ - zc->matchState.hashTable = (U32*)(ptr); - zc->matchState.chainTable = zc->matchState.hashTable + hSize; - zc->matchState.hashTable3 = zc->matchState.chainTable + chainSize; - ptr = zc->matchState.hashTable3 + h3Size; + ms->hashTable = (U32*)(ptr); + ms->chainTable = ms->hashTable + hSize; + ms->hashTable3 = ms->chainTable + chainSize; + ptr = ms->hashTable3 + h3Size; /* sequences storage */ zc->seqStore.sequencesStart = (seqDef*)ptr; @@ -1037,8 +1038,8 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, * do not use with extDict variant ! */ void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) { int i; - for (i=0; iprevBlock->rep[i] = 0; - assert(/* !extDict */ cctx->matchState.lowLimit == cctx->matchState.dictLimit); + for (i=0; iblockState.prevCBlock->rep[i] = 0; + assert(/* !extDict */ cctx->blockState.matchState.lowLimit == cctx->blockState.matchState.dictLimit); } @@ -1072,17 +1073,17 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, /* copy tables */ { size_t const chainSize = (srcCCtx->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << srcCCtx->appliedParams.cParams.chainLog); size_t const hSize = (size_t)1 << srcCCtx->appliedParams.cParams.hashLog; - size_t const h3Size = (size_t)1 << srcCCtx->matchState.hashLog3; + size_t const h3Size = (size_t)1 << srcCCtx->blockState.matchState.hashLog3; size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); - assert((U32*)dstCCtx->matchState.chainTable == (U32*)dstCCtx->matchState.hashTable + hSize); /* chainTable must follow hashTable */ - assert((U32*)dstCCtx->matchState.hashTable3 == (U32*)dstCCtx->matchState.chainTable + chainSize); - memcpy(dstCCtx->matchState.hashTable, srcCCtx->matchState.hashTable, tableSpace); /* presumes all tables follow each other */ + assert((U32*)dstCCtx->blockState.matchState.chainTable == (U32*)dstCCtx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */ + assert((U32*)dstCCtx->blockState.matchState.hashTable3 == (U32*)dstCCtx->blockState.matchState.chainTable + chainSize); + memcpy(dstCCtx->blockState.matchState.hashTable, srcCCtx->blockState.matchState.hashTable, tableSpace); /* presumes all tables follow each other */ } /* copy dictionary offsets */ { - ZSTD_matchState_t const* srcMatchState = &srcCCtx->matchState; - ZSTD_matchState_t* dstMatchState = &dstCCtx->matchState; + ZSTD_matchState_t const* srcMatchState = &srcCCtx->blockState.matchState; + ZSTD_matchState_t* dstMatchState = &dstCCtx->blockState.matchState; dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3; dstMatchState->nextSrc = srcMatchState->nextSrc; @@ -1095,7 +1096,7 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, dstCCtx->dictID = srcCCtx->dictID; /* copy block state */ - memcpy(dstCCtx->prevBlock, srcCCtx->prevBlock, sizeof(*srcCCtx->prevBlock)); + memcpy(dstCCtx->blockState.prevCBlock, srcCCtx->blockState.prevCBlock, sizeof(*srcCCtx->blockState.prevCBlock)); return 0; } @@ -1162,7 +1163,7 @@ static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size, * rescale all indexes to avoid future overflow (indexes are U32) */ static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue) { - ZSTD_matchState_t* const ms = &zc->matchState; + ZSTD_matchState_t* const ms = &zc->blockState.matchState; { U32 const hSize = (U32)1 << zc->appliedParams.cParams.hashLog; ZSTD_reduceTable(ms->hashTable, hSize, reducerValue); } @@ -1721,23 +1722,23 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u) (dictLimit=%u, nextToUpdate=%u)", - (U32)dstCapacity, zc->matchState.dictLimit, zc->matchState.nextToUpdate); + (U32)dstCapacity, zc->blockState.matchState.dictLimit, zc->blockState.matchState.nextToUpdate); if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) return 0; /* don't even attempt compression below a certain srcSize */ ZSTD_resetSeqStore(&(zc->seqStore)); /* limited update after a very long match */ - { const BYTE* const base = zc->matchState.base; + { const BYTE* const base = zc->blockState.matchState.base; const BYTE* const istart = (const BYTE*)src; const U32 current = (U32)(istart-base); - if (current > zc->matchState.nextToUpdate + 384) - zc->matchState.nextToUpdate = current - MIN(192, (U32)(current - zc->matchState.nextToUpdate - 384)); + if (current > zc->blockState.matchState.nextToUpdate + 384) + zc->blockState.matchState.nextToUpdate = current - MIN(192, (U32)(current - zc->blockState.matchState.nextToUpdate - 384)); } /* find and store sequences */ { - U32 const extDict = zc->matchState.lowLimit < zc->matchState.dictLimit; + U32 const extDict = zc->blockState.matchState.lowLimit < zc->blockState.matchState.dictLimit; size_t lastLLSize; - { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) zc->nextBlock->rep[i] = zc->prevBlock->rep[i]; } + { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i]; } if (zc->appliedParams.ldmParams.enableLdm) { typedef size_t (*ZSTD_ldmBlockCompressor)( ldmState_t* ldms, ZSTD_matchState_t* ms, seqStore_t* seqStore, @@ -1745,11 +1746,11 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void const* src, size_t srcSize); ZSTD_ldmBlockCompressor const ldmBlockCompressor = extDict ? ZSTD_compressBlock_ldm_extDict : ZSTD_compressBlock_ldm; - lastLLSize = ldmBlockCompressor(&zc->ldmState, &zc->matchState, &zc->seqStore, zc->nextBlock->rep, &zc->appliedParams, src, srcSize); + lastLLSize = ldmBlockCompressor(&zc->ldmState, &zc->blockState.matchState, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams, src, srcSize); } else { ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict); - lastLLSize = blockCompressor(&zc->matchState, &zc->seqStore, zc->nextBlock->rep, &zc->appliedParams.cParams, src, srcSize); + lastLLSize = blockCompressor(&zc->blockState.matchState, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, src, srcSize); } { const BYTE* const anchor = (const BYTE*)src + srcSize - lastLLSize; @@ -1758,14 +1759,14 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, } /* encode */ { - size_t const cSize = ZSTD_compressSequences(&zc->seqStore, &zc->prevBlock->entropy, &zc->nextBlock->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize, zc->entropyWorkspace); + size_t const cSize = ZSTD_compressSequences(&zc->seqStore, &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize, zc->entropyWorkspace); if (ZSTD_isError(cSize) || cSize == 0) return cSize; /* confirm repcodes and entropy tables */ { - ZSTD_blockState_t* const tmp = zc->prevBlock; - zc->prevBlock = zc->nextBlock; - zc->nextBlock = tmp; + ZSTD_compressedBlockState_t* const tmp = zc->blockState.prevCBlock; + zc->blockState.prevCBlock = zc->blockState.nextCBlock; + zc->blockState.nextCBlock = tmp; } return cSize; } @@ -1797,7 +1798,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, XXH64_update(&cctx->xxhState, src, srcSize); while (remaining) { - ZSTD_matchState_t* const ms = &cctx->matchState; + ZSTD_matchState_t* const ms = &cctx->blockState.matchState; U32 const lastBlock = lastFrameChunk & (blockSize >= remaining); if (dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE) @@ -1823,7 +1824,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, */ if (ms->lowLimit > (3U<<29)) { U32 const cycleMask = ((U32)1 << ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy)) - 1; - U32 const current = (U32)(ip - cctx->matchState.base); + U32 const current = (U32)(ip - cctx->blockState.matchState.base); U32 const newCurrent = (current & cycleMask) + ((U32)1 << cctx->appliedParams.cParams.windowLog); U32 const correction = current - newCurrent; ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30); @@ -1975,7 +1976,7 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, if (!srcSize) return fhSize; /* do not generate an empty block if no input */ - ZSTD_manageWindowContinuity(&cctx->matchState, src, srcSize); + ZSTD_manageWindowContinuity(&cctx->blockState.matchState, src, srcSize); DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (U32)cctx->blockSize); { size_t const cSize = frame ? @@ -2085,7 +2086,7 @@ static size_t ZSTD_checkDictNCount(short* normalizedCounter, unsigned dictMaxSym * assumptions : magic number supposed already checked * dictSize supposed > 8 */ -static size_t ZSTD_loadZstdDictionary(ZSTD_blockState_t* bs, ZSTD_matchState_t* ms, ZSTD_CCtx_params const* params, const void* dict, size_t dictSize, void* workspace) +static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_matchState_t* ms, ZSTD_CCtx_params const* params, const void* dict, size_t dictSize, void* workspace) { const BYTE* dictPtr = (const BYTE*)dict; const BYTE* const dictEnd = dictPtr + dictSize; @@ -2181,12 +2182,12 @@ static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* cctx, /* dict restricted modes */ if (dictMode==ZSTD_dm_rawContent) - return ZSTD_loadDictionaryContent(&cctx->matchState, &cctx->appliedParams, dict, dictSize); + return ZSTD_loadDictionaryContent(&cctx->blockState.matchState, &cctx->appliedParams, dict, dictSize); if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) { if (dictMode == ZSTD_dm_auto) { DEBUGLOG(4, "raw content dictionary detected"); - return ZSTD_loadDictionaryContent(&cctx->matchState, &cctx->appliedParams, dict, dictSize); + return ZSTD_loadDictionaryContent(&cctx->blockState.matchState, &cctx->appliedParams, dict, dictSize); } if (dictMode == ZSTD_dm_fullDict) return ERROR(dictionary_wrong); @@ -2195,7 +2196,7 @@ static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* cctx, /* dict as full zstd dictionary */ { - size_t const dictID = ZSTD_loadZstdDictionary(cctx->prevBlock, &cctx->matchState, &cctx->appliedParams, dict, dictSize, cctx->entropyWorkspace); + size_t const dictID = ZSTD_loadZstdDictionary(cctx->blockState.prevCBlock, &cctx->blockState.matchState, &cctx->appliedParams, dict, dictSize, cctx->entropyWorkspace); if (ZSTD_isError(dictID)) return dictID; cctx->dictID = (U32)dictID; } diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 7a7b1db1d..7be20d4c5 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -57,11 +57,6 @@ typedef struct { FSE_repeat litlength_repeatMode; } ZSTD_entropyCTables_t; -typedef struct { - ZSTD_entropyCTables_t entropy; - U32 rep[ZSTD_REP_NUM]; -} ZSTD_blockState_t; - typedef struct { U32 off; U32 len; @@ -98,7 +93,12 @@ typedef struct { } optState_t; typedef struct { - BYTE const* nextSrc; /* next block here to continue on current prefix */ /* TODO: Does this belong here? */ + ZSTD_entropyCTables_t entropy; + U32 rep[ZSTD_REP_NUM]; +} ZSTD_compressedBlockState_t; + +typedef struct { + BYTE const* nextSrc; /* next block here to continue on current prefix */ BYTE const* base; /* All regular indexes relative to this position */ BYTE const* dictBase; /* extDict indexes relative to this position */ U32 dictLimit; /* below that point, need extDict */ @@ -106,13 +106,19 @@ typedef struct { U32 nextToUpdate; /* index from which to continue table update */ U32 nextToUpdate3; /* index from which to continue table update */ U32 hashLog3; /* dispatch table : larger == faster, more memory */ - U32 loadedDictEnd; /* index of end of dictionary */ /* TODO: Does this belong here? */ + U32 loadedDictEnd; /* index of end of dictionary */ U32* hashTable; U32* hashTable3; U32* chainTable; optState_t opt; /* optimal parser state */ } ZSTD_matchState_t; +typedef struct { + ZSTD_compressedBlockState_t* prevCBlock; + ZSTD_compressedBlockState_t* nextCBlock; + ZSTD_matchState_t matchState; +} ZSTD_blockState_t; + typedef struct { U32 offset; U32 checksum; @@ -171,9 +177,7 @@ struct ZSTD_CCtx_s { seqStore_t seqStore; /* sequences storage ptrs */ ldmState_t ldmState; /* long distance matching state */ - ZSTD_matchState_t matchState; - ZSTD_blockState_t* prevBlock; - ZSTD_blockState_t* nextBlock; + ZSTD_blockState_t blockState; U32* entropyWorkspace; /* entropy workspace of HUF_WORKSPACE_SIZE bytes */ /* streaming */ @@ -202,7 +206,7 @@ struct ZSTD_CCtx_s { typedef size_t (*ZSTD_blockCompressor) ( - ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict); From 19cb37f837165fb3d8cbf6c4f890a67191ed96d3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 16 Jan 2018 11:40:42 -0800 Subject: [PATCH 050/255] travis ci : added gcc-7 test also added `-Werror` to sanitizer tests --- .travis.yml | 1 + Makefile | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 48d9ac3a6..bb6ca8474 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ matrix: # Ubuntu 14.04 - env: Cmd='make gcc6install && CC=gcc-6 make clean uasan-test-zstd' - env: Cmd='make gcc6install libc6install && CC=gcc-6 make clean uasan-test-zstd32' + - env: Cmd='make gcc7install && CC=gcc-7 make clean uasan-test-zstd' - env: Cmd='make clang38install && CC=clang-3.8 make clean msan-test-zstd' - env: Cmd='make gcc6install && CC=gcc-6 make clean uasan-fuzztest' diff --git a/Makefile b/Makefile index c8f0ed4d4..8157bdd9d 100644 --- a/Makefile +++ b/Makefile @@ -231,31 +231,31 @@ msanregressiontest: # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63303 usan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=signed-integer-overflow -fsanitize=undefined" + $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=signed-integer-overflow -fsanitize=undefined -Werror" asan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=address" + $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=address -Werror" asan-%: clean - LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=address" $(MAKE) -C $(TESTDIR) $* + LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=address -Werror" $(MAKE) -C $(TESTDIR) $* msan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=memory -fno-omit-frame-pointer" HAVE_LZMA=0 # datagen.c fails this test for no obvious reason + $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=memory -fno-omit-frame-pointer -Werror" HAVE_LZMA=0 # datagen.c fails this test for no obvious reason msan-%: clean - LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=memory -fno-omit-frame-pointer" FUZZER_FLAGS=--no-big-tests $(MAKE) -C $(TESTDIR) HAVE_LZMA=0 $* + LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=memory -fno-omit-frame-pointer -Werror" FUZZER_FLAGS=--no-big-tests $(MAKE) -C $(TESTDIR) HAVE_LZMA=0 $* asan32: clean $(MAKE) -C $(TESTDIR) test32 CC=clang MOREFLAGS="-g -fsanitize=address" uasan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=signed-integer-overflow -fsanitize=address,undefined" + $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=signed-integer-overflow -fsanitize=address,undefined -Werror" uasan-%: clean - LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=signed-integer-overflow -fsanitize=address,undefined" $(MAKE) -C $(TESTDIR) $* + LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=signed-integer-overflow -fsanitize=address,undefined -Werror" $(MAKE) -C $(TESTDIR) $* tsan-%: clean - LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=thread" $(MAKE) -C $(TESTDIR) $* FUZZER_FLAGS=--no-big-tests + LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=thread -Werror" $(MAKE) -C $(TESTDIR) $* FUZZER_FLAGS=--no-big-tests apt-install: sudo apt-get -yq --no-install-suggests --no-install-recommends --force-yes install $(APT_PACKAGES) @@ -279,6 +279,9 @@ libc6install: gcc6install: apt-add-repo APT_PACKAGES="libc6-dev-i386 gcc-multilib gcc-6 gcc-6-multilib" $(MAKE) apt-install +gcc7install: apt-add-repo + APT_PACKAGES="libc6-dev-i386 gcc-multilib gcc-7 gcc-7-multilib" $(MAKE) apt-install + gpp6install: apt-add-repo APT_PACKAGES="libc6-dev-i386 g++-multilib gcc-6 g++-6 g++-6-multilib" $(MAKE) apt-install From f4e58455f67de7451b0316925e2944d7caa5f1fb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 16 Jan 2018 11:50:16 -0800 Subject: [PATCH 051/255] ensure MOREFLAGS are not lost in root->tests Makefile invocation --- Makefile | 2 +- tests/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8157bdd9d..d2c71ad39 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ check: shortest .PHONY: test shortest test shortest: - $(MAKE) -C $(PRGDIR) allVariants MOREFLAGS="-g -DZSTD_DEBUG=1" + $(MAKE) -C $(PRGDIR) allVariants MOREFLAGS+="-g -DZSTD_DEBUG=1" $(MAKE) -C $(TESTDIR) $@ .PHONY: examples diff --git a/tests/Makefile b/tests/Makefile index 95d0bf549..f3171efa5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -98,7 +98,7 @@ zstd-nolegacy: $(MAKE) -C $(PRGDIR) $@ MOREFLAGS+="$(DEBUGFLAGS)" gzstd: - $(MAKE) -C $(PRGDIR) zstd HAVE_ZLIB=1 MOREFLAGS="$(DEBUGFLAGS)" + $(MAKE) -C $(PRGDIR) zstd HAVE_ZLIB=1 MOREFLAGS+="$(DEBUGFLAGS)" .PHONY: zstd-dll : From 2e23333094e6df6f84b7e21b2dcf64deff88ec30 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 16 Jan 2018 15:28:43 -0800 Subject: [PATCH 052/255] ZSTDMT can now work in non-blocking mode with 1 thread it still fallbacks to single-thread blocking invocation when input is small (<1job) or when invoking ZSTDMT_compress(), which is blocking. Also : fixed a bug in new block-granular compression routine. --- lib/compress/zstd_compress.c | 3 +- lib/compress/zstdmt_compress.c | 71 ++++++++++++++++++-------------- lib/compress/zstdmt_compress.h | 2 +- lib/decompress/zstd_decompress.c | 12 +++--- tests/zstreamtest.c | 7 ++++ 5 files changed, 57 insertions(+), 38 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 49f73dc53..d2703f231 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1962,7 +1962,8 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, { size_t fhSize = 0; - DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u", cctx->stage); + DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u, srcSize: %u", + cctx->stage, (U32)srcSize); if (cctx->stage==ZSTDcs_created) return ERROR(stage_wrong); /* missing init (ZSTD_compressBegin) */ if (frame && (cctx->stage==ZSTDcs_init)) { diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 3a5b58a72..47b8923ac 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -365,11 +365,12 @@ void ZSTDMT_compressChunk(void* jobDescription) if (!job->firstChunk) { /* flush and overwrite frame header when it's not first job */ size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, 0); if (ZSTD_isError(hSize)) { job->cSize = hSize; /* save error code */ goto _endJob; } + DEBUGLOG(5, "ZSTDMT_compressChunk: flush and overwrite %u bytes of frame header (not first chunk)", (U32)hSize); ZSTD_invalidateRepCodes(cctx); } /* compress */ -#if 1 +#if 0 job->cSize = (job->lastChunk) ? ZSTD_compressEnd (cctx, dstBuff.start, dstBuff.size, src, job->srcSize) : ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, job->srcSize); @@ -382,26 +383,28 @@ void ZSTDMT_compressChunk(void* jobDescription) BYTE* op = ostart; BYTE* oend = op + dstBuff.size; int blockNb; + DEBUGLOG(5, "ZSTDMT_compressChunk: compress %u bytes in %i blocks", (U32)job->srcSize, nbBlocks); job->cSize = 0; - for (blockNb = 0; blockNb < nbBlocks-1; blockNb++) { + for (blockNb = 1; blockNb < nbBlocks; blockNb++) { size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, ZSTD_BLOCKSIZE_MAX); if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; } ip += ZSTD_BLOCKSIZE_MAX; op += cSize; assert(op < oend); /* stats */ job->cSize += cSize; - job->readSize = ZSTD_BLOCKSIZE_MAX * (blockNb+1); + job->readSize = ZSTD_BLOCKSIZE_MAX * blockNb; } /* last block */ - { size_t const lastBlockSize1 = job->srcSize & (ZSTD_BLOCKSIZE_MAX-1); - size_t const lastBlockSize = (lastBlockSize1==0) ? ZSTD_BLOCKSIZE_MAX : lastBlockSize1; + if ((nbBlocks > 0) | job->lastChunk /*need to output a "last block" flag*/ ) { + size_t const lastBlockSize1 = job->srcSize & (ZSTD_BLOCKSIZE_MAX-1); + size_t const lastBlockSize = ((lastBlockSize1==0) & (job->srcSize>=ZSTD_BLOCKSIZE_MAX)) ? ZSTD_BLOCKSIZE_MAX : lastBlockSize1; size_t const cSize = (job->lastChunk) ? ZSTD_compressEnd (cctx, op, oend-op, ip, lastBlockSize) : ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize); if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; } /* stats */ job->cSize += cSize; - job->readSize = ZSTD_BLOCKSIZE_MAX * (blockNb+1); + job->readSize = job->srcSize; } } #endif @@ -443,7 +446,7 @@ struct ZSTDMT_CCtx_s { size_t targetDictSize; inBuff_t inBuff; XXH64_state_t xxhState; - unsigned singleThreaded; + unsigned singleBlockingThread; unsigned jobIDMask; unsigned doneJobID; unsigned nextJobID; @@ -457,7 +460,7 @@ struct ZSTDMT_CCtx_s { /* Sets parameters relevant to the compression job, initializing others to * default values. Notably, nbThreads should probably be zero. */ -static ZSTD_CCtx_params ZSTDMT_makeJobCCtxParams(ZSTD_CCtx_params const params) +static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) { ZSTD_CCtx_params jobParams; memset(&jobParams, 0, sizeof(jobParams)); @@ -646,17 +649,21 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, /* ===== Multi-threaded compression ===== */ /* ------------------------------------------ */ -static unsigned computeNbChunks(size_t srcSize, unsigned windowLog, unsigned nbThreads) { - size_t const chunkSizeTarget = (size_t)1 << (windowLog + 2); - size_t const chunkMaxSize = chunkSizeTarget << 2; - size_t const passSizeMax = chunkMaxSize * nbThreads; - unsigned const multiplier = (unsigned)(srcSize / passSizeMax) + 1; - unsigned const nbChunksLarge = multiplier * nbThreads; - unsigned const nbChunksMax = (unsigned)(srcSize / chunkSizeTarget) + 1; - unsigned const nbChunksSmall = MIN(nbChunksMax, nbThreads); - return (multiplier>1) ? nbChunksLarge : nbChunksSmall; -} +static unsigned ZSTDMT_computeNbChunks(size_t srcSize, unsigned windowLog, unsigned nbThreads) { + assert(nbThreads>0); + { size_t const chunkSizeTarget = (size_t)1 << (windowLog + 2); + size_t const chunkMaxSize = chunkSizeTarget << 2; + size_t const passSizeMax = chunkMaxSize * nbThreads; + unsigned const multiplier = (unsigned)(srcSize / passSizeMax) + 1; + unsigned const nbChunksLarge = multiplier * nbThreads; + unsigned const nbChunksMax = (unsigned)(srcSize / chunkSizeTarget) + 1; + unsigned const nbChunksSmall = MIN(nbChunksMax, nbThreads); + return (multiplier>1) ? nbChunksLarge : nbChunksSmall; +} } +/* ZSTDMT_compress_advanced_internal() : + * This is a blocking function : it will only give back control to caller after finishing its compression job. + */ static size_t ZSTDMT_compress_advanced_internal( ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, @@ -664,10 +671,10 @@ static size_t ZSTDMT_compress_advanced_internal( const ZSTD_CDict* cdict, ZSTD_CCtx_params const params) { - ZSTD_CCtx_params const jobParams = ZSTDMT_makeJobCCtxParams(params); + ZSTD_CCtx_params const jobParams = ZSTDMT_initJobCCtxParams(params); unsigned const overlapRLog = (params.overlapSizeLog>9) ? 0 : 9-params.overlapSizeLog; size_t const overlapSize = (overlapRLog>=9) ? 0 : (size_t)1 << (params.cParams.windowLog - overlapRLog); - unsigned nbChunks = computeNbChunks(srcSize, params.cParams.windowLog, params.nbThreads); + unsigned nbChunks = ZSTDMT_computeNbChunks(srcSize, params.cParams.windowLog, params.nbThreads); size_t const proposedChunkSize = (srcSize + (nbChunks-1)) / nbChunks; size_t const avgChunkSize = (((proposedChunkSize-1) & 0x1FFFF) < 0x7FFF) ? proposedChunkSize + 0xFFFF : proposedChunkSize; /* avoid too small last block */ const char* const srcStart = (const char*)src; @@ -678,14 +685,16 @@ static size_t ZSTDMT_compress_advanced_internal( assert(jobParams.nbThreads == 0); assert(mtctx->cctxPool->totalCCtx == params.nbThreads); - DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbChunks=%2u (rawSize=%u bytes; fixedSize=%u) ", + DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbChunks=%2u (rawSize=%u bytes; fixedSize=%u) ", nbChunks, (U32)proposedChunkSize, (U32)avgChunkSize); - if (nbChunks==1) { /* fallback to single-thread mode */ + + if ((nbChunks==1) | (params.nbThreads<=1)) { /* fallback to single-thread mode : this is a blocking invocation anyway */ ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0]; if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams); return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, NULL, 0, jobParams); } - assert(avgChunkSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), which is required for compressWithinDst */ + + assert(avgChunkSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), required to compress directly into Dst (no additional buffer) */ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgChunkSize) ); XXH64_reset(&xxh64, 0); @@ -695,6 +704,7 @@ static size_t ZSTDMT_compress_advanced_internal( mtctx->jobIDMask = 0; mtctx->jobs = ZSTDMT_allocJobsTable(&nbJobs, mtctx->cMem); if (mtctx->jobs==NULL) return ERROR(memory_allocation); + assert((nbJobs != 0) && ((nbJobs & (nbJobs - 1)) == 0)); /* ensure nbJobs is a power of 2 */ mtctx->jobIDMask = nbJobs - 1; } @@ -827,10 +837,10 @@ size_t ZSTDMT_initCStream_internal( assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ assert(zcs->cctxPool->totalCCtx == params.nbThreads); - zcs->singleThreaded = (params.nbThreads==1) | (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */ + zcs->singleBlockingThread = pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN; /* do not trigger multi-threading when srcSize is too small */ - if (zcs->singleThreaded) { - ZSTD_CCtx_params const singleThreadParams = ZSTDMT_makeJobCCtxParams(params); + if (zcs->singleBlockingThread) { + ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(params); DEBUGLOG(4, "single thread mode"); assert(singleThreadParams.nbThreads == 0); return ZSTD_initCStream_internal(zcs->cctxPool->cctx[0], @@ -921,10 +931,11 @@ size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize) } size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { - ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, 0); + ZSTD_parameters const params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0); ZSTD_CCtx_params cctxParams = zcs->params; cctxParams.cParams = params.cParams; cctxParams.fParams = params.fParams; + DEBUGLOG(4, "ZSTDMT_initCStream (cLevel=%i)", compressionLevel); return ZSTDMT_initCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN); } @@ -1071,7 +1082,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, assert(output->pos <= output->size); assert(input->pos <= input->size); - if (mtctx->singleThreaded) { /* delegate to single-thread (synchronous) */ + if (mtctx->singleBlockingThread) { /* delegate to single-thread (synchronous) */ return ZSTD_compressStream_generic(mtctx->cctxPool->cctx[0], output, input, endOp); } @@ -1166,7 +1177,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* ou size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output) { DEBUGLOG(5, "ZSTDMT_flushStream"); - if (mtctx->singleThreaded) + if (mtctx->singleBlockingThread) return ZSTD_flushStream(mtctx->cctxPool->cctx[0], output); return ZSTDMT_flushStream_internal(mtctx, output, 0 /* endFrame */); } @@ -1174,7 +1185,7 @@ size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output) size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output) { DEBUGLOG(4, "ZSTDMT_endStream"); - if (mtctx->singleThreaded) + if (mtctx->singleBlockingThread) return ZSTD_endStream(mtctx->cctxPool->cctx[0], output); return ZSTDMT_flushStream_internal(mtctx, output, 1 /* endFrame */); } diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index d12f0adb8..b6e68684e 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -85,7 +85,7 @@ ZSTDLIB_API size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, * List of parameters that can be set using ZSTDMT_setMTCtxParameter() */ typedef enum { ZSTDMT_p_jobSize, /* Each job is compressed in parallel. By default, this value is dynamically determined depending on compression parameters. Can be set explicitly here. */ - ZSTDMT_p_overlapSectionLog /* Each job may reload a part of previous job to enhance compressionr ratio; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window */ + ZSTDMT_p_overlapSectionLog /* Each job may reload a part of previous job to enhance compressionr ratio; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window. This is a "sticky" parameter : its value will be re-used on next compression job */ } ZSTDMT_parameter; /* ZSTDMT_setMTCtxParameter() : diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index ab6c2edc5..a573cc19f 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1759,7 +1759,7 @@ static int ZSTD_isSkipFrame(ZSTD_DCtx* dctx) { return dctx->stage == ZSTDds_skip * or an error code, which can be tested using ZSTD_isError() */ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { - DEBUGLOG(5, "ZSTD_decompressContinue"); + DEBUGLOG(5, "ZSTD_decompressContinue (srcSize:%u)", (U32)srcSize); /* Sanity check */ if (srcSize != dctx->expected) return ERROR(srcSize_wrong); /* not allowed */ if (dstCapacity) ZSTD_checkContinuity(dctx, dst); @@ -1820,12 +1820,12 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c case ZSTDds_decompressLastBlock: case ZSTDds_decompressBlock: - DEBUGLOG(5, "case ZSTDds_decompressBlock"); + DEBUGLOG(5, "ZSTD_decompressContinue: case ZSTDds_decompressBlock"); { size_t rSize; switch(dctx->bType) { case bt_compressed: - DEBUGLOG(5, "case bt_compressed"); + DEBUGLOG(5, "ZSTD_decompressContinue: case bt_compressed"); rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 1); break; case bt_raw : @@ -1839,12 +1839,12 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c return ERROR(corruption_detected); } if (ZSTD_isError(rSize)) return rSize; - DEBUGLOG(5, "decoded size from block : %u", (U32)rSize); + DEBUGLOG(5, "ZSTD_decompressContinue: decoded size from block : %u", (U32)rSize); dctx->decodedSize += rSize; if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, dst, rSize); if (dctx->stage == ZSTDds_decompressLastBlock) { /* end of frame */ - DEBUGLOG(4, "decoded size from frame : %u", (U32)dctx->decodedSize); + DEBUGLOG(4, "ZSTD_decompressContinue: decoded size from frame : %u", (U32)dctx->decodedSize); if (dctx->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) { if (dctx->decodedSize != dctx->fParams.frameContentSize) { return ERROR(corruption_detected); @@ -1868,7 +1868,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c assert(srcSize == 4); /* guaranteed by dctx->expected */ { U32 const h32 = (U32)XXH64_digest(&dctx->xxhState); U32 const check32 = MEM_readLE32(src); - DEBUGLOG(4, "checksum : calculated %08X :: %08X read", h32, check32); + DEBUGLOG(4, "ZSTD_decompressContinue: checksum : calculated %08X :: %08X read", h32, check32); if (check32 != h32) return ERROR(checksum_wrong); dctx->expected = 0; dctx->stage = ZSTDds_getFrameHeaderSize; diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 5590c9af1..5cd1ea0fb 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -1243,6 +1243,7 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp maxTestSize = FUZ_randomLength(&lseed, oldTestLog+2); if (maxTestSize >= srcBufferSize) maxTestSize = srcBufferSize-1; { int const compressionLevel = (FUZ_rand(&lseed) % 5) + 1; + DISPLAYLEVEL(5, "Init with compression level = %i \n", compressionLevel); CHECK_Z( ZSTDMT_initCStream(zc, compressionLevel) ); } } else { @@ -1301,9 +1302,12 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp if ((FUZ_rand(&lseed) & 15) == 0) { size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize); + size_t const previousPos = outBuff.pos; outBuff.size = outBuff.pos + adjustedDstSize; DISPLAYLEVEL(5, "Flushing into dst buffer of size %u \n", (U32)adjustedDstSize); CHECK_Z( ZSTDMT_flushStream(zc, &outBuff) ); + assert(outBuff.pos >= previousPos); + DISPLAYLEVEL(6, "%u bytes flushed by ZSTDMT_flushStream \n", (U32)(outBuff.pos-previousPos)); } } /* final frame epilogue */ @@ -1311,10 +1315,13 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp while (remainingToFlush) { size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize); + size_t const previousPos = outBuff.pos; outBuff.size = outBuff.pos + adjustedDstSize; DISPLAYLEVEL(5, "Ending into dst buffer of size %u \n", (U32)adjustedDstSize); remainingToFlush = ZSTDMT_endStream(zc, &outBuff); CHECK (ZSTD_isError(remainingToFlush), "ZSTDMT_endStream error : %s", ZSTD_getErrorName(remainingToFlush)); + assert(outBuff.pos >= previousPos); + DISPLAYLEVEL(6, "%u bytes flushed by ZSTDMT_endStream \n", (U32)(outBuff.pos-previousPos)); DISPLAYLEVEL(5, "endStream : remainingToFlush : %u \n", (U32)remainingToFlush); } } crcOrig = XXH64_digest(&xxhState); From 6025465e42dfce6d3312b14a80e7614bc52b362c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 16 Jan 2018 15:34:41 -0800 Subject: [PATCH 053/255] ZSTDMT : minor CCtx memory optimization can be useful when a compression job only has small amount of data to compress. --- lib/compress/zstdmt_compress.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 47b8923ac..0dadda284 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -346,7 +346,7 @@ void ZSTDMT_compressChunk(void* jobDescription) assert(job->firstChunk); /* only allowed for first job */ if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ - U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : ZSTD_CONTENTSIZE_UNKNOWN; + U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : job->srcSize; ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */ { size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk); if (ZSTD_isError(forceWindowError)) { @@ -355,7 +355,7 @@ void ZSTDMT_compressChunk(void* jobDescription) } } { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->prefixSize, ZSTD_dm_rawContent, /* load dictionary in "content-only" mode (no header analysis) */ - NULL, + NULL, /*cdict*/ jobParams, pledgedSrcSize); if (ZSTD_isError(initError)) { job->cSize = initError; @@ -404,8 +404,8 @@ void ZSTDMT_compressChunk(void* jobDescription) if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; } /* stats */ job->cSize += cSize; - job->readSize = job->srcSize; } + job->readSize = job->srcSize; } #endif From 1dba98d563d81fb42b4ac575e70eaf52654bbf8e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 16 Jan 2018 16:15:47 -0800 Subject: [PATCH 054/255] introduced parameter ZSTD_p_nonBlockingMode This new parameter makes it possible to call streaming ZSTDMT with a single thread set which is non blocking. It makes it possible for the main thread to do other tasks in parallel while the worker thread does compression. Typically, for zstd cli, it means it can do I/O stuff. Applied within fileio.c, this patch provides non-negligible gains during compression. Tested on my laptop, with enwik9 (1000000000 bytes) : time zstd -f enwik9 With traditional single-thread blocking mode : real 0m9.557s user 0m8.861s sys 0m0.538s With new single-worker non blocking mode : real 0m7.938s user 0m8.049s sys 0m0.514s => 20% faster --- lib/compress/zstd_compress.c | 29 ++++++++++++++++++--------- lib/compress/zstd_compress_internal.h | 1 + lib/compress/zstdmt_compress.c | 2 +- lib/compress/zstdmt_compress.h | 2 +- lib/zstd.h | 10 +++++++++ programs/fileio.c | 1 + 6 files changed, 34 insertions(+), 11 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d2703f231..fbae3c3f2 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -281,9 +281,8 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v } return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); + case ZSTD_p_nonBlockingMode: case ZSTD_p_jobSize: - return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); - case ZSTD_p_overlapSizeLog: return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); @@ -407,11 +406,18 @@ size_t ZSTD_CCtxParam_setParameter( return ZSTDMT_CCtxParam_setNbThreads(CCtxParams, value); #endif + case ZSTD_p_nonBlockingMode : +#ifndef ZSTD_MULTITHREAD + return ERROR(parameter_unsupported); +#else + CCtxParams->nonBlockingMode = (value>0); + return CCtxParams->nonBlockingMode; +#endif + case ZSTD_p_jobSize : #ifndef ZSTD_MULTITHREAD return ERROR(parameter_unsupported); #else - if (CCtxParams->nbThreads <= 1) return ERROR(parameter_unsupported); return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_jobSize, value); #endif @@ -419,7 +425,6 @@ size_t ZSTD_CCtxParam_setParameter( #ifndef ZSTD_MULTITHREAD return ERROR(parameter_unsupported); #else - if (CCtxParams->nbThreads <= 1) return ERROR(parameter_unsupported); return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_overlapSectionLog, value); #endif @@ -3007,12 +3012,17 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/); #ifdef ZSTD_MULTITHREAD - if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) + if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) { params.nbThreads = 1; /* do not invoke multi-threading when src size is too small */ - if (params.nbThreads > 1) { + params.nonBlockingMode = 0; + } + if ((params.nbThreads > 1) | (params.nonBlockingMode == 1)) { if (cctx->mtctx == NULL || (params.nbThreads != ZSTDMT_getNbThreads(cctx->mtctx))) { - DEBUGLOG(4, "ZSTD_compress_generic: creating new mtctx for nbThreads=%u (previous: %u)", - params.nbThreads, (unsigned)ZSTDMT_getNbThreads(cctx->mtctx)); + DEBUGLOG(4, "ZSTD_compress_generic: creating new mtctx for nbThreads=%u", + params.nbThreads); + if (cctx->mtctx != NULL) + DEBUGLOG(4, "ZSTD_compress_generic: previous nbThreads was %u", + ZSTDMT_getNbThreads(cctx->mtctx)); ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbThreads, cctx->customMem); if (cctx->mtctx == NULL) return ERROR(memory_allocation); @@ -3024,6 +3034,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) ); cctx->streamStage = zcss_load; cctx->appliedParams.nbThreads = params.nbThreads; + cctx->appliedParams.nonBlockingMode = params.nonBlockingMode; } else #endif { CHECK_F( ZSTD_resetCStream_internal( @@ -3036,7 +3047,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, /* compression stage */ #ifdef ZSTD_MULTITHREAD - if (cctx->appliedParams.nbThreads > 1) { + if ((cctx->appliedParams.nbThreads > 1) | (cctx->appliedParams.nonBlockingMode==1)) { size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp); if ( ZSTD_isError(flushMin) || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */ diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 7be20d4c5..eb18cbc5e 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -150,6 +150,7 @@ struct ZSTD_CCtx_params_s { /* Multithreading: used to pass parameters to mtctx */ U32 nbThreads; + int nonBlockingMode; /* will trigger ZSTDMT even with nbThreads==1 */ unsigned jobSize; unsigned overlapSizeLog; diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 0dadda284..0c28eb787 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -497,7 +497,7 @@ size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThread /* ZSTDMT_getNbThreads(): * @return nb threads currently active in mtctx. * mtctx must be valid */ -size_t ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx) +unsigned ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx) { assert(mtctx != NULL); return mtctx->params.nbThreads; diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index b6e68684e..7716ea68e 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -120,7 +120,7 @@ size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThread /* ZSTDMT_getNbThreads(): * @return nb threads currently active in mtctx. * mtctx must be valid */ -size_t ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx); +unsigned ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx); /*! ZSTDMT_initCStream_internal() : * Private use only. Init streaming operation. diff --git a/lib/zstd.h b/lib/zstd.h index a84490d99..6ac132a66 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -972,10 +972,20 @@ typedef enum { ZSTD_p_dictIDFlag, /* When applicable, dictionary's ID is written into frame header (default:1) */ /* multi-threading parameters */ + /* These parameters are only useful if multi-threading is enabled (ZSTD_MULTITHREAD). + * They return an error otherwise. */ ZSTD_p_nbThreads=400, /* Select how many threads a compression job can spawn (default:1) * More threads improve speed, but also increase memory usage. * Can only receive a value > 1 if ZSTD_MULTITHREAD is enabled. * Special: value 0 means "do not change nbThreads" */ + ZSTD_p_nonBlockingMode, /* Single thread mode is by default "blocking" : + * it finishes its job as much as possible, and only then gives back control to caller. + * In contrast, multi-thread is by default "non-blocking" : + * it takes some input, flush some output if available, and immediately gives back control to caller. + * Compression work is performed in parallel, within worker threads. + * (note : a strong exception to this rule is when first job is called with ZSTD_e_end : it becomes blocking) + * Setting this parameter to 1 will enforce non-blocking mode even when only 1 thread is selected. + * It allows the caller to do other tasks while the worker thread compresses in parallel. */ ZSTD_p_jobSize, /* Size of a compression job. This value is only enforced in streaming (non-blocking) mode. * Each compression job is completed in parallel, so indirectly controls the nb of active threads. * 0 means default, which is dynamically determined based on compression parameters. diff --git a/programs/fileio.c b/programs/fileio.c index 3ae2d4057..ea84853c7 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -457,6 +457,7 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, /* multi-threading */ DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbThreads); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbThreads, g_nbThreads) ); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nonBlockingMode, 1) ); /* dictionary */ CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) ); /* just for dictionary loading, for compression parameters adaptation */ CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, dictBuffer, dictBuffSize) ); From 10c213761ac8714bfa9b9e6cf70648ec458308e8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 16 Jan 2018 17:28:11 -0800 Subject: [PATCH 055/255] cli: fix for no-MT mode when cli is compiled without MT support, invoking ZSTD_p_nonBlockingMode result in an error code. This patch only sets ZSTD_p_nonBlockingMode when ZSTD_MULTITHREAD is set, meaning there is MT support. The error code could also be intentionnally ignored (there is no side effect). --- programs/fileio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/programs/fileio.c b/programs/fileio.c index ea84853c7..58b77a813 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -457,7 +457,9 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, /* multi-threading */ DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbThreads); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbThreads, g_nbThreads) ); +#ifdef ZSTD_MULTITHREAD CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nonBlockingMode, 1) ); +#endif /* dictionary */ CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) ); /* just for dictionary loading, for compression parameters adaptation */ CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, dictBuffer, dictBuffSize) ); From 3e1e57db2775c49595b483e22589eb2e20015b14 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 16 Jan 2018 17:35:00 -0800 Subject: [PATCH 056/255] fix fileio progression status update The compression % is no longer correct, since it's no longer possible to make direct correlation between nb bytes read and nb bytes written due to large internal buffer inside CCtx (exacerbated with --long). The current "fix" is to no longer display the %. A more complex solution will have to count exactly how much data has been consumed and compressed internally, within CCtx buffers. --- programs/fileio.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 58b77a813..326f0e673 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -809,21 +809,11 @@ static int FIO_compressFilename_internal(cRess_t ress, compressedfilesize += outBuff.pos; } } - if (g_nbThreads > 1) { - if (fileSize == UTIL_FILESIZE_UNKNOWN) - DISPLAYUPDATE(2, "\rRead : %u MB", (U32)(readsize>>20)) - else - DISPLAYUPDATE(2, "\rRead : %u / %u MB", - (U32)(readsize>>20), (U32)(fileSize>>20)); + if (fileSize == UTIL_FILESIZE_UNKNOWN) { + DISPLAYUPDATE(2, "\rRead : %u MB", (U32)(readsize>>20)); } else { - if (fileSize == UTIL_FILESIZE_UNKNOWN) - DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%", - (U32)(readsize>>20), - (double)compressedfilesize/readsize*100) - else - DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%", - (U32)(readsize>>20), (U32)(fileSize>>20), - (double)compressedfilesize/readsize*100); + DISPLAYUPDATE(2, "\rRead : %u / %u MB", + (U32)(readsize>>20), (U32)(fileSize>>20)); } } while (directive != ZSTD_e_end); From 22303da601ccf2e068113c51ce85cc192ec6cd07 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Wed, 17 Jan 2018 10:27:52 +0300 Subject: [PATCH 057/255] CMake: fixed multithreading build on Windows `ZSTD_MULTITHREAD_SUPPORT` option fixed for Windows. Signed-off-by: Alexey Ivanov --- build/cmake/CMakeLists.txt | 42 +++++++++++++++++++++-------- build/cmake/lib/CMakeLists.txt | 13 ++++++++- build/cmake/programs/CMakeLists.txt | 16 +++++------ 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 7361bc8a6..fd9bc2b1e 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -31,18 +31,9 @@ MESSAGE(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_LIBDIR}") #----------------------------------------------------------------------------- # Options #----------------------------------------------------------------------------- + +# Legacy support OPTION(ZSTD_LEGACY_SUPPORT "LEGACY SUPPORT" OFF) -IF (UNIX) - OPTION(ZSTD_MULTITHREAD_SUPPORT "MULTITHREADING SUPPORT" ON) -ELSE (UNIX) - OPTION(ZSTD_MULTITHREAD_SUPPORT "MULTITHREADING SUPPORT" OFF) -ENDIF (UNIX) -OPTION(ZSTD_BUILD_PROGRAMS "BUILD PROGRAMS" ON) -OPTION(ZSTD_BUILD_CONTRIB "BUILD CONTRIB" OFF) -OPTION(ZSTD_BUILD_TESTS "BUILD TESTS" OFF) -if (MSVC) - OPTION(ZSTD_USE_STATIC_RUNTIME "LINK TO STATIC RUN-TIME LIBRARIES" OFF) -endif () IF (ZSTD_LEGACY_SUPPORT) MESSAGE(STATUS "ZSTD_LEGACY_SUPPORT defined!") @@ -52,6 +43,35 @@ ELSE (ZSTD_LEGACY_SUPPORT) ADD_DEFINITIONS(-DZSTD_LEGACY_SUPPORT=0) ENDIF (ZSTD_LEGACY_SUPPORT) +# Multi-threading support +OPTION(ZSTD_MULTITHREAD_SUPPORT "MULTITHREADING SUPPORT" ON) + +IF (ZSTD_MULTITHREAD_SUPPORT) + MESSAGE(STATUS "ZSTD_MULTITHREAD_SUPPORT is enabled") +ELSE (ZSTD_MULTITHREAD_SUPPORT) + MESSAGE(STATUS "ZSTD_MULTITHREAD_SUPPORT is disabled") +ENDIF (ZSTD_MULTITHREAD_SUPPORT) + +OPTION(ZSTD_BUILD_PROGRAMS "BUILD PROGRAMS" ON) +OPTION(ZSTD_BUILD_CONTRIB "BUILD CONTRIB" OFF) +OPTION(ZSTD_BUILD_TESTS "BUILD TESTS" OFF) +if (MSVC) + OPTION(ZSTD_USE_STATIC_RUNTIME "LINK TO STATIC RUN-TIME LIBRARIES" OFF) +endif () + +#----------------------------------------------------------------------------- +# External dependencies +#----------------------------------------------------------------------------- +IF (ZSTD_MULTITHREAD_SUPPORT AND UNIX) + SET(THREADS_PREFER_PTHREAD_FLAG ON) + FIND_PACKAGE(Threads REQUIRED) + IF(CMAKE_USE_PTHREADS_INIT) + SET(THREADS_LIBS "${CMAKE_THREAD_LIBS_INIT}") + ELSE() + MESSAGE(SEND_ERROR "ZSTD currently does not support thread libraries other than pthreads") + ENDIF() +ENDIF (ZSTD_MULTITHREAD_SUPPORT AND UNIX) + #----------------------------------------------------------------------------- # Add source directories #----------------------------------------------------------------------------- diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index 3435e1bb3..945fdb922 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -24,7 +24,7 @@ INCLUDE_DIRECTORIES(${LIBRARY_DIR} ${LIBRARY_DIR}/common) # Parse version INCLUDE(GetZstdLibraryVersion) GetZstdLibraryVersion(${LIBRARY_DIR}/zstd.h LIBVER_MAJOR LIBVER_MINOR LIBVER_RELEASE) -MESSAGE("ZSTD VERSION ${LIBVER_MAJOR}.${LIBVER_MINOR}.${LIBVER_RELEASE}") +MESSAGE(STATUS "ZSTD VERSION ${LIBVER_MAJOR}.${LIBVER_MINOR}.${LIBVER_RELEASE}") SET(Sources ${LIBRARY_DIR}/common/entropy_common.c @@ -120,6 +120,17 @@ IF (MSVC) ENDIF (ZSTD_BUILD_STATIC) ENDIF (MSVC) +# Add multi-threading support definitions + +IF (ZSTD_MULTITHREAD_SUPPORT) + SET_PROPERTY(TARGET libzstd_shared libzstd_static APPEND PROPERTY COMPILE_DEFINITIONS "ZSTD_MULTITHREAD") + + IF (UNIX) + TARGET_LINK_LIBRARIES(libzstd_shared ${THREADS_LIBS}) + TARGET_LINK_LIBRARIES(libzstd_static ${THREADS_LIBS}) + ENDIF () +ENDIF (ZSTD_MULTITHREAD_SUPPORT) + # With MSVC static library needs to be renamed to avoid conflict with import library IF (MSVC) SET(STATIC_LIBRARY_BASE_NAME zstd_static) diff --git a/build/cmake/programs/CMakeLists.txt b/build/cmake/programs/CMakeLists.txt index 9251fd295..6c0ed05a1 100644 --- a/build/cmake/programs/CMakeLists.txt +++ b/build/cmake/programs/CMakeLists.txt @@ -54,19 +54,17 @@ IF (UNIX) SET_PROPERTY(TARGET zstd-frugal APPEND PROPERTY COMPILE_DEFINITIONS "ZSTD_NOBENCH;ZSTD_NODICT") ENDIF (UNIX) +# Add multi-threading support definitions + IF (ZSTD_MULTITHREAD_SUPPORT) SET_PROPERTY(TARGET zstd APPEND PROPERTY COMPILE_DEFINITIONS "ZSTD_MULTITHREAD") - SET(THREADS_PREFER_PTHREAD_FLAG ON) - FIND_PACKAGE(Threads REQUIRED) - IF (CMAKE_USE_PTHREADS_INIT) - TARGET_LINK_LIBRARIES(zstd ${CMAKE_THREAD_LIBS_INIT}) - ELSE() - MESSAGE(SEND_ERROR "ZSTD currently does not support thread libraries other than pthreads") - ENDIF() + IF (UNIX) + TARGET_LINK_LIBRARIES(zstd ${THREADS_LIBS}) - ADD_CUSTOM_TARGET(zstdmt ALL ${CMAKE_COMMAND} -E create_symlink zstd zstdmt DEPENDS zstd COMMENT "Creating zstdmt symlink") - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/zstdmt DESTINATION "bin") + ADD_CUSTOM_TARGET(zstdmt ALL ${CMAKE_COMMAND} -E create_symlink zstd zstdmt DEPENDS zstd COMMENT "Creating zstdmt symlink") + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/zstdmt DESTINATION "bin") + ENDIF (UNIX) ENDIF (ZSTD_MULTITHREAD_SUPPORT) OPTION(ZSTD_ZLIB_SUPPORT "ZLIB SUPPORT" OFF) From cb57c107ff12b1f39adbb25ff14db56112a68cd1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 17 Jan 2018 11:39:07 -0800 Subject: [PATCH 058/255] zstdmt: minor variable renaming, for clarity --- lib/compress/zstd_compress.c | 1 + lib/compress/zstdmt_compress.c | 58 +++++++++++++++++----------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index fbae3c3f2..5cc02bcc3 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2579,6 +2579,7 @@ ZSTD_CDict* ZSTD_initStaticCDict(void* workspace, size_t workspaceSize, } ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict) { + assert(cdict != NULL); return cdict->refContext->appliedParams.cParams; } diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 0c28eb787..c65f3ea15 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -442,8 +442,8 @@ struct ZSTDMT_CCtx_s { ZSTD_CCtx_params params; size_t targetSectionSize; size_t inBuffSize; - size_t dictSize; - size_t targetDictSize; + size_t prefixSize; + size_t targetPrefixSize; inBuff_t inBuff; XXH64_state_t xxhState; unsigned singleBlockingThread; @@ -841,13 +841,13 @@ size_t ZSTDMT_initCStream_internal( if (zcs->singleBlockingThread) { ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(params); - DEBUGLOG(4, "single thread mode"); + DEBUGLOG(4, "ZSTDMT_initCStream_internal: switch to single blocking thread mode"); assert(singleThreadParams.nbThreads == 0); return ZSTD_initCStream_internal(zcs->cctxPool->cctx[0], dict, dictSize, cdict, singleThreadParams, pledgedSrcSize); } - DEBUGLOG(4, "multi-threading mode (%u threads)", params.nbThreads); + DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u threads", params.nbThreads); if (zcs->allJobsCompleted == 0) { /* previous compression not correctly finished */ ZSTDMT_waitForAllJobsCompleted(zcs); @@ -871,17 +871,17 @@ size_t ZSTDMT_initCStream_internal( } assert(params.overlapSizeLog <= 9); - zcs->targetDictSize = (params.overlapSizeLog==0) ? 0 : (size_t)1 << (params.cParams.windowLog - (9 - params.overlapSizeLog)); - DEBUGLOG(4, "overlapLog=%u => %u KB", params.overlapSizeLog, (U32)(zcs->targetDictSize>>10)); + zcs->targetPrefixSize = (params.overlapSizeLog==0) ? 0 : (size_t)1 << (params.cParams.windowLog - (9 - params.overlapSizeLog)); + DEBUGLOG(4, "overlapLog=%u => %u KB", params.overlapSizeLog, (U32)(zcs->targetPrefixSize>>10)); zcs->targetSectionSize = params.jobSize ? params.jobSize : (size_t)1 << (params.cParams.windowLog + 2); if (zcs->targetSectionSize < ZSTDMT_JOBSIZE_MIN) zcs->targetSectionSize = ZSTDMT_JOBSIZE_MIN; - if (zcs->targetSectionSize < zcs->targetDictSize) zcs->targetSectionSize = zcs->targetDictSize; /* job size must be >= overlap size */ + if (zcs->targetSectionSize < zcs->targetPrefixSize) zcs->targetSectionSize = zcs->targetPrefixSize; /* job size must be >= overlap size */ DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(zcs->targetSectionSize>>10), params.jobSize); - zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize; + zcs->inBuffSize = zcs->targetPrefixSize + zcs->targetSectionSize; DEBUGLOG(4, "inBuff Size : %u KB", (U32)(zcs->inBuffSize>>10)); ZSTDMT_setBufferSize(zcs->bufPool, MAX(zcs->inBuffSize, ZSTD_compressBound(zcs->targetSectionSize)) ); zcs->inBuff.buffer = g_nullBuffer; - zcs->dictSize = 0; + zcs->prefixSize = 0; zcs->doneJobID = 0; zcs->nextJobID = 0; zcs->frameEnded = 0; @@ -895,8 +895,8 @@ size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, ZSTD_parameters params, unsigned long long pledgedSrcSize) { - ZSTD_CCtx_params cctxParams = mtctx->params; - DEBUGLOG(5, "ZSTDMT_initCStream_advanced (pledgedSrcSize=%u)", (U32)pledgedSrcSize); + ZSTD_CCtx_params cctxParams = mtctx->params; /* retrieve sticky params */ + DEBUGLOG(4, "ZSTDMT_initCStream_advanced (pledgedSrcSize=%u)", (U32)pledgedSrcSize); cctxParams.cParams = params.cParams; cctxParams.fParams = params.fParams; return ZSTDMT_initCStream_internal(mtctx, dict, dictSize, ZSTD_dm_auto, NULL, @@ -909,9 +909,9 @@ size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize) { ZSTD_CCtx_params cctxParams = mtctx->params; + if (cdict==NULL) return ERROR(dictionary_wrong); /* method incompatible with NULL cdict */ cctxParams.cParams = ZSTD_getCParamsFromCDict(cdict); cctxParams.fParams = fParams; - if (cdict==NULL) return ERROR(dictionary_wrong); /* method incompatible with NULL cdict */ return ZSTDMT_initCStream_internal(mtctx, NULL, 0 /*dictSize*/, ZSTD_dm_auto, cdict, cctxParams, pledgedSrcSize); } @@ -932,10 +932,10 @@ size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize) size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { ZSTD_parameters const params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0); - ZSTD_CCtx_params cctxParams = zcs->params; + ZSTD_CCtx_params cctxParams = zcs->params; /* retrieve sticky params */ + DEBUGLOG(4, "ZSTDMT_initCStream (cLevel=%i)", compressionLevel); cctxParams.cParams = params.cParams; cctxParams.fParams = params.fParams; - DEBUGLOG(4, "ZSTDMT_initCStream (cLevel=%i)", compressionLevel); return ZSTDMT_initCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN); } @@ -945,13 +945,13 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ", - zcs->nextJobID, (U32)srcSize, (U32)zcs->dictSize); + zcs->nextJobID, (U32)srcSize, (U32)zcs->prefixSize); zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; zcs->jobs[jobID].readSize = 0; - zcs->jobs[jobID].prefixSize = zcs->dictSize; - assert(zcs->inBuff.filled >= srcSize + zcs->dictSize); + zcs->jobs[jobID].prefixSize = zcs->prefixSize; + assert(zcs->inBuff.filled >= srcSize + zcs->prefixSize); zcs->jobs[jobID].params = zcs->params; /* do not calculate checksum within sections, but write it in header for first section */ if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0; @@ -968,11 +968,11 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond; if (zcs->params.fParams.checksumFlag) - XXH64_update(&zcs->xxhState, (const char*)zcs->inBuff.buffer.start + zcs->dictSize, srcSize); + XXH64_update(&zcs->xxhState, (const char*)zcs->inBuff.buffer.start + zcs->prefixSize, srcSize); /* get a new buffer for next input */ if (!endFrame) { - size_t const newDictSize = MIN(srcSize + zcs->dictSize, zcs->targetDictSize); + size_t const newPrefixSize = MIN(srcSize + zcs->prefixSize, zcs->targetPrefixSize); zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->bufPool); if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */ zcs->jobs[jobID].jobCompleted = 1; @@ -981,15 +981,15 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi ZSTDMT_releaseAllJobResources(zcs); return ERROR(memory_allocation); } - zcs->inBuff.filled -= srcSize + zcs->dictSize - newDictSize; + zcs->inBuff.filled -= srcSize + zcs->prefixSize - newPrefixSize; memmove(zcs->inBuff.buffer.start, - (const char*)zcs->jobs[jobID].srcStart + zcs->dictSize + srcSize - newDictSize, + (const char*)zcs->jobs[jobID].srcStart + zcs->prefixSize + srcSize - newPrefixSize, zcs->inBuff.filled); - zcs->dictSize = newDictSize; + zcs->prefixSize = newPrefixSize; } else { /* if (endFrame==1) */ zcs->inBuff.buffer = g_nullBuffer; zcs->inBuff.filled = 0; - zcs->dictSize = 0; + zcs->prefixSize = 0; zcs->frameEnded = 1; if (zcs->nextJobID == 0) { /* single chunk exception : checksum is calculated directly within worker thread */ @@ -1028,17 +1028,17 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi { ZSTDMT_jobDescription job = zcs->jobs[wJobID]; if (!job.jobScanned) { if (ZSTD_isError(job.cSize)) { - DEBUGLOG(5, "job %u : compression error detected : %s", + DEBUGLOG(5, "ZSTDMT_flushNextJob: job %u : compression error detected : %s", zcs->doneJobID, ZSTD_getErrorName(job.cSize)); ZSTDMT_waitForAllJobsCompleted(zcs); ZSTDMT_releaseAllJobResources(zcs); return job.cSize; } - DEBUGLOG(5, "zcs->params.fParams.checksumFlag : %u ", zcs->params.fParams.checksumFlag); + DEBUGLOG(5, "ZSTDMT_flushNextJob: zcs->params.fParams.checksumFlag : %u ", zcs->params.fParams.checksumFlag); if (zcs->params.fParams.checksumFlag) { if (zcs->frameEnded && (zcs->doneJobID+1 == zcs->nextJobID)) { /* write checksum at end of last section */ U32 const checksum = (U32)XXH64_digest(&zcs->xxhState); - DEBUGLOG(5, "writing checksum : %08X \n", checksum); + DEBUGLOG(5, "ZSTDMT_flushNextJob: writing checksum : %08X \n", checksum); MEM_writeLE32((char*)job.dstBuff.start + job.cSize, checksum); job.cSize += 4; zcs->jobs[wJobID].cSize += 4; @@ -1046,7 +1046,7 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi zcs->jobs[wJobID].jobScanned = 1; } { size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); - DEBUGLOG(5, "Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); + DEBUGLOG(5, "ZSTDMT_flushNextJob: Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); output->pos += toWrite; job.dstFlushed += toWrite; @@ -1076,7 +1076,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, ZSTD_inBuffer* input, ZSTD_EndDirective endOp) { - size_t const newJobThreshold = mtctx->dictSize + mtctx->targetSectionSize; + size_t const newJobThreshold = mtctx->prefixSize + mtctx->targetSectionSize; unsigned forwardInputProgress = 0; DEBUGLOG(5, "ZSTDMT_compressStream_generic "); assert(output->pos <= output->size); @@ -1160,7 +1160,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, unsigned endFrame) { - size_t const srcSize = mtctx->inBuff.filled - mtctx->dictSize; + size_t const srcSize = mtctx->inBuff.filled - mtctx->prefixSize; DEBUGLOG(5, "ZSTDMT_flushStream_internal"); if ( ((srcSize > 0) || (endFrame && !mtctx->frameEnded)) From 16bd0fd4dfe72b0af6a1fa672c2ef90a6d0788d7 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 16 Jan 2018 15:23:39 -0800 Subject: [PATCH 059/255] Reduce size of ZSTD_CDict Shaves 492,076 B off of the `ZSTD_CDict`. The size of a `ZSTD_CDict` created from a 112,640 B dictionary is: | Level | Before (B) | After (B) | |-------|------------|-----------| | 1 | 648,448 | 156,412 | | 3 | 1,140,008 | 647,932 | --- lib/compress/zstd_compress.c | 279 +++++++++++++++++++++++------------ 1 file changed, 188 insertions(+), 91 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 49f73dc53..4ad5a6e61 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -49,7 +49,13 @@ struct ZSTD_CDict_s { void* dictBuffer; const void* dictContent; size_t dictContentSize; - ZSTD_CCtx* refContext; + void* workspace; + size_t workspaceSize; + ZSTD_matchState_t matchState; + ZSTD_compressedBlockState_t cBlockState; + ZSTD_compressionParameters cParams; + ZSTD_customMem customMem; + U32 dictID; }; /* typedef'd to ZSTD_CDict within "zstd.h" */ ZSTD_CCtx* ZSTD_createCCtx(void) @@ -799,7 +805,7 @@ static U32 ZSTD_equivalentParams(ZSTD_CCtx_params params1, ZSTD_sufficientBuff(buffSize1, blockSize1, buffPol2, params2.cParams, pledgedSrcSize); } -static void ZSTD_resetBlockState(ZSTD_compressedBlockState_t* bs) +static void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs) { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) @@ -846,13 +852,74 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pl cctx->stage = ZSTDcs_init; cctx->dictID = 0; ZSTD_invalidateMatchState(&cctx->blockState.matchState); - ZSTD_resetBlockState(cctx->blockState.prevCBlock); + ZSTD_reset_compressedBlockState(cctx->blockState.prevCBlock); XXH64_reset(&cctx->xxhState, 0); return 0; } typedef enum { ZSTDcrp_continue, ZSTDcrp_noMemset } ZSTD_compResetPolicy_e; +static size_t ZSTD_sizeof_matchState(ZSTD_compressionParameters const* cParams, const U32 opt) +{ + size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog); + size_t const hSize = ((size_t)1) << cParams->hashLog; + U32 const hashLog3 = (cParams->searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog); + size_t const h3Size = ((size_t)1) << hashLog3; + size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); + size_t const optPotentialSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<strategy == ZSTD_btopt) || + (cParams->strategy == ZSTD_btultra))) + ? optPotentialSpace + : 0; + DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u", + (U32)chainSize, (U32)hSize, (U32)h3Size); + return tableSpace + optSpace; +} + +static void* ZSTD_reset_matchState(ZSTD_matchState_t* ms, void* ptr, ZSTD_compressionParameters const* cParams, ZSTD_compResetPolicy_e const crp, U32 const opt) +{ + size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog); + size_t const hSize = ((size_t)1) << cParams->hashLog; + U32 const hashLog3 = (cParams->searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog); + size_t const h3Size = ((size_t)1) << hashLog3; + size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); + + assert(((size_t)ptr & 3) == 0); + + ms->nextSrc = NULL; + ms->base = NULL; + ms->dictBase = NULL; + ms->hashLog3 = hashLog3; + ZSTD_invalidateMatchState(ms); + + /* opt parser space */ + if (opt && ((cParams->strategy == ZSTD_btopt) | (cParams->strategy == ZSTD_btultra))) { + DEBUGLOG(4, "reserving optimal parser space"); + ms->opt.litFreq = (U32*)ptr; + ms->opt.litLengthFreq = ms->opt.litFreq + (1<opt.matchLengthFreq = ms->opt.litLengthFreq + (MaxLL+1); + ms->opt.offCodeFreq = ms->opt.matchLengthFreq + (MaxML+1); + ptr = ms->opt.offCodeFreq + (MaxOff+1); + ms->opt.matchTable = (ZSTD_match_t*)ptr; + ptr = ms->opt.matchTable + ZSTD_OPT_NUM+1; + ms->opt.priceTable = (ZSTD_optimal_t*)ptr; + ptr = ms->opt.priceTable + ZSTD_OPT_NUM+1; + } + + /* table Space */ + DEBUGLOG(4, "reset table : %u", crp!=ZSTDcrp_noMemset); + assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ + if (crp!=ZSTDcrp_noMemset) memset(ptr, 0, tableSpace); /* reset tables only */ + ms->hashTable = (U32*)(ptr); + ms->chainTable = ms->hashTable + hSize; + ms->hashTable3 = ms->chainTable + chainSize; + ptr = ms->hashTable3 + h3Size; + + assert(((size_t)ptr & 3) == 0); + return ptr; +} + /*! ZSTD_resetCCtx_internal() : note : `params` are assumed fully validated at this stage */ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, @@ -860,7 +927,6 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ZSTD_compResetPolicy_e const crp, ZSTD_buffered_policy_e const zbuff) { - ZSTD_matchState_t* const ms = &zc->blockState.matchState; DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u", (U32)pledgedSrcSize, params.cParams.windowLog); assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); @@ -891,35 +957,23 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, U32 const divider = (params.cParams.searchLength==3) ? 3 : 4; size_t const maxNbSeq = blockSize / divider; size_t const tokenSpace = blockSize + 11*maxNbSeq; - size_t const chainSize = (params.cParams.strategy == ZSTD_fast) ? - 0 : ((size_t)1 << params.cParams.chainLog); - size_t const hSize = ((size_t)1) << params.cParams.hashLog; - U32 const hashLog3 = (params.cParams.searchLength>3) ? - 0 : MIN(ZSTD_HASHLOG3_MAX, params.cParams.windowLog); - size_t const h3Size = ((size_t)1) << hashLog3; - size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); size_t const buffOutSize = (zbuff==ZSTDb_buffered) ? ZSTD_compressBound(blockSize)+1 : 0; size_t const buffInSize = (zbuff==ZSTDb_buffered) ? windowSize + blockSize : 0; + size_t const matchStateSize = ZSTD_sizeof_matchState(¶ms.cParams, /* opt */ 1); void* ptr; /* Check if workSpace is large enough, alloc a new one if needed */ { size_t const entropySpace = HUF_WORKSPACE_SIZE; size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t); - size_t const optPotentialSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<>10), (U32)(tableSpace>>10), (U32)(bufferSpace>>10)); - DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u - windowSize: %u - blockSize: %u", - (U32)chainSize, (U32)hSize, (U32)h3Size, (U32)windowSize, (U32)blockSize); + size_t const neededSpace = entropySpace + blockStateSpace + ldmSpace + + matchStateSize + tokenSpace + bufferSpace; + DEBUGLOG(4, "Need %uKB workspace, including %uKB for match state, and %uKB for buffers", + (U32)(neededSpace>>10), (U32)(matchStateSize>>10), (U32)(bufferSpace>>10)); + DEBUGLOG(4, "windowSize: %u - blockSize: %u", (U32)windowSize, (U32)blockSize); if (zc->workSpaceSize < neededSpace) { /* too small : resize */ DEBUGLOG(4, "Need to update workSpaceSize from %uK to %uK", @@ -958,32 +1012,10 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->stage = ZSTDcs_init; zc->dictID = 0; - ms->nextSrc = NULL; - ms->base = NULL; - ms->dictBase = NULL; - ms->hashLog3 = hashLog3; - ZSTD_invalidateMatchState(ms); - - ZSTD_resetBlockState(zc->blockState.prevCBlock); + ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock); ptr = zc->entropyWorkspace + HUF_WORKSPACE_SIZE_U32; - /* opt parser space */ - if ((params.cParams.strategy == ZSTD_btopt) | (params.cParams.strategy == ZSTD_btultra)) { - optState_t* opt = &zc->blockState.matchState.opt; - DEBUGLOG(4, "reserving optimal parser space"); - assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ - opt->litFreq = (U32*)ptr; - opt->litLengthFreq = opt->litFreq + (1<matchLengthFreq = opt->litLengthFreq + (MaxLL+1); - opt->offCodeFreq = opt->matchLengthFreq + (MaxML+1); - ptr = opt->offCodeFreq + (MaxOff+1); - opt->matchTable = (ZSTD_match_t*)ptr; - ptr = opt->matchTable + ZSTD_OPT_NUM+1; - opt->priceTable = (ZSTD_optimal_t*)ptr; - ptr = opt->priceTable + ZSTD_OPT_NUM+1; - } - /* ldm hash table */ /* initialize bucketOffsets table later for pointer alignment */ if (params.ldmParams.enableLdm) { @@ -994,14 +1026,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ptr = zc->ldmState.hashTable + ldmHSize; } - /* table Space */ - DEBUGLOG(4, "reset table : %u", crp!=ZSTDcrp_noMemset); - if (crp!=ZSTDcrp_noMemset) memset(ptr, 0, tableSpace); /* reset tables only */ - assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ - ms->hashTable = (U32*)(ptr); - ms->chainTable = ms->hashTable + hSize; - ms->hashTable3 = ms->chainTable + chainSize; - ptr = ms->hashTable3 + h3Size; + ptr = ZSTD_reset_matchState(&zc->blockState.matchState, ptr, ¶ms.cParams, crp, /* opt */ 1); /* sequences storage */ zc->seqStore.sequencesStart = (seqDef*)ptr; @@ -1042,6 +1067,54 @@ void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) { assert(/* !extDict */ cctx->blockState.matchState.lowLimit == cctx->blockState.matchState.dictLimit); } +static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, + const ZSTD_CDict* cdict, + unsigned windowLog, + ZSTD_frameParameters fParams, + U64 pledgedSrcSize, + ZSTD_buffered_policy_e zbuff) +{ + { ZSTD_CCtx_params params = cctx->requestedParams; + /* Copy only compression parameters related to tables. */ + params.cParams = cdict->cParams; + if (windowLog) params.cParams.windowLog = windowLog; + params.fParams = fParams; + ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, + ZSTDcrp_noMemset, zbuff); + } + + /* copy tables */ + { size_t const chainSize = (cctx->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << cctx->appliedParams.cParams.chainLog); + size_t const hSize = (size_t)1 << cctx->appliedParams.cParams.hashLog; + size_t const h3Size = (size_t)1 << cctx->blockState.matchState.hashLog3; + size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); + assert((U32*)cctx->blockState.matchState.chainTable == (U32*)cctx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */ + assert((U32*)cctx->blockState.matchState.hashTable3 == (U32*)cctx->blockState.matchState.chainTable + chainSize); + assert((U32*)cdict->matchState.chainTable == (U32*)cdict->matchState.hashTable + hSize); /* chainTable must follow hashTable */ + assert((U32*)cdict->matchState.hashTable3 == (U32*)cdict->matchState.chainTable + chainSize); + memcpy(cctx->blockState.matchState.hashTable, cdict->matchState.hashTable, tableSpace); /* presumes all tables follow each other */ + } + + /* copy dictionary offsets */ + { + ZSTD_matchState_t const* srcMatchState = &cdict->matchState; + ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState; + dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; + dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3; + dstMatchState->nextSrc = srcMatchState->nextSrc; + dstMatchState->base = srcMatchState->base; + dstMatchState->dictBase = srcMatchState->dictBase; + dstMatchState->dictLimit = srcMatchState->dictLimit; + dstMatchState->lowLimit = srcMatchState->lowLimit; + dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd; + } + cctx->dictID = cdict->dictID; + + /* copy block state */ + memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState)); + + return 0; +} /*! ZSTD_copyCCtx_internal() : * Duplicate an existing context `srcCCtx` into another one `dstCCtx`. @@ -2172,22 +2245,26 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_matc } /** ZSTD_compress_insertDictionary() : -* @return : 0, or an error code */ -static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* cctx, +* @return : dictID, or an error code */ +static size_t ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_matchState_t* ms, + ZSTD_CCtx_params const* params, const void* dict, size_t dictSize, - ZSTD_dictMode_e dictMode) + ZSTD_dictMode_e dictMode, + void* workspace) { DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize); if ((dict==NULL) || (dictSize<=8)) return 0; + ZSTD_reset_compressedBlockState(bs); + /* dict restricted modes */ if (dictMode==ZSTD_dm_rawContent) - return ZSTD_loadDictionaryContent(&cctx->blockState.matchState, &cctx->appliedParams, dict, dictSize); + return ZSTD_loadDictionaryContent(ms, params, dict, dictSize); if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) { if (dictMode == ZSTD_dm_auto) { DEBUGLOG(4, "raw content dictionary detected"); - return ZSTD_loadDictionaryContent(&cctx->blockState.matchState, &cctx->appliedParams, dict, dictSize); + return ZSTD_loadDictionaryContent(ms, params, dict, dictSize); } if (dictMode == ZSTD_dm_fullDict) return ERROR(dictionary_wrong); @@ -2195,12 +2272,7 @@ static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* cctx, } /* dict as full zstd dictionary */ - { - size_t const dictID = ZSTD_loadZstdDictionary(cctx->blockState.prevCBlock, &cctx->blockState.matchState, &cctx->appliedParams, dict, dictSize, cctx->entropyWorkspace); - if (ZSTD_isError(dictID)) return dictID; - cctx->dictID = (U32)dictID; - } - return 0; + return ZSTD_loadZstdDictionary(bs, ms, params, dict, dictSize, workspace); } /*! ZSTD_compressBegin_internal() : @@ -2219,14 +2291,21 @@ size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, if (cdict && cdict->dictContentSize>0) { cctx->requestedParams = params; - return ZSTD_copyCCtx_internal(cctx, cdict->refContext, - params.cParams.windowLog, params.fParams, pledgedSrcSize, - zbuff); + return ZSTD_resetCCtx_usingCDict(cctx, cdict, params.cParams.windowLog, + params.fParams, pledgedSrcSize, zbuff); } CHECK_F( ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, ZSTDcrp_continue, zbuff) ); - return ZSTD_compress_insertDictionary(cctx, dict, dictSize, dictMode); + { + size_t const dictID = ZSTD_compress_insertDictionary( + cctx->blockState.prevCBlock, &cctx->blockState.matchState, + ¶ms, dict, dictSize, dictMode, cctx->entropyWorkspace); + if (ZSTD_isError(dictID)) return dictID; + assert(dictID <= (size_t)(U32)-1); + cctx->dictID = (U32)dictID; + } + return 0; } size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx, @@ -2417,9 +2496,7 @@ size_t ZSTD_estimateCDictSize_advanced( ZSTD_dictLoadMethod_e dictLoadMethod) { DEBUGLOG(5, "sizeof(ZSTD_CDict) : %u", (U32)sizeof(ZSTD_CDict)); - DEBUGLOG(5, "CCtx estimate : %u", - (U32)ZSTD_estimateCCtxSize_usingCParams(cParams)); - return sizeof(ZSTD_CDict) + ZSTD_estimateCCtxSize_usingCParams(cParams) + return sizeof(ZSTD_CDict) + HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* opt */ 0) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize); } @@ -2433,8 +2510,7 @@ size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict) { if (cdict==NULL) return 0; /* support sizeof on NULL */ DEBUGLOG(5, "sizeof(*cdict) : %u", (U32)sizeof(*cdict)); - DEBUGLOG(5, "ZSTD_sizeof_CCtx : %u", (U32)ZSTD_sizeof_CCtx(cdict->refContext)); - return ZSTD_sizeof_CCtx(cdict->refContext) + (cdict->dictBuffer ? cdict->dictContentSize : 0) + sizeof(*cdict); + return cdict->workspaceSize + (cdict->dictBuffer ? cdict->dictContentSize : 0) + sizeof(*cdict); } static size_t ZSTD_initCDict_internal( @@ -2445,11 +2521,12 @@ static size_t ZSTD_initCDict_internal( ZSTD_compressionParameters cParams) { DEBUGLOG(3, "ZSTD_initCDict_internal, mode %u", (U32)dictMode); + cdict->cParams = cParams; if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) { cdict->dictBuffer = NULL; cdict->dictContent = dictBuffer; } else { - void* const internalBuffer = ZSTD_malloc(dictSize, cdict->refContext->customMem); + void* const internalBuffer = ZSTD_malloc(dictSize, cdict->customMem); cdict->dictBuffer = internalBuffer; cdict->dictContent = internalBuffer; if (!internalBuffer) return ERROR(memory_allocation); @@ -2457,13 +2534,29 @@ static size_t ZSTD_initCDict_internal( } cdict->dictContentSize = dictSize; - { ZSTD_CCtx_params cctxParams = cdict->refContext->requestedParams; - cctxParams.cParams = cParams; - CHECK_F( ZSTD_compressBegin_internal(cdict->refContext, - cdict->dictContent, dictSize, dictMode, - NULL, - cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, - ZSTDb_not_buffered) ); + { + void* const end = ZSTD_reset_matchState( + &cdict->matchState, + (U32*)cdict->workspace + HUF_WORKSPACE_SIZE_U32, + &cParams, ZSTDcrp_continue, /* opt */ 0); + assert(end == (char*)cdict->workspace + cdict->workspaceSize); + (void)end; + } + { + ZSTD_CCtx_params params; + memset(¶ms, 0, sizeof(params)); + params.compressionLevel = ZSTD_CLEVEL_DEFAULT; + params.fParams.contentSizeFlag = 1; + params.cParams = cParams; + { + size_t const dictID = ZSTD_compress_insertDictionary( + &cdict->cBlockState, &cdict->matchState,¶ms, + cdict->dictContent, cdict->dictContentSize, + dictMode, cdict->workspace); + if (ZSTD_isError(dictID)) return dictID; + assert(dictID <= (size_t)(U32)-1); + cdict->dictID = (U32)dictID; + } } return 0; @@ -2478,14 +2571,17 @@ ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize, if (!customMem.customAlloc ^ !customMem.customFree) return NULL; { ZSTD_CDict* const cdict = (ZSTD_CDict*)ZSTD_malloc(sizeof(ZSTD_CDict), customMem); - ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(customMem); + size_t const workspaceSize = HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* opt */ 0); + void* const workspace = ZSTD_malloc(workspaceSize, customMem); - if (!cdict || !cctx) { + if (!cdict || !workspace) { ZSTD_free(cdict, customMem); - ZSTD_freeCCtx(cctx); + ZSTD_free(workspace, customMem); return NULL; } - cdict->refContext = cctx; + cdict->customMem = customMem; + cdict->workspace = workspace; + cdict->workspaceSize = workspaceSize; if (ZSTD_isError( ZSTD_initCDict_internal(cdict, dictBuffer, dictSize, dictLoadMethod, dictMode, @@ -2517,8 +2613,8 @@ ZSTD_CDict* ZSTD_createCDict_byReference(const void* dict, size_t dictSize, int size_t ZSTD_freeCDict(ZSTD_CDict* cdict) { if (cdict==NULL) return 0; /* support free on NULL */ - { ZSTD_customMem const cMem = cdict->refContext->customMem; - ZSTD_freeCCtx(cdict->refContext); + { ZSTD_customMem const cMem = cdict->customMem; + ZSTD_free(cdict->workspace, cMem); ZSTD_free(cdict->dictBuffer, cMem); ZSTD_free(cdict, cMem); return 0; @@ -2544,9 +2640,9 @@ ZSTD_CDict* ZSTD_initStaticCDict(void* workspace, size_t workspaceSize, ZSTD_dictMode_e dictMode, ZSTD_compressionParameters cParams) { - size_t const cctxSize = ZSTD_estimateCCtxSize_usingCParams(cParams); + size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* opt */ 0); size_t const neededSize = sizeof(ZSTD_CDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize) - + cctxSize; + + HUF_WORKSPACE_SIZE + matchStateSize; ZSTD_CDict* const cdict = (ZSTD_CDict*) workspace; void* ptr; if ((size_t)workspace & 7) return NULL; /* 8-aligned */ @@ -2561,7 +2657,8 @@ ZSTD_CDict* ZSTD_initStaticCDict(void* workspace, size_t workspaceSize, } else { ptr = cdict+1; } - cdict->refContext = ZSTD_initStaticCCtx(ptr, cctxSize); + cdict->workspace = ptr; + cdict->workspaceSize = HUF_WORKSPACE_SIZE + matchStateSize; if (ZSTD_isError( ZSTD_initCDict_internal(cdict, dict, dictSize, @@ -2573,7 +2670,7 @@ ZSTD_CDict* ZSTD_initStaticCDict(void* workspace, size_t workspaceSize, } ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict) { - return cdict->refContext->appliedParams.cParams; + return cdict->cParams; } /* ZSTD_compressBegin_usingCDict_advanced() : From 58dd7de640e630f19d64a10fbecb52f2628e9750 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 17 Jan 2018 12:10:15 -0800 Subject: [PATCH 060/255] zstdmt: fixed an endless loop on allocation failure this happened on 32-bits build when requiring a too large input buffer, typically on wlog=29, creating jobs of 2 GB size. also : zstd32 now compiles with multithread support enabled by default (can be disabled with HAVE_THREAD=0) --- lib/compress/zstdmt_compress.c | 21 +++++++++++++++------ programs/Makefile | 2 ++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index c65f3ea15..fe6ca52c2 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -150,7 +150,9 @@ static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* const bufPool, size_t const } /** ZSTDMT_getBuffer() : - * assumption : bufPool must be valid */ + * assumption : bufPool must be valid + * @return : a buffer, with start pointer and size + * note: allocation may fail, in this case, start==NULL and size==0 */ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool) { size_t const bSize = bufPool->bufferSize; @@ -178,7 +180,11 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool) void* const start = ZSTD_malloc(bSize, bufPool->cMem); buffer.start = start; /* note : start can be NULL if malloc fails ! */ buffer.size = (start==NULL) ? 0 : bSize; - DEBUGLOG(5, "ZSTDMT_getBuffer: created buffer of size %u", (U32)bSize); + if (start==NULL) { + DEBUGLOG(5, "ZSTDMT_getBuffer: buffer allocation failure !!"); + } else { + DEBUGLOG(5, "ZSTDMT_getBuffer: created buffer of size %u", (U32)bSize); + } return buffer; } } @@ -1015,7 +1021,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush) { unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; - DEBUGLOG(5, "ZSTDMT_flushNextJob"); + DEBUGLOG(5, "ZSTDMT_flushNextJob (blocking:%u)", blockToFlush); if (zcs->doneJobID == zcs->nextJobID) return 0; /* all flushed ! */ ZSTD_PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); while (zcs->jobs[wJobID].jobCompleted==0) { @@ -1112,10 +1118,13 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, /* fill input buffer */ if (input->size > input->pos) { /* support NULL input */ if (mtctx->inBuff.buffer.start == NULL) { - mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool); /* note : may fail, in which case, no forward input progress */ + mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool); /* note : allocation can fail, in which case, no forward input progress */ mtctx->inBuff.filled = 0; - } - if (mtctx->inBuff.buffer.start) { + if ( (mtctx->inBuff.buffer.start == NULL) /* allocation failure */ + && (mtctx->doneJobID == mtctx->nextJobID) ) { /* and nothing to flush */ + return ERROR(memory_allocation); /* no forward progress possible => output an error */ + } } + if (mtctx->inBuff.buffer.start != NULL) { size_t const toLoad = MIN(input->size - input->pos, mtctx->inBuffSize - mtctx->inBuff.filled); DEBUGLOG(5, "inBuff:%08X; inBuffSize=%u; ToCopy=%u", (U32)(size_t)mtctx->inBuff.buffer.start, (U32)mtctx->inBuffSize, (U32)toLoad); memcpy((char*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, (const char*)input->src + input->pos, toLoad); diff --git a/programs/Makefile b/programs/Makefile index 51888d3ef..be666b4ef 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -157,6 +157,8 @@ endif zstd-release: DEBUGFLAGS := zstd-release: zstd +zstd32 : CPPFLAGS += $(THREAD_CPP) +zstd32 : LDFLAGS += $(THREAD_LD) zstd32 : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) zstd32 : $(ZSTDLIB_FILES) zstdcli.c fileio.c bench.c datagen.c dibio.c ifneq (,$(filter Windows%,$(OS))) From d14cc881b0f039d641cd3a7df33bad897320e654 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 17 Jan 2018 12:39:58 -0800 Subject: [PATCH 061/255] zstdmt : fixed very large window sizes would create too large buffers, since default job size == window size * 4. This would crash on 32-bit systems. Also : jobSize being a 32-bit unsigned, it cannot be >= 4 GB, so the formula was failing for large window sizes >= 1 GB. Fixed now : max job Size is 2 GB, whatever the window size. --- lib/compress/zstdmt_compress.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index fe6ca52c2..b3ccfe398 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -11,6 +11,7 @@ /* ====== Tuning parameters ====== */ #define ZSTDMT_NBTHREADS_MAX 200 +#define ZSTDMT_JOBSIZE_MAX (MEM_32bits() ? (512 MB) : (2 GB)) /* note : limited by `jobSize` type, which is `unsigned` */ #define ZSTDMT_OVERLAPLOG_DEFAULT 6 @@ -843,7 +844,14 @@ size_t ZSTDMT_initCStream_internal( assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ assert(zcs->cctxPool->totalCCtx == params.nbThreads); - zcs->singleBlockingThread = pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN; /* do not trigger multi-threading when srcSize is too small */ + zcs->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */ + if (params.jobSize == 0) { + if (params.cParams.windowLog >= 29) + params.jobSize = ZSTDMT_JOBSIZE_MAX; + else + params.jobSize = 1 << (params.cParams.windowLog + 2); + } + if (params.jobSize > ZSTDMT_JOBSIZE_MAX) params.jobSize = ZSTDMT_JOBSIZE_MAX; if (zcs->singleBlockingThread) { ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(params); @@ -879,7 +887,7 @@ size_t ZSTDMT_initCStream_internal( assert(params.overlapSizeLog <= 9); zcs->targetPrefixSize = (params.overlapSizeLog==0) ? 0 : (size_t)1 << (params.cParams.windowLog - (9 - params.overlapSizeLog)); DEBUGLOG(4, "overlapLog=%u => %u KB", params.overlapSizeLog, (U32)(zcs->targetPrefixSize>>10)); - zcs->targetSectionSize = params.jobSize ? params.jobSize : (size_t)1 << (params.cParams.windowLog + 2); + zcs->targetSectionSize = params.jobSize; if (zcs->targetSectionSize < ZSTDMT_JOBSIZE_MIN) zcs->targetSectionSize = ZSTDMT_JOBSIZE_MIN; if (zcs->targetSectionSize < zcs->targetPrefixSize) zcs->targetSectionSize = zcs->targetPrefixSize; /* job size must be >= overlap size */ DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(zcs->targetSectionSize>>10), params.jobSize); From f3b8f90b6d7f6b2b00ab07577f77a4a4ec84dab7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 17 Jan 2018 14:08:48 -0800 Subject: [PATCH 062/255] changed initStatic?Dict() return type to const ZSTD_?Dict* ZSTD_create?Dict() is required to produce a ?Dict* return type because `free()` does not accept a `const type*` argument. If it wasn't for this restriction, I would have preferred to create a `const ?Dict*` object to emphasize the fact that, once created, a dictionary never changes (hence can be shared concurrently until the end of its lifetime). There is no such limitation with initStatic?Dict() : as stated in the doc, there is no corresponding free() function, since `workspace` is provided, hence allocated, externally, it can only be free() externally. Which means, ZSTD_initStatic?Dict() can return a `const ZSTD_?Dict*` pointer. Tested with `make all`, to catch initStatic's users, which, incidentally, also updated zstd.h documentation. --- doc/zstd_manual.html | 231 +++++++++++-------------------- lib/compress/zstd_compress.c | 3 +- lib/decompress/zstd_decompress.c | 7 +- lib/zstd.h | 18 +-- tests/fuzzer.c | 5 +- 5 files changed, 101 insertions(+), 163 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index da11ddb2a..a2e0966d7 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -11,7 +11,7 @@
  • Introduction
  • Version
  • Simple API
  • -
  • Explicit memory management
  • +
  • Explicit context
  • Simple dictionary API
  • Bulk processing dictionary API
  • Streaming
  • @@ -19,17 +19,16 @@
  • Streaming decompression - HowTo
  • START OF ADVANCED AND EXPERIMENTAL FUNCTIONS
  • Advanced types
  • -
  • Custom memory allocation functions
  • -
  • Frame size functions
  • -
  • Context memory usage
  • -
  • Advanced compression functions
  • -
  • Advanced decompression functions
  • -
  • Advanced streaming functions
  • -
  • Buffer-less and synchronous inner streaming functions
  • -
  • Buffer-less streaming compression (synchronous mode)
  • -
  • Buffer-less streaming decompression (synchronous mode)
  • -
  • New advanced API (experimental)
  • -
  • Block level API
  • +
  • Frame size functions
  • +
  • Memory management
  • +
  • Advanced compression functions
  • +
  • Advanced decompression functions
  • +
  • Advanced streaming functions
  • +
  • Buffer-less and synchronous inner streaming functions
  • +
  • Buffer-less streaming compression (synchronous mode)
  • +
  • Buffer-less streaming decompression (synchronous mode)
  • +
  • New advanced API (experimental)
  • +
  • Block level API

  • Introduction

    @@ -40,11 +39,11 @@
       Levels >= 20, labeled `--ultra`, should be used with caution, as they require more memory.
       Compression can be done in:
         - a single step (described as Simple API)
    -    - a single step, reusing a context (described as Explicit memory management)
    +    - a single step, reusing a context (described as Explicit context)
         - unbounded multiple steps (described as Streaming compression)
       The compression ratio achievable on small data can be highly improved using a dictionary in:
         - a single step (described as Simple dictionary API)
    -    - a single step, reusing a dictionary (described as Fast dictionary API)
    +    - a single step, reusing a dictionary (described as Bulk-processing dictionary API)
     
       Advanced experimental functions can be accessed using #define ZSTD_STATIC_LINKING_ONLY before including zstd.h.
       Advanced experimental APIs shall never be used with a dynamic library.
    @@ -103,22 +102,20 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize);
     
     
    unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
     

    NOTE: This function is now obsolete, in favor of ZSTD_getFrameContentSize(). - Both functions work the same way, - but ZSTD_getDecompressedSize() blends - "empty", "unknown" and "error" results in the same return value (0), - while ZSTD_getFrameContentSize() distinguishes them. - - 'src' is the start of a zstd compressed frame. - @return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise. + Both functions work the same way, but ZSTD_getDecompressedSize() blends + "empty", "unknown" and "error" results to the same return value (0), + while ZSTD_getFrameContentSize() gives them separate return values. + `src` is the start of a zstd compressed frame. + @return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise.


    Helper functions

    #define ZSTD_COMPRESSBOUND(srcSize)   ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0))  /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */
    -size_t      ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case scenario */
    +size_t      ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */
     unsigned    ZSTD_isError(size_t code);          /*!< tells if a `size_t` function result is an error code */
     const char* ZSTD_getErrorName(size_t code);     /*!< provides readable string from an error code */
     int         ZSTD_maxCLevel(void);               /*!< maximum compression level available */
     

    -

    Explicit memory management

    
    +

    Explicit context

    
     
     

    Compression context

      When compressing many times,
       it is recommended to allocate a context just once, and re-use it for each successive compression operation.
    @@ -347,11 +344,18 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
         ZSTD_frameParameters fParams;
     } ZSTD_parameters;
     

    -

    Custom memory allocation functions

    
    -
    -
    typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
    +
    typedef enum {
    +    ZSTD_dm_auto=0,      /* dictionary is "full" if it starts with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */
    +    ZSTD_dm_rawContent,  /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */
    +    ZSTD_dm_fullDict     /* refuses to load a dictionary if it does not respect Zstandard's specification */
    +} ZSTD_dictMode_e;
     

    -

    Frame size functions

    
    +
    typedef enum {
    +    ZSTD_dlm_byCopy = 0, /**< Copy dictionary content internally */
    +    ZSTD_dlm_byRef,      /**< Reference dictionary content -- the dictionary buffer must outlive its users. */
    +} ZSTD_dictLoadMethod_e;
    +

    +

    Frame size functions

    
     
     
    size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize);
     

    `src` should point to the start of a ZSTD encoded frame or skippable frame @@ -390,7 +394,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB @return : size of the Frame Header


    -

    Context memory usage

    
    +

    Memory management

    
     
     
    size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx);
     size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx);
    @@ -399,7 +403,7 @@ size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds);
     size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict);
     size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
     

    These functions give the current memory usage of selected object. - Object memory usage can evolve when re-used multiple times. + Object memory usage can evolve when re-used.


    size_t ZSTD_estimateCCtxSize(int compressionLevel);
    @@ -413,7 +417,7 @@ size_t ZSTD_estimateDCtxSize(void);
       If srcSize is known to always be small, ZSTD_estimateCCtxSize_usingCParams() can provide a tighter estimation.
       ZSTD_estimateCCtxSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.
       ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is > 1.
    -  Note : CCtx estimation is only correct for single-threaded compression 
    +  Note : CCtx size estimation is only correct for single-threaded compression. 
     


    size_t ZSTD_estimateCStreamSize(int compressionLevel);
    @@ -426,7 +430,7 @@ size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize);
       If srcSize is known to always be small, ZSTD_estimateCStreamSize_usingCParams() can provide a tighter estimation.
       ZSTD_estimateCStreamSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.
       ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is set to a value > 1.
    -  Note : CStream estimation is only correct for single-threaded compression.
    +  Note : CStream size estimation is only correct for single-threaded compression.
       ZSTD_DStream memory budget depends on window Size.
       This information can be passed manually, using ZSTD_estimateDStreamSize,
       or deducted from a valid frame Header, using ZSTD_estimateDStreamSize_fromFrame();
    @@ -435,83 +439,59 @@ size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize);
              In this case, get total size by adding ZSTD_estimate?DictSize 
     


    -
    typedef enum {
    -    ZSTD_dlm_byCopy = 0,     /**< Copy dictionary content internally */
    -    ZSTD_dlm_byRef,          /**< Reference dictionary content -- the dictionary buffer must outlive its users. */
    -} ZSTD_dictLoadMethod_e;
    -

    size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel);
     size_t ZSTD_estimateCDictSize_advanced(size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod);
     size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod);
     

    ZSTD_estimateCDictSize() will bet that src size is relatively "small", and content is copied, like ZSTD_createCDict(). - ZSTD_estimateCStreamSize_advanced_usingCParams() makes it possible to control precisely compression parameters, like ZSTD_createCDict_advanced(). - Note : dictionary created by reference using ZSTD_dlm_byRef are smaller + ZSTD_estimateCDictSize_advanced() makes it possible to control compression parameters precisely, like ZSTD_createCDict_advanced(). + Note : dictionaries created by reference (`ZSTD_dlm_byRef`) are logically smaller.


    -

    Advanced compression functions

    
    -
    -
    ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
    -

    Create a ZSTD compression context using external alloc and free functions -


    - -
    ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
    -

    workspace: The memory area to emplace the context into. - Provided pointer must 8-bytes aligned. - It must outlive context usage. - workspaceSize: Use ZSTD_estimateCCtxSize() or ZSTD_estimateCStreamSize() - to determine how large workspace must be to support scenario. - @return : pointer to ZSTD_CCtx* (same address as workspace, but different type), - or NULL if error (typically size too small) - Note : zstd will never resize nor malloc() when using a static cctx. - If it needs more memory than available, it will simply error out. +

    ZSTD_CCtx*    ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
    +ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticCCtx() */
    +

    Initialize an object using a pre-allocated fixed-size buffer. + workspace: The memory area to emplace the object into. + Provided pointer *must be 8-bytes aligned*. + Buffer must outlive object. + workspaceSize: Use ZSTD_estimate*Size() to determine + how large workspace must be to support target scenario. + @return : pointer to object (same address as workspace, just different type), + or NULL if error (size too small, incorrect alignment, etc.) + Note : zstd will never resize nor malloc() when using a static buffer. + If the object requires more memory than available, + zstd will just error out (typically ZSTD_error_memory_allocation). Note 2 : there is no corresponding "free" function. - Since workspace was allocated externally, it must be freed externally too. - Limitation 1 : currently not compatible with internal CDict creation, such as - ZSTD_CCtx_loadDictionary() or ZSTD_initCStream_usingDict(). - Limitation 2 : currently not compatible with multi-threading + Since workspace is allocated externally, it must be freed externally too. + Note 3 : cParams : use ZSTD_getCParams() to convert a compression level + into its associated cParams. + Limitation 1 : currently not compatible with internal dictionary creation, triggered by + ZSTD_CCtx_loadDictionary(), ZSTD_initCStream_usingDict() or ZSTD_initDStream_usingDict(). + Limitation 2 : static cctx currently not compatible with multi-threading. + Limitation 3 : static dctx is incompatible with legacy support.


    +
    ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticDCtx() */
    +

    +
    typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
    +typedef void  (*ZSTD_freeFunction) (void* opaque, void* address);
    +typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
    +static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL };  /**< this constant defers to stdlib's functions */
    +

    These prototypes make it possible to pass your own allocation/free functions. + ZSTD_customMem is provided at creation time, using ZSTD_create*_advanced() variants listed below. + All allocation/free operations will be completed using these custom variants instead of regular ones. + +


    + +

    Advanced compression functions

    
    +
     
    ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);
     

    Create a digested dictionary for compression Dictionary content is simply referenced, and therefore stays in dictBuffer. It is important that dictBuffer outlives CDict, it must remain read accessible throughout the lifetime of CDict


    -
    typedef enum { ZSTD_dm_auto=0,        /* dictionary is "full" if it starts with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */
    -               ZSTD_dm_rawContent,    /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */
    -               ZSTD_dm_fullDict       /* refuses to load a dictionary if it does not respect Zstandard's specification */
    -} ZSTD_dictMode_e;
    -

    -
    ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize,
    -                                      ZSTD_dictLoadMethod_e dictLoadMethod,
    -                                      ZSTD_dictMode_e dictMode,
    -                                      ZSTD_compressionParameters cParams,
    -                                      ZSTD_customMem customMem);
    -

    Create a ZSTD_CDict using external alloc and free, and customized compression parameters -


    - -
    ZSTD_CDict* ZSTD_initStaticCDict(
    -                void* workspace, size_t workspaceSize,
    -          const void* dict, size_t dictSize,
    -                ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode,
    -                ZSTD_compressionParameters cParams);
    -

    Generate a digested dictionary in provided memory area. - workspace: The memory area to emplace the dictionary into. - Provided pointer must 8-bytes aligned. - It must outlive dictionary usage. - workspaceSize: Use ZSTD_estimateCDictSize() - to determine how large workspace must be. - cParams : use ZSTD_getCParams() to transform a compression level - into its relevants cParams. - @return : pointer to ZSTD_CDict* (same address as workspace, but different type), - or NULL if error (typically, size too small). - Note : there is no corresponding "free" function. - Since workspace was allocated externally, it must be freed externally. - -


    -
    ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
     

    @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize. `estimatedSrcSize` value is optional, select 0 if not known @@ -546,7 +526,7 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet

    Same as ZSTD_compress_usingCDict(), with fine-tune control over frame parameters


    -

    Advanced decompression functions

    
    +

    Advanced decompression functions

    
     
     
    unsigned ZSTD_isFrame(const void* buffer, size_t size);
     

    Tells if the content of `buffer` starts with a valid Frame Identifier. @@ -555,28 +535,6 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet Note 3 : Skippable Frame Identifiers are considered valid.


    -
    ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem);
    -

    Create a ZSTD decompression context using external alloc and free functions -


    - -
    ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize);
    -

    workspace: The memory area to emplace the context into. - Provided pointer must 8-bytes aligned. - It must outlive context usage. - workspaceSize: Use ZSTD_estimateDCtxSize() or ZSTD_estimateDStreamSize() - to determine how large workspace must be to support scenario. - @return : pointer to ZSTD_DCtx* (same address as workspace, but different type), - or NULL if error (typically size too small) - Note : zstd will never resize nor malloc() when using a static dctx. - If it needs more memory than available, it will simply error out. - Note 2 : static dctx is incompatible with legacy support - Note 3 : there is no corresponding "free" function. - Since workspace was allocated externally, it must be freed externally. - Limitation : currently not compatible with internal DDict creation, - such as ZSTD_initDStream_usingDict(). - -


    -
    ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize);
     

    Create a digested dictionary, ready to start decompression operation without startup delay. Dictionary content is referenced, and therefore stays in dictBuffer. @@ -584,27 +542,6 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet it must remain read accessible throughout the lifetime of DDict


    -
    ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize,
    -                                      ZSTD_dictLoadMethod_e dictLoadMethod,
    -                                      ZSTD_customMem customMem);
    -

    Create a ZSTD_DDict using external alloc and free, optionally by reference -


    - -
    ZSTD_DDict* ZSTD_initStaticDDict(void* workspace, size_t workspaceSize,
    -                                 const void* dict, size_t dictSize,
    -                                 ZSTD_dictLoadMethod_e dictLoadMethod);
    -

    Generate a digested dictionary in provided memory area. - workspace: The memory area to emplace the dictionary into. - Provided pointer must 8-bytes aligned. - It must outlive dictionary usage. - workspaceSize: Use ZSTD_estimateDDictSize() - to determine how large workspace must be. - @return : pointer to ZSTD_DDict*, or NULL if error (size too small) - Note : there is no corresponding "free" function. - Since workspace was allocated externally, it must be freed externally. - -


    -
    unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize);
     

    Provides the dictID stored within dictionary. if @return == 0, the dictionary is not conformant with Zstandard specification. @@ -629,11 +566,9 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet When identifying the exact failure cause, it's possible to use ZSTD_getFrameHeader(), which will provide a more precise error code.


    -

    Advanced streaming functions

    
    +

    Advanced streaming functions

    
     
    -

    Advanced Streaming compression functions

    ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
    -ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticCCtx() */
    -size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);   /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */
    +

    Advanced Streaming compression functions

    size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);   /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */
     size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/
     size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize,
                                                  ZSTD_parameters params, unsigned long long pledgedSrcSize);  /**< pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */
    @@ -651,22 +586,20 @@ size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict*
      @return : 0, or an error code (which can be tested using ZSTD_isError()) 
     


    -

    Advanced Streaming decompression functions

    ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
    -ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticDCtx() */
    -typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e;
    +

    Advanced Streaming decompression functions

    typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e;
     size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue);   /* obsolete : this API will be removed in a future version */
     size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: no dictionary will be used if dict == NULL or dictSize < 8 */
     size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict);  /**< note : ddict is referenced, it must outlive decompression session */
     size_t ZSTD_resetDStream(ZSTD_DStream* zds);  /**< re-use decompression parameters from previous init; saves dictionary loading */
     

    -

    Buffer-less and synchronous inner streaming functions

    +

    Buffer-less and synchronous inner streaming functions

       This is an advanced API, giving full control over buffer management, for users which need direct control over memory.
       But it's also a complex one, with several restrictions, documented below.
       Prefer normal streaming API for an easier experience.
      
     
    -

    Buffer-less streaming compression (synchronous mode)

    +

    Buffer-less streaming compression (synchronous mode)

       A ZSTD_CCtx object is required to track streaming operations.
       Use ZSTD_createCCtx() / ZSTD_freeCCtx() to manage resource.
       ZSTD_CCtx object can be re-used multiple times within successive compression operations.
    @@ -702,7 +635,7 @@ size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
     size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize);   /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */
     size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**<  note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
     

    -

    Buffer-less streaming decompression (synchronous mode)

    +

    Buffer-less streaming decompression (synchronous mode)

       A ZSTD_DCtx object is required to track streaming operations.
       Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it.
       A ZSTD_DCtx object can be re-used multiple times.
    @@ -788,7 +721,7 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
     

    typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
     

    -

    New advanced API (experimental)

    
    +

    New advanced API (experimental)

    
     
     
    typedef enum {
         /* Question : should we have a format ZSTD_f_auto ?
    @@ -1007,7 +940,7 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t
      
     


    -
    void ZSTD_CCtx_reset(ZSTD_CCtx* cctx);   /* Not ready yet ! */
    +
    void ZSTD_CCtx_reset(ZSTD_CCtx* cctx);
     

    Return a CCtx to clean state. Useful after an error, or to interrupt an ongoing compression job and start a new one. Any internal data not yet flushed is cancelled. @@ -1178,7 +1111,7 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t


    -

    Block level API

    
    +

    Block level API

    
     
     

    Frame metadata cost is typically ~18 bytes, which can be non-negligible for very small blocks (< 100 bytes). User will have to take in charge required information to regenerate data, such as compressed and content sizes. @@ -1206,7 +1139,7 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t

    Raw zstd block functions

    size_t ZSTD_getBlockSize   (const ZSTD_CCtx* cctx);
     size_t ZSTD_compressBlock  (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
     size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
    -size_t ZSTD_insertBlock(ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize);  /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression */
    +size_t ZSTD_insertBlock    (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize);  /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */
     

    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 4ad5a6e61..217bb0640 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2634,7 +2634,8 @@ size_t ZSTD_freeCDict(ZSTD_CDict* cdict) * Note : there is no corresponding "free" function. * Since workspace was allocated externally, it must be freed externally. */ -ZSTD_CDict* ZSTD_initStaticCDict(void* workspace, size_t workspaceSize, +const ZSTD_CDict* ZSTD_initStaticCDict( + void* workspace, size_t workspaceSize, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode, diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index ab6c2edc5..9063d5a34 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2139,9 +2139,10 @@ ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize } -ZSTD_DDict* ZSTD_initStaticDDict(void* workspace, size_t workspaceSize, - const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod) +const ZSTD_DDict* ZSTD_initStaticDDict( + void* workspace, size_t workspaceSize, + const void* dict, size_t dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod) { size_t const neededSpace = sizeof(ZSTD_DDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize); diff --git a/lib/zstd.h b/lib/zstd.h index a84490d99..ae569abaf 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -568,15 +568,17 @@ ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspa ZSTDLIB_API ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize); ZSTDLIB_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticDCtx() */ -ZSTDLIB_API ZSTD_CDict* ZSTD_initStaticCDict(void* workspace, size_t workspaceSize, - const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictMode, - ZSTD_compressionParameters cParams); +ZSTDLIB_API const ZSTD_CDict* ZSTD_initStaticCDict( + void* workspace, size_t workspaceSize, + const void* dict, size_t dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictMode_e dictMode, + ZSTD_compressionParameters cParams); -ZSTDLIB_API ZSTD_DDict* ZSTD_initStaticDDict(void* workspace, size_t workspaceSize, - const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod); +ZSTDLIB_API const ZSTD_DDict* ZSTD_initStaticDDict( + void* workspace, size_t workspaceSize, + const void* dict, size_t dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod); /*! Custom memory allocation : * These prototypes make it possible to pass your own allocation/free functions. diff --git a/tests/fuzzer.c b/tests/fuzzer.c index f9166c0cf..0a6cf61d2 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -634,7 +634,7 @@ static int basicUnitTests(U32 seed, double compressibility) { size_t const ddictBufferSize = ZSTD_estimateDDictSize(dictSize, ZSTD_dlm_byCopy); void* ddictBuffer = malloc(ddictBufferSize); if (ddictBuffer == NULL) goto _output_error; - { ZSTD_DDict* const ddict = ZSTD_initStaticDDict(ddictBuffer, ddictBufferSize, CNBuffer, dictSize, ZSTD_dlm_byCopy); + { const ZSTD_DDict* const ddict = ZSTD_initStaticDDict(ddictBuffer, ddictBufferSize, CNBuffer, dictSize, ZSTD_dlm_byCopy); size_t const r = ZSTD_decompress_usingDDict(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize, ddict); if (r != CNBuffSize - dictSize) goto _output_error; } @@ -767,7 +767,8 @@ static int basicUnitTests(U32 seed, double compressibility) size_t const cdictSize = ZSTD_estimateCDictSize_advanced(dictSize, cParams, ZSTD_dlm_byCopy); void* const cdictBuffer = malloc(cdictSize); if (cdictBuffer==NULL) goto _output_error; - { ZSTD_CDict* const cdict = ZSTD_initStaticCDict(cdictBuffer, cdictSize, + { const ZSTD_CDict* const cdict = ZSTD_initStaticCDict( + cdictBuffer, cdictSize, dictBuffer, dictSize, ZSTD_dlm_byCopy, ZSTD_dm_auto, cParams); From 394eec697b77f3be8079f2fb2673b23c84f37101 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 17 Jan 2018 16:39:02 -0800 Subject: [PATCH 063/255] Introduce ZSTD_getFrameProgression() Produces 3 statistics for ongoing frame compression : - ingested - consumed (effectively compressed) - produced Ingested can be larger than consumed due to buffering effect. For the time being, this patch mostly fixes the % ratio issue, since it computes consumed / produced, instead of ingested / produced. That being said, update is not "smooth", because on a slow enough setting, fileio spends most of its time waiting for a worker to complete its job. This could be improved thanks to more granular flushing i.e. start flushing before ongoing job is fully completed. --- doc/zstd_manual.html | 241 ++++++++++---------------- lib/compress/zstd_compress.c | 26 +++ lib/compress/zstd_compress_internal.h | 1 + lib/compress/zstdmt_compress.c | 64 +++++-- lib/compress/zstdmt_compress.h | 7 + lib/zstd.h | 16 +- programs/fileio.c | 22 ++- 7 files changed, 208 insertions(+), 169 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index da11ddb2a..473d5be8b 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -11,7 +11,7 @@
  • Introduction
  • Version
  • Simple API
  • -
  • Explicit memory management
  • +
  • Explicit context
  • Simple dictionary API
  • Bulk processing dictionary API
  • Streaming
  • @@ -19,17 +19,16 @@
  • Streaming decompression - HowTo
  • START OF ADVANCED AND EXPERIMENTAL FUNCTIONS
  • Advanced types
  • -
  • Custom memory allocation functions
  • -
  • Frame size functions
  • -
  • Context memory usage
  • -
  • Advanced compression functions
  • -
  • Advanced decompression functions
  • -
  • Advanced streaming functions
  • -
  • Buffer-less and synchronous inner streaming functions
  • -
  • Buffer-less streaming compression (synchronous mode)
  • -
  • Buffer-less streaming decompression (synchronous mode)
  • -
  • New advanced API (experimental)
  • -
  • Block level API
  • +
  • Frame size functions
  • +
  • Memory management
  • +
  • Advanced compression functions
  • +
  • Advanced decompression functions
  • +
  • Advanced streaming functions
  • +
  • Buffer-less and synchronous inner streaming functions
  • +
  • Buffer-less streaming compression (synchronous mode)
  • +
  • Buffer-less streaming decompression (synchronous mode)
  • +
  • New advanced API (experimental)
  • +
  • Block level API

  • Introduction

    @@ -40,11 +39,11 @@
       Levels >= 20, labeled `--ultra`, should be used with caution, as they require more memory.
       Compression can be done in:
         - a single step (described as Simple API)
    -    - a single step, reusing a context (described as Explicit memory management)
    +    - a single step, reusing a context (described as Explicit context)
         - unbounded multiple steps (described as Streaming compression)
       The compression ratio achievable on small data can be highly improved using a dictionary in:
         - a single step (described as Simple dictionary API)
    -    - a single step, reusing a dictionary (described as Fast dictionary API)
    +    - a single step, reusing a dictionary (described as Bulk-processing dictionary API)
     
       Advanced experimental functions can be accessed using #define ZSTD_STATIC_LINKING_ONLY before including zstd.h.
       Advanced experimental APIs shall never be used with a dynamic library.
    @@ -103,22 +102,20 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize);
     
     
    unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
     

    NOTE: This function is now obsolete, in favor of ZSTD_getFrameContentSize(). - Both functions work the same way, - but ZSTD_getDecompressedSize() blends - "empty", "unknown" and "error" results in the same return value (0), - while ZSTD_getFrameContentSize() distinguishes them. - - 'src' is the start of a zstd compressed frame. - @return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise. + Both functions work the same way, but ZSTD_getDecompressedSize() blends + "empty", "unknown" and "error" results to the same return value (0), + while ZSTD_getFrameContentSize() gives them separate return values. + `src` is the start of a zstd compressed frame. + @return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise.


    Helper functions

    #define ZSTD_COMPRESSBOUND(srcSize)   ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0))  /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */
    -size_t      ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case scenario */
    +size_t      ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */
     unsigned    ZSTD_isError(size_t code);          /*!< tells if a `size_t` function result is an error code */
     const char* ZSTD_getErrorName(size_t code);     /*!< provides readable string from an error code */
     int         ZSTD_maxCLevel(void);               /*!< maximum compression level available */
     

    -

    Explicit memory management

    
    +

    Explicit context

    
     
     

    Compression context

      When compressing many times,
       it is recommended to allocate a context just once, and re-use it for each successive compression operation.
    @@ -347,11 +344,18 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
         ZSTD_frameParameters fParams;
     } ZSTD_parameters;
     

    -

    Custom memory allocation functions

    
    -
    -
    typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
    +
    typedef enum {
    +    ZSTD_dm_auto=0,      /* dictionary is "full" if it starts with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */
    +    ZSTD_dm_rawContent,  /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */
    +    ZSTD_dm_fullDict     /* refuses to load a dictionary if it does not respect Zstandard's specification */
    +} ZSTD_dictMode_e;
     

    -

    Frame size functions

    
    +
    typedef enum {
    +    ZSTD_dlm_byCopy = 0, /**< Copy dictionary content internally */
    +    ZSTD_dlm_byRef,      /**< Reference dictionary content -- the dictionary buffer must outlive its users. */
    +} ZSTD_dictLoadMethod_e;
    +

    +

    Frame size functions

    
     
     
    size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize);
     

    `src` should point to the start of a ZSTD encoded frame or skippable frame @@ -390,7 +394,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB @return : size of the Frame Header


    -

    Context memory usage

    
    +

    Memory management

    
     
     
    size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx);
     size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx);
    @@ -399,7 +403,7 @@ size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds);
     size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict);
     size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
     

    These functions give the current memory usage of selected object. - Object memory usage can evolve when re-used multiple times. + Object memory usage can evolve when re-used.


    size_t ZSTD_estimateCCtxSize(int compressionLevel);
    @@ -413,7 +417,7 @@ size_t ZSTD_estimateDCtxSize(void);
       If srcSize is known to always be small, ZSTD_estimateCCtxSize_usingCParams() can provide a tighter estimation.
       ZSTD_estimateCCtxSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.
       ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is > 1.
    -  Note : CCtx estimation is only correct for single-threaded compression 
    +  Note : CCtx size estimation is only correct for single-threaded compression. 
     


    size_t ZSTD_estimateCStreamSize(int compressionLevel);
    @@ -426,7 +430,7 @@ size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize);
       If srcSize is known to always be small, ZSTD_estimateCStreamSize_usingCParams() can provide a tighter estimation.
       ZSTD_estimateCStreamSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.
       ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is set to a value > 1.
    -  Note : CStream estimation is only correct for single-threaded compression.
    +  Note : CStream size estimation is only correct for single-threaded compression.
       ZSTD_DStream memory budget depends on window Size.
       This information can be passed manually, using ZSTD_estimateDStreamSize,
       or deducted from a valid frame Header, using ZSTD_estimateDStreamSize_fromFrame();
    @@ -435,83 +439,59 @@ size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize);
              In this case, get total size by adding ZSTD_estimate?DictSize 
     


    -
    typedef enum {
    -    ZSTD_dlm_byCopy = 0,     /**< Copy dictionary content internally */
    -    ZSTD_dlm_byRef,          /**< Reference dictionary content -- the dictionary buffer must outlive its users. */
    -} ZSTD_dictLoadMethod_e;
    -

    size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel);
     size_t ZSTD_estimateCDictSize_advanced(size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod);
     size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod);
     

    ZSTD_estimateCDictSize() will bet that src size is relatively "small", and content is copied, like ZSTD_createCDict(). - ZSTD_estimateCStreamSize_advanced_usingCParams() makes it possible to control precisely compression parameters, like ZSTD_createCDict_advanced(). - Note : dictionary created by reference using ZSTD_dlm_byRef are smaller + ZSTD_estimateCDictSize_advanced() makes it possible to control compression parameters precisely, like ZSTD_createCDict_advanced(). + Note : dictionaries created by reference (`ZSTD_dlm_byRef`) are logically smaller.


    -

    Advanced compression functions

    
    -
    -
    ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
    -

    Create a ZSTD compression context using external alloc and free functions -


    - -
    ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
    -

    workspace: The memory area to emplace the context into. - Provided pointer must 8-bytes aligned. - It must outlive context usage. - workspaceSize: Use ZSTD_estimateCCtxSize() or ZSTD_estimateCStreamSize() - to determine how large workspace must be to support scenario. - @return : pointer to ZSTD_CCtx* (same address as workspace, but different type), - or NULL if error (typically size too small) - Note : zstd will never resize nor malloc() when using a static cctx. - If it needs more memory than available, it will simply error out. +

    ZSTD_CCtx*    ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
    +ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticCCtx() */
    +

    Initialize an object using a pre-allocated fixed-size buffer. + workspace: The memory area to emplace the object into. + Provided pointer *must be 8-bytes aligned*. + Buffer must outlive object. + workspaceSize: Use ZSTD_estimate*Size() to determine + how large workspace must be to support target scenario. + @return : pointer to object (same address as workspace, just different type), + or NULL if error (size too small, incorrect alignment, etc.) + Note : zstd will never resize nor malloc() when using a static buffer. + If the object requires more memory than available, + zstd will just error out (typically ZSTD_error_memory_allocation). Note 2 : there is no corresponding "free" function. - Since workspace was allocated externally, it must be freed externally too. - Limitation 1 : currently not compatible with internal CDict creation, such as - ZSTD_CCtx_loadDictionary() or ZSTD_initCStream_usingDict(). - Limitation 2 : currently not compatible with multi-threading + Since workspace is allocated externally, it must be freed externally too. + Note 3 : cParams : use ZSTD_getCParams() to convert a compression level + into its associated cParams. + Limitation 1 : currently not compatible with internal dictionary creation, triggered by + ZSTD_CCtx_loadDictionary(), ZSTD_initCStream_usingDict() or ZSTD_initDStream_usingDict(). + Limitation 2 : static cctx currently not compatible with multi-threading. + Limitation 3 : static dctx is incompatible with legacy support.


    +
    ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticDCtx() */
    +

    +
    typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
    +typedef void  (*ZSTD_freeFunction) (void* opaque, void* address);
    +typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
    +static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL };  /**< this constant defers to stdlib's functions */
    +

    These prototypes make it possible to pass your own allocation/free functions. + ZSTD_customMem is provided at creation time, using ZSTD_create*_advanced() variants listed below. + All allocation/free operations will be completed using these custom variants instead of regular ones. + +


    + +

    Advanced compression functions

    
    +
     
    ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);
     

    Create a digested dictionary for compression Dictionary content is simply referenced, and therefore stays in dictBuffer. It is important that dictBuffer outlives CDict, it must remain read accessible throughout the lifetime of CDict


    -
    typedef enum { ZSTD_dm_auto=0,        /* dictionary is "full" if it starts with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */
    -               ZSTD_dm_rawContent,    /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */
    -               ZSTD_dm_fullDict       /* refuses to load a dictionary if it does not respect Zstandard's specification */
    -} ZSTD_dictMode_e;
    -

    -
    ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize,
    -                                      ZSTD_dictLoadMethod_e dictLoadMethod,
    -                                      ZSTD_dictMode_e dictMode,
    -                                      ZSTD_compressionParameters cParams,
    -                                      ZSTD_customMem customMem);
    -

    Create a ZSTD_CDict using external alloc and free, and customized compression parameters -


    - -
    ZSTD_CDict* ZSTD_initStaticCDict(
    -                void* workspace, size_t workspaceSize,
    -          const void* dict, size_t dictSize,
    -                ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode,
    -                ZSTD_compressionParameters cParams);
    -

    Generate a digested dictionary in provided memory area. - workspace: The memory area to emplace the dictionary into. - Provided pointer must 8-bytes aligned. - It must outlive dictionary usage. - workspaceSize: Use ZSTD_estimateCDictSize() - to determine how large workspace must be. - cParams : use ZSTD_getCParams() to transform a compression level - into its relevants cParams. - @return : pointer to ZSTD_CDict* (same address as workspace, but different type), - or NULL if error (typically, size too small). - Note : there is no corresponding "free" function. - Since workspace was allocated externally, it must be freed externally. - -


    -
    ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
     

    @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize. `estimatedSrcSize` value is optional, select 0 if not known @@ -546,7 +526,7 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet

    Same as ZSTD_compress_usingCDict(), with fine-tune control over frame parameters


    -

    Advanced decompression functions

    
    +

    Advanced decompression functions

    
     
     
    unsigned ZSTD_isFrame(const void* buffer, size_t size);
     

    Tells if the content of `buffer` starts with a valid Frame Identifier. @@ -555,28 +535,6 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet Note 3 : Skippable Frame Identifiers are considered valid.


    -
    ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem);
    -

    Create a ZSTD decompression context using external alloc and free functions -


    - -
    ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize);
    -

    workspace: The memory area to emplace the context into. - Provided pointer must 8-bytes aligned. - It must outlive context usage. - workspaceSize: Use ZSTD_estimateDCtxSize() or ZSTD_estimateDStreamSize() - to determine how large workspace must be to support scenario. - @return : pointer to ZSTD_DCtx* (same address as workspace, but different type), - or NULL if error (typically size too small) - Note : zstd will never resize nor malloc() when using a static dctx. - If it needs more memory than available, it will simply error out. - Note 2 : static dctx is incompatible with legacy support - Note 3 : there is no corresponding "free" function. - Since workspace was allocated externally, it must be freed externally. - Limitation : currently not compatible with internal DDict creation, - such as ZSTD_initDStream_usingDict(). - -


    -
    ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize);
     

    Create a digested dictionary, ready to start decompression operation without startup delay. Dictionary content is referenced, and therefore stays in dictBuffer. @@ -584,27 +542,6 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet it must remain read accessible throughout the lifetime of DDict


    -
    ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize,
    -                                      ZSTD_dictLoadMethod_e dictLoadMethod,
    -                                      ZSTD_customMem customMem);
    -

    Create a ZSTD_DDict using external alloc and free, optionally by reference -


    - -
    ZSTD_DDict* ZSTD_initStaticDDict(void* workspace, size_t workspaceSize,
    -                                 const void* dict, size_t dictSize,
    -                                 ZSTD_dictLoadMethod_e dictLoadMethod);
    -

    Generate a digested dictionary in provided memory area. - workspace: The memory area to emplace the dictionary into. - Provided pointer must 8-bytes aligned. - It must outlive dictionary usage. - workspaceSize: Use ZSTD_estimateDDictSize() - to determine how large workspace must be. - @return : pointer to ZSTD_DDict*, or NULL if error (size too small) - Note : there is no corresponding "free" function. - Since workspace was allocated externally, it must be freed externally. - -


    -
    unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize);
     

    Provides the dictID stored within dictionary. if @return == 0, the dictionary is not conformant with Zstandard specification. @@ -629,11 +566,9 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet When identifying the exact failure cause, it's possible to use ZSTD_getFrameHeader(), which will provide a more precise error code.


    -

    Advanced streaming functions

    
    +

    Advanced streaming functions

    
     
    -

    Advanced Streaming compression functions

    ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
    -ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticCCtx() */
    -size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);   /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */
    +

    Advanced Streaming compression functions

    size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);   /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */
     size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/
     size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize,
                                                  ZSTD_parameters params, unsigned long long pledgedSrcSize);  /**< pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */
    @@ -651,22 +586,20 @@ size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict*
      @return : 0, or an error code (which can be tested using ZSTD_isError()) 
     


    -

    Advanced Streaming decompression functions

    ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
    -ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticDCtx() */
    -typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e;
    +

    Advanced Streaming decompression functions

    typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e;
     size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue);   /* obsolete : this API will be removed in a future version */
     size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: no dictionary will be used if dict == NULL or dictSize < 8 */
     size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict);  /**< note : ddict is referenced, it must outlive decompression session */
     size_t ZSTD_resetDStream(ZSTD_DStream* zds);  /**< re-use decompression parameters from previous init; saves dictionary loading */
     

    -

    Buffer-less and synchronous inner streaming functions

    +

    Buffer-less and synchronous inner streaming functions

       This is an advanced API, giving full control over buffer management, for users which need direct control over memory.
       But it's also a complex one, with several restrictions, documented below.
       Prefer normal streaming API for an easier experience.
      
     
    -

    Buffer-less streaming compression (synchronous mode)

    +

    Buffer-less streaming compression (synchronous mode)

       A ZSTD_CCtx object is required to track streaming operations.
       Use ZSTD_createCCtx() / ZSTD_freeCCtx() to manage resource.
       ZSTD_CCtx object can be re-used multiple times within successive compression operations.
    @@ -702,7 +635,7 @@ size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
     size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize);   /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */
     size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**<  note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
     

    -

    Buffer-less streaming decompression (synchronous mode)

    +

    Buffer-less streaming decompression (synchronous mode)

       A ZSTD_DCtx object is required to track streaming operations.
       Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it.
       A ZSTD_DCtx object can be re-used multiple times.
    @@ -788,7 +721,7 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
     

    typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
     

    -

    New advanced API (experimental)

    
    +

    New advanced API (experimental)

    
     
     
    typedef enum {
         /* Question : should we have a format ZSTD_f_auto ?
    @@ -859,10 +792,20 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
         ZSTD_p_dictIDFlag,       /* When applicable, dictionary's ID is written into frame header (default:1) */
     
         /* multi-threading parameters */
    +    /* These parameters are only useful if multi-threading is enabled (ZSTD_MULTITHREAD).
    +     * They return an error otherwise. */
         ZSTD_p_nbThreads=400,    /* Select how many threads a compression job can spawn (default:1)
                                   * More threads improve speed, but also increase memory usage.
                                   * Can only receive a value > 1 if ZSTD_MULTITHREAD is enabled.
                                   * Special: value 0 means "do not change nbThreads" */
    +    ZSTD_p_nonBlockingMode,  /* Single thread mode is by default "blocking" :
    +                              * it finishes its job as much as possible, and only then gives back control to caller.
    +                              * In contrast, multi-thread is by default "non-blocking" :
    +                              * it takes some input, flush some output if available, and immediately gives back control to caller.
    +                              * Compression work is performed in parallel, within worker threads.
    +                              * (note : a strong exception to this rule is when first job is called with ZSTD_e_end : it becomes blocking)
    +                              * Setting this parameter to 1 will enforce non-blocking mode even when only 1 thread is selected.
    +                              * It allows the caller to do other tasks while the worker thread compresses in parallel. */
         ZSTD_p_jobSize,          /* Size of a compression job. This value is only enforced in streaming (non-blocking) mode.
                                   * Each compression job is completed in parallel, so indirectly controls the nb of active threads.
                                   * 0 means default, which is dynamically determined based on compression parameters.
    @@ -1007,7 +950,7 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t
      
     


    -
    void ZSTD_CCtx_reset(ZSTD_CCtx* cctx);   /* Not ready yet ! */
    +
    void ZSTD_CCtx_reset(ZSTD_CCtx* cctx);
     

    Return a CCtx to clean state. Useful after an error, or to interrupt an ongoing compression job and start a new one. Any internal data not yet flushed is cancelled. @@ -1178,7 +1121,7 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t


    -

    Block level API

    
    +

    Block level API

    
     
     

    Frame metadata cost is typically ~18 bytes, which can be non-negligible for very small blocks (< 100 bytes). User will have to take in charge required information to regenerate data, such as compressed and content sizes. @@ -1206,7 +1149,7 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t

    Raw zstd block functions

    size_t ZSTD_getBlockSize   (const ZSTD_CCtx* cctx);
     size_t ZSTD_compressBlock  (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
     size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
    -size_t ZSTD_insertBlock(ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize);  /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression */
    +size_t ZSTD_insertBlock    (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize);  /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */
     

    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index bbe9146bd..2ac700d0f 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -754,6 +754,29 @@ size_t ZSTD_estimateCStreamSize(int compressionLevel) { return memBudget; } +/* ZSTD_getFrameProgression(): + * tells how much data has been consumed (input) and produced (output) for current frame. + * able to count progression inside worker threads (non-blocking mode). + */ +ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx) +{ +#ifdef ZSTD_MULTITHREAD + if ((cctx->appliedParams.nbThreads > 1) || (cctx->appliedParams.nonBlockingMode)) { + return ZSTDMT_getFrameProgression(cctx->mtctx); + } +#endif + { ZSTD_frameProgression fp; + size_t const buffered = (cctx->inBuff == NULL) ? 0 : + cctx->inBuffPos - cctx->inToCompress; + if (buffered) assert(cctx->inBuffPos >= cctx->inToCompress); + assert(buffered <= ZSTD_BLOCKSIZE_MAX); + fp.ingested = cctx->consumedSrcSize + buffered; + fp.consumed = cctx->consumedSrcSize; + fp.produced = cctx->producedCSize; + return fp; +} } + + static U32 ZSTD_equivalentCParams(ZSTD_compressionParameters cParams1, ZSTD_compressionParameters cParams2) { @@ -850,6 +873,7 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pl cctx->appliedParams = params; cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1; cctx->consumedSrcSize = 0; + cctx->producedCSize = 0; if (pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN) cctx->appliedParams.fParams.contentSizeFlag = 0; DEBUGLOG(4, "pledged content size : %u ; flag : %u", @@ -1007,6 +1031,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->appliedParams = params; zc->pledgedSrcSizePlusOne = pledgedSrcSize+1; zc->consumedSrcSize = 0; + zc->producedCSize = 0; if (pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN) zc->appliedParams.fParams.contentSizeFlag = 0; DEBUGLOG(4, "pledged content size : %u ; flag : %u", @@ -2063,6 +2088,7 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, ZSTD_compressBlock_internal (cctx, dst, dstCapacity, src, srcSize); if (ZSTD_isError(cSize)) return cSize; cctx->consumedSrcSize += srcSize; + cctx->producedCSize += (cSize + fhSize); return cSize + fhSize; } } diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index eb18cbc5e..899c5b08b 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -172,6 +172,7 @@ struct ZSTD_CCtx_s { size_t blockSize; U64 pledgedSrcSizePlusOne; /* this way, 0 (default) == unknown */ U64 consumedSrcSize; + U64 producedCSize; XXH64_state_t xxhState; ZSTD_customMem customMem; size_t staticSize; diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index b3ccfe398..08171f5fc 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -308,7 +308,7 @@ typedef struct { const void* srcStart; size_t prefixSize; size_t srcSize; - size_t readSize; + size_t consumed; buffer_t dstBuff; size_t cSize; size_t dstFlushed; @@ -382,8 +382,7 @@ void ZSTDMT_compressChunk(void* jobDescription) ZSTD_compressEnd (cctx, dstBuff.start, dstBuff.size, src, job->srcSize) : ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, job->srcSize); #else - if (sizeof(size_t) > sizeof(int)) - assert(job->srcSize < ((size_t)INT_MAX) * ZSTD_BLOCKSIZE_MAX); /* check overflow */ + if (sizeof(size_t) > sizeof(int)) assert(job->srcSize < ((size_t)INT_MAX) * ZSTD_BLOCKSIZE_MAX); /* check overflow */ { int const nbBlocks = (int)((job->srcSize + (ZSTD_BLOCKSIZE_MAX-1)) / ZSTD_BLOCKSIZE_MAX); const BYTE* ip = (const BYTE*) src; BYTE* const ostart = (BYTE*)dstBuff.start; @@ -399,7 +398,7 @@ void ZSTDMT_compressChunk(void* jobDescription) op += cSize; assert(op < oend); /* stats */ job->cSize += cSize; - job->readSize = ZSTD_BLOCKSIZE_MAX * blockNb; + job->consumed = ZSTD_BLOCKSIZE_MAX * blockNb; } /* last block */ if ((nbBlocks > 0) | job->lastChunk /*need to output a "last block" flag*/ ) { @@ -412,7 +411,7 @@ void ZSTDMT_compressChunk(void* jobDescription) /* stats */ job->cSize += cSize; } - job->readSize = job->srcSize; + job->consumed = job->srcSize; } #endif @@ -460,6 +459,8 @@ struct ZSTDMT_CCtx_s { unsigned frameEnded; unsigned allJobsCompleted; unsigned long long frameContentSize; + unsigned long long consumed; + unsigned long long produced; ZSTD_customMem cMem; ZSTD_CDict* cdictLocal; const ZSTD_CDict* cdict; @@ -501,15 +502,6 @@ size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThread return nbThreads; } -/* ZSTDMT_getNbThreads(): - * @return nb threads currently active in mtctx. - * mtctx must be valid */ -unsigned ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx) -{ - assert(mtctx != NULL); - return mtctx->params.nbThreads; -} - ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem) { ZSTDMT_CCtx* mtctx; @@ -553,6 +545,7 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbThreads) return ZSTDMT_createCCtx_advanced(nbThreads, ZSTD_defaultCMem); } + /* ZSTDMT_releaseAllJobResources() : * note : ensure all workers are killed first ! */ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx) @@ -652,6 +645,43 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, } } +/* ZSTDMT_getNbThreads(): + * @return nb threads currently active in mtctx. + * mtctx must be valid */ +unsigned ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx) +{ + assert(mtctx != NULL); + return mtctx->params.nbThreads; +} + +/* ZSTDMT_getFrameProgression(): + * tells how much data has been consumed (input) and produced (output) for current frame. + * able to count progression inside worker threads. + * Note : mutex will be triggered during statistics collection. */ +ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx) +{ + ZSTD_frameProgression fs; + DEBUGLOG(5, "ZSTDMT_getFrameProgression"); + ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobCompleted_mutex); + fs.consumed = mtctx->consumed; + fs.produced = mtctx->produced; + assert(mtctx->inBuff.filled >= mtctx->prefixSize); + fs.ingested = mtctx->consumed + (mtctx->inBuff.filled - mtctx->prefixSize); + { unsigned jobNb; + for (jobNb = mtctx->doneJobID ; jobNb < mtctx->nextJobID ; jobNb++) { + unsigned const wJobID = jobNb & mtctx->jobIDMask; + size_t const cResult = mtctx->jobs[wJobID].cSize; + size_t const produced = ZSTD_isError(cResult) ? 0 : cResult; + fs.consumed += mtctx->jobs[wJobID].consumed; + fs.ingested += mtctx->jobs[wJobID].srcSize; + fs.produced += produced; + } + } + ZSTD_pthread_mutex_unlock(&mtctx->jobCompleted_mutex); + return fs; +} + + /* ------------------------------------------ */ /* ===== Multi-threaded compression ===== */ /* ------------------------------------------ */ @@ -900,6 +930,8 @@ size_t ZSTDMT_initCStream_internal( zcs->nextJobID = 0; zcs->frameEnded = 0; zcs->allJobsCompleted = 0; + zcs->consumed = 0; + zcs->produced = 0; if (params.fParams.checksumFlag) XXH64_reset(&zcs->xxhState, 0); return 0; } @@ -963,7 +995,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; - zcs->jobs[jobID].readSize = 0; + zcs->jobs[jobID].consumed = 0; zcs->jobs[jobID].prefixSize = zcs->prefixSize; assert(zcs->inBuff.filled >= srcSize + zcs->prefixSize); zcs->jobs[jobID].params = zcs->params; @@ -1070,6 +1102,8 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi zcs->jobs[wJobID].dstBuff = g_nullBuffer; zcs->jobs[wJobID].jobCompleted = 0; zcs->doneJobID++; + zcs->consumed += job.srcSize; + zcs->produced += job.cSize; } else { zcs->jobs[wJobID].dstFlushed = job.dstFlushed; } diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 7716ea68e..7c1e7e27b 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -122,6 +122,13 @@ size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThread * mtctx must be valid */ unsigned ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx); +/* ZSTDMT_getFrameProgression(): + * tells how much data has been consumed (input) and produced (output) for current frame. + * able to count progression inside worker threads. + */ +ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx); + + /*! ZSTDMT_initCStream_internal() : * Private use only. Init streaming operation. * expects params to be valid. diff --git a/lib/zstd.h b/lib/zstd.h index 6ac132a66..a34d37f0b 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -711,11 +711,25 @@ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const * If pledgedSrcSize is not known at reset time, use macro ZSTD_CONTENTSIZE_UNKNOWN. * If pledgedSrcSize > 0, its value must be correct, as it will be written in header, and controlled at the end. * For the time being, pledgedSrcSize==0 is interpreted as "srcSize unknown" for compatibility with older programs, - * but it may change to mean "empty" in some future version, so prefer using macro ZSTD_CONTENTSIZE_UNKNOWN. + * but it will change to mean "empty" in future version, so use macro ZSTD_CONTENTSIZE_UNKNOWN instead. * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ ZSTDLIB_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize); +typedef struct { + unsigned long long ingested; + unsigned long long consumed; + unsigned long long produced; +} ZSTD_frameProgression; + +/* ZSTD_getFrameProgression(): + * tells how much data has been consumed (input) and produced (output) for current frame. + * able to count progression inside worker threads (non-blocking mode). + */ +ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx); + + + /*===== Advanced Streaming decompression functions =====*/ typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e; ZSTDLIB_API size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue); /* obsolete : this API will be removed in a future version */ diff --git a/programs/fileio.c b/programs/fileio.c index 326f0e673..6a7bb1b02 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -84,10 +84,13 @@ void FIO_setNotificationLevel(unsigned level) { g_displayLevel=level; } static const U64 g_refreshRate = SEC_TO_MICRO / 6; static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; -#define DISPLAYUPDATE(l, ...) { if (g_displayLevel>=l) { \ - if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ - { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stderr); } } } +#define READY_FOR_UPDATE (UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) +#define DISPLAYUPDATE(l, ...) { \ + if (g_displayLevel>=l) { \ + if (READY_FOR_UPDATE || (g_displayLevel>=4)) { \ + g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ + if (g_displayLevel>=4) fflush(stderr); \ + } } } #undef MIN /* in case it would be already defined */ #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -809,12 +812,23 @@ static int FIO_compressFilename_internal(cRess_t ress, compressedfilesize += outBuff.pos; } } +#if 1 + if (READY_FOR_UPDATE) + { ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx); + DISPLAYUPDATE(2, "\rRead :%6u MB - Consumed :%6u MB - Compressed :%6u MB => %.2f%%", + (U32)(zfp.ingested >> 20), + (U32)(zfp.consumed >> 20), + (U32)(zfp.produced >> 20), + (double)zfp.produced / (zfp.consumed + !zfp.consumed/*avoid div0*/) * 100 ); + } +#else if (fileSize == UTIL_FILESIZE_UNKNOWN) { DISPLAYUPDATE(2, "\rRead : %u MB", (U32)(readsize>>20)); } else { DISPLAYUPDATE(2, "\rRead : %u / %u MB", (U32)(readsize>>20), (U32)(fileSize>>20)); } +#endif } while (directive != ZSTD_e_end); finish: From aa79c18e3f25cb660bfadee9e0b3b57a3a5d2d08 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 17 Jan 2018 17:18:19 -0800 Subject: [PATCH 064/255] fixed a few access contention passes thread sanitizer test --- lib/compress/zstdmt_compress.c | 12 ++++++++++-- programs/fileio.c | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 08171f5fc..5624b5e3d 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -390,15 +390,17 @@ void ZSTDMT_compressChunk(void* jobDescription) BYTE* oend = op + dstBuff.size; int blockNb; DEBUGLOG(5, "ZSTDMT_compressChunk: compress %u bytes in %i blocks", (U32)job->srcSize, nbBlocks); - job->cSize = 0; + assert(job->cSize == 0); for (blockNb = 1; blockNb < nbBlocks; blockNb++) { size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, ZSTD_BLOCKSIZE_MAX); if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; } ip += ZSTD_BLOCKSIZE_MAX; op += cSize; assert(op < oend); /* stats */ + ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); /* note : it's a mtctx mutex */ job->cSize += cSize; job->consumed = ZSTD_BLOCKSIZE_MAX * blockNb; + ZSTD_pthread_mutex_unlock(job->jobCompleted_mutex); } /* last block */ if ((nbBlocks > 0) | job->lastChunk /*need to output a "last block" flag*/ ) { @@ -409,9 +411,11 @@ void ZSTDMT_compressChunk(void* jobDescription) ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize); if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; } /* stats */ + ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); /* note : it's a mtctx mutex */ job->cSize += cSize; + job->consumed = job->srcSize; + ZSTD_pthread_mutex_unlock(job->jobCompleted_mutex); } - job->consumed = job->srcSize; } #endif @@ -422,6 +426,7 @@ _endJob: job->src = g_nullBuffer; job->srcStart = NULL; /* report */ ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); + job->consumed = job->srcSize; job->jobCompleted = 1; job->jobScanned = 0; ZSTD_pthread_cond_signal(job->jobCompleted_cond); @@ -757,6 +762,8 @@ static size_t ZSTDMT_compress_advanced_internal( mtctx->jobs[u].srcStart = srcStart + frameStartPos - dictSize; mtctx->jobs[u].prefixSize = dictSize; mtctx->jobs[u].srcSize = chunkSize; + mtctx->jobs[u].consumed = 0; + mtctx->jobs[u].cSize = 0; mtctx->jobs[u].cdict = (u==0) ? cdict : NULL; mtctx->jobs[u].fullFrameSize = srcSize; mtctx->jobs[u].params = jobParams; @@ -996,6 +1003,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; zcs->jobs[jobID].consumed = 0; + zcs->jobs[jobID].cSize = 0; zcs->jobs[jobID].prefixSize = zcs->prefixSize; assert(zcs->inBuff.filled >= srcSize + zcs->prefixSize); zcs->jobs[jobID].params = zcs->params; diff --git a/programs/fileio.c b/programs/fileio.c index 6a7bb1b02..18552aa3b 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -813,8 +813,8 @@ static int FIO_compressFilename_internal(cRess_t ress, } } #if 1 - if (READY_FOR_UPDATE) - { ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx); + if (READY_FOR_UPDATE) { + ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx); DISPLAYUPDATE(2, "\rRead :%6u MB - Consumed :%6u MB - Compressed :%6u MB => %.2f%%", (U32)(zfp.ingested >> 20), (U32)(zfp.consumed >> 20), From 1b5d80d633827bdb4d934b54833d5bfbebc000bf Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 18 Jan 2018 11:03:27 -0800 Subject: [PATCH 065/255] zstdmt: added ability to flush current job before it's completed however, zstdmt may still wait on next available worker, so it's not smooth yet. --- lib/compress/zstdmt_compress.c | 66 +++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 5624b5e3d..812bc89b1 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -315,7 +315,7 @@ typedef struct { unsigned firstChunk; unsigned lastChunk; unsigned jobCompleted; - unsigned jobScanned; + unsigned checksumWritten; ZSTD_pthread_mutex_t* jobCompleted_mutex; ZSTD_pthread_cond_t* jobCompleted_cond; ZSTD_CCtx_params params; @@ -389,7 +389,7 @@ void ZSTDMT_compressChunk(void* jobDescription) BYTE* op = ostart; BYTE* oend = op + dstBuff.size; int blockNb; - DEBUGLOG(5, "ZSTDMT_compressChunk: compress %u bytes in %i blocks", (U32)job->srcSize, nbBlocks); + DEBUGLOG(2, "ZSTDMT_compressChunk: compress %u bytes in %i blocks", (U32)job->srcSize, nbBlocks); assert(job->cSize == 0); for (blockNb = 1; blockNb < nbBlocks; blockNb++) { size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, ZSTD_BLOCKSIZE_MAX); @@ -400,10 +400,13 @@ void ZSTDMT_compressChunk(void* jobDescription) ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); /* note : it's a mtctx mutex */ job->cSize += cSize; job->consumed = ZSTD_BLOCKSIZE_MAX * blockNb; + DEBUGLOG(2, "ZSTDMT_compressChunk: compress new block : cSize==%u bytes (total: %u)", + (U32)cSize, (U32)job->cSize); + ZSTD_pthread_cond_signal(job->jobCompleted_cond); ZSTD_pthread_mutex_unlock(job->jobCompleted_mutex); } /* last block */ - if ((nbBlocks > 0) | job->lastChunk /*need to output a "last block" flag*/ ) { + if ((nbBlocks > 0) | job->lastChunk /*must output a "last block" flag*/ ) { size_t const lastBlockSize1 = job->srcSize & (ZSTD_BLOCKSIZE_MAX-1); size_t const lastBlockSize = ((lastBlockSize1==0) & (job->srcSize>=ZSTD_BLOCKSIZE_MAX)) ? ZSTD_BLOCKSIZE_MAX : lastBlockSize1; size_t const cSize = (job->lastChunk) ? @@ -428,7 +431,6 @@ _endJob: ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); job->consumed = job->srcSize; job->jobCompleted = 1; - job->jobScanned = 0; ZSTD_pthread_cond_signal(job->jobCompleted_cond); ZSTD_pthread_mutex_unlock(job->jobCompleted_mutex); } @@ -1017,6 +1019,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0); zcs->jobs[jobID].lastChunk = endFrame; zcs->jobs[jobID].jobCompleted = 0; + zcs->jobs[jobID].checksumWritten = 0; zcs->jobs[jobID].dstFlushed = 0; zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex; zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond; @@ -1069,43 +1072,48 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush) { unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; - DEBUGLOG(5, "ZSTDMT_flushNextJob (blocking:%u)", blockToFlush); + DEBUGLOG(2, "ZSTDMT_flushNextJob (blocking:%u)", blockToFlush); if (zcs->doneJobID == zcs->nextJobID) return 0; /* all flushed ! */ ZSTD_PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); - while (zcs->jobs[wJobID].jobCompleted==0) { - DEBUGLOG(5, "waiting for jobCompleted signal from job %u", zcs->doneJobID); + while (zcs->jobs[wJobID].dstFlushed == zcs->jobs[wJobID].cSize) { + DEBUGLOG(2, "waiting for something to flush from job %u (currently flushed: %u bytes)", + zcs->doneJobID, (U32)zcs->jobs[wJobID].dstFlushed); + assert(zcs->jobs[wJobID].jobCompleted==0); if (!blockToFlush) { ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex); return 0; } /* nothing ready to be flushed => skip */ ZSTD_pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); /* block when nothing available to flush */ } - ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex); - /* compression job completed : output can be flushed */ + + /* some output is available to be flushed */ { ZSTDMT_jobDescription job = zcs->jobs[wJobID]; - if (!job.jobScanned) { - if (ZSTD_isError(job.cSize)) { - DEBUGLOG(5, "ZSTDMT_flushNextJob: job %u : compression error detected : %s", - zcs->doneJobID, ZSTD_getErrorName(job.cSize)); - ZSTDMT_waitForAllJobsCompleted(zcs); - ZSTDMT_releaseAllJobResources(zcs); - return job.cSize; - } - DEBUGLOG(5, "ZSTDMT_flushNextJob: zcs->params.fParams.checksumFlag : %u ", zcs->params.fParams.checksumFlag); - if (zcs->params.fParams.checksumFlag) { - if (zcs->frameEnded && (zcs->doneJobID+1 == zcs->nextJobID)) { /* write checksum at end of last section */ - U32 const checksum = (U32)XXH64_digest(&zcs->xxhState); - DEBUGLOG(5, "ZSTDMT_flushNextJob: writing checksum : %08X \n", checksum); - MEM_writeLE32((char*)job.dstBuff.start + job.cSize, checksum); - job.cSize += 4; - zcs->jobs[wJobID].cSize += 4; - } } - zcs->jobs[wJobID].jobScanned = 1; + ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex); + if (ZSTD_isError(job.cSize)) { + DEBUGLOG(5, "ZSTDMT_flushNextJob: job %u : compression error detected : %s", + zcs->doneJobID, ZSTD_getErrorName(job.cSize)); + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + return job.cSize; } + /* add frame checksum if necessary */ + if ( zcs->frameEnded + && (zcs->doneJobID+1 == zcs->nextJobID) + && (zcs->params.fParams.checksumFlag) + && (!job.checksumWritten) ) { + U32 const checksum = (U32)XXH64_digest(&zcs->xxhState); + DEBUGLOG(5, "ZSTDMT_flushNextJob: writing checksum : %08X \n", checksum); + MEM_writeLE32((char*)job.dstBuff.start + job.cSize, checksum); + job.cSize += 4; + zcs->jobs[wJobID].cSize += 4; + zcs->jobs[wJobID].checksumWritten = 1; + } + assert(job.cSize >= job.dstFlushed); { size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); - DEBUGLOG(5, "ZSTDMT_flushNextJob: Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); + DEBUGLOG(2, "ZSTDMT_flushNextJob: Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); output->pos += toWrite; job.dstFlushed += toWrite; } - if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => move to next one */ + if ( job.jobCompleted + && (job.dstFlushed == job.cSize) ) { /* output buffer fully flushed => move to next one */ ZSTDMT_releaseBuffer(zcs->bufPool, job.dstBuff); zcs->jobs[wJobID].dstBuff = g_nullBuffer; zcs->jobs[wJobID].jobCompleted = 0; From c7190c69ccb83b68b3b4e1a34d7e57470d7d6258 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 18 Jan 2018 11:15:23 -0800 Subject: [PATCH 066/255] fixes for @terrelln comments --- lib/compress/zstd_compress_internal.h | 6 +++--- lib/compress/zstdmt_compress.c | 2 +- lib/zstd.h | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 899c5b08b..64f03a0d2 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -170,9 +170,9 @@ struct ZSTD_CCtx_s { void* workSpace; size_t workSpaceSize; size_t blockSize; - U64 pledgedSrcSizePlusOne; /* this way, 0 (default) == unknown */ - U64 consumedSrcSize; - U64 producedCSize; + unsigned long long pledgedSrcSizePlusOne; /* this way, 0 (default) == unknown */ + unsigned long long consumedSrcSize; + unsigned long long producedCSize; XXH64_state_t xxhState; ZSTD_customMem customMem; size_t staticSize; diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 5624b5e3d..f9a25270a 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -662,7 +662,7 @@ unsigned ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx) /* ZSTDMT_getFrameProgression(): * tells how much data has been consumed (input) and produced (output) for current frame. * able to count progression inside worker threads. - * Note : mutex will be triggered during statistics collection. */ + * Note : mutex will be acquired during statistics collection. */ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx) { ZSTD_frameProgression fs; diff --git a/lib/zstd.h b/lib/zstd.h index a34d37f0b..aab6be3b0 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -723,8 +723,10 @@ typedef struct { } ZSTD_frameProgression; /* ZSTD_getFrameProgression(): - * tells how much data has been consumed (input) and produced (output) for current frame. - * able to count progression inside worker threads (non-blocking mode). + * tells how much data has been ingested (read from input) + * consumed (input actually compressed) and produced (output) for current frame. + * Therefore, (ingested - consumed) is amount of input data buffered internally, not yet compressed. + * Can report progression inside worker threads (multi-threading and non-blocking mode). */ ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx); From 4d08ba8b779e4e653a45102667b04611b0213529 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 18 Jan 2018 11:27:13 -0800 Subject: [PATCH 067/255] fileio: READY_FOR_UPDATE() is now a function-like macro as suggested by @terrelln --- programs/fileio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 18552aa3b..7045a5323 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -84,10 +84,10 @@ void FIO_setNotificationLevel(unsigned level) { g_displayLevel=level; } static const U64 g_refreshRate = SEC_TO_MICRO / 6; static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; -#define READY_FOR_UPDATE (UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) +#define READY_FOR_UPDATE() (UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) #define DISPLAYUPDATE(l, ...) { \ if (g_displayLevel>=l) { \ - if (READY_FOR_UPDATE || (g_displayLevel>=4)) { \ + if (READY_FOR_UPDATE() || (g_displayLevel>=4)) { \ g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ if (g_displayLevel>=4) fflush(stderr); \ } } } @@ -813,7 +813,7 @@ static int FIO_compressFilename_internal(cRess_t ress, } } #if 1 - if (READY_FOR_UPDATE) { + if (READY_FOR_UPDATE()) { ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx); DISPLAYUPDATE(2, "\rRead :%6u MB - Consumed :%6u MB - Compressed :%6u MB => %.2f%%", (U32)(zfp.ingested >> 20), From 9d96761520e75d9902bc94cf319e8c18d4ce9e1a Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 18 Jan 2018 13:28:30 -0800 Subject: [PATCH 068/255] Set repcodes for empty ZSTD_CDict When the dictionary is <= 8 bytes, no data is loaded from the dictionary. In this case the repcodes weren't set, because they were inserted after the size check. Fix this problem in general by first setting the cdict state to a clean state of an empty dictionary, then filling the state from there. --- lib/compress/zstd_compress.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index bbe9146bd..9b927367f 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2540,6 +2540,8 @@ static size_t ZSTD_initCDict_internal( } cdict->dictContentSize = dictSize; + /* Reset the state to no dictionary */ + ZSTD_reset_compressedBlockState(&cdict->cBlockState); { void* const end = ZSTD_reset_matchState( &cdict->matchState, @@ -2548,6 +2550,9 @@ static size_t ZSTD_initCDict_internal( assert(end == (char*)cdict->workspace + cdict->workspaceSize); (void)end; } + /* (Maybe) load the dictionary + * Skips loading the dictionary if it is <= 8 bytes. + */ { ZSTD_CCtx_params params; memset(¶ms, 0, sizeof(params)); From 997e4d0ccd4d4122949ab6726569542285f6b85b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 18 Jan 2018 14:39:51 -0800 Subject: [PATCH 069/255] added POOL_tryAdd() --- doc/zstd_manual.html | 8 ++++- lib/common/pool.c | 71 +++++++++++++++++++++++++++++++------------- lib/common/pool.h | 27 +++++++++++------ 3 files changed, 75 insertions(+), 31 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 473d5be8b..dc3f8be1b 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -582,10 +582,16 @@ size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* If pledgedSrcSize is not known at reset time, use macro ZSTD_CONTENTSIZE_UNKNOWN. If pledgedSrcSize > 0, its value must be correct, as it will be written in header, and controlled at the end. For the time being, pledgedSrcSize==0 is interpreted as "srcSize unknown" for compatibility with older programs, - but it may change to mean "empty" in some future version, so prefer using macro ZSTD_CONTENTSIZE_UNKNOWN. + but it will change to mean "empty" in future version, so use macro ZSTD_CONTENTSIZE_UNKNOWN instead. @return : 0, or an error code (which can be tested using ZSTD_isError())


    +
    typedef struct {
    +    unsigned long long ingested;
    +    unsigned long long consumed;
    +    unsigned long long produced;
    +} ZSTD_frameProgression;
    +

    Advanced Streaming decompression functions

    typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e;
     size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue);   /* obsolete : this API will be removed in a future version */
     size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: no dictionary will be used if dict == NULL or dictSize < 8 */
    diff --git a/lib/common/pool.c b/lib/common/pool.c
    index 98b109e72..773488b07 100644
    --- a/lib/common/pool.c
    +++ b/lib/common/pool.c
    @@ -12,6 +12,7 @@
     /* ======   Dependencies   ======= */
     #include   /* size_t */
     #include "pool.h"
    +#include "zstd_internal.h"  /* ZSTD_malloc, ZSTD_free */
     
     /* ======   Compiler specifics   ====== */
     #if defined(_MSC_VER)
    @@ -193,32 +194,54 @@ static int isQueueFull(POOL_ctx const* ctx) {
         }
     }
     
    -void POOL_add(void* ctxVoid, POOL_function function, void *opaque) {
    -    POOL_ctx* const ctx = (POOL_ctx*)ctxVoid;
    -    if (!ctx) { return; }
     
    -    ZSTD_pthread_mutex_lock(&ctx->queueMutex);
    -    {   POOL_job const job = {function, opaque};
    +static void POOL_add_internal(POOL_ctx* ctx, POOL_function function, void *opaque)
    +{
    +    POOL_job const job = {function, opaque};
    +    assert(ctx != NULL);
    +    if (ctx->shutdown) return;
     
    -        /* Wait until there is space in the queue for the new job */
    -        while (isQueueFull(ctx) && !ctx->shutdown) {
    -          ZSTD_pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex);
    -        }
    -        /* The queue is still going => there is space */
    -        if (!ctx->shutdown) {
    -            ctx->queueEmpty = 0;
    -            ctx->queue[ctx->queueTail] = job;
    -            ctx->queueTail = (ctx->queueTail + 1) % ctx->queueSize;
    -        }
    -    }
    -    ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
    +    ctx->queueEmpty = 0;
    +    ctx->queue[ctx->queueTail] = job;
    +    ctx->queueTail = (ctx->queueTail + 1) % ctx->queueSize;
         ZSTD_pthread_cond_signal(&ctx->queuePopCond);
     }
     
    -#else  /* ZSTD_MULTITHREAD  not defined */
    -/* No multi-threading support */
    +void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque)
    +{
    +    assert(ctx != NULL);
    +    ZSTD_pthread_mutex_lock(&ctx->queueMutex);
    +    /* Wait until there is space in the queue for the new job */
    +    while (isQueueFull(ctx) && (!ctx->shutdown)) {
    +        ZSTD_pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex);
    +    }
    +    POOL_add_internal(ctx, function, opaque);
    +    ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
    +}
     
    -/* We don't need any data, but if it is empty malloc() might return NULL. */
    +
    +int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque)
    +{
    +    assert(ctx != NULL);
    +    ZSTD_pthread_mutex_lock(&ctx->queueMutex);
    +    if (isQueueFull(ctx)) {
    +        ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
    +        return 0;
    +    }
    +    POOL_add_internal(ctx, function, opaque);
    +    ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
    +    return 1;
    +}
    +
    +
    +#else  /* ZSTD_MULTITHREAD  not defined */
    +
    +/* ========================== */
    +/* No multi-threading support */
    +/* ========================== */
    +
    +
    +/* We don't need any data, but if it is empty, malloc() might return NULL. */
     struct POOL_ctx_s {
         int dummy;
     };
    @@ -240,11 +263,17 @@ void POOL_free(POOL_ctx* ctx) {
         (void)ctx;
     }
     
    -void POOL_add(void* ctx, POOL_function function, void* opaque) {
    +void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque) {
         (void)ctx;
         function(opaque);
     }
     
    +int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque) {
    +    (void)ctx;
    +    function(opaque);
    +    return 1;
    +}
    +
     size_t POOL_sizeof(POOL_ctx* ctx) {
         if (ctx==NULL) return 0;  /* supports sizeof NULL */
         assert(ctx == &g_ctx);
    diff --git a/lib/common/pool.h b/lib/common/pool.h
    index 08c63715a..a57e9b4fa 100644
    --- a/lib/common/pool.h
    +++ b/lib/common/pool.h
    @@ -17,7 +17,8 @@ extern "C" {
     
     
     #include    /* size_t */
    -#include "zstd_internal.h"   /* ZSTD_customMem */
    +#define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_customMem */
    +#include "zstd.h"
     
     typedef struct POOL_ctx_s POOL_ctx;
     
    @@ -27,35 +28,43 @@ typedef struct POOL_ctx_s POOL_ctx;
      *  The maximum number of queued jobs before blocking is `queueSize`.
      * @return : POOL_ctx pointer on success, else NULL.
     */
    -POOL_ctx *POOL_create(size_t numThreads, size_t queueSize);
    +POOL_ctx* POOL_create(size_t numThreads, size_t queueSize);
     
    -POOL_ctx *POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem);
    +POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem);
     
     /*! POOL_free() :
         Free a thread pool returned by POOL_create().
     */
    -void POOL_free(POOL_ctx *ctx);
    +void POOL_free(POOL_ctx* ctx);
     
     /*! POOL_sizeof() :
         return memory usage of pool returned by POOL_create().
     */
    -size_t POOL_sizeof(POOL_ctx *ctx);
    +size_t POOL_sizeof(POOL_ctx* ctx);
     
     /*! POOL_function :
         The function type that can be added to a thread pool.
     */
    -typedef void (*POOL_function)(void *);
    +typedef void (*POOL_function)(void*);
     /*! POOL_add_function :
         The function type for a generic thread pool add function.
     */
    -typedef void (*POOL_add_function)(void *, POOL_function, void *);
    +typedef void (*POOL_add_function)(void*, POOL_function, void*);
     
     /*! POOL_add() :
    -    Add the job `function(opaque)` to the thread pool.
    +    Add the job `function(opaque)` to the thread pool. `ctx` must be valid.
         Possibly blocks until there is room in the queue.
         Note : The function may be executed asynchronously, so `opaque` must live until the function has been completed.
     */
    -void POOL_add(void *ctx, POOL_function function, void *opaque);
    +void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque);
    +
    +
    +/*! POOL_tryAdd() :
    +    Add the job `function(opaque)` to the thread pool if a worker is available.
    +    return immediately otherwise.
    +   @return : 1 if successful, 0 if not.
    +*/
    +int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque);
     
     
     #if defined (__cplusplus)
    
    From 6f7280fb33f5717f2f30e2c19b57512a96325bd8 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 18 Jan 2018 16:20:26 -0800
    Subject: [PATCH 070/255] fixed frame checksum issue
    
    and race conditions
    ---
     lib/compress/zstd_compress.c   |  3 ++-
     lib/compress/zstdmt_compress.c | 40 ++++++++++++++++++----------------
     tests/zstreamtest.c            |  6 ++++-
     3 files changed, 28 insertions(+), 21 deletions(-)
    
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index e8840be97..a87c368ea 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -2396,7 +2396,7 @@ static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity)
         BYTE* op = ostart;
         size_t fhSize = 0;
     
    -    DEBUGLOG(5, "ZSTD_writeEpilogue");
    +    DEBUGLOG(4, "ZSTD_writeEpilogue");
         if (cctx->stage == ZSTDcs_created) return ERROR(stage_wrong);  /* init missing */
     
         /* special case : empty frame */
    @@ -2420,6 +2420,7 @@ static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity)
         if (cctx->appliedParams.fParams.checksumFlag) {
             U32 const checksum = (U32) XXH64_digest(&cctx->xxhState);
             if (dstCapacity<4) return ERROR(dstSize_tooSmall);
    +        DEBUGLOG(4, "ZSTD_writeEpilogue: write checksum : %08X", checksum);
             MEM_writeLE32(op, checksum);
             op += 4;
         }
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index ffcbdf5f8..1aa6b866f 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -315,7 +315,7 @@ typedef struct {
         unsigned firstChunk;
         unsigned lastChunk;
         unsigned jobCompleted;
    -    unsigned checksumWritten;
    +    unsigned frameChecksumNeeded;
         ZSTD_pthread_mutex_t* jobCompleted_mutex;
         ZSTD_pthread_cond_t* jobCompleted_cond;
         ZSTD_CCtx_params params;
    @@ -1019,7 +1019,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
         zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0);
         zcs->jobs[jobID].lastChunk = endFrame;
         zcs->jobs[jobID].jobCompleted = 0;
    -    zcs->jobs[jobID].checksumWritten = 0;
    +    zcs->jobs[jobID].frameChecksumNeeded = endFrame && (zcs->nextJobID>0) && zcs->params.fParams.checksumFlag;
         zcs->jobs[jobID].dstFlushed = 0;
         zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex;
         zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond;
    @@ -1065,22 +1065,25 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
     }
     
     
    -/* ZSTDMT_flushNextJob() :
    - * output : will be updated with amount of data flushed .
    - * blockToFlush : if >0, the function will block and wait if there is no data available to flush .
    - * @return : amount of data remaining within internal buffer, 1 if unknown but > 0, 0 if no more, or an error code */
    +/*! ZSTDMT_flushNextJob() :
    + * `output` : will be updated with amount of data flushed .
    + * `blockToFlush` : if >0, the function will block and wait if there is no data available to flush .
    + * @return : amount of data remaining within internal buffer, 0 if no more, 1 if unknown but > 0, or an error code */
     static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush)
     {
         unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask;
         DEBUGLOG(2, "ZSTDMT_flushNextJob (blocking:%u)", blockToFlush);
    -    if (zcs->doneJobID == zcs->nextJobID) return 0;   /* all flushed ! */
    +    if (zcs->doneJobID == zcs->nextJobID) {
    +        DEBUGLOG(2, "ZSTDMT_flushNextJob: doneJobID==nextJobID : nothing to flush !")
    +        return 0;   /* all flushed ! */
    +    }
         ZSTD_PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex);
         while (zcs->jobs[wJobID].dstFlushed == zcs->jobs[wJobID].cSize) {
    +        if (!blockToFlush) { ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex); return 0; }  /* nothing ready to be flushed => skip */
    +        if (zcs->jobs[wJobID].jobCompleted==1) break;
             DEBUGLOG(2, "waiting for something to flush from job %u (currently flushed: %u bytes)",
                         zcs->doneJobID, (U32)zcs->jobs[wJobID].dstFlushed);
    -        assert(zcs->jobs[wJobID].jobCompleted==0);
    -        if (!blockToFlush) { ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex); return 0; }  /* nothing ready to be flushed => skip */
    -        ZSTD_pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex);  /* block when nothing available to flush */
    +        ZSTD_pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex);  /* block when nothing available to flush but more to come */
         }
     
         /* some output is available to be flushed */
    @@ -1094,16 +1097,14 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi
                 return job.cSize;
             }
             /* add frame checksum if necessary */
    -        if ( zcs->frameEnded
    -          && (zcs->doneJobID+1 == zcs->nextJobID)
    -          && (zcs->params.fParams.checksumFlag)
    -          && (!job.checksumWritten) ) {
    +        if ( job.jobCompleted
    +          && job.frameChecksumNeeded ) {
                 U32 const checksum = (U32)XXH64_digest(&zcs->xxhState);
                 DEBUGLOG(5, "ZSTDMT_flushNextJob: writing checksum : %08X \n", checksum);
                 MEM_writeLE32((char*)job.dstBuff.start + job.cSize, checksum);
                 job.cSize += 4;
                 zcs->jobs[wJobID].cSize += 4;
    -            zcs->jobs[wJobID].checksumWritten = 1;
    +            zcs->jobs[wJobID].frameChecksumNeeded = 0;
             }
             assert(job.cSize >= job.dstFlushed);
             {   size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos);
    @@ -1114,19 +1115,20 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi
             }
             if ( job.jobCompleted
               && (job.dstFlushed == job.cSize) ) {   /* output buffer fully flushed => move to next one */
    +            DEBUGLOG(2, "Job %u completed, moving to next one", zcs->doneJobID);
                 ZSTDMT_releaseBuffer(zcs->bufPool, job.dstBuff);
                 zcs->jobs[wJobID].dstBuff = g_nullBuffer;
                 zcs->jobs[wJobID].jobCompleted = 0;
    -            zcs->doneJobID++;
                 zcs->consumed += job.srcSize;
                 zcs->produced += job.cSize;
    +            zcs->doneJobID++;
             } else {
                 zcs->jobs[wJobID].dstFlushed = job.dstFlushed;
             }
    -        /* return value : how many bytes left in buffer ; fake it to 1 if unknown but >0 */
    +        /* return value : how many bytes left in buffer ; fake it to 1 when unknown but >0 */
             if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed);
    -        if (zcs->doneJobID < zcs->nextJobID) return 1;   /* still some buffer to flush */
    -        zcs->allJobsCompleted = zcs->frameEnded;   /* frame completed and entirely flushed */
    +        if (zcs->doneJobID < zcs->nextJobID) return 1;   /* still some more buffer to flush */
    +        zcs->allJobsCompleted = zcs->frameEnded;   /* last frame entirely flushed */
             return 0;   /* everything flushed */
     }   }
     
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index 5cd1ea0fb..c26cb3295 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -1346,8 +1346,12 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp
                     outBuff.size = outBuff.pos + dstBuffSize;
                     DISPLAYLEVEL(6, "ZSTD_decompressStream input %u bytes \n", (U32)readCSrcSize);
                     decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff);
    +                if (ZSTD_getErrorCode(decompressionResult) == ZSTD_error_corruption_detected) {
    +                    DISPLAY("ZSTD_decompressStream: checksum error : \n");
    +                    findDiff(copyBuffer, dstBuffer, totalTestSize);
    +                }
                     CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult));
    -                DISPLAYLEVEL(6, "inBuff.pos = %u \n", (U32)readCSrcSize);
    +                DISPLAYLEVEL(6, "total ingested (inBuff.pos) = %u \n", (U32)inBuff.pos);
                 }
                 CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size (%u != %u)", (U32)outBuff.pos, (U32)totalTestSize);
                 CHECK (inBuff.pos != cSize, "compressed data should be fully read (%u != %u)", (U32)inBuff.pos, (U32)cSize);
    
    From 70f81d603037242124d7ae446429462f996f16ab Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 19 Jan 2018 10:01:40 -0800
    Subject: [PATCH 071/255] zstdmt uses POOL_tryAdd() to call a new worker
    
    so that it's no longer a blocking call.
    This makes it possible to stream out data gradually,
    while waiting for a worker to become available.
    ---
     lib/compress/zstdmt_compress.c | 177 ++++++++++++++++++---------------
     programs/fileio.c              |  30 ++----
     2 files changed, 108 insertions(+), 99 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 1aa6b866f..68f974d7b 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -389,7 +389,7 @@ void ZSTDMT_compressChunk(void* jobDescription)
             BYTE* op = ostart;
             BYTE* oend = op + dstBuff.size;
             int blockNb;
    -        DEBUGLOG(2, "ZSTDMT_compressChunk: compress %u bytes in %i blocks", (U32)job->srcSize, nbBlocks);
    +        DEBUGLOG(5, "ZSTDMT_compressChunk: compress %u bytes in %i blocks", (U32)job->srcSize, nbBlocks);
             assert(job->cSize == 0);
             for (blockNb = 1; blockNb < nbBlocks; blockNb++) {
                 size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, ZSTD_BLOCKSIZE_MAX);
    @@ -400,7 +400,7 @@ void ZSTDMT_compressChunk(void* jobDescription)
                 ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex);   /* note : it's a mtctx mutex */
                 job->cSize += cSize;
                 job->consumed = ZSTD_BLOCKSIZE_MAX * blockNb;
    -            DEBUGLOG(2, "ZSTDMT_compressChunk: compress new block : cSize==%u bytes (total: %u)",
    +            DEBUGLOG(5, "ZSTDMT_compressChunk: compress new block : cSize==%u bytes (total: %u)",
                             (U32)cSize, (U32)job->cSize);
                 ZSTD_pthread_cond_signal(job->jobCompleted_cond);
                 ZSTD_pthread_mutex_unlock(job->jobCompleted_mutex);
    @@ -458,6 +458,7 @@ struct ZSTDMT_CCtx_s {
         size_t prefixSize;
         size_t targetPrefixSize;
         inBuff_t inBuff;
    +    int jobReady;        /* 1 => one job is already prepared, but pool has shortage of workers. Don't create another one. */
         XXH64_state_t xxhState;
         unsigned singleBlockingThread;
         unsigned jobIDMask;
    @@ -668,14 +669,17 @@ unsigned ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx)
     ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
     {
         ZSTD_frameProgression fs;
    -    DEBUGLOG(5, "ZSTDMT_getFrameProgression");
    +    DEBUGLOG(6, "ZSTDMT_getFrameProgression");
         ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobCompleted_mutex);
         fs.consumed = mtctx->consumed;
         fs.produced = mtctx->produced;
         assert(mtctx->inBuff.filled >= mtctx->prefixSize);
         fs.ingested = mtctx->consumed + (mtctx->inBuff.filled - mtctx->prefixSize);
         {   unsigned jobNb;
    -        for (jobNb = mtctx->doneJobID ; jobNb < mtctx->nextJobID ; jobNb++) {
    +        unsigned lastJobNb = mtctx->nextJobID + mtctx->jobReady; assert(mtctx->jobReady <= 1);
    +        DEBUGLOG(6, "ZSTDMT_getFrameProgression: jobs: from %u to <%u (jobReady:%u)",
    +                    mtctx->doneJobID, lastJobNb, mtctx->jobReady)
    +        for (jobNb = mtctx->doneJobID ; jobNb < lastJobNb ; jobNb++) {
                 unsigned const wJobID = jobNb & mtctx->jobIDMask;
                 size_t const cResult = mtctx->jobs[wJobID].cSize;
                 size_t const produced = ZSTD_isError(cResult) ? 0 : cResult;
    @@ -999,59 +1003,60 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
     {
         unsigned const jobID = zcs->nextJobID & zcs->jobIDMask;
     
    -    DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
    -                zcs->nextJobID, (U32)srcSize, (U32)zcs->prefixSize);
    -    zcs->jobs[jobID].src = zcs->inBuff.buffer;
    -    zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start;
    -    zcs->jobs[jobID].srcSize = srcSize;
    -    zcs->jobs[jobID].consumed = 0;
    -    zcs->jobs[jobID].cSize = 0;
    -    zcs->jobs[jobID].prefixSize = zcs->prefixSize;
    -    assert(zcs->inBuff.filled >= srcSize + zcs->prefixSize);
    -    zcs->jobs[jobID].params = zcs->params;
    -    /* do not calculate checksum within sections, but write it in header for first section */
    -    if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0;
    -    zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL;
    -    zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize;
    -    zcs->jobs[jobID].dstBuff = g_nullBuffer;
    -    zcs->jobs[jobID].cctxPool = zcs->cctxPool;
    -    zcs->jobs[jobID].bufPool = zcs->bufPool;
    -    zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0);
    -    zcs->jobs[jobID].lastChunk = endFrame;
    -    zcs->jobs[jobID].jobCompleted = 0;
    -    zcs->jobs[jobID].frameChecksumNeeded = endFrame && (zcs->nextJobID>0) && zcs->params.fParams.checksumFlag;
    -    zcs->jobs[jobID].dstFlushed = 0;
    -    zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex;
    -    zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond;
    +    if (!zcs->jobReady) {
    +        DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
    +                    zcs->nextJobID, (U32)srcSize, (U32)zcs->prefixSize);
    +        zcs->jobs[jobID].src = zcs->inBuff.buffer;
    +        zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start;
    +        zcs->jobs[jobID].srcSize = srcSize;
    +        zcs->jobs[jobID].consumed = 0;
    +        zcs->jobs[jobID].cSize = 0;
    +        zcs->jobs[jobID].prefixSize = zcs->prefixSize;
    +        assert(zcs->inBuff.filled >= srcSize + zcs->prefixSize);
    +        zcs->jobs[jobID].params = zcs->params;
    +        /* do not calculate checksum within sections, but write it in header for first section */
    +        if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0;
    +        zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL;
    +        zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize;
    +        zcs->jobs[jobID].dstBuff = g_nullBuffer;
    +        zcs->jobs[jobID].cctxPool = zcs->cctxPool;
    +        zcs->jobs[jobID].bufPool = zcs->bufPool;
    +        zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0);
    +        zcs->jobs[jobID].lastChunk = endFrame;
    +        zcs->jobs[jobID].jobCompleted = 0;
    +        zcs->jobs[jobID].frameChecksumNeeded = endFrame && (zcs->nextJobID>0) && zcs->params.fParams.checksumFlag;
    +        zcs->jobs[jobID].dstFlushed = 0;
    +        zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex;
    +        zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond;
     
    -    if (zcs->params.fParams.checksumFlag)
    -        XXH64_update(&zcs->xxhState, (const char*)zcs->inBuff.buffer.start + zcs->prefixSize, srcSize);
    +        if (zcs->params.fParams.checksumFlag)
    +            XXH64_update(&zcs->xxhState, (const char*)zcs->inBuff.buffer.start + zcs->prefixSize, srcSize);
     
    -    /* get a new buffer for next input */
    -    if (!endFrame) {
    -        size_t const newPrefixSize = MIN(srcSize + zcs->prefixSize, zcs->targetPrefixSize);
    -        zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->bufPool);
    -        if (zcs->inBuff.buffer.start == NULL) {   /* not enough memory to allocate next input buffer */
    -            zcs->jobs[jobID].jobCompleted = 1;
    -            zcs->nextJobID++;
    -            ZSTDMT_waitForAllJobsCompleted(zcs);
    -            ZSTDMT_releaseAllJobResources(zcs);
    -            return ERROR(memory_allocation);
    -        }
    -        zcs->inBuff.filled -= srcSize + zcs->prefixSize - newPrefixSize;
    -        memmove(zcs->inBuff.buffer.start,
    -            (const char*)zcs->jobs[jobID].srcStart + zcs->prefixSize + srcSize - newPrefixSize,
    -            zcs->inBuff.filled);
    -        zcs->prefixSize = newPrefixSize;
    -    } else {   /* if (endFrame==1) */
    -        zcs->inBuff.buffer = g_nullBuffer;
    -        zcs->inBuff.filled = 0;
    -        zcs->prefixSize = 0;
    -        zcs->frameEnded = 1;
    -        if (zcs->nextJobID == 0) {
    -            /* single chunk exception : checksum is calculated directly within worker thread */
    -            zcs->params.fParams.checksumFlag = 0;
    -    }   }
    +        /* get a new buffer for next input */
    +        if (!endFrame) {
    +            size_t const newPrefixSize = MIN(srcSize + zcs->prefixSize, zcs->targetPrefixSize);
    +            zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->bufPool);
    +            if (zcs->inBuff.buffer.start == NULL) {   /* not enough memory to allocate next input buffer */
    +                zcs->jobs[jobID].jobCompleted = 1;
    +                zcs->nextJobID++;
    +                ZSTDMT_waitForAllJobsCompleted(zcs);
    +                ZSTDMT_releaseAllJobResources(zcs);
    +                return ERROR(memory_allocation);
    +            }
    +            zcs->inBuff.filled -= srcSize + zcs->prefixSize - newPrefixSize;
    +            memmove(zcs->inBuff.buffer.start,   /* copy end of current job into next job, as "prefix" */
    +                (const char*)zcs->jobs[jobID].srcStart + zcs->prefixSize + srcSize - newPrefixSize,
    +                zcs->inBuff.filled);
    +            zcs->prefixSize = newPrefixSize;
    +        } else {   /* endFrame==1 => no need for another input buffer */
    +            zcs->inBuff.buffer = g_nullBuffer;
    +            zcs->inBuff.filled = 0;
    +            zcs->prefixSize = 0;
    +            zcs->frameEnded = 1;
    +            if (zcs->nextJobID == 0) {
    +                /* single chunk exception : checksum is calculated directly within worker thread */
    +                zcs->params.fParams.checksumFlag = 0;
    +    }   }   }
     
         DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes  (end:%u) (note : doneJob = %u=>%u)",
                     zcs->nextJobID,
    @@ -1059,8 +1064,13 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
                     zcs->jobs[jobID].lastChunk,
                     zcs->doneJobID,
                     zcs->doneJobID & zcs->jobIDMask);
    -    POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]);   /* this call is blocking when thread worker pool is exhausted */
    -    zcs->nextJobID++;
    +    if (POOL_tryAdd(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID])) {
    +        zcs->nextJobID++;
    +        zcs->jobReady = 0;
    +    } else {
    +        DEBUGLOG(5, "ZSTDMT_createCompressionJob: no worker available for job %u", zcs->nextJobID);
    +        zcs->jobReady = 1;
    +    }
         return 0;
     }
     
    @@ -1072,16 +1082,17 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
     static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush)
     {
         unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask;
    -    DEBUGLOG(2, "ZSTDMT_flushNextJob (blocking:%u)", blockToFlush);
    +    DEBUGLOG(5, "ZSTDMT_flushNextJob (blocking:%u)", blockToFlush);
         if (zcs->doneJobID == zcs->nextJobID) {
    -        DEBUGLOG(2, "ZSTDMT_flushNextJob: doneJobID==nextJobID : nothing to flush !")
    +        DEBUGLOG(5, "ZSTDMT_flushNextJob: doneJobID(%u)==(%u)nextJobID : nothing to flush !",
    +                    zcs->doneJobID, zcs->nextJobID)
             return 0;   /* all flushed ! */
         }
         ZSTD_PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex);
         while (zcs->jobs[wJobID].dstFlushed == zcs->jobs[wJobID].cSize) {
             if (!blockToFlush) { ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex); return 0; }  /* nothing ready to be flushed => skip */
             if (zcs->jobs[wJobID].jobCompleted==1) break;
    -        DEBUGLOG(2, "waiting for something to flush from job %u (currently flushed: %u bytes)",
    +        DEBUGLOG(5, "waiting for something to flush from job %u (currently flushed: %u bytes)",
                         zcs->doneJobID, (U32)zcs->jobs[wJobID].dstFlushed);
             ZSTD_pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex);  /* block when nothing available to flush but more to come */
         }
    @@ -1108,14 +1119,16 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi
             }
             assert(job.cSize >= job.dstFlushed);
             {   size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos);
    -            DEBUGLOG(2, "ZSTDMT_flushNextJob: Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID);
    +            DEBUGLOG(5, "ZSTDMT_flushNextJob: Flushing %u bytes from job %u (completion:%.1f%%)",
    +                        (U32)toWrite, zcs->doneJobID, (double)job.consumed / job.srcSize * 100);
                 memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite);
                 output->pos += toWrite;
                 job.dstFlushed += toWrite;
             }
             if ( job.jobCompleted
               && (job.dstFlushed == job.cSize) ) {   /* output buffer fully flushed => move to next one */
    -            DEBUGLOG(2, "Job %u completed, moving to next one", zcs->doneJobID);
    +            DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one",
    +                    zcs->doneJobID, (U32)job.dstFlushed);
                 ZSTDMT_releaseBuffer(zcs->bufPool, job.dstBuff);
                 zcs->jobs[wJobID].dstBuff = g_nullBuffer;
                 zcs->jobs[wJobID].jobCompleted = 0;
    @@ -1128,6 +1141,7 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi
             /* return value : how many bytes left in buffer ; fake it to 1 when unknown but >0 */
             if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed);
             if (zcs->doneJobID < zcs->nextJobID) return 1;   /* still some more buffer to flush */
    +        if (zcs->jobReady) return 1;   /* some more work to do ! */
             zcs->allJobsCompleted = zcs->frameEnded;   /* last frame entirely flushed */
             return 0;   /* everything flushed */
     }   }
    @@ -1176,7 +1190,8 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
         }
     
         /* fill input buffer */
    -    if (input->size > input->pos) {   /* support NULL input */
    +    if ( (!mtctx->jobReady)
    +      && (input->size > input->pos) ) {   /* support NULL input */
             if (mtctx->inBuff.buffer.start == NULL) {
                 mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool);  /* note : allocation can fail, in which case, no forward input progress */
                 mtctx->inBuff.filled = 0;
    @@ -1186,34 +1201,36 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
             }   }
             if (mtctx->inBuff.buffer.start != NULL) {
                 size_t const toLoad = MIN(input->size - input->pos, mtctx->inBuffSize - mtctx->inBuff.filled);
    -            DEBUGLOG(5, "inBuff:%08X;  inBuffSize=%u;  ToCopy=%u", (U32)(size_t)mtctx->inBuff.buffer.start, (U32)mtctx->inBuffSize, (U32)toLoad);
    +            DEBUGLOG(5, "ZSTDMT_compressStream_generic: adding %u bytes on top of %u to buffer of size %u",
    +                        (U32)toLoad, (U32)mtctx->inBuff.filled, (U32)mtctx->inBuffSize);
                 memcpy((char*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, (const char*)input->src + input->pos, toLoad);
                 input->pos += toLoad;
                 mtctx->inBuff.filled += toLoad;
                 forwardInputProgress = toLoad>0;
         }   }
     
    -    if ( (mtctx->inBuff.filled >= newJobThreshold)  /* filled enough : let's compress */
    -      && (mtctx->nextJobID <= mtctx->doneJobID + mtctx->jobIDMask) ) {   /* avoid overwriting job round buffer */
    +    if ( (mtctx->jobReady)
    +      || ( (mtctx->inBuff.filled >= newJobThreshold)  /* filled enough : let's compress */
    +        && (mtctx->nextJobID <= mtctx->doneJobID + mtctx->jobIDMask) ) ) {   /* avoid overwriting job round buffer */
             CHECK_F( ZSTDMT_createCompressionJob(mtctx, mtctx->targetSectionSize, 0 /* endFrame */) );
         }
     
         /* check for potential compressed data ready to be flushed */
    -    CHECK_F( ZSTDMT_flushNextJob(mtctx, output, !forwardInputProgress /* blockToFlush */) ); /* block if there was no forward input progress */
    +    {   size_t const remainingToFlush = ZSTDMT_flushNextJob(mtctx, output, !forwardInputProgress); /* block if there was no forward input progress */
    +        if (input->pos < input->size) return MAX(remainingToFlush, 1);  /* input not consumed : do not flush yet */
    +        if (mtctx->jobReady) return remainingToFlush;   /* some more input ready to be compressed */
     
    -    if (input->pos < input->size)  /* input not consumed : do not flush yet */
    -        endOp = ZSTD_e_continue;
    -
    -    switch(endOp)
    -    {
    -        case ZSTD_e_flush:
    -            return ZSTDMT_flushStream(mtctx, output);
    -        case ZSTD_e_end:
    -            return ZSTDMT_endStream(mtctx, output);
    -        case ZSTD_e_continue:
    -            return 1;
    -        default:
    -            return ERROR(GENERIC);   /* invalid endDirective */
    +        switch(endOp)
    +        {
    +            case ZSTD_e_flush:
    +                return ZSTDMT_flushStream(mtctx, output);
    +            case ZSTD_e_end:
    +                return ZSTDMT_endStream(mtctx, output);
    +            case ZSTD_e_continue:
    +                return 1;
    +            default:
    +                return ERROR(GENERIC);   /* invalid endDirective */
    +        }
         }
     }
     
    diff --git a/programs/fileio.c b/programs/fileio.c
    index 7045a5323..6039a75a2 100644
    --- a/programs/fileio.c
    +++ b/programs/fileio.c
    @@ -792,6 +792,7 @@ static int FIO_compressFilename_internal(cRess_t ress,
             /* Fill input Buffer */
             size_t const inSize = fread(ress.srcBuffer, (size_t)1, ress.srcBufferSize, srcFile);
             ZSTD_inBuffer inBuff = { ress.srcBuffer, inSize, 0 };
    +        DISPLAYLEVEL(6, "fread %u bytes from source \n", (U32)inSize);
             readsize += inSize;
     
             if (inSize == 0 || (fileSize != UTIL_FILESIZE_UNKNOWN && readsize == fileSize))
    @@ -803,32 +804,23 @@ static int FIO_compressFilename_internal(cRess_t ress,
                 CHECK_V(result, ZSTD_compress_generic(ress.cctx, &outBuff, &inBuff, directive));
     
                 /* Write compressed stream */
    -            DISPLAYLEVEL(6, "ZSTD_compress_generic,ZSTD_e_continue: generated %u bytes \n",
    -                            (U32)outBuff.pos);
    +            DISPLAYLEVEL(6, "ZSTD_compress_generic(end:%u) => intput pos(%u)<=(%u)size ; output generated %u bytes \n",
    +                            (U32)directive, (U32)inBuff.pos, (U32)inBuff.size, (U32)outBuff.pos);
                 if (outBuff.pos) {
                     size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile);
                     if (sizeCheck!=outBuff.pos)
                         EXM_THROW(25, "Write error : cannot write compressed block into %s", dstFileName);
                     compressedfilesize += outBuff.pos;
                 }
    +            if (READY_FOR_UPDATE()) {
    +                ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx);
    +                DISPLAYUPDATE(2, "\rRead :%6u MB - Consumed :%6u MB - Compressed :%6u MB => %.2f%%",
    +                                (U32)(zfp.ingested >> 20),
    +                                (U32)(zfp.consumed >> 20),
    +                                (U32)(zfp.produced >> 20),
    +                                (double)zfp.produced / (zfp.consumed + !zfp.consumed/*avoid div0*/) * 100 );
    +            }
             }
    -#if 1
    -    if (READY_FOR_UPDATE()) {
    -        ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx);
    -        DISPLAYUPDATE(2, "\rRead :%6u MB - Consumed :%6u MB - Compressed :%6u MB => %.2f%%",
    -                        (U32)(zfp.ingested >> 20),
    -                        (U32)(zfp.consumed >> 20),
    -                        (U32)(zfp.produced >> 20),
    -                        (double)zfp.produced / (zfp.consumed + !zfp.consumed/*avoid div0*/) * 100 );
    -    }
    -#else
    -        if (fileSize == UTIL_FILESIZE_UNKNOWN) {
    -            DISPLAYUPDATE(2, "\rRead : %u MB", (U32)(readsize>>20));
    -        } else {
    -            DISPLAYUPDATE(2, "\rRead : %u / %u MB",
    -                                (U32)(readsize>>20), (U32)(fileSize>>20));
    -        }
    -#endif
         } while (directive != ZSTD_e_end);
     
     finish:
    
    From cb5eba8e206fb4a28c23809b7da949efd4603abc Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 19 Jan 2018 11:26:35 -0800
    Subject: [PATCH 072/255] add `zcat` symlink support, suggested by @wtarreau
    
    added some test
    also updated relevant doc
    
    + fixed a mistake in `lz4` symlink support :
      lz4 utility doesn't remove source files by default (like zstd, but unlike gzip).
      The symlink must behave the same.
    ---
     Makefile           | 14 ++++---
     programs/README.md | 94 +++++++++++++++++++++++++---------------------
     programs/zstdcli.c | 22 ++++++-----
     tests/.gitignore   |  3 ++
     tests/playTests.sh | 12 +++++-
     5 files changed, 86 insertions(+), 59 deletions(-)
    
    diff --git a/Makefile b/Makefile
    index d2c71ad39..47746ab1d 100644
    --- a/Makefile
    +++ b/Makefile
    @@ -72,14 +72,18 @@ zstdmt:
     zlibwrapper:
     	$(MAKE) -C $(ZWRAPDIR) test
     
    -.PHONY: check
    -check: shortest
    -
    -.PHONY: test shortest
    -test shortest:
    +.PHONY: test
    +test:
     	$(MAKE) -C $(PRGDIR) allVariants MOREFLAGS+="-g -DZSTD_DEBUG=1"
     	$(MAKE) -C $(TESTDIR) $@
     
    +.PHONY: shortest
    +shortest:
    +	$(MAKE) -C $(TESTDIR) $@
    +
    +.PHONY: check
    +check: shortest
    +
     .PHONY: examples
     examples:
     	CPPFLAGS=-I../lib LDFLAGS=-L../lib $(MAKE) -C examples/ all
    diff --git a/programs/README.md b/programs/README.md
    index 2da9a6db1..1331a684a 100644
    --- a/programs/README.md
    +++ b/programs/README.md
    @@ -11,44 +11,14 @@ There are however other Makefile targets that create different variations of CLI
     
     
     #### Compilation variables
    -`zstd` scope can be altered by modifying the following compilation variables :
    +`zstd` scope can be altered by modifying the following `make` variables :
     
     - __HAVE_THREAD__ : multithreading is automatically enabled when `pthread` is detected.
    -  It's possible to disable multithread support, by setting HAVE_THREAD=0 .
    -  Example : make zstd HAVE_THREAD=0
    -  It's also possible to force compilation with multithread support, using HAVE_THREAD=1.
    -  In which case, linking stage will fail if `pthread` library cannot be found.
    -  This might be useful to prevent silent feature disabling.
    -
    -- __HAVE_ZLIB__ : `zstd` can compress and decompress files in `.gz` format.
    -  This is ordered through command `--format=gzip`.
    -  Alternatively, symlinks named `gzip` or `gunzip` will mimic intended behavior.
    -  `.gz` support is automatically enabled when `zlib` library is detected at build time.
    -  It's possible to disable `.gz` support, by setting HAVE_ZLIB=0.
    -  Example : make zstd HAVE_ZLIB=0
    -  It's also possible to force compilation with zlib support, using HAVE_ZLIB=1.
    -  In which case, linking stage will fail if `zlib` library cannot be found.
    -  This might be useful to prevent silent feature disabling.
    -
    -- __HAVE_LZMA__ : `zstd` can compress and decompress files in `.xz` and `.lzma` formats.
    -  This is ordered through commands `--format=xz` and `--format=lzma` respectively.
    -  Alternatively, symlinks named `xz`, `unxz`, `lzma`, or `unlzma` will mimic intended behavior.
    -  `.xz` and `.lzma` support is automatically enabled when `lzma` library is detected at build time.
    -  It's possible to disable `.xz` and `.lzma` support, by setting HAVE_LZMA=0 .
    -  Example : make zstd HAVE_LZMA=0
    -  It's also possible to force compilation with lzma support, using HAVE_LZMA=1.
    -  In which case, linking stage will fail if `lzma` library cannot be found.
    -  This might be useful to prevent silent feature disabling.
    -
    -- __HAVE_LZ4__ : `zstd` can compress and decompress files in `.lz4` formats.
    -  This is ordered through commands `--format=lz4`.
    -  Alternatively, symlinks named `lz4`, or `unlz4` will mimic intended behavior.
    -  `.lz4` support is automatically enabled when `lz4` library is detected at build time.
    -  It's possible to disable `.lz4` support, by setting HAVE_LZ4=0 .
    -  Example : make zstd HAVE_LZ4=0
    -  It's also possible to force compilation with lz4 support, using HAVE_LZ4=1.
    -  In which case, linking stage will fail if `lz4` library cannot be found.
    -  This might be useful to prevent silent feature disabling.
    +  It's possible to disable multithread support, by setting `HAVE_THREAD=0`.
    +  Example : `make zstd HAVE_THREAD=0`
    +  It's also possible to force multithread support, using `HAVE_THREAD=1`.
    +  In which case, linking stage will fail if neither `pthread` nor `windows.h` library can be found.
    +  This is useful to ensure this feature is not silently disabled.
     
     - __ZSTD_LEGACY_SUPPORT__ : `zstd` can decompress files compressed by older versions of `zstd`.
       Starting v0.8.0, all versions of `zstd` produce frames compliant with the [specification](../doc/zstd_compression_format.md), and are therefore compatible.
    @@ -61,11 +31,54 @@ There are however other Makefile targets that create different variations of CLI
       if `ZSTD_LEGACY_SUPPORT >= 8`, it's the same as `0`, since there is no legacy format after `7`.
       Note : `zstd` only supports decoding older formats, and cannot generate any legacy format.
     
    +- __HAVE_ZLIB__ : `zstd` can compress and decompress files in `.gz` format.
    +  This is ordered through command `--format=gzip`.
    +  Alternatively, symlinks named `gzip` or `gunzip` will mimic intended behavior.
    +  `.gz` support is automatically enabled when `zlib` library is detected at build time.
    +  It's possible to disable `.gz` support, by setting `HAVE_ZLIB=0`.
    +  Example : `make zstd HAVE_ZLIB=0`
    +  It's also possible to force compilation with zlib support, `using HAVE_ZLIB=1`.
    +  In which case, linking stage will fail if `zlib` library cannot be found.
    +  This is useful to prevent silent feature disabling.
    +
    +- __HAVE_LZMA__ : `zstd` can compress and decompress files in `.xz` and `.lzma` formats.
    +  This is ordered through commands `--format=xz` and `--format=lzma` respectively.
    +  Alternatively, symlinks named `xz`, `unxz`, `lzma`, or `unlzma` will mimic intended behavior.
    +  `.xz` and `.lzma` support is automatically enabled when `lzma` library is detected at build time.
    +  It's possible to disable `.xz` and `.lzma` support, by setting `HAVE_LZMA=0` .
    +  Example : `make zstd HAVE_LZMA=0`
    +  It's also possible to force compilation with lzma support, using `HAVE_LZMA=1`.
    +  In which case, linking stage will fail if `lzma` library cannot be found.
    +  This is useful to prevent silent feature disabling.
    +
    +- __HAVE_LZ4__ : `zstd` can compress and decompress files in `.lz4` formats.
    +  This is ordered through commands `--format=lz4`.
    +  Alternatively, symlinks named `lz4`, or `unlz4` will mimic intended behavior.
    +  `.lz4` support is automatically enabled when `lz4` library is detected at build time.
    +  It's possible to disable `.lz4` support, by setting `HAVE_LZ4=0` .
    +  Example : `make zstd HAVE_LZ4=0`
    +  It's also possible to force compilation with lz4 support, using `HAVE_LZ4=1`.
    +  In which case, linking stage will fail if `lz4` library cannot be found.
    +  This is useful to prevent silent feature disabling.
    +
     
     #### Aggregation of parameters
     CLI supports aggregation of parameters i.e. `-b1`, `-e18`, and `-i1` can be joined into `-b1e18i1`.
     
     
    +#### Symlink shortcuts
    +It's possible to invoke `zstd` through a symlink.
    +When the name of the symlink has a specific value, it triggers an associated behavior.
    +- `zstdmt` : compress using all cores available on local system.
    +- `zcat` : will decompress and output target file using any of the supported formats. `gzcat` and `zstdcat` are also equivalent.
    +- `gzip` : if zlib support is enabled, will mimic `gzip` by compressing file using `.gz` format, removing source file by default (use `--keep` to preserve). If zlib is not supported, triggers an error.
    +- `xz` : if lzma support is enabled, will mimic `xz` by compressing file using `.xz` format, removing source file by default (use `--keep` to preserve). If xz is not supported, triggers an error.
    +- `lzma` : if lzma support is enabled, will mimic `lzma` by compressing file using `.lzma` format, removing source file by default (use `--keep` to preserve). If lzma is not supported, triggers an error.
    +- `lz4` : if lz4 support is enabled, will mimic `lz4` by compressing file using `.lz4` format. If lz4 is not supported, triggers an error.
    +- `unzstd` and `unlz4` will decompress any of the supported format.
    +- `ungz`, `unxz` and `unlzma` will do the same, and will also remove source file by default (use `--keep` to preserve).
    +
    +
     #### Dictionary builder in Command Line Interface
     Zstd offers a training mode, which can be used to tune the algorithm for a selected
     type of data, by providing it with a few samples. The result of the training is stored
    @@ -155,7 +168,7 @@ Benchmark arguments :
     #### Long distance matching mode
     The long distance matching mode, enabled with `--long`, is designed to improve
     the compression ratio for files with long matches at a large distance (up to the
    -maximum window size, `128 MiB`) while still maintaining compression speed. 
    +maximum window size, `128 MiB`) while still maintaining compression speed.
     
     Enabling this mode sets the window size to `128 MiB` and thus increases the memory
     usage for both the compressor and decompressor. Performance in terms of speed is
    @@ -168,7 +181,7 @@ decompression speed with and without long distance matching on an ideal use
     case: a tar of four versions of clang (versions `3.4.1`, `3.4.2`, `3.5.0`,
     `3.5.1`) with a total size of `244889600 B`. This is an ideal use case as there
     are many long distance matches within the maximum window size of `128 MiB` (each
    -version is less than `128 MiB`). 
    +version is less than `128 MiB`).
     
     Compression Speed vs Ratio | Decompression Speed
     ---------------------------|---------------------
    @@ -202,8 +215,3 @@ The below table illustrates this on the [Silesia compression corpus].
     | `zstd -5 --long` | `3.319` | `51.7 MB/s` | `371.9 MB/s` |
     | `zstd -10` | `3.523`    | `16.4 MB/s`   | `489.2 MB/s`  |
     | `zstd -10 --long`| `3.566` | `16.2 MB/s` | `415.7 MB/s`  |
    -
    -
    -
    -
    -
    diff --git a/programs/zstdcli.c b/programs/zstdcli.c
    index eae208c52..7beb2cb4c 100644
    --- a/programs/zstdcli.c
    +++ b/programs/zstdcli.c
    @@ -54,6 +54,7 @@
     #define ZSTD_ZSTDMT "zstdmt"
     #define ZSTD_UNZSTD "unzstd"
     #define ZSTD_CAT "zstdcat"
    +#define ZSTD_ZCAT "zcat"
     #define ZSTD_GZ "gzip"
     #define ZSTD_GUNZIP "gunzip"
     #define ZSTD_GZCAT "gzcat"
    @@ -423,16 +424,17 @@ int main(int argCount, const char* argv[])
         /* preset behaviors */
         if (exeNameMatch(programName, ZSTD_ZSTDMT)) nbThreads=0;
         if (exeNameMatch(programName, ZSTD_UNZSTD)) operation=zom_decompress;
    -    if (exeNameMatch(programName, ZSTD_CAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(); outFileName=stdoutmark; g_displayLevel=1; }
    -    if (exeNameMatch(programName, ZSTD_GZ)) { suffix = GZ_EXTENSION; FIO_setCompressionType(FIO_gzipCompression); FIO_setRemoveSrcFile(1); }    /* behave like gzip */
    -    if (exeNameMatch(programName, ZSTD_GUNZIP)) { operation=zom_decompress; FIO_setRemoveSrcFile(1); }                                          /* behave like gunzip */
    -    if (exeNameMatch(programName, ZSTD_GZCAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(); outFileName=stdoutmark; g_displayLevel=1; }  /* behave like gzcat */
    -    if (exeNameMatch(programName, ZSTD_LZMA)) { suffix = LZMA_EXTENSION; FIO_setCompressionType(FIO_lzmaCompression); FIO_setRemoveSrcFile(1); }    /* behave like lzma */
    -    if (exeNameMatch(programName, ZSTD_UNLZMA)) { operation=zom_decompress; FIO_setCompressionType(FIO_lzmaCompression); FIO_setRemoveSrcFile(1); }    /* behave like unlzma */
    -    if (exeNameMatch(programName, ZSTD_XZ)) { suffix = XZ_EXTENSION; FIO_setCompressionType(FIO_xzCompression); FIO_setRemoveSrcFile(1); }    /* behave like xz */
    -    if (exeNameMatch(programName, ZSTD_UNXZ)) { operation=zom_decompress; FIO_setCompressionType(FIO_xzCompression); FIO_setRemoveSrcFile(1); }    /* behave like unxz */
    -    if (exeNameMatch(programName, ZSTD_LZ4)) { suffix = LZ4_EXTENSION; FIO_setCompressionType(FIO_lz4Compression); FIO_setRemoveSrcFile(1); }    /* behave like xz */
    -    if (exeNameMatch(programName, ZSTD_UNLZ4)) { operation=zom_decompress; FIO_setCompressionType(FIO_lz4Compression); FIO_setRemoveSrcFile(1); }    /* behave like unxz */
    +    if (exeNameMatch(programName, ZSTD_CAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(); outFileName=stdoutmark; g_displayLevel=1; }   /* supports multiple formats */
    +    if (exeNameMatch(programName, ZSTD_ZCAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(); outFileName=stdoutmark; g_displayLevel=1; }  /* behave like zcat, also supports multiple formats */
    +    if (exeNameMatch(programName, ZSTD_GZ)) { suffix = GZ_EXTENSION; FIO_setCompressionType(FIO_gzipCompression); FIO_setRemoveSrcFile(1); }               /* behave like gzip */
    +    if (exeNameMatch(programName, ZSTD_GUNZIP)) { operation=zom_decompress; FIO_setRemoveSrcFile(1); }                                                     /* behave like gunzip, also supports multiple formats */
    +    if (exeNameMatch(programName, ZSTD_GZCAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(); outFileName=stdoutmark; g_displayLevel=1; } /* behave like gzcat, also supports multiple formats */
    +    if (exeNameMatch(programName, ZSTD_LZMA)) { suffix = LZMA_EXTENSION; FIO_setCompressionType(FIO_lzmaCompression); FIO_setRemoveSrcFile(1); }           /* behave like lzma */
    +    if (exeNameMatch(programName, ZSTD_UNLZMA)) { operation=zom_decompress; FIO_setCompressionType(FIO_lzmaCompression); FIO_setRemoveSrcFile(1); }        /* behave like unlzma, also supports multiple formats */
    +    if (exeNameMatch(programName, ZSTD_XZ)) { suffix = XZ_EXTENSION; FIO_setCompressionType(FIO_xzCompression); FIO_setRemoveSrcFile(1); }                 /* behave like xz */
    +    if (exeNameMatch(programName, ZSTD_UNXZ)) { operation=zom_decompress; FIO_setCompressionType(FIO_xzCompression); FIO_setRemoveSrcFile(1); }            /* behave like unxz, also supports multiple formats */
    +    if (exeNameMatch(programName, ZSTD_LZ4)) { suffix = LZ4_EXTENSION; FIO_setCompressionType(FIO_lz4Compression); }                                       /* behave like lz4 */
    +    if (exeNameMatch(programName, ZSTD_UNLZ4)) { operation=zom_decompress; FIO_setCompressionType(FIO_lz4Compression); }                                   /* behave like unlz4, also supports multiple formats */
         memset(&compressionParams, 0, sizeof(compressionParams));
     
         /* command switches */
    diff --git a/tests/.gitignore b/tests/.gitignore
    index a06fa20be..4911b2d62 100644
    --- a/tests/.gitignore
    +++ b/tests/.gitignore
    @@ -24,6 +24,8 @@ pool
     poolTests
     invalidDictionaries
     checkTag
    +zcat
    +zstdcat
     
     # Tmp test directory
     zstdtest
    @@ -53,6 +55,7 @@ tmp*
     *.gz
     result
     out
    +*.zstd
     
     # fuzzer
     afl
    diff --git a/tests/playTests.sh b/tests/playTests.sh
    index 1bd39545b..0e12ec8e7 100755
    --- a/tests/playTests.sh
    +++ b/tests/playTests.sh
    @@ -239,8 +239,18 @@ $ZSTD -c hello.tmp > hello.zstd --no-check
     $ZSTD -c world.tmp > world.zstd --no-check
     cat hello.zstd world.zstd > helloworld.zstd
     $ZSTD -dc helloworld.zstd > result.tmp
    -cat result.tmp
     $DIFF helloworld.tmp result.tmp
    +$ECHO "testing zstdcat symlink"
    +ln -sf $ZSTD zstdcat
    +./zstdcat helloworld.zstd > result.tmp
    +$DIFF helloworld.tmp result.tmp
    +rm zstdcat
    +rm result.tmp
    +$ECHO "testing zcat symlink"
    +ln -sf $ZSTD zcat
    +./zcat helloworld.zstd > result.tmp
    +$DIFF helloworld.tmp result.tmp
    +rm zcat
     rm ./*.tmp ./*.zstd
     $ECHO "frame concatenation tests completed"
     
    
    From dc696234539e5741ced8a1c663c3343ddaf59e10 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 19 Jan 2018 12:41:56 -0800
    Subject: [PATCH 073/255] zstdmt: fixed corruption issue in ZSTDMT_endStream()
    
    when invoked directly.
    ---
     lib/compress/zstdmt_compress.c |  2 +-
     tests/zstreamtest.c            | 14 ++++++++++----
     2 files changed, 11 insertions(+), 5 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 68f974d7b..303bc1f71 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -1249,7 +1249,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* ou
         size_t const srcSize = mtctx->inBuff.filled - mtctx->prefixSize;
         DEBUGLOG(5, "ZSTDMT_flushStream_internal");
     
    -    if ( ((srcSize > 0) || (endFrame && !mtctx->frameEnded))
    +    if ( (mtctx->jobReady || (srcSize > 0) || (endFrame && !mtctx->frameEnded))
            && (mtctx->nextJobID <= mtctx->doneJobID + mtctx->jobIDMask) ) {
                DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job");
             CHECK_F( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) );
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index c26cb3295..b21611abc 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -864,10 +864,16 @@ static size_t findDiff(const void* buf1, const void* buf2, size_t max)
             if (b1[u] != b2[u]) break;
         }
         DISPLAY("Error at position %u / %u \n", (U32)u, (U32)max);
    -    DISPLAY(" %02X %02X %02X  :%02X:  %02X %02X %02X %02X %02X \n",
    -            b1[u-3], b1[u-2], b1[u-1], b1[u-0], b1[u+1], b1[u+2], b1[u+3], b1[u+4], b1[u+5]);
    -    DISPLAY(" %02X %02X %02X  :%02X:  %02X %02X %02X %02X %02X \n",
    -            b2[u-3], b2[u-2], b2[u-1], b2[u-0], b2[u+1], b2[u+2], b2[u+3], b2[u+4], b2[u+5]);
    +    if (u>=3)
    +        DISPLAY(" %02X %02X %02X ",
    +                b1[u-3], b1[u-2], b1[u-1]);
    +    DISPLAY(" :%02X:  %02X %02X %02X %02X %02X \n",
    +            b1[u], b1[u+1], b1[u+2], b1[u+3], b1[u+4], b1[u+5]);
    +    if (u>=3)
    +        DISPLAY(" %02X %02X %02X ",
    +                b2[u-3], b2[u-2], b2[u-1]);
    +    DISPLAY(" :%02X:  %02X %02X %02X %02X %02X \n",
    +            b2[u], b2[u+1], b2[u+2], b2[u+3], b2[u+4], b2[u+5]);
         return u;
     }
     
    
    From 940634a610bc108d42571e959a51886a34f30c46 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 19 Jan 2018 13:19:59 -0800
    Subject: [PATCH 074/255] zstdmt : simplify job creation
    
    job will not be created when not enough room within job Table
    ---
     lib/compress/zstdmt_compress.c | 27 ++++++++++-----------------
     tests/zstreamtest.c            |  8 ++++++--
     2 files changed, 16 insertions(+), 19 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 303bc1f71..5b253f162 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -1003,6 +1003,10 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
     {
         unsigned const jobID = zcs->nextJobID & zcs->jobIDMask;
     
    +    unsigned const limitID = zcs->doneJobID & zcs->jobIDMask;
    +    if ((zcs->doneJobID < zcs->nextJobID) & (jobID == limitID))
    +        return 0;  /* new job would overwrite unflushed older job */
    +
         if (!zcs->jobReady) {
             DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
                         zcs->nextJobID, (U32)srcSize, (U32)zcs->prefixSize);
    @@ -1210,27 +1214,15 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
         }   }
     
         if ( (mtctx->jobReady)
    -      || ( (mtctx->inBuff.filled >= newJobThreshold)  /* filled enough : let's compress */
    -        && (mtctx->nextJobID <= mtctx->doneJobID + mtctx->jobIDMask) ) ) {   /* avoid overwriting job round buffer */
    +      || (mtctx->inBuff.filled >= newJobThreshold)  /* filled enough : let's compress */
    +      || ( (endOp != ZSTD_e_continue) && (mtctx->inBuff.filled > 0) ) ) {   /* avoid overwriting job round buffer */
             CHECK_F( ZSTDMT_createCompressionJob(mtctx, mtctx->targetSectionSize, 0 /* endFrame */) );
         }
     
         /* check for potential compressed data ready to be flushed */
         {   size_t const remainingToFlush = ZSTDMT_flushNextJob(mtctx, output, !forwardInputProgress); /* block if there was no forward input progress */
             if (input->pos < input->size) return MAX(remainingToFlush, 1);  /* input not consumed : do not flush yet */
    -        if (mtctx->jobReady) return remainingToFlush;   /* some more input ready to be compressed */
    -
    -        switch(endOp)
    -        {
    -            case ZSTD_e_flush:
    -                return ZSTDMT_flushStream(mtctx, output);
    -            case ZSTD_e_end:
    -                return ZSTDMT_endStream(mtctx, output);
    -            case ZSTD_e_continue:
    -                return 1;
    -            default:
    -                return ERROR(GENERIC);   /* invalid endDirective */
    -        }
    +        return remainingToFlush;
         }
     }
     
    @@ -1249,8 +1241,9 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* ou
         size_t const srcSize = mtctx->inBuff.filled - mtctx->prefixSize;
         DEBUGLOG(5, "ZSTDMT_flushStream_internal");
     
    -    if ( (mtctx->jobReady || (srcSize > 0) || (endFrame && !mtctx->frameEnded))
    -       && (mtctx->nextJobID <= mtctx->doneJobID + mtctx->jobIDMask) ) {
    +    if ( mtctx->jobReady     /* one job ready for a worker to pick up */
    +      || (srcSize > 0)       /* still some data within input buffer */
    +      || (endFrame && !mtctx->frameEnded)) {  /* need a last 0-size block to end frame */
                DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job");
             CHECK_F( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) );
         }
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index b21611abc..6ec14bb8e 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -863,6 +863,10 @@ static size_t findDiff(const void* buf1, const void* buf2, size_t max)
         for (u=0; u No difference detected within %u bytes \n", (U32)max);
    +        return u;
    +    }
         DISPLAY("Error at position %u / %u \n", (U32)u, (U32)max);
         if (u>=3)
             DISPLAY(" %02X %02X %02X ",
    @@ -1352,8 +1356,8 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp
                     outBuff.size = outBuff.pos + dstBuffSize;
                     DISPLAYLEVEL(6, "ZSTD_decompressStream input %u bytes \n", (U32)readCSrcSize);
                     decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff);
    -                if (ZSTD_getErrorCode(decompressionResult) == ZSTD_error_corruption_detected) {
    -                    DISPLAY("ZSTD_decompressStream: checksum error : \n");
    +                if (ZSTD_isError(decompressionResult)) {
    +                    DISPLAY("ZSTD_decompressStream error : %s \n", ZSTD_getErrorName(decompressionResult));
                         findDiff(copyBuffer, dstBuffer, totalTestSize);
                     }
                     CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult));
    
    From 3ad7d4951c9c54b9709935ad54c9fb4e81b940f2 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 19 Jan 2018 17:35:08 -0800
    Subject: [PATCH 075/255] zstdmt : finally vanquished an elusive and rare race
     condition
    
    ---
     lib/compress/zstdmt_compress.c | 97 ++++++++++++++++++----------------
     tests/zstreamtest.c            | 88 +++++++++++++++---------------
     2 files changed, 97 insertions(+), 88 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 5b253f162..3e665f574 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -1003,9 +1003,11 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
     {
         unsigned const jobID = zcs->nextJobID & zcs->jobIDMask;
     
    -    unsigned const limitID = zcs->doneJobID & zcs->jobIDMask;
    -    if ((zcs->doneJobID < zcs->nextJobID) & (jobID == limitID))
    -        return 0;  /* new job would overwrite unflushed older job */
    +    if (zcs->nextJobID > zcs->doneJobID + zcs->jobIDMask) {
    +        DEBUGLOG(5, "ZSTDMT_createCompressionJob: will not create new job : table is full");
    +        assert((zcs->nextJobID & zcs->jobIDMask) == (zcs->doneJobID & zcs->jobIDMask));
    +        return 0;
    +    }
     
         if (!zcs->jobReady) {
             DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
    @@ -1079,76 +1081,78 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
     }
     
     
    -/*! ZSTDMT_flushNextJob() :
    - * `output` : will be updated with amount of data flushed .
    +/*! ZSTDMT_flushProduced() :
    + * `output` : `pos` will be updated with amount of data flushed .
      * `blockToFlush` : if >0, the function will block and wait if there is no data available to flush .
      * @return : amount of data remaining within internal buffer, 0 if no more, 1 if unknown but > 0, or an error code */
    -static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush)
    +static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush)
     {
         unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask;
    -    DEBUGLOG(5, "ZSTDMT_flushNextJob (blocking:%u)", blockToFlush);
    -    if (zcs->doneJobID == zcs->nextJobID) {
    -        DEBUGLOG(5, "ZSTDMT_flushNextJob: doneJobID(%u)==(%u)nextJobID : nothing to flush !",
    -                    zcs->doneJobID, zcs->nextJobID)
    -        return 0;   /* all flushed ! */
    -    }
    +    DEBUGLOG(5, "ZSTDMT_flushProduced (blocking:%u)", blockToFlush);
    +    assert(output->size >= output->pos);
    +
         ZSTD_PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex);
    -    while (zcs->jobs[wJobID].dstFlushed == zcs->jobs[wJobID].cSize) {
    -        if (!blockToFlush) { ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex); return 0; }  /* nothing ready to be flushed => skip */
    -        if (zcs->jobs[wJobID].jobCompleted==1) break;
    -        DEBUGLOG(5, "waiting for something to flush from job %u (currently flushed: %u bytes)",
    -                    zcs->doneJobID, (U32)zcs->jobs[wJobID].dstFlushed);
    -        ZSTD_pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex);  /* block when nothing available to flush but more to come */
    -    }
    +    if (blockToFlush && (zcs->doneJobID < zcs->nextJobID)) {
    +        while (zcs->jobs[wJobID].dstFlushed == zcs->jobs[wJobID].cSize) {
    +            if (zcs->jobs[wJobID].jobCompleted==1) break;
    +            DEBUGLOG(5, "waiting for something to flush from job %u (currently flushed: %u bytes)",
    +                        zcs->doneJobID, (U32)zcs->jobs[wJobID].dstFlushed);
    +            ZSTD_pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex);  /* block when nothing available to flush but more to come */
    +    }   }
     
         /* some output is available to be flushed */
         {   ZSTDMT_jobDescription job = zcs->jobs[wJobID];
             ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex);
             if (ZSTD_isError(job.cSize)) {
    -            DEBUGLOG(5, "ZSTDMT_flushNextJob: job %u : compression error detected : %s",
    +            DEBUGLOG(5, "ZSTDMT_flushProduced: job %u : compression error detected : %s",
                             zcs->doneJobID, ZSTD_getErrorName(job.cSize));
                 ZSTDMT_waitForAllJobsCompleted(zcs);
                 ZSTDMT_releaseAllJobResources(zcs);
                 return job.cSize;
             }
    -        /* add frame checksum if necessary */
    +        /* add frame checksum if necessary (can only happen once) */
             if ( job.jobCompleted
               && job.frameChecksumNeeded ) {
                 U32 const checksum = (U32)XXH64_digest(&zcs->xxhState);
    -            DEBUGLOG(5, "ZSTDMT_flushNextJob: writing checksum : %08X \n", checksum);
    +            DEBUGLOG(5, "ZSTDMT_flushProduced: writing checksum : %08X \n", checksum);
                 MEM_writeLE32((char*)job.dstBuff.start + job.cSize, checksum);
                 job.cSize += 4;
                 zcs->jobs[wJobID].cSize += 4;
                 zcs->jobs[wJobID].frameChecksumNeeded = 0;
             }
             assert(job.cSize >= job.dstFlushed);
    -        {   size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos);
    -            DEBUGLOG(5, "ZSTDMT_flushNextJob: Flushing %u bytes from job %u (completion:%.1f%%)",
    +        if (job.dstBuff.start != NULL) {  /* one buffer present : some job is ongoing */
    +            size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos);
    +            DEBUGLOG(5, "ZSTDMT_flushProduced: Flushing %u bytes from job %u (completion:%.1f%%)",
                             (U32)toWrite, zcs->doneJobID, (double)job.consumed / job.srcSize * 100);
                 memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite);
                 output->pos += toWrite;
                 job.dstFlushed += toWrite;
    -        }
    -        if ( job.jobCompleted
    -          && (job.dstFlushed == job.cSize) ) {   /* output buffer fully flushed => move to next one */
    -            DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one",
    -                    zcs->doneJobID, (U32)job.dstFlushed);
    -            ZSTDMT_releaseBuffer(zcs->bufPool, job.dstBuff);
    -            zcs->jobs[wJobID].dstBuff = g_nullBuffer;
    -            zcs->jobs[wJobID].jobCompleted = 0;
    -            zcs->consumed += job.srcSize;
    -            zcs->produced += job.cSize;
    -            zcs->doneJobID++;
    -        } else {
    -            zcs->jobs[wJobID].dstFlushed = job.dstFlushed;
    -        }
    +
    +            if ( job.jobCompleted
    +              && (job.dstFlushed == job.cSize) ) {   /* output buffer fully flushed => move to next one */
    +                DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one",
    +                        zcs->doneJobID, (U32)job.dstFlushed);
    +                ZSTDMT_releaseBuffer(zcs->bufPool, job.dstBuff);
    +                zcs->jobs[wJobID].dstBuff = g_nullBuffer;
    +                zcs->jobs[wJobID].jobCompleted = 0;
    +                zcs->consumed += job.srcSize;
    +                zcs->produced += job.cSize;
    +                zcs->doneJobID++;
    +            } else {
    +                zcs->jobs[wJobID].dstFlushed = job.dstFlushed;   /* remember how much was flushed for next attempt */
    +        }   }
    +
             /* return value : how many bytes left in buffer ; fake it to 1 when unknown but >0 */
             if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed);
    -        if (zcs->doneJobID < zcs->nextJobID) return 1;   /* still some more buffer to flush */
    -        if (zcs->jobReady) return 1;   /* some more work to do ! */
    -        zcs->allJobsCompleted = zcs->frameEnded;   /* last frame entirely flushed */
    -        return 0;   /* everything flushed */
    -}   }
    +        if (job.srcSize > job.consumed) return 1;   /* current job not completely compressed */
    +    }
    +    if (zcs->doneJobID < zcs->nextJobID) return 1;   /* some more jobs to flush */
    +    if (zcs->jobReady) return 1;   /* at least one more job to do ! */
    +    if (zcs->inBuff.filled > 0) return 1;   /* input not empty */
    +    zcs->allJobsCompleted = zcs->frameEnded;   /* last frame entirely flushed */
    +    return 0;   /* everything flushed */
    +}
     
     
     /** ZSTDMT_compressStream_generic() :
    @@ -1220,7 +1224,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
         }
     
         /* check for potential compressed data ready to be flushed */
    -    {   size_t const remainingToFlush = ZSTDMT_flushNextJob(mtctx, output, !forwardInputProgress); /* block if there was no forward input progress */
    +    {   size_t const remainingToFlush = ZSTDMT_flushProduced(mtctx, output, !forwardInputProgress); /* block if there was no forward input progress */
             if (input->pos < input->size) return MAX(remainingToFlush, 1);  /* input not consumed : do not flush yet */
             return remainingToFlush;
         }
    @@ -1244,12 +1248,13 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* ou
         if ( mtctx->jobReady     /* one job ready for a worker to pick up */
           || (srcSize > 0)       /* still some data within input buffer */
           || (endFrame && !mtctx->frameEnded)) {  /* need a last 0-size block to end frame */
    -           DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job");
    +           DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job (%u bytes, end:%u)",
    +                        (U32)srcSize, endFrame);
             CHECK_F( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) );
         }
     
         /* check if there is any data available to flush */
    -    return ZSTDMT_flushNextJob(mtctx, output, 1 /* blockToFlush */);
    +    return ZSTDMT_flushProduced(mtctx, output, 1 /* blockToFlush */);
     }
     
     
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index 6ec14bb8e..8c66c53bd 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -99,8 +99,8 @@ unsigned int FUZ_rand(unsigned int* seedPtr)
         if (cond) {                                              \
             DISPLAY("Error => ");                                \
             DISPLAY(__VA_ARGS__);                                \
    -        DISPLAY(" (seed %u, test nb %u, line %u)  \n",       \
    -                seed, testNb, __LINE__);                     \
    +        DISPLAY(" (seed %u, test nb %u, line %u (sig %08X) \n", \
    +                seed, testNb, __LINE__, coreSeed);           \
             goto _output_error;                                  \
     }   }
     
    @@ -219,6 +219,7 @@ static int basicUnitTests(U32 seed, double compressibility)
         size_t cSize;
         int testResult = 0;
         U32 testNb = 1;
    +    U32 coreSeed = 0;  /* just to conform with CHECK_Z macro display */
         ZSTD_CStream* zc = ZSTD_createCStream();
         ZSTD_DStream* zd = ZSTD_createDStream();
         ZSTDMT_CCtx* mtctx = ZSTDMT_createCCtx(2);
    @@ -958,10 +959,13 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres
             size_t maxTestSize;
     
             /* init */
    -        if (nbTests >= testNb) { DISPLAYUPDATE(2, "\r%6u/%6u    ", testNb, nbTests); }
    -        else { DISPLAYUPDATE(2, "\r%6u          ", testNb); }
             FUZ_rand(&coreSeed);
             lseed = coreSeed ^ prime32;
    +        if (nbTests >= testNb) {
    +            DISPLAYUPDATE(2, "\r%6u/%6u (%08X)   ", testNb, nbTests, lseed);
    +        } else {
    +            DISPLAYUPDATE(2, "\r%6u  (%08X)        ", testNb, lseed);
    +        }
     
             /* states full reset (deliberately not synchronized) */
             /* some issues can only happen when reusing states */
    @@ -1171,7 +1175,6 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp
         UTIL_time_t const startClock = UTIL_getTime();
         const BYTE* dict=NULL;   /* can keep same dict on 2 consecutive tests */
         size_t dictSize = 0;
    -    U32 oldTestLog = 0;
         int const cLevelMax = bigTests ? (U32)ZSTD_maxCLevel()-1 : g_cLevelMax_smallTests;
         U32 const nbThreadsMax = bigTests ? 4 : 2;
     
    @@ -1193,6 +1196,7 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp
         RDG_genBuffer(cNoiseBuffer[4], srcBufferSize, 1.00, 0., coreSeed);    /* sparse content */
         memset(copyBuffer, 0x65, copyBufferSize);                             /* make copyBuffer considered initialized */
         ZSTD_initDStream_usingDict(zd, NULL, 0);  /* ensure at least one init */
    +    DISPLAYLEVEL(6, "Creating initial context with %u threads \n", nbThreads);
     
         /* catch up testNb */
         for (testNb=1; testNb < startTest; testNb++)
    @@ -1205,14 +1209,14 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp
             size_t totalTestSize, totalGenSize, cSize;
             XXH64_state_t xxhState;
             U64 crcOrig;
    -        U32 resetAllowed = 1;
             size_t maxTestSize;
     
    -        /* init */
    -        if (testNb < nbTests) {
    -            DISPLAYUPDATE(2, "\r%6u/%6u    ", testNb, nbTests);
    -        } else { DISPLAYUPDATE(2, "\r%6u          ", testNb); }
             FUZ_rand(&coreSeed);
    +        if (nbTests >= testNb) {
    +            DISPLAYUPDATE(2, "\r%6u/%6u (%08X)   ", testNb, nbTests, coreSeed);
    +        } else {
    +            DISPLAYUPDATE(2, "\r%6u  (%08X)        ", testNb, coreSeed);
    +        }
             lseed = coreSeed ^ prime32;
     
             /* states full reset (deliberately not synchronized) */
    @@ -1223,7 +1227,6 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp
                 ZSTDMT_freeCCtx(zc);
                 zc = ZSTDMT_createCCtx(nbThreads);
                 CHECK(zc==NULL, "ZSTDMT_createCCtx allocation error")
    -            resetAllowed=0;
             }
             if ((FUZ_rand(&lseed) & 0xFF) == 132) {
                 ZSTD_freeDStream(zd);
    @@ -1248,16 +1251,7 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp
             }
     
             /* compression init */
    -        if ((FUZ_rand(&lseed)&1) /* at beginning, to keep same nb of rand */
    -            && oldTestLog /* at least one test happened */ && resetAllowed) {
    -            maxTestSize = FUZ_randomLength(&lseed, oldTestLog+2);
    -            if (maxTestSize >= srcBufferSize) maxTestSize = srcBufferSize-1;
    -            {   int const compressionLevel = (FUZ_rand(&lseed) % 5) + 1;
    -                DISPLAYLEVEL(5, "Init with compression level = %i \n", compressionLevel);
    -                CHECK_Z( ZSTDMT_initCStream(zc, compressionLevel) );
    -            }
    -        } else {
    -            U32 const testLog = FUZ_rand(&lseed) % maxSrcLog;
    +        {   U32 const testLog = FUZ_rand(&lseed) % maxSrcLog;
                 U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog;
                 int const cLevelCandidate = ( FUZ_rand(&lseed)
                                 % (ZSTD_maxCLevel() - (MAX(testLog, dictLog) / 2)) )
    @@ -1266,24 +1260,29 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp
                 int const cLevelMin = MAX(cLevelThreadAdjusted, 1);  /* no negative cLevel yet */
                 int const cLevel = MIN(cLevelMin, cLevelMax);
                 maxTestSize = FUZ_rLogLength(&lseed, testLog);
    -            oldTestLog = testLog;
    -            /* random dictionary selection */
    -            dictSize  = ((FUZ_rand(&lseed)&63)==1) ? FUZ_rLogLength(&lseed, dictLog) : 0;
    -            {   size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize);
    -                dict = srcBuffer + dictStart;
    -            }
    -            {   U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? ZSTD_CONTENTSIZE_UNKNOWN : maxTestSize;
    -                ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize);
    -                DISPLAYLEVEL(5, "Init with windowLog = %u, pledgedSrcSize = %u, dictSize = %u \n",
    -                    params.cParams.windowLog, (U32)pledgedSrcSize, (U32)dictSize);
    -                params.fParams.checksumFlag = FUZ_rand(&lseed) & 1;
    -                params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1;
    -                params.fParams.contentSizeFlag = FUZ_rand(&lseed) & 1;
    -                DISPLAYLEVEL(5, "checksumFlag : %u \n", params.fParams.checksumFlag);
    -                CHECK_Z( ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_overlapSectionLog, FUZ_rand(&lseed) % 12) );
    -                CHECK_Z( ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_jobSize, FUZ_rand(&lseed) % (2*maxTestSize+1)) );   /* custome job size */
    -                CHECK_Z( ZSTDMT_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize) );
    -        }   }
    +
    +            if (FUZ_rand(&lseed)&1) {   /* simple init */
    +                int const compressionLevel = (FUZ_rand(&lseed) % 5) + 1;
    +                DISPLAYLEVEL(5, "Init with compression level = %i \n", compressionLevel);
    +                CHECK_Z( ZSTDMT_initCStream(zc, compressionLevel) );
    +            } else {   /* advanced init */
    +                /* random dictionary selection */
    +                dictSize  = ((FUZ_rand(&lseed)&63)==1) ? FUZ_rLogLength(&lseed, dictLog) : 0;
    +                {   size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize);
    +                    dict = srcBuffer + dictStart;
    +                }
    +                {   U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? ZSTD_CONTENTSIZE_UNKNOWN : maxTestSize;
    +                    ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize);
    +                    DISPLAYLEVEL(5, "Init with windowLog = %u, pledgedSrcSize = %u, dictSize = %u \n",
    +                        params.cParams.windowLog, (U32)pledgedSrcSize, (U32)dictSize);
    +                    params.fParams.checksumFlag = FUZ_rand(&lseed) & 1;
    +                    params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1;
    +                    params.fParams.contentSizeFlag = FUZ_rand(&lseed) & 1;
    +                    DISPLAYLEVEL(5, "checksumFlag : %u \n", params.fParams.checksumFlag);
    +                    CHECK_Z( ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_overlapSectionLog, FUZ_rand(&lseed) % 12) );
    +                    CHECK_Z( ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_jobSize, FUZ_rand(&lseed) % (2*maxTestSize+1)) );   /* custome job size */
    +                    CHECK_Z( ZSTDMT_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize) );
    +        }   }   }
     
             /* multi-segments compression test */
             XXH64_reset(&xxhState, 0);
    @@ -1336,10 +1335,13 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp
                 }   }
                 crcOrig = XXH64_digest(&xxhState);
                 cSize = outBuff.pos;
    -            DISPLAYLEVEL(5, "Frame completed : %u bytes \n", (U32)cSize);
    +            DISPLAYLEVEL(5, "Frame completed : %u bytes compressed into %u bytes \n",
    +                            (U32)totalTestSize, (U32)cSize);
             }
     
             /* multi - fragments decompression test */
    +        assert(totalTestSize < dstBufferSize);
    +        memset(dstBuffer, 170, totalTestSize);   /* init dest area */
             if (!dictSize /* don't reset if dictionary : could be different */ && (FUZ_rand(&lseed) & 1)) {
                 CHECK_Z( ZSTD_resetDStream(zd) );
             } else {
    @@ -1354,14 +1356,16 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp
                     size_t const dstBuffSize = MIN(dstBufferSize - totalGenSize, randomDstSize);
                     inBuff.size = inBuff.pos + readCSrcSize;
                     outBuff.size = outBuff.pos + dstBuffSize;
    -                DISPLAYLEVEL(6, "ZSTD_decompressStream input %u bytes \n", (U32)readCSrcSize);
    +                DISPLAYLEVEL(6, "ZSTD_decompressStream input %u bytes into outBuff %u bytes \n",
    +                                (U32)readCSrcSize, (U32)dstBuffSize);
                     decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff);
                     if (ZSTD_isError(decompressionResult)) {
                         DISPLAY("ZSTD_decompressStream error : %s \n", ZSTD_getErrorName(decompressionResult));
                         findDiff(copyBuffer, dstBuffer, totalTestSize);
                     }
                     CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult));
    -                DISPLAYLEVEL(6, "total ingested (inBuff.pos) = %u \n", (U32)inBuff.pos);
    +                DISPLAYLEVEL(6, "total ingested (inBuff.pos) = %u and produced (outBuff.pos) = %u \n",
    +                                (U32)inBuff.pos, (U32)outBuff.pos);
                 }
                 CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size (%u != %u)", (U32)outBuff.pos, (U32)totalTestSize);
                 CHECK (inBuff.pos != cSize, "compressed data should be fully read (%u != %u)", (U32)inBuff.pos, (U32)cSize);
    
    From a7ef3a219c1981617adfcd9c04086e67b41a123b Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 19 Jan 2018 18:19:09 -0800
    Subject: [PATCH 076/255] zstdmt : fixed last job size
    
    ---
     lib/compress/zstdmt_compress.c | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 3e665f574..c204fb84b 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -1220,7 +1220,8 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
         if ( (mtctx->jobReady)
           || (mtctx->inBuff.filled >= newJobThreshold)  /* filled enough : let's compress */
           || ( (endOp != ZSTD_e_continue) && (mtctx->inBuff.filled > 0) ) ) {   /* avoid overwriting job round buffer */
    -        CHECK_F( ZSTDMT_createCompressionJob(mtctx, mtctx->targetSectionSize, 0 /* endFrame */) );
    +        size_t const jobSize = MIN(mtctx->inBuff.filled - mtctx->prefixSize, mtctx->targetSectionSize);
    +        CHECK_F( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp==ZSTD_e_end) );
         }
     
         /* check for potential compressed data ready to be flushed */
    
    From 6711396d97f631f8513fb3b8867ccd5acc8421dd Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 19 Jan 2018 22:11:11 -0800
    Subject: [PATCH 077/255] zstreamtest : fixed test 32 : multi-thread
     compression
    
    using ZSTD_compress_generic(,,ZSTD_e_end)
    Since it already provides ZSTD_e_end as directive,
    it should not be followed by ZSTDMT_endStream().
    ---
     lib/compress/zstdmt_compress.h |  5 +++--
     tests/zstreamtest.c            | 25 ++++++++++++++++---------
     2 files changed, 19 insertions(+), 11 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h
    index 7c1e7e27b..34dfc488f 100644
    --- a/lib/compress/zstdmt_compress.h
    +++ b/lib/compress/zstdmt_compress.h
    @@ -97,11 +97,12 @@ ZSTDLIB_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter
     
     
     /*! ZSTDMT_compressStream_generic() :
    - *  Combines ZSTDMT_compressStream() with ZSTDMT_flushStream() or ZSTDMT_endStream()
    + *  Combines ZSTDMT_compressStream() with optional ZSTDMT_flushStream() or ZSTDMT_endStream()
      *  depending on flush directive.
      * @return : minimum amount of data still to be flushed
      *           0 if fully flushed
    - *           or an error code */
    + *           or an error code
    + *  note : needs to be init using any ZSTD_initCStream*() variant */
     ZSTDLIB_API size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
                                                     ZSTD_outBuffer* output,
                                                     ZSTD_inBuffer* input,
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index 8c66c53bd..250857120 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -219,7 +219,7 @@ static int basicUnitTests(U32 seed, double compressibility)
         size_t cSize;
         int testResult = 0;
         U32 testNb = 1;
    -    U32 coreSeed = 0;  /* just to conform with CHECK_Z macro display */
    +    U32 coreSeed = 0;  /* this name to conform with CHECK_Z macro display */
         ZSTD_CStream* zc = ZSTD_createCStream();
         ZSTD_DStream* zd = ZSTD_createDStream();
         ZSTDMT_CCtx* mtctx = ZSTDMT_createCCtx(2);
    @@ -373,7 +373,7 @@ static int basicUnitTests(U32 seed, double compressibility)
           DISPLAYLEVEL(3, "OK (%u bytes) \n", (U32)s);
         }
     
    -    /* Byte-by-byte decompression test */
    +    /* Decompression by small increment */
         DISPLAYLEVEL(3, "test%3i : decompress byte-by-byte : ", testNb++);
         {   /* skippable frame */
             size_t r = 1;
    @@ -383,8 +383,10 @@ static int basicUnitTests(U32 seed, double compressibility)
             inBuff.pos = 0;
             outBuff.pos = 0;
             while (r) {   /* skippable frame */
    -            inBuff.size = inBuff.pos + 1;
    -            outBuff.size = outBuff.pos + 1;
    +            size_t const inSize = FUZ_rand(&coreSeed) & 15;
    +            size_t const outSize = FUZ_rand(&coreSeed) & 15;
    +            inBuff.size = inBuff.pos + inSize;
    +            outBuff.size = outBuff.pos + outSize;
                 r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
                 if (ZSTD_isError(r)) goto _output_error;
             }
    @@ -392,8 +394,10 @@ static int basicUnitTests(U32 seed, double compressibility)
             ZSTD_initDStream_usingDict(zd, CNBuffer, dictSize);
             r=1;
             while (r) {
    -            inBuff.size = inBuff.pos + 1;
    -            outBuff.size = outBuff.pos + 1;
    +            size_t const inSize = FUZ_rand(&coreSeed) & 15;
    +            size_t const outSize = FUZ_rand(&coreSeed) & 15;
    +            inBuff.size = inBuff.pos + inSize;
    +            outBuff.size = outBuff.pos + outSize;
                 r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
                 if (ZSTD_isError(r)) goto _output_error;
             }
    @@ -695,10 +699,13 @@ static int basicUnitTests(U32 seed, double compressibility)
         inBuff.src = CNBuffer;
         inBuff.size = CNBufferSize;
         inBuff.pos = 0;
    -    CHECK_Z( ZSTDMT_compressStream_generic(mtctx, &outBuff, &inBuff, ZSTD_e_end) );
    +    {   size_t const compressResult = ZSTDMT_compressStream_generic(mtctx, &outBuff, &inBuff, ZSTD_e_end);
    +        if (compressResult != 0) goto _output_error;  /* compression must be completed in a single round */
    +    }
         if (inBuff.pos != inBuff.size) goto _output_error;   /* entire input should be consumed */
    -    { size_t const r = ZSTDMT_endStream(mtctx, &outBuff);
    -      if (r != 0) goto _output_error; }  /* error, or some data not flushed */
    +    {   size_t const compressedSize = ZSTD_findFrameCompressedSize(compressedBuffer, outBuff.pos);
    +        if (compressedSize != outBuff.pos) goto _output_error;  /* must be a full valid frame */
    +    }
         DISPLAYLEVEL(3, "OK \n");
     
         /* Complex multithreading + dictionary test */
    
    From ebd955e26a86cf5dc317126a5bbed2b5738840f5 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Tue, 23 Jan 2018 13:12:40 -0800
    Subject: [PATCH 078/255] zstdmt : fixed ending frame with 0-size block
    
    ---
     lib/compress/zstdmt_compress.c | 10 +++++++---
     tests/zstreamtest.c            | 22 +++++++++++++++-------
     2 files changed, 22 insertions(+), 10 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index c204fb84b..be683e83f 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -1166,7 +1166,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
     {
         size_t const newJobThreshold = mtctx->prefixSize + mtctx->targetSectionSize;
         unsigned forwardInputProgress = 0;
    -    DEBUGLOG(5, "ZSTDMT_compressStream_generic ");
    +    DEBUGLOG(5, "ZSTDMT_compressStream_generic (endOp=%u)", (U32)endOp);
         assert(output->pos <= output->size);
         assert(input->pos  <= input->size);
     
    @@ -1215,11 +1215,15 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
                 input->pos += toLoad;
                 mtctx->inBuff.filled += toLoad;
                 forwardInputProgress = toLoad>0;
    -    }   }
    +        }
    +        if ((input->pos < input->size) && (endOp == ZSTD_e_end))
    +            endOp = ZSTD_e_flush;   /* can't end now : not all input consumed */
    +    }
     
         if ( (mtctx->jobReady)
           || (mtctx->inBuff.filled >= newJobThreshold)  /* filled enough : let's compress */
    -      || ( (endOp != ZSTD_e_continue) && (mtctx->inBuff.filled > 0) ) ) {   /* avoid overwriting job round buffer */
    +      || ((endOp != ZSTD_e_continue) && (mtctx->inBuff.filled > 0))  /* something to flush : let's go */
    +      || ((endOp == ZSTD_e_end) && (!mtctx->frameEnded)) ) {   /* must finish the frame with a zero-size block */
             size_t const jobSize = MIN(mtctx->inBuff.filled - mtctx->prefixSize, mtctx->targetSectionSize);
             CHECK_F( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp==ZSTD_e_end) );
         }
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index 250857120..3a738c2fc 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -99,8 +99,8 @@ unsigned int FUZ_rand(unsigned int* seedPtr)
         if (cond) {                                              \
             DISPLAY("Error => ");                                \
             DISPLAY(__VA_ARGS__);                                \
    -        DISPLAY(" (seed %u, test nb %u, line %u (sig %08X) \n", \
    -                seed, testNb, __LINE__, coreSeed);           \
    +        DISPLAY(" (seed %u, test nb %u, line %u) \n",        \
    +                seed, testNb, __LINE__);                     \
             goto _output_error;                                  \
     }   }
     
    @@ -1611,7 +1611,11 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double
                     }
     
                     /* mess with frame parameters */
    -                if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_checksumFlag, FUZ_rand(&lseed) & 1, useOpaqueAPI) );
    +                if (FUZ_rand(&lseed) & 1) {
    +                    U32 const checksumFlag = FUZ_rand(&lseed) & 1;
    +                    DISPLAYLEVEL(5, "t%u: frame checksum : %u \n", testNb, checksumFlag);
    +                    CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_checksumFlag, checksumFlag, useOpaqueAPI) );
    +                }
                     if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_dictIDFlag, FUZ_rand(&lseed) & 1, useOpaqueAPI) );
                     if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_contentSizeFlag, FUZ_rand(&lseed) & 1, useOpaqueAPI) );
                     if (FUZ_rand(&lseed) & 1) {
    @@ -1676,8 +1680,8 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double
                     outBuff.size = outBuff.pos + dstBuffSize;
     
                     CHECK_Z( ZSTD_compress_generic(zc, &outBuff, &inBuff, flush) );
    -                DISPLAYLEVEL(6, "t%u: compress consumed %u bytes (total : %u) \n",
    -                    testNb, (U32)inBuff.pos, (U32)(totalTestSize + inBuff.pos));
    +                DISPLAYLEVEL(6, "t%u: compress consumed %u bytes (total : %u) ; flush: %u (total : %u) \n",
    +                    testNb, (U32)inBuff.pos, (U32)(totalTestSize + inBuff.pos), (U32)flush, (U32)outBuff.pos);
     
                     XXH64_update(&xxhState, srcBuffer+srcStart, inBuff.pos);
                     memcpy(copyBuffer+totalTestSize, srcBuffer+srcStart, inBuff.pos);
    @@ -1685,7 +1689,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double
                 }
     
                 /* final frame epilogue */
    -            {   size_t remainingToFlush = (size_t)(-1);
    +            {   size_t remainingToFlush = 1;
                     while (remainingToFlush) {
                         ZSTD_inBuffer inBuff = { NULL, 0, 0 };
                         size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog+1);
    @@ -1722,8 +1726,12 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double
                     DISPLAYLEVEL(6, "ZSTD_decompressStream input %u bytes (pos:%u/%u)\n",
                                 (U32)readCSrcSize, (U32)inBuff.pos, (U32)cSize);
                     decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff);
    +                if (ZSTD_isError(decompressionResult)) {
    +                    DISPLAY("ZSTD_decompressStream error : %s \n", ZSTD_getErrorName(decompressionResult));
    +                    findDiff(copyBuffer, dstBuffer, totalTestSize);
    +                }
                     CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult));
    -                DISPLAYLEVEL(6, "inBuff.pos = %u \n", (U32)readCSrcSize);
    +                CHECK (inBuff.pos > cSize, "ZSTD_decompressStream consumes too much input : %u > %u ", (U32)inBuff.pos, (U32)cSize);
                 }
                 CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size (%u != %u)", (U32)outBuff.pos, (U32)totalTestSize);
                 CHECK (inBuff.pos != cSize, "compressed data should be fully read (%u != %u)", (U32)inBuff.pos, (U32)cSize);
    
    From de5e38a7a60ef80f4e71838df87cea15c5e15047 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Tue, 23 Jan 2018 14:03:07 -0800
    Subject: [PATCH 079/255] zstdmt: fixed minor race condition
    
    no real consequence, but pollute tsan tests :
    
    job->dstBuff is being modified inside worker,
    while main thread might read it accidentally
    because it copies whole job.
    But since it doesn't used dstBuff, there is no real consequence.
    
    Other potential solution : only copy useful data, instead of whole job
    ---
     lib/compress/zstdmt_compress.c | 2 ++
     1 file changed, 2 insertions(+)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index be683e83f..922839810 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -344,7 +344,9 @@ void ZSTDMT_compressChunk(void* jobDescription)
                 job->cSize = ERROR(memory_allocation);
                 goto _endJob;
             }
    +        ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex);   /* note : it's a mtctx mutex */
             job->dstBuff = dstBuff;
    +        ZSTD_pthread_mutex_unlock(job->jobCompleted_mutex);
         }
     
         /* init */
    
    From c1cc57f270e07caf0a8356be921511fedf3a6c8f Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Tue, 23 Jan 2018 15:19:11 -0800
    Subject: [PATCH 080/255] zstdmt : fix end condition (ZSTD_e_end)
    
    When ZSTD_e_end directive is provided,
    the question is not only "are internal buffers completely flushed",
    it is also "is current frame completed".
    
    In some rare cases,
    it was possible for internal buffers to be completely flushed,
    triggering a @return == 0,
    but frame was not completed as it needed a last null-size block to mark the end,
    resulting in an unfinished frame.
    ---
     lib/compress/zstdmt_compress.c | 21 +++++++++++----------
     lib/zstd.h                     |  5 +++--
     tests/zstreamtest.c            |  3 ++-
     3 files changed, 16 insertions(+), 13 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 922839810..448a3e73f 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -1087,7 +1087,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
      * `output` : `pos` will be updated with amount of data flushed .
      * `blockToFlush` : if >0, the function will block and wait if there is no data available to flush .
      * @return : amount of data remaining within internal buffer, 0 if no more, 1 if unknown but > 0, or an error code */
    -static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush)
    +static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush, ZSTD_EndDirective end)
     {
         unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask;
         DEBUGLOG(5, "ZSTDMT_flushProduced (blocking:%u)", blockToFlush);
    @@ -1116,7 +1116,7 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, uns
             if ( job.jobCompleted
               && job.frameChecksumNeeded ) {
                 U32 const checksum = (U32)XXH64_digest(&zcs->xxhState);
    -            DEBUGLOG(5, "ZSTDMT_flushProduced: writing checksum : %08X \n", checksum);
    +            DEBUGLOG(4, "ZSTDMT_flushProduced: writing checksum : %08X \n", checksum);
                 MEM_writeLE32((char*)job.dstBuff.start + job.cSize, checksum);
                 job.cSize += 4;
                 zcs->jobs[wJobID].cSize += 4;
    @@ -1150,9 +1150,10 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, uns
             if (job.srcSize > job.consumed) return 1;   /* current job not completely compressed */
         }
         if (zcs->doneJobID < zcs->nextJobID) return 1;   /* some more jobs to flush */
    -    if (zcs->jobReady) return 1;   /* at least one more job to do ! */
    +    if (zcs->jobReady) return 1;   /* one job is ready and queued! */
         if (zcs->inBuff.filled > 0) return 1;   /* input not empty */
         zcs->allJobsCompleted = zcs->frameEnded;   /* last frame entirely flushed */
    +    if (end == ZSTD_e_end) return !zcs->frameEnded;  /* for ZSTD_e_end, question becomes : is frame completed ? It may need a last null-block */
         return 0;   /* everything flushed */
     }
     
    @@ -1231,7 +1232,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
         }
     
         /* check for potential compressed data ready to be flushed */
    -    {   size_t const remainingToFlush = ZSTDMT_flushProduced(mtctx, output, !forwardInputProgress); /* block if there was no forward input progress */
    +    {   size_t const remainingToFlush = ZSTDMT_flushProduced(mtctx, output, !forwardInputProgress, endOp); /* block if there was no forward input progress */
             if (input->pos < input->size) return MAX(remainingToFlush, 1);  /* input not consumed : do not flush yet */
             return remainingToFlush;
         }
    @@ -1247,21 +1248,21 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu
     }
     
     
    -static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, unsigned endFrame)
    +static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_EndDirective endFrame)
     {
         size_t const srcSize = mtctx->inBuff.filled - mtctx->prefixSize;
         DEBUGLOG(5, "ZSTDMT_flushStream_internal");
     
         if ( mtctx->jobReady     /* one job ready for a worker to pick up */
           || (srcSize > 0)       /* still some data within input buffer */
    -      || (endFrame && !mtctx->frameEnded)) {  /* need a last 0-size block to end frame */
    +      || ((endFrame==ZSTD_e_end) && !mtctx->frameEnded)) {  /* need a last 0-size block to end frame */
                DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job (%u bytes, end:%u)",
    -                        (U32)srcSize, endFrame);
    +                        (U32)srcSize, (U32)endFrame);
             CHECK_F( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) );
         }
     
         /* check if there is any data available to flush */
    -    return ZSTDMT_flushProduced(mtctx, output, 1 /* blockToFlush */);
    +    return ZSTDMT_flushProduced(mtctx, output, 1 /* blockToFlush */, endFrame);
     }
     
     
    @@ -1270,7 +1271,7 @@ size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output)
         DEBUGLOG(5, "ZSTDMT_flushStream");
         if (mtctx->singleBlockingThread)
             return ZSTD_flushStream(mtctx->cctxPool->cctx[0], output);
    -    return ZSTDMT_flushStream_internal(mtctx, output, 0 /* endFrame */);
    +    return ZSTDMT_flushStream_internal(mtctx, output, ZSTD_e_flush);
     }
     
     size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output)
    @@ -1278,5 +1279,5 @@ size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output)
         DEBUGLOG(4, "ZSTDMT_endStream");
         if (mtctx->singleBlockingThread)
             return ZSTD_endStream(mtctx->cctxPool->cctx[0], output);
    -    return ZSTDMT_flushStream_internal(mtctx, output, 1 /* endFrame */);
    +    return ZSTDMT_flushStream_internal(mtctx, output, ZSTD_e_end);
     }
    diff --git a/lib/zstd.h b/lib/zstd.h
    index aab6be3b0..da9d295cd 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -1136,10 +1136,11 @@ typedef enum {
      *                                                     and then immediately returns, just indicating that there is some data remaining to be flushed.
      *                                                     The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte.
      *  - Exception : in multi-threading mode, if the first call requests a ZSTD_e_end directive, it is blocking : it will complete compression before giving back control to caller.
    - *  - @return provides the minimum amount of data remaining to be flushed from internal buffers
    + *  - @return provides a minimum amount of data remaining to be flushed from internal buffers
      *            or an error code, which can be tested using ZSTD_isError().
      *            if @return != 0, flush is not fully completed, there is still some data left within internal buffers.
    - *            This is useful to determine if a ZSTD_e_flush or ZSTD_e_end directive is completed.
    + *            This is useful for ZSTD_e_flush, since in this case more flushes are necessary to empty all buffers.
    + *            For ZSTD_e_end, @return == 0 when internal buffers are fully flushed and frame is completed.
      *  - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0),
      *            only ZSTD_e_end or ZSTD_e_flush operations are allowed.
      *            Before starting a new compression job, or changing compression parameters,
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index 3a738c2fc..762224d53 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -1695,8 +1695,9 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double
                         size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog+1);
                         size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize);
                         outBuff.size = outBuff.pos + adjustedDstSize;
    -                    DISPLAYLEVEL(6, "End-flush into dst buffer of size %u \n", (U32)adjustedDstSize);
    +                    DISPLAYLEVEL(6, "t%u: End-flush into dst buffer of size %u \n", testNb, (U32)adjustedDstSize);
                         remainingToFlush = ZSTD_compress_generic(zc, &outBuff, &inBuff, ZSTD_e_end);
    +                    DISPLAYLEVEL(6, "t%u: Total flushed so far : %u bytes \n", testNb, (U32)outBuff.pos);
                         CHECK( ZSTD_isError(remainingToFlush),
                               "ZSTD_compress_generic w/ ZSTD_e_end error : %s",
                                ZSTD_getErrorName(remainingToFlush) );
    
    From 5f349b129c118b65fdaf60f3f87f84669040680c Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Tue, 23 Jan 2018 15:52:40 -0800
    Subject: [PATCH 081/255] zstdmt : correctly set end of frame
    
    ---
     lib/compress/zstdmt_compress.c | 7 ++++---
     lib/zstd.h                     | 2 +-
     2 files changed, 5 insertions(+), 4 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 448a3e73f..0207920dc 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -1001,9 +1001,10 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) {
     }
     
     
    -static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsigned endFrame)
    +static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, ZSTD_EndDirective endOp)
     {
         unsigned const jobID = zcs->nextJobID & zcs->jobIDMask;
    +    int const endFrame = (endOp == ZSTD_e_end);
     
         if (zcs->nextJobID > zcs->doneJobID + zcs->jobIDMask) {
             DEBUGLOG(5, "ZSTDMT_createCompressionJob: will not create new job : table is full");
    @@ -1060,7 +1061,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
                 zcs->inBuff.buffer = g_nullBuffer;
                 zcs->inBuff.filled = 0;
                 zcs->prefixSize = 0;
    -            zcs->frameEnded = 1;
    +            zcs->frameEnded = endFrame;
                 if (zcs->nextJobID == 0) {
                     /* single chunk exception : checksum is calculated directly within worker thread */
                     zcs->params.fParams.checksumFlag = 0;
    @@ -1228,7 +1229,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
           || ((endOp != ZSTD_e_continue) && (mtctx->inBuff.filled > 0))  /* something to flush : let's go */
           || ((endOp == ZSTD_e_end) && (!mtctx->frameEnded)) ) {   /* must finish the frame with a zero-size block */
             size_t const jobSize = MIN(mtctx->inBuff.filled - mtctx->prefixSize, mtctx->targetSectionSize);
    -        CHECK_F( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp==ZSTD_e_end) );
    +        CHECK_F( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp) );
         }
     
         /* check for potential compressed data ready to be flushed */
    diff --git a/lib/zstd.h b/lib/zstd.h
    index da9d295cd..e573daf5b 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -1120,7 +1120,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* pre
     
     
     typedef enum {
    -    ZSTD_e_continue=0, /* collect more data, encoder transparently decides when to output result, for optimal conditions */
    +    ZSTD_e_continue=0, /* collect more data, encoder decides when to output compressed result, for optimal conditions */
         ZSTD_e_flush,      /* flush any data provided so far - frame will continue, future data can still reference previous data for better compression */
         ZSTD_e_end         /* flush any remaining data and close current frame. Any additional data starts a new frame. */
     } ZSTD_EndDirective;
    
    From 4f7c896113e39557257c73cd38bf9362fc095e72 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Tue, 23 Jan 2018 18:00:51 -0800
    Subject: [PATCH 082/255] zstdmt : fixed complex sequencing bug
    
    zstdmt would shortcut to single-thread blocking mode
    in some rare cases where data is sent to be compressed but is not yet ready.
    ---
     tests/zstreamtest.c | 8 +++++---
     1 file changed, 5 insertions(+), 3 deletions(-)
    
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index 762224d53..ef5cf65fb 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -1724,9 +1724,11 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double
                     size_t const dstBuffSize = MIN(dstBufferSize - totalGenSize, randomDstSize);
                     inBuff.size = inBuff.pos + readCSrcSize;
                     outBuff.size = outBuff.pos + dstBuffSize;
    -                DISPLAYLEVEL(6, "ZSTD_decompressStream input %u bytes (pos:%u/%u)\n",
    -                            (U32)readCSrcSize, (U32)inBuff.pos, (U32)cSize);
    +                DISPLAYLEVEL(6, "decompression presented %u new bytes (pos:%u/%u)\n",
    +                                (U32)readCSrcSize, (U32)inBuff.pos, (U32)cSize);
                     decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff);
    +                DISPLAYLEVEL(6, "so far: consumed = %u, produced = %u \n",
    +                                (U32)inBuff.pos, (U32)outBuff.pos);
                     if (ZSTD_isError(decompressionResult)) {
                         DISPLAY("ZSTD_decompressStream error : %s \n", ZSTD_getErrorName(decompressionResult));
                         findDiff(copyBuffer, dstBuffer, totalTestSize);
    @@ -1734,8 +1736,8 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double
                     CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult));
                     CHECK (inBuff.pos > cSize, "ZSTD_decompressStream consumes too much input : %u > %u ", (U32)inBuff.pos, (U32)cSize);
                 }
    -            CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size (%u != %u)", (U32)outBuff.pos, (U32)totalTestSize);
                 CHECK (inBuff.pos != cSize, "compressed data should be fully read (%u != %u)", (U32)inBuff.pos, (U32)cSize);
    +            CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size (%u != %u)", (U32)outBuff.pos, (U32)totalTestSize);
                 {   U64 const crcDest = XXH64(dstBuffer, totalTestSize, 0);
                     if (crcDest!=crcOrig) findDiff(copyBuffer, dstBuffer, totalTestSize);
                     CHECK (crcDest!=crcOrig, "decompressed data corrupted");
    
    From 50aea2f293ae3f1fff5cb918e36dbf812cffa9e2 Mon Sep 17 00:00:00 2001
    From: ne-sted <33594142+ne-sted@users.noreply.github.com>
    Date: Wed, 24 Jan 2018 14:59:44 +0200
    Subject: [PATCH 083/255] cli: fix align of defaults
    
    ---
     programs/README.md | 12 ++++++------
     programs/zstd.1    |  4 ++--
     programs/zstd.1.md |  6 +++---
     programs/zstdcli.c | 20 ++++++++++----------
     4 files changed, 21 insertions(+), 21 deletions(-)
    
    diff --git a/programs/README.md b/programs/README.md
    index 1331a684a..a308fccf9 100644
    --- a/programs/README.md
    +++ b/programs/README.md
    @@ -120,7 +120,7 @@ Usage :
     FILE    : a filename
               with no FILE, or when FILE is - , read standard input
     Arguments :
    - -#     : # compression level (1-19, default:3)
    + -#     : # compression level (1-19, default: 3)
      -d     : decompression
      -D file: use `file` as Dictionary
      -o file: result stored into `file` (only if 1 input file)
    @@ -138,13 +138,13 @@ Advanced arguments :
     --ultra : enable levels beyond 19, up to 22 (requires more memory)
     --long  : enable long distance matching (requires more memory)
     --no-dictID : don't write dictID into header (dictionary compression)
    ---[no-]check : integrity check (default:enabled)
    +--[no-]check : integrity check (default: enabled)
      -r     : operate recursively on directories
     --format=gzip : compress files to the .gz format
     --format=xz : compress files to the .xz format
     --format=lzma : compress files to the .lzma format
     --test  : test compressed file integrity
    ---[no-]sparse : sparse mode (default:disabled)
    +--[no-]sparse : sparse mode (default: disabled)
      -M#    : Set a memory usage limit for decompression
     --      : All arguments after "--" are treated as files
     
    @@ -153,13 +153,13 @@ Dictionary builder :
     --train-cover[=k=#,d=#,steps=#] : use the cover algorithm with optional args
     --train-legacy[=s=#] : use the legacy algorithm with selectivity (default: 9)
      -o file : `file` is dictionary name (default: dictionary)
    ---maxdict=# : limit dictionary to specified size (default : 112640)
    +--maxdict=# : limit dictionary to specified size (default: 112640)
     --dictID=# : force dictionary ID to specified value (default: random)
     
     Benchmark arguments :
    - -b#    : benchmark file(s), using # compression level (default : 1)
    + -b#    : benchmark file(s), using # compression level (default: 3)
      -e#    : test all compression levels from -bX to # (default: 1)
    - -i#    : minimum evaluation time in seconds (default : 3s)
    + -i#    : minimum evaluation time in seconds (default: 3s)
      -B#    : cut file into independent blocks of size # (default: no block)
     --priority=rt : set process priority to real-time
     ```
    diff --git a/programs/zstd.1 b/programs/zstd.1
    index e0902e858..d253124fd 100644
    --- a/programs/zstd.1
    +++ b/programs/zstd.1
    @@ -172,7 +172,7 @@ suppress warnings, interactivity, and notifications\. specify twice to suppress
     .
     .TP
     \fB\-C\fR, \fB\-\-[no\-]check\fR
    -add integrity check computed from uncompressed data (default : enabled)
    +add integrity check computed from uncompressed data (default: enabled)
     .
     .TP
     \fB\-\-\fR
    @@ -251,7 +251,7 @@ benchmark file(s) using multiple compression levels, from \fB\-b#\fR to \fB\-e#\
     .
     .TP
     \fB\-i#\fR
    -minimum evaluation time, in seconds (default : 3s), benchmark mode only
    +minimum evaluation time, in seconds (default: 3s), benchmark mode only
     .
     .TP
     \fB\-B#\fR, \fB\-\-block\-size=#\fR
    diff --git a/programs/zstd.1.md b/programs/zstd.1.md
    index 9e4c5d305..c5ff53567 100644
    --- a/programs/zstd.1.md
    +++ b/programs/zstd.1.md
    @@ -137,7 +137,7 @@ the last one takes effect.
         to make files with many zeroes smaller on disk.
         Creating sparse files may save disk space and speed up decompression by
         reducing the amount of disk I/O.
    -    default : enabled when output is into a file,
    +    default: enabled when output is into a file,
         and disabled when output is stdout.
         This setting overrides default and can force sparse mode over stdout.
     * `--rm`:
    @@ -163,7 +163,7 @@ the last one takes effect.
         suppress warnings, interactivity, and notifications.
         specify twice to suppress errors too.
     * `-C`, `--[no-]check`:
    -    add integrity check computed from uncompressed data (default : enabled)
    +    add integrity check computed from uncompressed data (default: enabled)
     * `--`:
         All arguments after `--` are treated as files
     
    @@ -252,7 +252,7 @@ BENCHMARK
     * `-e#`:
         benchmark file(s) using multiple compression levels, from `-b#` to `-e#` (inclusive)
     * `-i#`:
    -    minimum evaluation time, in seconds (default : 3s), benchmark mode only
    +    minimum evaluation time, in seconds (default: 3s), benchmark mode only
     * `-B#`, `--block-size=#`:
         cut file(s) into independent blocks of size # (default: no block)
     * `--priority=rt`:
    diff --git a/programs/zstdcli.c b/programs/zstdcli.c
    index 7beb2cb4c..c80c36acf 100644
    --- a/programs/zstdcli.c
    +++ b/programs/zstdcli.c
    @@ -106,7 +106,7 @@ static int usage(const char* programName)
         DISPLAY( "          with no FILE, or when FILE is - , read standard input\n");
         DISPLAY( "Arguments : \n");
     #ifndef ZSTD_NOCOMPRESS
    -    DISPLAY( " -#     : # compression level (1-%d, default:%d) \n", ZSTDCLI_CLEVEL_MAX, ZSTDCLI_CLEVEL_DEFAULT);
    +    DISPLAY( " -#     : # compression level (1-%d, default: %d) \n", ZSTDCLI_CLEVEL_MAX, ZSTDCLI_CLEVEL_DEFAULT);
     #endif
     #ifndef ZSTD_NODECOMPRESS
         DISPLAY( " -d     : decompression \n");
    @@ -133,13 +133,13 @@ static int usage_advanced(const char* programName)
         DISPLAY( " -l     : print information about zstd compressed files \n");
     #ifndef ZSTD_NOCOMPRESS
         DISPLAY( "--ultra : enable levels beyond %i, up to %i (requires more memory)\n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel());
    -    DISPLAY( "--long[=#]  : enable long distance matching with given window log (default : %u)\n", g_defaultMaxWindowLog);
    +    DISPLAY( "--long[=#]  : enable long distance matching with given window log (default: %u)\n", g_defaultMaxWindowLog);
     #ifdef ZSTD_MULTITHREAD
    -    DISPLAY( " -T#    : use # threads for compression (default:1) \n");
    -    DISPLAY( " -B#    : select size of each job (default:0==automatic) \n");
    +    DISPLAY( " -T#    : use # threads for compression (default: 1) \n");
    +    DISPLAY( " -B#    : select size of each job (default: 0==automatic) \n");
     #endif
         DISPLAY( "--no-dictID : don't write dictID into header (dictionary compression)\n");
    -    DISPLAY( "--[no-]check : integrity check (default:enabled) \n");
    +    DISPLAY( "--[no-]check : integrity check (default: enabled) \n");
     #endif
     #ifdef UTIL_HAS_CREATEFILELIST
         DISPLAY( " -r     : operate recursively on directories \n");
    @@ -157,9 +157,9 @@ static int usage_advanced(const char* programName)
     #ifndef ZSTD_NODECOMPRESS
         DISPLAY( "--test  : test compressed file integrity \n");
     #if ZSTD_SPARSE_DEFAULT
    -    DISPLAY( "--[no-]sparse : sparse mode (default:enabled on file, disabled on stdout)\n");
    +    DISPLAY( "--[no-]sparse : sparse mode (default: enabled on file, disabled on stdout)\n");
     #else
    -    DISPLAY( "--[no-]sparse : sparse mode (default:disabled)\n");
    +    DISPLAY( "--[no-]sparse : sparse mode (default: disabled)\n");
     #endif
     #endif
         DISPLAY( " -M#    : Set a memory usage limit for decompression \n");
    @@ -171,15 +171,15 @@ static int usage_advanced(const char* programName)
         DISPLAY( "--train-cover[=k=#,d=#,steps=#] : use the cover algorithm with optional args\n");
         DISPLAY( "--train-legacy[=s=#] : use the legacy algorithm with selectivity (default: %u)\n", g_defaultSelectivityLevel);
         DISPLAY( " -o file : `file` is dictionary name (default: %s) \n", g_defaultDictName);
    -    DISPLAY( "--maxdict=# : limit dictionary to specified size (default : %u) \n", g_defaultMaxDictSize);
    +    DISPLAY( "--maxdict=# : limit dictionary to specified size (default: %u) \n", g_defaultMaxDictSize);
         DISPLAY( "--dictID=# : force dictionary ID to specified value (default: random)\n");
     #endif
     #ifndef ZSTD_NOBENCH
         DISPLAY( "\n");
         DISPLAY( "Benchmark arguments : \n");
    -    DISPLAY( " -b#    : benchmark file(s), using # compression level (default : 1) \n");
    +    DISPLAY( " -b#    : benchmark file(s), using # compression level (default: %d) \n", ZSTDCLI_CLEVEL_DEFAULT);
         DISPLAY( " -e#    : test all compression levels from -bX to # (default: 1)\n");
    -    DISPLAY( " -i#    : minimum evaluation time in seconds (default : 3s) \n");
    +    DISPLAY( " -i#    : minimum evaluation time in seconds (default: 3s) \n");
         DISPLAY( " -B#    : cut file into independent blocks of size # (default: no block)\n");
         DISPLAY( "--priority=rt : set process priority to real-time \n");
     #endif
    
    From 1272d8e760eb8d6ad42cbc2fe1ec898a26b815f5 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 25 Jan 2018 14:52:34 -0800
    Subject: [PATCH 084/255] zstdmt:: renamed mutex and cond to underline they are
     context-global
    
    ---
     lib/compress/zstdmt_compress.c | 73 +++++++++++++++++-----------------
     1 file changed, 37 insertions(+), 36 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 0207920dc..bc9209d5b 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -316,8 +316,8 @@ typedef struct {
         unsigned lastChunk;
         unsigned jobCompleted;
         unsigned frameChecksumNeeded;
    -    ZSTD_pthread_mutex_t* jobCompleted_mutex;
    -    ZSTD_pthread_cond_t* jobCompleted_cond;
    +    ZSTD_pthread_mutex_t* mtctx_mutex;
    +    ZSTD_pthread_cond_t* mtctx_cond;
         ZSTD_CCtx_params params;
         const ZSTD_CDict* cdict;
         ZSTDMT_CCtxPool* cctxPool;
    @@ -344,9 +344,9 @@ void ZSTDMT_compressChunk(void* jobDescription)
                 job->cSize = ERROR(memory_allocation);
                 goto _endJob;
             }
    -        ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex);   /* note : it's a mtctx mutex */
    +        ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);
             job->dstBuff = dstBuff;
    -        ZSTD_pthread_mutex_unlock(job->jobCompleted_mutex);
    +        ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
         }
     
         /* init */
    @@ -399,13 +399,13 @@ void ZSTDMT_compressChunk(void* jobDescription)
                 ip += ZSTD_BLOCKSIZE_MAX;
                 op += cSize; assert(op < oend);
                 /* stats */
    -            ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex);   /* note : it's a mtctx mutex */
    +            ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);   /* note : it's a mtctx mutex */
                 job->cSize += cSize;
                 job->consumed = ZSTD_BLOCKSIZE_MAX * blockNb;
                 DEBUGLOG(5, "ZSTDMT_compressChunk: compress new block : cSize==%u bytes (total: %u)",
                             (U32)cSize, (U32)job->cSize);
    -            ZSTD_pthread_cond_signal(job->jobCompleted_cond);
    -            ZSTD_pthread_mutex_unlock(job->jobCompleted_mutex);
    +            ZSTD_pthread_cond_signal(job->mtctx_cond);
    +            ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
             }
             /* last block */
             if ((nbBlocks > 0) | job->lastChunk /*must output a "last block" flag*/ ) {
    @@ -416,10 +416,10 @@ void ZSTDMT_compressChunk(void* jobDescription)
                      ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize);
                 if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; }
                 /* stats */
    -            ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex);   /* note : it's a mtctx mutex */
    +            ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);   /* note : it's a mtctx mutex */
                 job->cSize += cSize;
                 job->consumed = job->srcSize;
    -            ZSTD_pthread_mutex_unlock(job->jobCompleted_mutex);
    +            ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
             }
         }
     #endif
    @@ -430,11 +430,11 @@ _endJob:
         ZSTDMT_releaseBuffer(job->bufPool, job->src);
         job->src = g_nullBuffer; job->srcStart = NULL;
         /* report */
    -    ZSTD_PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex);
    +    ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);
         job->consumed = job->srcSize;
         job->jobCompleted = 1;
    -    ZSTD_pthread_cond_signal(job->jobCompleted_cond);
    -    ZSTD_pthread_mutex_unlock(job->jobCompleted_mutex);
    +    ZSTD_pthread_cond_signal(job->mtctx_cond);
    +    ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
     }
     
     
    @@ -452,8 +452,8 @@ struct ZSTDMT_CCtx_s {
         ZSTDMT_jobDescription* jobs;
         ZSTDMT_bufferPool* bufPool;
         ZSTDMT_CCtxPool* cctxPool;
    -    ZSTD_pthread_mutex_t jobCompleted_mutex;
    -    ZSTD_pthread_cond_t jobCompleted_cond;
    +    ZSTD_pthread_mutex_t mtctx_mutex;
    +    ZSTD_pthread_cond_t mtctx_cond;
         ZSTD_CCtx_params params;
         size_t targetSectionSize;
         size_t inBuffSize;
    @@ -538,11 +538,11 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem)
             ZSTDMT_freeCCtx(mtctx);
             return NULL;
         }
    -    if (ZSTD_pthread_mutex_init(&mtctx->jobCompleted_mutex, NULL)) {
    +    if (ZSTD_pthread_mutex_init(&mtctx->mtctx_mutex, NULL)) {
             ZSTDMT_freeCCtx(mtctx);
             return NULL;
         }
    -    if (ZSTD_pthread_cond_init(&mtctx->jobCompleted_cond, NULL)) {
    +    if (ZSTD_pthread_cond_init(&mtctx->mtctx_cond, NULL)) {
             ZSTDMT_freeCCtx(mtctx);
             return NULL;
         }
    @@ -582,12 +582,12 @@ static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* zcs)
         DEBUGLOG(4, "ZSTDMT_waitForAllJobsCompleted");
         while (zcs->doneJobID < zcs->nextJobID) {
             unsigned const jobID = zcs->doneJobID & zcs->jobIDMask;
    -        ZSTD_PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex);
    +        ZSTD_PTHREAD_MUTEX_LOCK(&zcs->mtctx_mutex);
             while (zcs->jobs[jobID].jobCompleted==0) {
                 DEBUGLOG(5, "waiting for jobCompleted signal from chunk %u", zcs->doneJobID);   /* we want to block when waiting for data to flush */
    -            ZSTD_pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex);
    +            ZSTD_pthread_cond_wait(&zcs->mtctx_cond, &zcs->mtctx_mutex);
             }
    -        ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex);
    +        ZSTD_pthread_mutex_unlock(&zcs->mtctx_mutex);
             zcs->doneJobID++;
         }
     }
    @@ -601,8 +601,8 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx)
         ZSTDMT_freeBufferPool(mtctx->bufPool);
         ZSTDMT_freeCCtxPool(mtctx->cctxPool);
         ZSTD_freeCDict(mtctx->cdictLocal);
    -    ZSTD_pthread_mutex_destroy(&mtctx->jobCompleted_mutex);
    -    ZSTD_pthread_cond_destroy(&mtctx->jobCompleted_cond);
    +    ZSTD_pthread_mutex_destroy(&mtctx->mtctx_mutex);
    +    ZSTD_pthread_cond_destroy(&mtctx->mtctx_cond);
         ZSTD_free(mtctx, mtctx->cMem);
         return 0;
     }
    @@ -672,7 +672,7 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
     {
         ZSTD_frameProgression fs;
         DEBUGLOG(6, "ZSTDMT_getFrameProgression");
    -    ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobCompleted_mutex);
    +    ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->mtctx_mutex);
         fs.consumed = mtctx->consumed;
         fs.produced = mtctx->produced;
         assert(mtctx->inBuff.filled >= mtctx->prefixSize);
    @@ -690,7 +690,7 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
                 fs.produced += produced;
             }
         }
    -    ZSTD_pthread_mutex_unlock(&mtctx->jobCompleted_mutex);
    +    ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
         return fs;
     }
     
    @@ -783,8 +783,8 @@ static size_t ZSTDMT_compress_advanced_internal(
                 mtctx->jobs[u].firstChunk = (u==0);
                 mtctx->jobs[u].lastChunk = (u==nbChunks-1);
                 mtctx->jobs[u].jobCompleted = 0;
    -            mtctx->jobs[u].jobCompleted_mutex = &mtctx->jobCompleted_mutex;
    -            mtctx->jobs[u].jobCompleted_cond = &mtctx->jobCompleted_cond;
    +            mtctx->jobs[u].mtctx_mutex = &mtctx->mtctx_mutex;
    +            mtctx->jobs[u].mtctx_cond = &mtctx->mtctx_cond;
     
                 if (params.fParams.checksumFlag) {
                     XXH64_update(&xxh64, srcStart + frameStartPos, chunkSize);
    @@ -804,12 +804,12 @@ static size_t ZSTDMT_compress_advanced_internal(
             unsigned chunkID;
             for (chunkID=0; chunkIDjobCompleted_mutex);
    +            ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->mtctx_mutex);
                 while (mtctx->jobs[chunkID].jobCompleted==0) {
                     DEBUGLOG(5, "waiting for jobCompleted signal from chunk %u", chunkID);
    -                ZSTD_pthread_cond_wait(&mtctx->jobCompleted_cond, &mtctx->jobCompleted_mutex);
    +                ZSTD_pthread_cond_wait(&mtctx->mtctx_cond, &mtctx->mtctx_mutex);
                 }
    -            ZSTD_pthread_mutex_unlock(&mtctx->jobCompleted_mutex);
    +            ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
                 DEBUGLOG(5, "ready to write chunk %u ", chunkID);
     
                 mtctx->jobs[chunkID].srcStart = NULL;
    @@ -1035,8 +1035,8 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, ZSTD
             zcs->jobs[jobID].jobCompleted = 0;
             zcs->jobs[jobID].frameChecksumNeeded = endFrame && (zcs->nextJobID>0) && zcs->params.fParams.checksumFlag;
             zcs->jobs[jobID].dstFlushed = 0;
    -        zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex;
    -        zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond;
    +        zcs->jobs[jobID].mtctx_mutex = &zcs->mtctx_mutex;
    +        zcs->jobs[jobID].mtctx_cond = &zcs->mtctx_cond;
     
             if (zcs->params.fParams.checksumFlag)
                 XXH64_update(&zcs->xxhState, (const char*)zcs->inBuff.buffer.start + zcs->prefixSize, srcSize);
    @@ -1067,7 +1067,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, ZSTD
                     zcs->params.fParams.checksumFlag = 0;
         }   }   }
     
    -    DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes  (end:%u) (note : doneJob = %u=>%u)",
    +    DEBUGLOG(2, "ZSTDMT_createCompressionJob: posting job %u : %u bytes  (end:%u) (note : doneJob = %u=>%u)",
                     zcs->nextJobID,
                     (U32)zcs->jobs[jobID].srcSize,
                     zcs->jobs[jobID].lastChunk,
    @@ -1094,18 +1094,18 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, uns
         DEBUGLOG(5, "ZSTDMT_flushProduced (blocking:%u)", blockToFlush);
         assert(output->size >= output->pos);
     
    -    ZSTD_PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex);
    +    ZSTD_PTHREAD_MUTEX_LOCK(&zcs->mtctx_mutex);
         if (blockToFlush && (zcs->doneJobID < zcs->nextJobID)) {
             while (zcs->jobs[wJobID].dstFlushed == zcs->jobs[wJobID].cSize) {
                 if (zcs->jobs[wJobID].jobCompleted==1) break;
                 DEBUGLOG(5, "waiting for something to flush from job %u (currently flushed: %u bytes)",
                             zcs->doneJobID, (U32)zcs->jobs[wJobID].dstFlushed);
    -            ZSTD_pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex);  /* block when nothing available to flush but more to come */
    +            ZSTD_pthread_cond_wait(&zcs->mtctx_cond, &zcs->mtctx_mutex);  /* block when nothing available to flush but more to come */
         }   }
     
         /* some output is available to be flushed */
         {   ZSTDMT_jobDescription job = zcs->jobs[wJobID];
    -        ZSTD_pthread_mutex_unlock(&zcs->jobCompleted_mutex);
    +        ZSTD_pthread_mutex_unlock(&zcs->mtctx_mutex);
             if (ZSTD_isError(job.cSize)) {
                 DEBUGLOG(5, "ZSTDMT_flushProduced: job %u : compression error detected : %s",
                             zcs->doneJobID, ZSTD_getErrorName(job.cSize));
    @@ -1186,8 +1186,9 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
         /* single-pass shortcut (note : synchronous-mode) */
         if ( (mtctx->nextJobID == 0)     /* just started */
           && (mtctx->inBuff.filled == 0) /* nothing buffered */
    +      && (!mtctx->jobReady)          /* no job already created */
           && (endOp == ZSTD_e_end)       /* end order */
    -      && (output->size - output->pos >= ZSTD_compressBound(input->size - input->pos)) ) { /* enough room */
    +      && (output->size - output->pos >= ZSTD_compressBound(input->size - input->pos)) ) { /* enough space in dst */
             size_t const cSize = ZSTDMT_compress_advanced_internal(mtctx,
                     (char*)output->dst + output->pos, output->size - output->pos,
                     (const char*)input->src + input->pos, input->size - input->pos,
    @@ -1234,7 +1235,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
     
         /* check for potential compressed data ready to be flushed */
         {   size_t const remainingToFlush = ZSTDMT_flushProduced(mtctx, output, !forwardInputProgress, endOp); /* block if there was no forward input progress */
    -        if (input->pos < input->size) return MAX(remainingToFlush, 1);  /* input not consumed : do not flush yet */
    +        if (input->pos < input->size) return MAX(remainingToFlush, 1);  /* input not consumed : do not end flush yet */
             return remainingToFlush;
         }
     }
    
    From a1d4041e69601b84c08082f866665a6a2eaaa8d4 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 25 Jan 2018 17:35:49 -0800
    Subject: [PATCH 085/255] zstdmt: removed job->jobCompleted
    
    replaced by equivalent signal job->consumer == job->srcSize.
    
    created additional functions
    ZSTD_writeLastEmptyBlock()
    and
    ZSTDMT_writeLastEmptyBlock()
    required when it's necessary to finish a frame with a last empty job, to create an "end of frame" marker.
    
    It avoids creating a job with srcSize==0.
    ---
     lib/compress/zstd_compress.c          |  45 ++--
     lib/compress/zstd_compress_internal.h |   9 +
     lib/compress/zstdmt_compress.c        | 366 ++++++++++++++------------
     3 files changed, 231 insertions(+), 189 deletions(-)
    
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index a87c368ea..7b16d7364 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -1824,7 +1824,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
                                             void* dst, size_t dstCapacity,
                                             const void* src, size_t srcSize)
     {
    -    DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u) (dictLimit=%u, nextToUpdate=%u)",
    +    DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",
                     (U32)dstCapacity, zc->blockState.matchState.dictLimit, zc->blockState.matchState.nextToUpdate);
         if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1)
             return 0;   /* don't even attempt compression below a certain srcSize */
    @@ -1837,9 +1837,9 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
             if (current > zc->blockState.matchState.nextToUpdate + 384)
                 zc->blockState.matchState.nextToUpdate = current - MIN(192, (U32)(current - zc->blockState.matchState.nextToUpdate - 384));
         }
    -    /* find and store sequences */
    -    {
    -        U32 const extDict = zc->blockState.matchState.lowLimit < zc->blockState.matchState.dictLimit;
    +
    +    /* select and store sequences */
    +    {   U32 const extDict = zc->blockState.matchState.lowLimit < zc->blockState.matchState.dictLimit;
             size_t lastLLSize;
             { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i]; }
             if (zc->appliedParams.ldmParams.enableLdm) {
    @@ -1848,26 +1848,20 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
                         U32 rep[ZSTD_REP_NUM], ZSTD_CCtx_params const* params,
                         void const* src, size_t srcSize);
                 ZSTD_ldmBlockCompressor const ldmBlockCompressor = extDict ? ZSTD_compressBlock_ldm_extDict : ZSTD_compressBlock_ldm;
    -
                 lastLLSize = ldmBlockCompressor(&zc->ldmState, &zc->blockState.matchState, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams, src, srcSize);
    -        } else {
    +        } else {   /* not long range mode */
                 ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict);
    -
                 lastLLSize = blockCompressor(&zc->blockState.matchState, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, src, srcSize);
             }
    -        {
    -            const BYTE* const anchor = (const BYTE*)src + srcSize - lastLLSize;
    -            ZSTD_storeLastLiterals(&zc->seqStore, anchor, lastLLSize);
    -        }
    -    }
    -    /* encode */
    -    {
    -        size_t const cSize = ZSTD_compressSequences(&zc->seqStore, &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize, zc->entropyWorkspace);
    -        if (ZSTD_isError(cSize) || cSize == 0)
    -            return cSize;
    +        {   const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize;
    +            ZSTD_storeLastLiterals(&zc->seqStore, lastLiterals, lastLLSize);
    +    }   }
    +
    +    /* encode sequences and literals */
    +    {   size_t const cSize = ZSTD_compressSequences(&zc->seqStore, &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize, zc->entropyWorkspace);
    +        if (ZSTD_isError(cSize) || cSize == 0) return cSize;
             /* confirm repcodes and entropy tables */
    -        {
    -            ZSTD_compressedBlockState_t* const tmp = zc->blockState.prevCBlock;
    +        {   ZSTD_compressedBlockState_t* const tmp = zc->blockState.prevCBlock;
                 zc->blockState.prevCBlock = zc->blockState.nextCBlock;
                 zc->blockState.nextCBlock = tmp;
             }
    @@ -2030,6 +2024,19 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity,
         return pos;
     }
     
    +/* ZSTD_writeLastEmptyBlock() :
    + * output an empty Block with end-of-frame mark to complete a frame
    + * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h))
    + *           or an error code if `dstCapcity` is too small (mtctx_mutex);
    -        job->dstBuff = dstBuff;
    +        job->dstBuff = dstBuff;   /* this value can be read in ZSTDMT_flush, when it copies the whole job */
             ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
         }
     
    @@ -369,8 +368,7 @@ void ZSTDMT_compressChunk(void* jobDescription)
                 if (ZSTD_isError(initError)) {
                     job->cSize = initError;
                     goto _endJob;
    -        }   }
    -    }
    +    }   }   }
         if (!job->firstChunk) {  /* flush and overwrite frame header when it's not first job */
             size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, 0);
             if (ZSTD_isError(hSize)) { job->cSize = hSize; /* save error code */ goto _endJob; }
    @@ -379,12 +377,9 @@ void ZSTDMT_compressChunk(void* jobDescription)
         }
     
         /* compress */
    -#if 0
    -    job->cSize = (job->lastChunk) ?
    -                 ZSTD_compressEnd     (cctx, dstBuff.start, dstBuff.size, src, job->srcSize) :
    -                 ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, job->srcSize);
    -#else
    -    if (sizeof(size_t) > sizeof(int)) assert(job->srcSize < ((size_t)INT_MAX) * ZSTD_BLOCKSIZE_MAX);   /* check overflow */
    +    if (sizeof(size_t) > sizeof(int))
    +        assert(job->srcSize < ((size_t)INT_MAX) * ZSTD_BLOCKSIZE_MAX);   /* check overflow */
    +
         {   int const nbBlocks = (int)((job->srcSize + (ZSTD_BLOCKSIZE_MAX-1)) / ZSTD_BLOCKSIZE_MAX);
             const BYTE* ip = (const BYTE*) src;
             BYTE* const ostart = (BYTE*)dstBuff.start;
    @@ -404,7 +399,7 @@ void ZSTDMT_compressChunk(void* jobDescription)
                 job->consumed = ZSTD_BLOCKSIZE_MAX * blockNb;
                 DEBUGLOG(5, "ZSTDMT_compressChunk: compress new block : cSize==%u bytes (total: %u)",
                             (U32)cSize, (U32)job->cSize);
    -            ZSTD_pthread_cond_signal(job->mtctx_cond);
    +            ZSTD_pthread_cond_signal(job->mtctx_cond);   /* warns some more data is ready to be flushed */
                 ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
             }
             /* last block */
    @@ -416,23 +411,19 @@ void ZSTDMT_compressChunk(void* jobDescription)
                      ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize);
                 if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; }
                 /* stats */
    -            ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);   /* note : it's a mtctx mutex */
    +            ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);
                 job->cSize += cSize;
    -            job->consumed = job->srcSize;
                 ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
    -        }
    -    }
    -#endif
    +    }   }
     
     _endJob:
    -    /* release */
    +    /* release resources */
         ZSTDMT_releaseCCtx(job->cctxPool, cctx);
         ZSTDMT_releaseBuffer(job->bufPool, job->src);
         job->src = g_nullBuffer; job->srcStart = NULL;
         /* report */
         ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);
         job->consumed = job->srcSize;
    -    job->jobCompleted = 1;
         ZSTD_pthread_cond_signal(job->mtctx_cond);
         ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
     }
    @@ -577,18 +568,18 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx)
         mtctx->allJobsCompleted = 1;
     }
     
    -static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* zcs)
    +static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* mtctx)
     {
         DEBUGLOG(4, "ZSTDMT_waitForAllJobsCompleted");
    -    while (zcs->doneJobID < zcs->nextJobID) {
    -        unsigned const jobID = zcs->doneJobID & zcs->jobIDMask;
    -        ZSTD_PTHREAD_MUTEX_LOCK(&zcs->mtctx_mutex);
    -        while (zcs->jobs[jobID].jobCompleted==0) {
    -            DEBUGLOG(5, "waiting for jobCompleted signal from chunk %u", zcs->doneJobID);   /* we want to block when waiting for data to flush */
    -            ZSTD_pthread_cond_wait(&zcs->mtctx_cond, &zcs->mtctx_mutex);
    +    while (mtctx->doneJobID < mtctx->nextJobID) {
    +        unsigned const jobID = mtctx->doneJobID & mtctx->jobIDMask;
    +        ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->mtctx_mutex);
    +        while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].srcSize) {
    +            DEBUGLOG(5, "waiting for jobCompleted signal from chunk %u", mtctx->doneJobID);   /* we want to block when waiting for data to flush */
    +            ZSTD_pthread_cond_wait(&mtctx->mtctx_cond, &mtctx->mtctx_mutex);
             }
    -        ZSTD_pthread_mutex_unlock(&zcs->mtctx_mutex);
    -        zcs->doneJobID++;
    +        ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
    +        mtctx->doneJobID++;
         }
     }
     
    @@ -769,7 +760,7 @@ static size_t ZSTDMT_compress_advanced_internal(
                 mtctx->jobs[u].src = g_nullBuffer;
                 mtctx->jobs[u].srcStart = srcStart + frameStartPos - dictSize;
                 mtctx->jobs[u].prefixSize = dictSize;
    -            mtctx->jobs[u].srcSize = chunkSize;
    +            mtctx->jobs[u].srcSize = chunkSize; assert(chunkSize > 0);  /* avoid job.srcSize == 0 */
                 mtctx->jobs[u].consumed = 0;
                 mtctx->jobs[u].cSize = 0;
                 mtctx->jobs[u].cdict = (u==0) ? cdict : NULL;
    @@ -782,7 +773,6 @@ static size_t ZSTDMT_compress_advanced_internal(
                 mtctx->jobs[u].bufPool = mtctx->bufPool;
                 mtctx->jobs[u].firstChunk = (u==0);
                 mtctx->jobs[u].lastChunk = (u==nbChunks-1);
    -            mtctx->jobs[u].jobCompleted = 0;
                 mtctx->jobs[u].mtctx_mutex = &mtctx->mtctx_mutex;
                 mtctx->jobs[u].mtctx_cond = &mtctx->mtctx_cond;
     
    @@ -805,7 +795,7 @@ static size_t ZSTDMT_compress_advanced_internal(
             for (chunkID=0; chunkIDmtctx_mutex);
    -            while (mtctx->jobs[chunkID].jobCompleted==0) {
    +            while (mtctx->jobs[chunkID].consumed < mtctx->jobs[chunkID].srcSize) {
                     DEBUGLOG(5, "waiting for jobCompleted signal from chunk %u", chunkID);
                     ZSTD_pthread_cond_wait(&mtctx->mtctx_cond, &mtctx->mtctx_mutex);
                 }
    @@ -879,7 +869,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx,
     /* ====================================== */
     
     size_t ZSTDMT_initCStream_internal(
    -        ZSTDMT_CCtx* zcs,
    +        ZSTDMT_CCtx* mtctx,
             const void* dict, size_t dictSize, ZSTD_dictMode_e dictMode,
             const ZSTD_CDict* cdict, ZSTD_CCtx_params params,
             unsigned long long pledgedSrcSize)
    @@ -888,8 +878,8 @@ size_t ZSTDMT_initCStream_internal(
         /* params are supposed to be fully validated at this point */
         assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
         assert(!((dict) && (cdict)));  /* either dict or cdict, not both */
    -    assert(zcs->cctxPool->totalCCtx == params.nbThreads);
    -    zcs->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN);  /* do not trigger multi-threading when srcSize is too small */
    +    assert(mtctx->cctxPool->totalCCtx == params.nbThreads);
    +    mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN);  /* do not trigger multi-threading when srcSize is too small */
         if (params.jobSize == 0) {
             if (params.cParams.windowLog >= 29)
                 params.jobSize = ZSTDMT_JOBSIZE_MAX;
    @@ -898,56 +888,56 @@ size_t ZSTDMT_initCStream_internal(
         }
         if (params.jobSize > ZSTDMT_JOBSIZE_MAX) params.jobSize = ZSTDMT_JOBSIZE_MAX;
     
    -    if (zcs->singleBlockingThread) {
    +    if (mtctx->singleBlockingThread) {
             ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(params);
             DEBUGLOG(4, "ZSTDMT_initCStream_internal: switch to single blocking thread mode");
             assert(singleThreadParams.nbThreads == 0);
    -        return ZSTD_initCStream_internal(zcs->cctxPool->cctx[0],
    +        return ZSTD_initCStream_internal(mtctx->cctxPool->cctx[0],
                                              dict, dictSize, cdict,
                                              singleThreadParams, pledgedSrcSize);
         }
         DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u threads", params.nbThreads);
     
    -    if (zcs->allJobsCompleted == 0) {   /* previous compression not correctly finished */
    -        ZSTDMT_waitForAllJobsCompleted(zcs);
    -        ZSTDMT_releaseAllJobResources(zcs);
    -        zcs->allJobsCompleted = 1;
    +    if (mtctx->allJobsCompleted == 0) {   /* previous compression not correctly finished */
    +        ZSTDMT_waitForAllJobsCompleted(mtctx);
    +        ZSTDMT_releaseAllJobResources(mtctx);
    +        mtctx->allJobsCompleted = 1;
         }
     
    -    zcs->params = params;
    -    zcs->frameContentSize = pledgedSrcSize;
    +    mtctx->params = params;
    +    mtctx->frameContentSize = pledgedSrcSize;
         if (dict) {
    -        ZSTD_freeCDict(zcs->cdictLocal);
    -        zcs->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize,
    +        ZSTD_freeCDict(mtctx->cdictLocal);
    +        mtctx->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize,
                                                         ZSTD_dlm_byCopy, dictMode, /* note : a loadPrefix becomes an internal CDict */
    -                                                    params.cParams, zcs->cMem);
    -        zcs->cdict = zcs->cdictLocal;
    -        if (zcs->cdictLocal == NULL) return ERROR(memory_allocation);
    +                                                    params.cParams, mtctx->cMem);
    +        mtctx->cdict = mtctx->cdictLocal;
    +        if (mtctx->cdictLocal == NULL) return ERROR(memory_allocation);
         } else {
    -        ZSTD_freeCDict(zcs->cdictLocal);
    -        zcs->cdictLocal = NULL;
    -        zcs->cdict = cdict;
    +        ZSTD_freeCDict(mtctx->cdictLocal);
    +        mtctx->cdictLocal = NULL;
    +        mtctx->cdict = cdict;
         }
     
         assert(params.overlapSizeLog <= 9);
    -    zcs->targetPrefixSize = (params.overlapSizeLog==0) ? 0 : (size_t)1 << (params.cParams.windowLog - (9 - params.overlapSizeLog));
    -    DEBUGLOG(4, "overlapLog=%u => %u KB", params.overlapSizeLog, (U32)(zcs->targetPrefixSize>>10));
    -    zcs->targetSectionSize = params.jobSize;
    -    if (zcs->targetSectionSize < ZSTDMT_JOBSIZE_MIN) zcs->targetSectionSize = ZSTDMT_JOBSIZE_MIN;
    -    if (zcs->targetSectionSize < zcs->targetPrefixSize) zcs->targetSectionSize = zcs->targetPrefixSize;  /* job size must be >= overlap size */
    -    DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(zcs->targetSectionSize>>10), params.jobSize);
    -    zcs->inBuffSize = zcs->targetPrefixSize + zcs->targetSectionSize;
    -    DEBUGLOG(4, "inBuff Size : %u KB", (U32)(zcs->inBuffSize>>10));
    -    ZSTDMT_setBufferSize(zcs->bufPool, MAX(zcs->inBuffSize, ZSTD_compressBound(zcs->targetSectionSize)) );
    -    zcs->inBuff.buffer = g_nullBuffer;
    -    zcs->prefixSize = 0;
    -    zcs->doneJobID = 0;
    -    zcs->nextJobID = 0;
    -    zcs->frameEnded = 0;
    -    zcs->allJobsCompleted = 0;
    -    zcs->consumed = 0;
    -    zcs->produced = 0;
    -    if (params.fParams.checksumFlag) XXH64_reset(&zcs->xxhState, 0);
    +    mtctx->targetPrefixSize = (params.overlapSizeLog==0) ? 0 : (size_t)1 << (params.cParams.windowLog - (9 - params.overlapSizeLog));
    +    DEBUGLOG(4, "overlapLog=%u => %u KB", params.overlapSizeLog, (U32)(mtctx->targetPrefixSize>>10));
    +    mtctx->targetSectionSize = params.jobSize;
    +    if (mtctx->targetSectionSize < ZSTDMT_JOBSIZE_MIN) mtctx->targetSectionSize = ZSTDMT_JOBSIZE_MIN;
    +    if (mtctx->targetSectionSize < mtctx->targetPrefixSize) mtctx->targetSectionSize = mtctx->targetPrefixSize;  /* job size must be >= overlap size */
    +    DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(mtctx->targetSectionSize>>10), params.jobSize);
    +    mtctx->inBuffSize = mtctx->targetPrefixSize + mtctx->targetSectionSize;
    +    DEBUGLOG(4, "inBuff Size : %u KB", (U32)(mtctx->inBuffSize>>10));
    +    ZSTDMT_setBufferSize(mtctx->bufPool, MAX(mtctx->inBuffSize, ZSTD_compressBound(mtctx->targetSectionSize)) );
    +    mtctx->inBuff.buffer = g_nullBuffer;
    +    mtctx->prefixSize = 0;
    +    mtctx->doneJobID = 0;
    +    mtctx->nextJobID = 0;
    +    mtctx->frameEnded = 0;
    +    mtctx->allJobsCompleted = 0;
    +    mtctx->consumed = 0;
    +    mtctx->produced = 0;
    +    if (params.fParams.checksumFlag) XXH64_reset(&mtctx->xxhState, 0);
         return 0;
     }
     
    @@ -982,103 +972,134 @@ size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx,
      * pledgedSrcSize can be zero == unknown (for the time being)
      * prefer using ZSTD_CONTENTSIZE_UNKNOWN,
      * as `0` might mean "empty" in the future */
    -size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize)
    +size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize)
     {
         if (!pledgedSrcSize) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
    -    if (zcs->params.nbThreads==1)
    -        return ZSTD_resetCStream(zcs->cctxPool->cctx[0], pledgedSrcSize);
    -    return ZSTDMT_initCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, 0, zcs->params,
    +    if (mtctx->params.nbThreads==1)
    +        return ZSTD_resetCStream(mtctx->cctxPool->cctx[0], pledgedSrcSize);
    +    return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dm_auto, 0, mtctx->params,
                                            pledgedSrcSize);
     }
     
    -size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) {
    +size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel) {
         ZSTD_parameters const params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0);
    -    ZSTD_CCtx_params cctxParams = zcs->params;   /* retrieve sticky params */
    +    ZSTD_CCtx_params cctxParams = mtctx->params;   /* retrieve sticky params */
         DEBUGLOG(4, "ZSTDMT_initCStream (cLevel=%i)", compressionLevel);
         cctxParams.cParams = params.cParams;
         cctxParams.fParams = params.fParams;
    -    return ZSTDMT_initCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN);
    +    return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dm_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN);
     }
     
     
    -static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, ZSTD_EndDirective endOp)
    +/* ZSTDMT_writeLastEmptyBlock()
    + * Write a single empty block with an end-of-frame
    + * to finish a frame.
    + * Completed synchronously.
    + * @return : 0, or an error code (can fail due to memory allocation)
    + */
    +static size_t ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job)
     {
    -    unsigned const jobID = zcs->nextJobID & zcs->jobIDMask;
    +    assert(job->srcSize == 0);
    +    assert(job->lastChunk == 1);
    +    assert(job->firstChunk == 0);   /* first chunk needs to create frame header too */
    +    assert(job->dstBuff.start == NULL);  /* invoked from streaming variant only */
    +    {   buffer_t const dstBuff = ZSTDMT_getBuffer(job->bufPool);
    +        if (dstBuff.start==NULL) return ERROR(memory_allocation);
    +        job->dstBuff = dstBuff;   /* will be released by ZSTDMT_flushProduced() */
    +        assert(dstBuff.size >= ZSTD_blockHeaderSize);
    +        job->cSize = ZSTD_writeLastEmptyBlock(dstBuff.start, dstBuff.size);
    +        assert(!ZSTD_isError(job->cSize));
    +        assert(job->consumed == 0);
    +    }
    +    return 0;
    +}
    +
    +static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZSTD_EndDirective endOp)
    +{
    +    unsigned const jobID = mtctx->nextJobID & mtctx->jobIDMask;
         int const endFrame = (endOp == ZSTD_e_end);
     
    -    if (zcs->nextJobID > zcs->doneJobID + zcs->jobIDMask) {
    +    if (mtctx->nextJobID > mtctx->doneJobID + mtctx->jobIDMask) {
             DEBUGLOG(5, "ZSTDMT_createCompressionJob: will not create new job : table is full");
    -        assert((zcs->nextJobID & zcs->jobIDMask) == (zcs->doneJobID & zcs->jobIDMask));
    +        assert((mtctx->nextJobID & mtctx->jobIDMask) == (mtctx->doneJobID & mtctx->jobIDMask));
             return 0;
         }
     
    -    if (!zcs->jobReady) {
    +    if (!mtctx->jobReady) {
             DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
    -                    zcs->nextJobID, (U32)srcSize, (U32)zcs->prefixSize);
    -        zcs->jobs[jobID].src = zcs->inBuff.buffer;
    -        zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start;
    -        zcs->jobs[jobID].srcSize = srcSize;
    -        zcs->jobs[jobID].consumed = 0;
    -        zcs->jobs[jobID].cSize = 0;
    -        zcs->jobs[jobID].prefixSize = zcs->prefixSize;
    -        assert(zcs->inBuff.filled >= srcSize + zcs->prefixSize);
    -        zcs->jobs[jobID].params = zcs->params;
    +                    mtctx->nextJobID, (U32)srcSize, (U32)mtctx->prefixSize);
    +        mtctx->jobs[jobID].src = mtctx->inBuff.buffer;
    +        mtctx->jobs[jobID].srcStart = mtctx->inBuff.buffer.start;
    +        mtctx->jobs[jobID].srcSize = srcSize;
    +        mtctx->jobs[jobID].consumed = 0;
    +        mtctx->jobs[jobID].cSize = 0;
    +        mtctx->jobs[jobID].prefixSize = mtctx->prefixSize;
    +        assert(mtctx->inBuff.filled >= srcSize + mtctx->prefixSize);
    +        mtctx->jobs[jobID].params = mtctx->params;
             /* do not calculate checksum within sections, but write it in header for first section */
    -        if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0;
    -        zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL;
    -        zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize;
    -        zcs->jobs[jobID].dstBuff = g_nullBuffer;
    -        zcs->jobs[jobID].cctxPool = zcs->cctxPool;
    -        zcs->jobs[jobID].bufPool = zcs->bufPool;
    -        zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0);
    -        zcs->jobs[jobID].lastChunk = endFrame;
    -        zcs->jobs[jobID].jobCompleted = 0;
    -        zcs->jobs[jobID].frameChecksumNeeded = endFrame && (zcs->nextJobID>0) && zcs->params.fParams.checksumFlag;
    -        zcs->jobs[jobID].dstFlushed = 0;
    -        zcs->jobs[jobID].mtctx_mutex = &zcs->mtctx_mutex;
    -        zcs->jobs[jobID].mtctx_cond = &zcs->mtctx_cond;
    +        if (mtctx->nextJobID) mtctx->jobs[jobID].params.fParams.checksumFlag = 0;
    +        mtctx->jobs[jobID].cdict = mtctx->nextJobID==0 ? mtctx->cdict : NULL;
    +        mtctx->jobs[jobID].fullFrameSize = mtctx->frameContentSize;
    +        mtctx->jobs[jobID].dstBuff = g_nullBuffer;
    +        mtctx->jobs[jobID].cctxPool = mtctx->cctxPool;
    +        mtctx->jobs[jobID].bufPool = mtctx->bufPool;
    +        mtctx->jobs[jobID].firstChunk = (mtctx->nextJobID==0);
    +        mtctx->jobs[jobID].lastChunk = endFrame;
    +        mtctx->jobs[jobID].frameChecksumNeeded = endFrame && (mtctx->nextJobID>0) && mtctx->params.fParams.checksumFlag;
    +        mtctx->jobs[jobID].dstFlushed = 0;
    +        mtctx->jobs[jobID].mtctx_mutex = &mtctx->mtctx_mutex;
    +        mtctx->jobs[jobID].mtctx_cond = &mtctx->mtctx_cond;
     
    -        if (zcs->params.fParams.checksumFlag)
    -            XXH64_update(&zcs->xxhState, (const char*)zcs->inBuff.buffer.start + zcs->prefixSize, srcSize);
    +        if (mtctx->params.fParams.checksumFlag)
    +            XXH64_update(&mtctx->xxhState, (const char*)mtctx->inBuff.buffer.start + mtctx->prefixSize, srcSize);
     
             /* get a new buffer for next input */
             if (!endFrame) {
    -            size_t const newPrefixSize = MIN(srcSize + zcs->prefixSize, zcs->targetPrefixSize);
    -            zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->bufPool);
    -            if (zcs->inBuff.buffer.start == NULL) {   /* not enough memory to allocate next input buffer */
    -                zcs->jobs[jobID].jobCompleted = 1;
    -                zcs->nextJobID++;
    -                ZSTDMT_waitForAllJobsCompleted(zcs);
    -                ZSTDMT_releaseAllJobResources(zcs);
    +            size_t const newPrefixSize = MIN(srcSize + mtctx->prefixSize, mtctx->targetPrefixSize);
    +            mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool);
    +            if (mtctx->inBuff.buffer.start == NULL) {   /* not enough memory to allocate next input buffer */
    +                mtctx->jobs[jobID].srcSize = mtctx->jobs[jobID].consumed = 0;
    +                mtctx->nextJobID++;
    +                ZSTDMT_waitForAllJobsCompleted(mtctx);
    +                ZSTDMT_releaseAllJobResources(mtctx);
                     return ERROR(memory_allocation);
                 }
    -            zcs->inBuff.filled -= srcSize + zcs->prefixSize - newPrefixSize;
    -            memmove(zcs->inBuff.buffer.start,   /* copy end of current job into next job, as "prefix" */
    -                (const char*)zcs->jobs[jobID].srcStart + zcs->prefixSize + srcSize - newPrefixSize,
    -                zcs->inBuff.filled);
    -            zcs->prefixSize = newPrefixSize;
    +            mtctx->inBuff.filled -= srcSize + mtctx->prefixSize - newPrefixSize;
    +            memmove(mtctx->inBuff.buffer.start,   /* copy end of current job into next job, as "prefix" */
    +                (const char*)mtctx->jobs[jobID].srcStart + mtctx->prefixSize + srcSize - newPrefixSize,
    +                mtctx->inBuff.filled);
    +            mtctx->prefixSize = newPrefixSize;
             } else {   /* endFrame==1 => no need for another input buffer */
    -            zcs->inBuff.buffer = g_nullBuffer;
    -            zcs->inBuff.filled = 0;
    -            zcs->prefixSize = 0;
    -            zcs->frameEnded = endFrame;
    -            if (zcs->nextJobID == 0) {
    -                /* single chunk exception : checksum is calculated directly within worker thread */
    -                zcs->params.fParams.checksumFlag = 0;
    -    }   }   }
    +            mtctx->inBuff.buffer = g_nullBuffer;
    +            mtctx->inBuff.filled = 0;
    +            mtctx->prefixSize = 0;
    +            mtctx->frameEnded = endFrame;
    +            if (mtctx->nextJobID == 0) {
    +                /* single chunk exception : checksum is already calculated directly within worker thread */
    +                mtctx->params.fParams.checksumFlag = 0;
    +        }   }
     
    -    DEBUGLOG(2, "ZSTDMT_createCompressionJob: posting job %u : %u bytes  (end:%u) (note : doneJob = %u=>%u)",
    -                zcs->nextJobID,
    -                (U32)zcs->jobs[jobID].srcSize,
    -                zcs->jobs[jobID].lastChunk,
    -                zcs->doneJobID,
    -                zcs->doneJobID & zcs->jobIDMask);
    -    if (POOL_tryAdd(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID])) {
    -        zcs->nextJobID++;
    -        zcs->jobReady = 0;
    +        if ( (srcSize == 0)
    +          && (mtctx->nextJobID>0)/*single chunk must also write frame header*/ ) {
    +            assert(endOp == ZSTD_e_end);  /* only possible case : need to end the frame with an empty last block */
    +            CHECK_F( ZSTDMT_writeLastEmptyBlock(mtctx->jobs + jobID) );
    +            mtctx->nextJobID++;
    +            return 0;
    +        }
    +    }
    +
    +    DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes  (end:%u) (note : doneJob = %u=>%u)",
    +                mtctx->nextJobID,
    +                (U32)mtctx->jobs[jobID].srcSize,
    +                mtctx->jobs[jobID].lastChunk,
    +                mtctx->doneJobID,
    +                mtctx->doneJobID & mtctx->jobIDMask);
    +    if (POOL_tryAdd(mtctx->factory, ZSTDMT_compressChunk, &mtctx->jobs[jobID])) {
    +        mtctx->nextJobID++;
    +        mtctx->jobReady = 0;
         } else {
    -        DEBUGLOG(5, "ZSTDMT_createCompressionJob: no worker available for job %u", zcs->nextJobID);
    -        zcs->jobReady = 1;
    +        DEBUGLOG(5, "ZSTDMT_createCompressionJob: no worker available for job %u", mtctx->nextJobID);
    +        mtctx->jobReady = 1;
         }
         return 0;
     }
    @@ -1088,73 +1109,78 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, ZSTD
      * `output` : `pos` will be updated with amount of data flushed .
      * `blockToFlush` : if >0, the function will block and wait if there is no data available to flush .
      * @return : amount of data remaining within internal buffer, 0 if no more, 1 if unknown but > 0, or an error code */
    -static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush, ZSTD_EndDirective end)
    +static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, unsigned blockToFlush, ZSTD_EndDirective end)
     {
    -    unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask;
    +    unsigned const wJobID = mtctx->doneJobID & mtctx->jobIDMask;
         DEBUGLOG(5, "ZSTDMT_flushProduced (blocking:%u)", blockToFlush);
         assert(output->size >= output->pos);
     
    -    ZSTD_PTHREAD_MUTEX_LOCK(&zcs->mtctx_mutex);
    -    if (blockToFlush && (zcs->doneJobID < zcs->nextJobID)) {
    -        while (zcs->jobs[wJobID].dstFlushed == zcs->jobs[wJobID].cSize) {
    -            if (zcs->jobs[wJobID].jobCompleted==1) break;
    +    ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->mtctx_mutex);
    +    if (blockToFlush && (mtctx->doneJobID < mtctx->nextJobID)) {
    +        assert(mtctx->jobs[wJobID].dstFlushed <= mtctx->jobs[wJobID].cSize);
    +        while (mtctx->jobs[wJobID].dstFlushed == mtctx->jobs[wJobID].cSize) {
    +            if (mtctx->jobs[wJobID].consumed == mtctx->jobs[wJobID].srcSize) {
    +                DEBUGLOG(5, "job %u is completely consumed (%u == %u) => don't wait for cond",
    +                            mtctx->doneJobID, (U32)mtctx->jobs[wJobID].consumed, (U32)mtctx->jobs[wJobID].srcSize);
    +                break;
    +            }
                 DEBUGLOG(5, "waiting for something to flush from job %u (currently flushed: %u bytes)",
    -                        zcs->doneJobID, (U32)zcs->jobs[wJobID].dstFlushed);
    -            ZSTD_pthread_cond_wait(&zcs->mtctx_cond, &zcs->mtctx_mutex);  /* block when nothing available to flush but more to come */
    +                        mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed);
    +            ZSTD_pthread_cond_wait(&mtctx->mtctx_cond, &mtctx->mtctx_mutex);  /* block when nothing available to flush but more to come */
         }   }
     
         /* some output is available to be flushed */
    -    {   ZSTDMT_jobDescription job = zcs->jobs[wJobID];
    -        ZSTD_pthread_mutex_unlock(&zcs->mtctx_mutex);
    +    {   ZSTDMT_jobDescription job = mtctx->jobs[wJobID];
    +        ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
             if (ZSTD_isError(job.cSize)) {
                 DEBUGLOG(5, "ZSTDMT_flushProduced: job %u : compression error detected : %s",
    -                        zcs->doneJobID, ZSTD_getErrorName(job.cSize));
    -            ZSTDMT_waitForAllJobsCompleted(zcs);
    -            ZSTDMT_releaseAllJobResources(zcs);
    +                        mtctx->doneJobID, ZSTD_getErrorName(job.cSize));
    +            ZSTDMT_waitForAllJobsCompleted(mtctx);
    +            ZSTDMT_releaseAllJobResources(mtctx);
                 return job.cSize;
             }
             /* add frame checksum if necessary (can only happen once) */
    -        if ( job.jobCompleted
    +        assert(job.consumed <= job.srcSize);
    +        if ( (job.consumed == job.srcSize)
               && job.frameChecksumNeeded ) {
    -            U32 const checksum = (U32)XXH64_digest(&zcs->xxhState);
    +            U32 const checksum = (U32)XXH64_digest(&mtctx->xxhState);
                 DEBUGLOG(4, "ZSTDMT_flushProduced: writing checksum : %08X \n", checksum);
                 MEM_writeLE32((char*)job.dstBuff.start + job.cSize, checksum);
                 job.cSize += 4;
    -            zcs->jobs[wJobID].cSize += 4;
    -            zcs->jobs[wJobID].frameChecksumNeeded = 0;
    +            mtctx->jobs[wJobID].cSize += 4;
    +            mtctx->jobs[wJobID].frameChecksumNeeded = 0;
             }
             assert(job.cSize >= job.dstFlushed);
    -        if (job.dstBuff.start != NULL) {  /* one buffer present : some job is ongoing */
    +        if (job.dstBuff.start != NULL) {  /* dst buffer present : some work is ongoing or completed */
                 size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos);
                 DEBUGLOG(5, "ZSTDMT_flushProduced: Flushing %u bytes from job %u (completion:%.1f%%)",
    -                        (U32)toWrite, zcs->doneJobID, (double)job.consumed / job.srcSize * 100);
    +                        (U32)toWrite, mtctx->doneJobID, (double)job.consumed / (job.srcSize + !job.srcSize /*avoid div0*/) * 100);
                 memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite);
                 output->pos += toWrite;
                 job.dstFlushed += toWrite;
     
    -            if ( job.jobCompleted
    +            if ( (job.consumed == job.srcSize)
                   && (job.dstFlushed == job.cSize) ) {   /* output buffer fully flushed => move to next one */
                     DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one",
    -                        zcs->doneJobID, (U32)job.dstFlushed);
    -                ZSTDMT_releaseBuffer(zcs->bufPool, job.dstBuff);
    -                zcs->jobs[wJobID].dstBuff = g_nullBuffer;
    -                zcs->jobs[wJobID].jobCompleted = 0;
    -                zcs->consumed += job.srcSize;
    -                zcs->produced += job.cSize;
    -                zcs->doneJobID++;
    +                        mtctx->doneJobID, (U32)job.dstFlushed);
    +                ZSTDMT_releaseBuffer(mtctx->bufPool, job.dstBuff);
    +                mtctx->jobs[wJobID].dstBuff = g_nullBuffer;
    +                mtctx->consumed += job.srcSize;
    +                mtctx->produced += job.cSize;
    +                mtctx->doneJobID++;
                 } else {
    -                zcs->jobs[wJobID].dstFlushed = job.dstFlushed;   /* remember how much was flushed for next attempt */
    +                mtctx->jobs[wJobID].dstFlushed = job.dstFlushed;   /* remember how much was flushed for next attempt */
             }   }
     
             /* return value : how many bytes left in buffer ; fake it to 1 when unknown but >0 */
             if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed);
             if (job.srcSize > job.consumed) return 1;   /* current job not completely compressed */
         }
    -    if (zcs->doneJobID < zcs->nextJobID) return 1;   /* some more jobs to flush */
    -    if (zcs->jobReady) return 1;   /* one job is ready and queued! */
    -    if (zcs->inBuff.filled > 0) return 1;   /* input not empty */
    -    zcs->allJobsCompleted = zcs->frameEnded;   /* last frame entirely flushed */
    -    if (end == ZSTD_e_end) return !zcs->frameEnded;  /* for ZSTD_e_end, question becomes : is frame completed ? It may need a last null-block */
    +    if (mtctx->doneJobID < mtctx->nextJobID) return 1;   /* some more jobs to flush */
    +    if (mtctx->jobReady) return 1;   /* one job is ready and queued! */
    +    if (mtctx->inBuff.filled > 0) return 1;   /* input not empty */
    +    mtctx->allJobsCompleted = mtctx->frameEnded;   /* last frame entirely flushed */
    +    if (end == ZSTD_e_end) return !mtctx->frameEnded;  /* for ZSTD_e_end, question becomes : is frame completed ? It may need a last null-block */
         return 0;   /* everything flushed */
     }
     
    @@ -1241,12 +1267,12 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
     }
     
     
    -size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input)
    +size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input)
     {
    -    CHECK_F( ZSTDMT_compressStream_generic(zcs, output, input, ZSTD_e_continue) );
    +    CHECK_F( ZSTDMT_compressStream_generic(mtctx, output, input, ZSTD_e_continue) );
     
         /* recommended next input size : fill current input buffer */
    -    return zcs->inBuffSize - zcs->inBuff.filled;   /* note : could be zero when input buffer is fully filled and no more availability to create new job */
    +    return mtctx->inBuffSize - mtctx->inBuff.filled;   /* note : could be zero when input buffer is fully filled and no more availability to create new job */
     }
     
     
    
    From 777d3c15593295457d8ecf2c60209e3bee910311 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 25 Jan 2018 17:45:18 -0800
    Subject: [PATCH 086/255] fixed minor declaration-after-statement warning
    
    ---
     lib/compress/zstd_compress.c | 7 ++++---
     1 file changed, 4 insertions(+), 3 deletions(-)
    
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index 7b16d7364..2904cc1da 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -2032,9 +2032,10 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity,
     size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity)
     {
         if (dstCapacity < ZSTD_blockHeaderSize) return ERROR(dstSize_tooSmall);
    -    U32 const cBlockHeader24 = 1 /*lastBlock*/ + (((U32)bt_raw)<<1);  /* 0 size */
    -    MEM_writeLE24(dst, cBlockHeader24);
    -    return ZSTD_blockHeaderSize;
    +    {   U32 const cBlockHeader24 = 1 /*lastBlock*/ + (((U32)bt_raw)<<1);  /* 0 size */
    +        MEM_writeLE24(dst, cBlockHeader24);
    +        return ZSTD_blockHeaderSize;
    +    }
     }
     
     
    
    From 8e128eaf058dfc9e70d19fa59ab21053845926b3 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 26 Jan 2018 10:20:38 -0800
    Subject: [PATCH 087/255] zstdmt : refactor job members
    
    grouped by sharing properties
    ---
     doc/zstd_manual.html           |  7 ++++---
     lib/compress/zstdmt_compress.c | 38 +++++++++++++++++-----------------
     tests/zstreamtest.c            |  4 ++--
     3 files changed, 25 insertions(+), 24 deletions(-)
    
    diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
    index dc3f8be1b..b4aed91e5 100644
    --- a/doc/zstd_manual.html
    +++ b/doc/zstd_manual.html
    @@ -926,7 +926,7 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t
     


    typedef enum {
    -    ZSTD_e_continue=0, /* collect more data, encoder transparently decides when to output result, for optimal conditions */
    +    ZSTD_e_continue=0, /* collect more data, encoder decides when to output compressed result, for optimal conditions */
         ZSTD_e_flush,      /* flush any data provided so far - frame will continue, future data can still reference previous data for better compression */
         ZSTD_e_end         /* flush any remaining data and close current frame. Any additional data starts a new frame. */
     } ZSTD_EndDirective;
    @@ -945,10 +945,11 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t
                                                          and then immediately returns, just indicating that there is some data remaining to be flushed.
                                                          The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte.
       - Exception : in multi-threading mode, if the first call requests a ZSTD_e_end directive, it is blocking : it will complete compression before giving back control to caller.
    -  - @return provides the minimum amount of data remaining to be flushed from internal buffers
    +  - @return provides a minimum amount of data remaining to be flushed from internal buffers
                 or an error code, which can be tested using ZSTD_isError().
                 if @return != 0, flush is not fully completed, there is still some data left within internal buffers.
    -            This is useful to determine if a ZSTD_e_flush or ZSTD_e_end directive is completed.
    +            This is useful for ZSTD_e_flush, since in this case more flushes are necessary to empty all buffers.
    +            For ZSTD_e_end, @return == 0 when internal buffers are fully flushed and frame is completed.
       - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0),
                 only ZSTD_e_end or ZSTD_e_flush operations are allowed.
                 Before starting a new compression job, or changing compression parameters,
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 451bb5bf6..4e53367fd 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -300,28 +300,28 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx)
     
     
     /* ------------------------------------------ */
    -/* =====          Thread worker         ===== */
    +/* =====          Worker thread         ===== */
     /* ------------------------------------------ */
     
     typedef struct {
    -    buffer_t src;
    -    const void* srcStart;
    -    size_t   prefixSize;
    -    size_t   srcSize;
    -    size_t   consumed;
    -    buffer_t dstBuff;
    -    size_t   cSize;
    -    size_t   dstFlushed;
    -    unsigned firstChunk;
    -    unsigned lastChunk;
    -    unsigned frameChecksumNeeded;
    -    ZSTD_pthread_mutex_t* mtctx_mutex;
    -    ZSTD_pthread_cond_t* mtctx_cond;
    -    ZSTD_CCtx_params params;
    -    const ZSTD_CDict* cdict;
    -    ZSTDMT_CCtxPool* cctxPool;
    -    ZSTDMT_bufferPool* bufPool;
    -    unsigned long long fullFrameSize;
    +    size_t   consumed;       /* SHARED - init0 by mtctx, then modified by worker AND read by mtctx */
    +    size_t   cSize;          /* SHARED - init0 by mtctx, then modified by worker AND read by mtctx */
    +    ZSTD_pthread_mutex_t* mtctx_mutex;   /* Thread-safe - used by mtctx and (all) worker */
    +    ZSTD_pthread_cond_t* mtctx_cond;     /* Thread-safe - used by mtctx and (all) worker */
    +    ZSTDMT_CCtxPool* cctxPool;           /* Thread-safe - used by mtctx and (all) worker */
    +    ZSTDMT_bufferPool* bufPool;          /* Thread-safe - used by mtctx and (all) worker */
    +    buffer_t dstBuff;        /* set by worker (or mtctx), then read by worker, then modified by mtctx => no barrier */
    +    buffer_t src;            /* set by mtctx, then modified by worker => no barrier */
    +    const void* srcStart;    /* set by mtctx, then read by worker => no barrier */
    +    size_t   prefixSize;     /* set by mtctx, then read by worker => no barrier */
    +    size_t   srcSize;        /* set by mtctx, then read by worker => no barrier */
    +    unsigned firstChunk;     /* set by mtctx, then read by worker => no barrier */
    +    unsigned lastChunk;      /* set by mtctx, then read by worker => no barrier */
    +    ZSTD_CCtx_params params;             /* set by mtctx, then read by worker => no barrier */
    +    const ZSTD_CDict* cdict;             /* set by mtctx, then read by worker => no barrier */
    +    unsigned long long fullFrameSize;    /* set by mtctx, then read by worker => no barrier */
    +    size_t   dstFlushed;                 /* used only by mtctx */
    +    unsigned frameChecksumNeeded;        /* used only by mtctx */
     } ZSTDMT_jobDescription;
     
     /* ZSTDMT_compressChunk() is a POOL_function type */
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index ef5cf65fb..16dcba73a 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -1220,9 +1220,9 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp
     
             FUZ_rand(&coreSeed);
             if (nbTests >= testNb) {
    -            DISPLAYUPDATE(2, "\r%6u/%6u (%08X)   ", testNb, nbTests, coreSeed);
    +            DISPLAYUPDATE(2, "\r%6u/%6u    ", testNb, nbTests);
             } else {
    -            DISPLAYUPDATE(2, "\r%6u  (%08X)        ", testNb, coreSeed);
    +            DISPLAYUPDATE(2, "\r%6u         ", testNb);
             }
             lseed = coreSeed ^ prime32;
     
    
    From fca13c6855d28d6ba8628794465372cb6088442d Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 26 Jan 2018 10:44:09 -0800
    Subject: [PATCH 088/255] zstdmt : fixed memory leak
    
    writeLastEmptyBlock() must release srcBuffer
    as mtctx assumes it's done by job worker.
    
    minor : changed 2 job member names (src->srcBuffer, srcStart->prefixStart) for clarity
    ---
     lib/compress/zstdmt_compress.c | 73 ++++++++++++++++++----------------
     1 file changed, 38 insertions(+), 35 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 4e53367fd..9fea4969f 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -304,19 +304,19 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx)
     /* ------------------------------------------ */
     
     typedef struct {
    -    size_t   consumed;       /* SHARED - init0 by mtctx, then modified by worker AND read by mtctx */
    -    size_t   cSize;          /* SHARED - init0 by mtctx, then modified by worker AND read by mtctx */
    +    size_t   consumed;                   /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx */
    +    size_t   cSize;                      /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx */
         ZSTD_pthread_mutex_t* mtctx_mutex;   /* Thread-safe - used by mtctx and (all) worker */
         ZSTD_pthread_cond_t* mtctx_cond;     /* Thread-safe - used by mtctx and (all) worker */
         ZSTDMT_CCtxPool* cctxPool;           /* Thread-safe - used by mtctx and (all) worker */
         ZSTDMT_bufferPool* bufPool;          /* Thread-safe - used by mtctx and (all) worker */
    -    buffer_t dstBuff;        /* set by worker (or mtctx), then read by worker, then modified by mtctx => no barrier */
    -    buffer_t src;            /* set by mtctx, then modified by worker => no barrier */
    -    const void* srcStart;    /* set by mtctx, then read by worker => no barrier */
    -    size_t   prefixSize;     /* set by mtctx, then read by worker => no barrier */
    -    size_t   srcSize;        /* set by mtctx, then read by worker => no barrier */
    -    unsigned firstChunk;     /* set by mtctx, then read by worker => no barrier */
    -    unsigned lastChunk;      /* set by mtctx, then read by worker => no barrier */
    +    buffer_t dstBuff;                    /* set by worker (or mtctx), then read by worker, then modified by mtctx => no barrier */
    +    buffer_t srcBuff;                    /* set by mtctx, then released by worker => no barrier */
    +    const void* prefixStart;             /* set by mtctx, then read by worker => no barrier */
    +    size_t   prefixSize;                 /* set by mtctx, then read by worker => no barrier */
    +    size_t   srcSize;                    /* set by mtctx, then read by worker => no barrier */
    +    unsigned firstChunk;                 /* set by mtctx, then read by worker => no barrier */
    +    unsigned lastChunk;                  /* set by mtctx, then read by worker => no barrier */
         ZSTD_CCtx_params params;             /* set by mtctx, then read by worker => no barrier */
         const ZSTD_CDict* cdict;             /* set by mtctx, then read by worker => no barrier */
         unsigned long long fullFrameSize;    /* set by mtctx, then read by worker => no barrier */
    @@ -329,7 +329,7 @@ void ZSTDMT_compressChunk(void* jobDescription)
     {
         ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription;
         ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool);
    -    const void* const src = (const char*)job->srcStart + job->prefixSize;
    +    const void* const src = (const char*)job->prefixStart + job->prefixSize;
         buffer_t dstBuff = job->dstBuff;
     
         /* ressources */
    @@ -362,7 +362,7 @@ void ZSTDMT_compressChunk(void* jobDescription)
                     goto _endJob;
             }   }
             {   size_t const initError = ZSTD_compressBegin_advanced_internal(cctx,
    -                                        job->srcStart, job->prefixSize, ZSTD_dm_rawContent, /* load dictionary in "content-only" mode (no header analysis) */
    +                                        job->prefixStart, job->prefixSize, ZSTD_dm_rawContent, /* load dictionary in "content-only" mode (no header analysis) */
                                             NULL, /*cdict*/
                                             jobParams, pledgedSrcSize);
                 if (ZSTD_isError(initError)) {
    @@ -419,8 +419,8 @@ void ZSTDMT_compressChunk(void* jobDescription)
     _endJob:
         /* release resources */
         ZSTDMT_releaseCCtx(job->cctxPool, cctx);
    -    ZSTDMT_releaseBuffer(job->bufPool, job->src);
    -    job->src = g_nullBuffer; job->srcStart = NULL;
    +    ZSTDMT_releaseBuffer(job->bufPool, job->srcBuff);
    +    job->srcBuff = g_nullBuffer; job->prefixStart = NULL;
         /* report */
         ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);
         job->consumed = job->srcSize;
    @@ -557,9 +557,9 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx)
             DEBUGLOG(4, "job%02u: release dst address %08X", jobID, (U32)(size_t)mtctx->jobs[jobID].dstBuff.start);
             ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);
             mtctx->jobs[jobID].dstBuff = g_nullBuffer;
    -        DEBUGLOG(4, "job%02u: release src address %08X", jobID, (U32)(size_t)mtctx->jobs[jobID].src.start);
    -        ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].src);
    -        mtctx->jobs[jobID].src = g_nullBuffer;
    +        DEBUGLOG(4, "job%02u: release src address %08X", jobID, (U32)(size_t)mtctx->jobs[jobID].srcBuff.start);
    +        ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].srcBuff);
    +        mtctx->jobs[jobID].srcBuff = g_nullBuffer;
         }
         memset(mtctx->jobs, 0, (mtctx->jobIDMask+1)*sizeof(ZSTDMT_jobDescription));
         DEBUGLOG(4, "input: release address %08X", (U32)(size_t)mtctx->inBuff.buffer.start);
    @@ -757,8 +757,8 @@ static size_t ZSTDMT_compress_advanced_internal(
                 buffer_t const dstBuffer = u < compressWithinDst ? dstAsBuffer : g_nullBuffer;
                 size_t dictSize = u ? overlapSize : 0;
     
    -            mtctx->jobs[u].src = g_nullBuffer;
    -            mtctx->jobs[u].srcStart = srcStart + frameStartPos - dictSize;
    +            mtctx->jobs[u].srcBuff = g_nullBuffer;
    +            mtctx->jobs[u].prefixStart = srcStart + frameStartPos - dictSize;
                 mtctx->jobs[u].prefixSize = dictSize;
                 mtctx->jobs[u].srcSize = chunkSize; assert(chunkSize > 0);  /* avoid job.srcSize == 0 */
                 mtctx->jobs[u].consumed = 0;
    @@ -781,7 +781,7 @@ static size_t ZSTDMT_compress_advanced_internal(
                 }
     
                 DEBUGLOG(5, "ZSTDMT_compress_advanced_internal: posting job %u  (%u bytes)", u, (U32)chunkSize);
    -            DEBUG_PRINTHEX(6, mtctx->jobs[u].srcStart, 12);
    +            DEBUG_PRINTHEX(6, mtctx->jobs[u].prefixStart, 12);
                 POOL_add(mtctx->factory, ZSTDMT_compressChunk, &mtctx->jobs[u]);
     
                 frameStartPos += chunkSize;
    @@ -802,7 +802,7 @@ static size_t ZSTDMT_compress_advanced_internal(
                 ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
                 DEBUGLOG(5, "ready to write chunk %u ", chunkID);
     
    -            mtctx->jobs[chunkID].srcStart = NULL;
    +            mtctx->jobs[chunkID].prefixStart = NULL;
                 {   size_t const cSize = mtctx->jobs[chunkID].cSize;
                     if (ZSTD_isError(cSize)) error = cSize;
                     if ((!error) && (dstPos + cSize > dstCapacity)) error = ERROR(dstSize_tooSmall);
    @@ -999,18 +999,21 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel) {
      */
     static size_t ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job)
     {
    -    assert(job->srcSize == 0);
         assert(job->lastChunk == 1);
    -    assert(job->firstChunk == 0);   /* first chunk needs to create frame header too */
    -    assert(job->dstBuff.start == NULL);  /* invoked from streaming variant only */
    -    {   buffer_t const dstBuff = ZSTDMT_getBuffer(job->bufPool);
    -        if (dstBuff.start==NULL) return ERROR(memory_allocation);
    -        job->dstBuff = dstBuff;   /* will be released by ZSTDMT_flushProduced() */
    -        assert(dstBuff.size >= ZSTD_blockHeaderSize);
    -        job->cSize = ZSTD_writeLastEmptyBlock(dstBuff.start, dstBuff.size);
    -        assert(!ZSTD_isError(job->cSize));
    -        assert(job->consumed == 0);
    -    }
    +    assert(job->srcSize == 0);      /* last chunk is empty -> will be simplified into a last empty block */
    +    assert(job->firstChunk == 0);   /* cannot be first chunk, as it also needs to create frame header */
    +    /* A job created by streaming variant starts with a src buffer, but no dst buffer.
    +     * It summons a dstBuffer itself, compresses into it, then releases srcBuffer, and gives result to mtctx.
    +     * When done, srcBuffer is empty, while dstBuffer is filled, and will be released by mtctx.
    +     * This shortcut will simply switch srcBuffer for dstBuffer, providing same outcome as a normal job */
    +    assert(job->dstBuff.start == NULL);  /* invoked from streaming variant only (otherwise, dstBuff might be user's output) */
    +    assert(job->srcBuff.start != NULL);      /* invoked from streaming variant only (otherwise, srcBuff might be user's input) */
    +    assert(job->srcBuff.size >= ZSTD_blockHeaderSize);
    +    job->dstBuff = job->srcBuff;
    +    job->srcBuff = g_nullBuffer;
    +    job->cSize = ZSTD_writeLastEmptyBlock(job->dstBuff.start, job->dstBuff.size);
    +    assert(!ZSTD_isError(job->cSize));
    +    assert(job->consumed == 0);
         return 0;
     }
     
    @@ -1028,12 +1031,12 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
         if (!mtctx->jobReady) {
             DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
                         mtctx->nextJobID, (U32)srcSize, (U32)mtctx->prefixSize);
    -        mtctx->jobs[jobID].src = mtctx->inBuff.buffer;
    -        mtctx->jobs[jobID].srcStart = mtctx->inBuff.buffer.start;
    +        mtctx->jobs[jobID].srcBuff = mtctx->inBuff.buffer;
    +        mtctx->jobs[jobID].prefixStart = mtctx->inBuff.buffer.start;
    +        mtctx->jobs[jobID].prefixSize = mtctx->prefixSize;
             mtctx->jobs[jobID].srcSize = srcSize;
             mtctx->jobs[jobID].consumed = 0;
             mtctx->jobs[jobID].cSize = 0;
    -        mtctx->jobs[jobID].prefixSize = mtctx->prefixSize;
             assert(mtctx->inBuff.filled >= srcSize + mtctx->prefixSize);
             mtctx->jobs[jobID].params = mtctx->params;
             /* do not calculate checksum within sections, but write it in header for first section */
    @@ -1066,7 +1069,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
                 }
                 mtctx->inBuff.filled -= srcSize + mtctx->prefixSize - newPrefixSize;
                 memmove(mtctx->inBuff.buffer.start,   /* copy end of current job into next job, as "prefix" */
    -                (const char*)mtctx->jobs[jobID].srcStart + mtctx->prefixSize + srcSize - newPrefixSize,
    +                (const char*)mtctx->jobs[jobID].prefixStart + mtctx->prefixSize + srcSize - newPrefixSize,
                     mtctx->inBuff.filled);
                 mtctx->prefixSize = newPrefixSize;
             } else {   /* endFrame==1 => no need for another input buffer */
    
    From d2b62b6fa5a752e8312a6c8b9fbedcf53bfa60ff Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 26 Jan 2018 11:06:34 -0800
    Subject: [PATCH 089/255] minor : ZSTDMT_writeLastEmptyBlock() is a void
     function
    
    because it cannot fail
    ---
     lib/compress/zstdmt_compress.c | 28 ++++++++++++++++------------
     1 file changed, 16 insertions(+), 12 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 9fea4969f..7b37c5b33 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -142,6 +142,10 @@ static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool)
         return poolSize + totalBufferSize;
     }
     
    +/* ZSTDMT_setBufferSize() :
    + * all future buffers provided by this buffer pool will have _at least_ this size
    + * note : it's better for all buffers to have same size,
    + * as they become freely interchangeable, reducing malloc/free usages and memory fragmentation */
     static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* const bufPool, size_t const bSize)
     {
         ZSTD_pthread_mutex_lock(&bufPool->poolMutex);
    @@ -992,12 +996,11 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel) {
     
     
     /* ZSTDMT_writeLastEmptyBlock()
    - * Write a single empty block with an end-of-frame
    - * to finish a frame.
    - * Completed synchronously.
    - * @return : 0, or an error code (can fail due to memory allocation)
    + * Write a single empty block with an end-of-frame to finish a frame.
    + * Job must be created from streaming variant.
    + * This function is always successfull if expected conditions are fulfilled.
      */
    -static size_t ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job)
    +static void ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job)
     {
         assert(job->lastChunk == 1);
         assert(job->srcSize == 0);      /* last chunk is empty -> will be simplified into a last empty block */
    @@ -1006,15 +1009,14 @@ static size_t ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job)
          * It summons a dstBuffer itself, compresses into it, then releases srcBuffer, and gives result to mtctx.
          * When done, srcBuffer is empty, while dstBuffer is filled, and will be released by mtctx.
          * This shortcut will simply switch srcBuffer for dstBuffer, providing same outcome as a normal job */
    -    assert(job->dstBuff.start == NULL);  /* invoked from streaming variant only (otherwise, dstBuff might be user's output) */
    -    assert(job->srcBuff.start != NULL);      /* invoked from streaming variant only (otherwise, srcBuff might be user's input) */
    -    assert(job->srcBuff.size >= ZSTD_blockHeaderSize);
    +    assert(job->dstBuff.start == NULL);   /* invoked from streaming variant only (otherwise, dstBuff might be user's output) */
    +    assert(job->srcBuff.start != NULL);   /* invoked from streaming variant only (otherwise, srcBuff might be user's input) */
    +    assert(job->srcBuff.size >= ZSTD_blockHeaderSize);   /* no buffer should ever be that small */
         job->dstBuff = job->srcBuff;
         job->srcBuff = g_nullBuffer;
         job->cSize = ZSTD_writeLastEmptyBlock(job->dstBuff.start, job->dstBuff.size);
         assert(!ZSTD_isError(job->cSize));
         assert(job->consumed == 0);
    -    return 0;
     }
     
     static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZSTD_EndDirective endOp)
    @@ -1031,6 +1033,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
         if (!mtctx->jobReady) {
             DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
                         mtctx->nextJobID, (U32)srcSize, (U32)mtctx->prefixSize);
    +        assert(mtctx->jobs[jobID].srcBuff.start == NULL);   /* no buffer left : supposed already released */
             mtctx->jobs[jobID].srcBuff = mtctx->inBuff.buffer;
             mtctx->jobs[jobID].prefixStart = mtctx->inBuff.buffer.start;
             mtctx->jobs[jobID].prefixSize = mtctx->prefixSize;
    @@ -1085,7 +1088,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
             if ( (srcSize == 0)
               && (mtctx->nextJobID>0)/*single chunk must also write frame header*/ ) {
                 assert(endOp == ZSTD_e_end);  /* only possible case : need to end the frame with an empty last block */
    -            CHECK_F( ZSTDMT_writeLastEmptyBlock(mtctx->jobs + jobID) );
    +            ZSTDMT_writeLastEmptyBlock(mtctx->jobs + jobID);
                 mtctx->nextJobID++;
                 return 0;
             }
    @@ -1162,10 +1165,11 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
                 output->pos += toWrite;
                 job.dstFlushed += toWrite;
     
    -            if ( (job.consumed == job.srcSize)
    -              && (job.dstFlushed == job.cSize) ) {   /* output buffer fully flushed => move to next one */
    +            if ( (job.consumed == job.srcSize)      /* job completed */
    +              && (job.dstFlushed == job.cSize) ) {  /* output buffer fully flushed => free this job position */
                     DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one",
                             mtctx->doneJobID, (U32)job.dstFlushed);
    +                assert(job.srcBuff.start == NULL);  /* srcBuff supposed already released */
                     ZSTDMT_releaseBuffer(mtctx->bufPool, job.dstBuff);
                     mtctx->jobs[wJobID].dstBuff = g_nullBuffer;
                     mtctx->consumed += job.srcSize;
    
    From 79b6e28b0a168bb076dbaf653b1a7d71bbd1a201 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 26 Jan 2018 12:15:43 -0800
    Subject: [PATCH 090/255] zstdmt : flush() only lock to read shared job members
    
    Other job members are accessed directly.
    This avoids a full job copy, which would access everything,
    including a few members that are supposed to be used by worker only,
    uselessly requiring additional locks to avoid race conditions.
    ---
     lib/compress/zstdmt_compress.c | 115 +++++++++++++++++----------------
     1 file changed, 58 insertions(+), 57 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 7b37c5b33..c7df32d37 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -309,16 +309,16 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx)
     
     typedef struct {
         size_t   consumed;                   /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx */
    -    size_t   cSize;                      /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx */
    -    ZSTD_pthread_mutex_t* mtctx_mutex;   /* Thread-safe - used by mtctx and (all) worker */
    -    ZSTD_pthread_cond_t* mtctx_cond;     /* Thread-safe - used by mtctx and (all) worker */
    -    ZSTDMT_CCtxPool* cctxPool;           /* Thread-safe - used by mtctx and (all) worker */
    -    ZSTDMT_bufferPool* bufPool;          /* Thread-safe - used by mtctx and (all) worker */
    -    buffer_t dstBuff;                    /* set by worker (or mtctx), then read by worker, then modified by mtctx => no barrier */
    +    size_t   cSize;                      /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx, then set0 by mtctx */
    +    ZSTD_pthread_mutex_t* mtctx_mutex;   /* Thread-safe - used by mtctx and (all) workers */
    +    ZSTD_pthread_cond_t* mtctx_cond;     /* Thread-safe - used by mtctx and (all) workers */
    +    ZSTDMT_CCtxPool* cctxPool;           /* Thread-safe - used by mtctx and (all) workers */
    +    ZSTDMT_bufferPool* bufPool;          /* Thread-safe - used by mtctx and (all) workers */
    +    buffer_t dstBuff;                    /* set by worker (or mtctx), then read by worker & mtctx, then modified by mtctx => no barrier */
         buffer_t srcBuff;                    /* set by mtctx, then released by worker => no barrier */
    -    const void* prefixStart;             /* set by mtctx, then read by worker => no barrier */
    +    const void* prefixStart;             /* set by mtctx, then read and set0 by worker => no barrier */
         size_t   prefixSize;                 /* set by mtctx, then read by worker => no barrier */
    -    size_t   srcSize;                    /* set by mtctx, then read by worker => no barrier */
    +    size_t   srcSize;                    /* set by mtctx, then read by worker & mtctx => no barrier */
         unsigned firstChunk;                 /* set by mtctx, then read by worker => no barrier */
         unsigned lastChunk;                  /* set by mtctx, then read by worker => no barrier */
         ZSTD_CCtx_params params;             /* set by mtctx, then read by worker => no barrier */
    @@ -341,15 +341,13 @@ void ZSTDMT_compressChunk(void* jobDescription)
             job->cSize = ERROR(memory_allocation);
             goto _endJob;
         }
    -    if (dstBuff.start == NULL) {
    +    if (dstBuff.start == NULL) {   /* streaming job : doesn't provide a dstBuffer */
             dstBuff = ZSTDMT_getBuffer(job->bufPool);
             if (dstBuff.start==NULL) {
                 job->cSize = ERROR(memory_allocation);
                 goto _endJob;
             }
    -        ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);
             job->dstBuff = dstBuff;   /* this value can be read in ZSTDMT_flush, when it copies the whole job */
    -        ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
         }
     
         /* init */
    @@ -1087,6 +1085,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
     
             if ( (srcSize == 0)
               && (mtctx->nextJobID>0)/*single chunk must also write frame header*/ ) {
    +            DEBUGLOG(5, "ZSTDMT_createCompressionJob: creating a last empty block to end frame");
                 assert(endOp == ZSTD_e_end);  /* only possible case : need to end the frame with an empty last block */
                 ZSTDMT_writeLastEmptyBlock(mtctx->jobs + jobID);
                 mtctx->nextJobID++;
    @@ -1094,12 +1093,12 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
             }
         }
     
    -    DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes  (end:%u) (note : doneJob = %u=>%u)",
    +    DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes  (end:%u, jobNb == %u (mod:%u))",
                     mtctx->nextJobID,
                     (U32)mtctx->jobs[jobID].srcSize,
                     mtctx->jobs[jobID].lastChunk,
    -                mtctx->doneJobID,
    -                mtctx->doneJobID & mtctx->jobIDMask);
    +                mtctx->nextJobID,
    +                jobID);
         if (POOL_tryAdd(mtctx->factory, ZSTDMT_compressChunk, &mtctx->jobs[jobID])) {
             mtctx->nextJobID++;
             mtctx->jobReady = 0;
    @@ -1118,15 +1117,17 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
     static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, unsigned blockToFlush, ZSTD_EndDirective end)
     {
         unsigned const wJobID = mtctx->doneJobID & mtctx->jobIDMask;
    -    DEBUGLOG(5, "ZSTDMT_flushProduced (blocking:%u)", blockToFlush);
    +    DEBUGLOG(5, "ZSTDMT_flushProduced (blocking:%u , job %u <= %u)",
    +                blockToFlush, mtctx->doneJobID, mtctx->nextJobID);
         assert(output->size >= output->pos);
     
         ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->mtctx_mutex);
    -    if (blockToFlush && (mtctx->doneJobID < mtctx->nextJobID)) {
    +    if (  blockToFlush
    +      && (mtctx->doneJobID < mtctx->nextJobID) ) {
             assert(mtctx->jobs[wJobID].dstFlushed <= mtctx->jobs[wJobID].cSize);
    -        while (mtctx->jobs[wJobID].dstFlushed == mtctx->jobs[wJobID].cSize) {
    +        while (mtctx->jobs[wJobID].dstFlushed == mtctx->jobs[wJobID].cSize) {  /* nothing to flush */
                 if (mtctx->jobs[wJobID].consumed == mtctx->jobs[wJobID].srcSize) {
    -                DEBUGLOG(5, "job %u is completely consumed (%u == %u) => don't wait for cond",
    +                DEBUGLOG(5, "job %u is completely consumed (%u == %u) => don't wait for cond, there will be none",
                                 mtctx->doneJobID, (U32)mtctx->jobs[wJobID].consumed, (U32)mtctx->jobs[wJobID].srcSize);
                     break;
                 }
    @@ -1135,60 +1136,60 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
                 ZSTD_pthread_cond_wait(&mtctx->mtctx_cond, &mtctx->mtctx_mutex);  /* block when nothing available to flush but more to come */
         }   }
     
    -    /* some output is available to be flushed */
    -    {   ZSTDMT_jobDescription job = mtctx->jobs[wJobID];
    +    /* try to flush something */
    +    {   size_t cSize = mtctx->jobs[wJobID].cSize;
    +        size_t const srcConsumed = mtctx->jobs[wJobID].consumed;
             ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
    -        if (ZSTD_isError(job.cSize)) {
    +        if (ZSTD_isError(cSize)) {
                 DEBUGLOG(5, "ZSTDMT_flushProduced: job %u : compression error detected : %s",
    -                        mtctx->doneJobID, ZSTD_getErrorName(job.cSize));
    +                        mtctx->doneJobID, ZSTD_getErrorName(cSize));
                 ZSTDMT_waitForAllJobsCompleted(mtctx);
                 ZSTDMT_releaseAllJobResources(mtctx);
    -            return job.cSize;
    +            return cSize;
             }
             /* add frame checksum if necessary (can only happen once) */
    -        assert(job.consumed <= job.srcSize);
    -        if ( (job.consumed == job.srcSize)
    -          && job.frameChecksumNeeded ) {
    +        assert(srcConsumed <= mtctx->jobs[wJobID].srcSize);
    +        if ( (srcConsumed == mtctx->jobs[wJobID].srcSize)   /* job completed -> worker no longer active */
    +          && mtctx->jobs[wJobID].frameChecksumNeeded ) {
                 U32 const checksum = (U32)XXH64_digest(&mtctx->xxhState);
                 DEBUGLOG(4, "ZSTDMT_flushProduced: writing checksum : %08X \n", checksum);
    -            MEM_writeLE32((char*)job.dstBuff.start + job.cSize, checksum);
    -            job.cSize += 4;
    -            mtctx->jobs[wJobID].cSize += 4;
    +            MEM_writeLE32((char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].cSize, checksum);
    +            cSize += 4;
    +            mtctx->jobs[wJobID].cSize += 4;  /* can write this shared value, as worker is no longer active */
                 mtctx->jobs[wJobID].frameChecksumNeeded = 0;
             }
    -        assert(job.cSize >= job.dstFlushed);
    -        if (job.dstBuff.start != NULL) {  /* dst buffer present : some work is ongoing or completed */
    -            size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos);
    -            DEBUGLOG(5, "ZSTDMT_flushProduced: Flushing %u bytes from job %u (completion:%.1f%%)",
    -                        (U32)toWrite, mtctx->doneJobID, (double)job.consumed / (job.srcSize + !job.srcSize /*avoid div0*/) * 100);
    -            memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite);
    +        if (cSize > 0) {  /* compression is ongoing or completed */
    +            size_t const toWrite = MIN(cSize - mtctx->jobs[wJobID].dstFlushed, output->size - output->pos);
    +            DEBUGLOG(5, "ZSTDMT_flushProduced: Flushing %u bytes from job %u (completion:%u/%u)",
    +                        (U32)toWrite, mtctx->doneJobID, (U32)srcConsumed, (U32)mtctx->jobs[wJobID].srcSize);
    +            assert(cSize >= mtctx->jobs[wJobID].dstFlushed);
    +            memcpy((char*)output->dst + output->pos, (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed, toWrite);
                 output->pos += toWrite;
    -            job.dstFlushed += toWrite;
    +            mtctx->jobs[wJobID].dstFlushed += toWrite;  /* can write : this value is only used by mtctx */
     
    -            if ( (job.consumed == job.srcSize)      /* job completed */
    -              && (job.dstFlushed == job.cSize) ) {  /* output buffer fully flushed => free this job position */
    +            if ( (srcConsumed == mtctx->jobs[wJobID].srcSize)    /* job completed */
    +              && (mtctx->jobs[wJobID].dstFlushed == cSize) ) {   /* output buffer fully flushed => free this job position */
                     DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one",
    -                        mtctx->doneJobID, (U32)job.dstFlushed);
    -                assert(job.srcBuff.start == NULL);  /* srcBuff supposed already released */
    -                ZSTDMT_releaseBuffer(mtctx->bufPool, job.dstBuff);
    +                        mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed);
    +                assert(mtctx->jobs[wJobID].srcBuff.start == NULL);  /* srcBuff supposed already released */
    +                ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[wJobID].dstBuff);
                     mtctx->jobs[wJobID].dstBuff = g_nullBuffer;
    -                mtctx->consumed += job.srcSize;
    -                mtctx->produced += job.cSize;
    +                mtctx->jobs[wJobID].cSize = 0;   /* ensure this job slot is considered "not started" in future check */
    +                mtctx->consumed += mtctx->jobs[wJobID].srcSize;
    +                mtctx->produced += cSize;
                     mtctx->doneJobID++;
    -            } else {
    -                mtctx->jobs[wJobID].dstFlushed = job.dstFlushed;   /* remember how much was flushed for next attempt */
             }   }
     
             /* return value : how many bytes left in buffer ; fake it to 1 when unknown but >0 */
    -        if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed);
    -        if (job.srcSize > job.consumed) return 1;   /* current job not completely compressed */
    +        if (cSize > mtctx->jobs[wJobID].dstFlushed) return (cSize - mtctx->jobs[wJobID].dstFlushed);
    +        if (mtctx->jobs[wJobID].srcSize > srcConsumed) return 1;   /* current job not completely compressed */
         }
    -    if (mtctx->doneJobID < mtctx->nextJobID) return 1;   /* some more jobs to flush */
    -    if (mtctx->jobReady) return 1;   /* one job is ready and queued! */
    -    if (mtctx->inBuff.filled > 0) return 1;   /* input not empty */
    -    mtctx->allJobsCompleted = mtctx->frameEnded;   /* last frame entirely flushed */
    -    if (end == ZSTD_e_end) return !mtctx->frameEnded;  /* for ZSTD_e_end, question becomes : is frame completed ? It may need a last null-block */
    -    return 0;   /* everything flushed */
    +    if (mtctx->doneJobID < mtctx->nextJobID) return 1;   /* some more jobs ongoing */
    +    if (mtctx->jobReady) return 1;      /* one job is ready to push, just not yet in the list */
    +    if (mtctx->inBuff.filled > 0) return 1;   /* input is not empty, and still needs to be converted into a job */
    +    mtctx->allJobsCompleted = mtctx->frameEnded;   /* all chunks are entirely flushed => if this one is last one, frame is completed */
    +    if (end == ZSTD_e_end) return !mtctx->frameEnded;  /* for ZSTD_e_end, question becomes : is frame completed ? instead of : are internal buffers fully flushed ? */
    +    return 0;   /* internal buffers fully flushed */
     }
     
     
    @@ -1217,10 +1218,10 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
         }
     
         /* single-pass shortcut (note : synchronous-mode) */
    -    if ( (mtctx->nextJobID == 0)     /* just started */
    -      && (mtctx->inBuff.filled == 0) /* nothing buffered */
    -      && (!mtctx->jobReady)          /* no job already created */
    -      && (endOp == ZSTD_e_end)       /* end order */
    +    if ( (mtctx->nextJobID == 0)      /* just started */
    +      && (mtctx->inBuff.filled == 0)  /* nothing buffered */
    +      && (!mtctx->jobReady)           /* no job already created */
    +      && (endOp == ZSTD_e_end)        /* end order */
           && (output->size - output->pos >= ZSTD_compressBound(input->size - input->pos)) ) { /* enough space in dst */
             size_t const cSize = ZSTDMT_compress_advanced_internal(mtctx,
                     (char*)output->dst + output->pos, output->size - output->pos,
    
    From 0d426f6b8391c3b5c5714fd83c014609ec238e7d Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 26 Jan 2018 13:00:14 -0800
    Subject: [PATCH 091/255] zstdmt : refactor a few member names
    
    for clarity
    ---
     lib/compress/zstdmt_compress.c | 94 +++++++++++++++++-----------------
     1 file changed, 48 insertions(+), 46 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index c7df32d37..3bf585d49 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -83,7 +83,7 @@ static unsigned long long GetCurrentClockTimeMicroseconds(void)
     
     typedef struct buffer_s {
         void* start;
    -    size_t size;
    +    size_t capacity;
     } buffer_t;
     
     static const buffer_t g_nullBuffer = { NULL, 0 };
    @@ -136,7 +136,7 @@ static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool)
         size_t totalBufferSize = 0;
         ZSTD_pthread_mutex_lock(&bufPool->poolMutex);
         for (u=0; utotalBuffers; u++)
    -        totalBufferSize += bufPool->bTable[u].size;
    +        totalBufferSize += bufPool->bTable[u].capacity;
         ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
     
         return poolSize + totalBufferSize;
    @@ -165,12 +165,12 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool)
         ZSTD_pthread_mutex_lock(&bufPool->poolMutex);
         if (bufPool->nbBuffers) {   /* try to use an existing buffer */
             buffer_t const buf = bufPool->bTable[--(bufPool->nbBuffers)];
    -        size_t const availBufferSize = buf.size;
    +        size_t const availBufferSize = buf.capacity;
             bufPool->bTable[bufPool->nbBuffers] = g_nullBuffer;
             if ((availBufferSize >= bSize) & ((availBufferSize>>3) <= bSize)) {
                 /* large enough, but not too much */
                 DEBUGLOG(5, "ZSTDMT_getBuffer: provide buffer %u of size %u",
    -                        bufPool->nbBuffers, (U32)buf.size);
    +                        bufPool->nbBuffers, (U32)buf.capacity);
                 ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
                 return buf;
             }
    @@ -184,7 +184,7 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool)
         {   buffer_t buffer;
             void* const start = ZSTD_malloc(bSize, bufPool->cMem);
             buffer.start = start;   /* note : start can be NULL if malloc fails ! */
    -        buffer.size = (start==NULL) ? 0 : bSize;
    +        buffer.capacity = (start==NULL) ? 0 : bSize;
             if (start==NULL) {
                 DEBUGLOG(5, "ZSTDMT_getBuffer: buffer allocation failure !!");
             } else {
    @@ -203,7 +203,7 @@ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf)
         if (bufPool->nbBuffers < bufPool->totalBuffers) {
             bufPool->bTable[bufPool->nbBuffers++] = buf;  /* stored for later use */
             DEBUGLOG(5, "ZSTDMT_releaseBuffer: stored buffer of size %u in slot %u",
    -                    (U32)buf.size, (U32)(bufPool->nbBuffers-1));
    +                    (U32)buf.capacity, (U32)(bufPool->nbBuffers-1));
             ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
             return;
         }
    @@ -372,7 +372,7 @@ void ZSTDMT_compressChunk(void* jobDescription)
                     goto _endJob;
         }   }   }
         if (!job->firstChunk) {  /* flush and overwrite frame header when it's not first job */
    -        size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, 0);
    +        size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, src, 0);
             if (ZSTD_isError(hSize)) { job->cSize = hSize; /* save error code */ goto _endJob; }
             DEBUGLOG(5, "ZSTDMT_compressChunk: flush and overwrite %u bytes of frame header (not first chunk)", (U32)hSize);
             ZSTD_invalidateRepCodes(cctx);
    @@ -386,7 +386,7 @@ void ZSTDMT_compressChunk(void* jobDescription)
             const BYTE* ip = (const BYTE*) src;
             BYTE* const ostart = (BYTE*)dstBuff.start;
             BYTE* op = ostart;
    -        BYTE* oend = op + dstBuff.size;
    +        BYTE* oend = op + dstBuff.capacity;
             int blockNb;
             DEBUGLOG(5, "ZSTDMT_compressChunk: compress %u bytes in %i blocks", (U32)job->srcSize, nbBlocks);
             assert(job->cSize == 0);
    @@ -437,6 +437,8 @@ _endJob:
     
     typedef struct {
         buffer_t buffer;
    +    size_t targetCapacity;  /* note : buffers provided by the pool may be larger than target capacity */
    +    size_t prefixSize;
         size_t filled;
     } inBuff_t;
     
    @@ -449,8 +451,6 @@ struct ZSTDMT_CCtx_s {
         ZSTD_pthread_cond_t mtctx_cond;
         ZSTD_CCtx_params params;
         size_t targetSectionSize;
    -    size_t inBuffSize;
    -    size_t prefixSize;
         size_t targetPrefixSize;
         inBuff_t inBuff;
         int jobReady;        /* 1 => one job is already prepared, but pool has shortage of workers. Don't create another one. */
    @@ -663,13 +663,13 @@ unsigned ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx)
      * Note : mutex will be acquired during statistics collection. */
     ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
     {
    -    ZSTD_frameProgression fs;
    +    ZSTD_frameProgression fps;
         DEBUGLOG(6, "ZSTDMT_getFrameProgression");
         ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->mtctx_mutex);
    -    fs.consumed = mtctx->consumed;
    -    fs.produced = mtctx->produced;
    -    assert(mtctx->inBuff.filled >= mtctx->prefixSize);
    -    fs.ingested = mtctx->consumed + (mtctx->inBuff.filled - mtctx->prefixSize);
    +    fps.consumed = mtctx->consumed;
    +    fps.produced = mtctx->produced;
    +    assert(mtctx->inBuff.filled >= mtctx->inBuff.prefixSize);
    +    fps.ingested = mtctx->consumed + (mtctx->inBuff.filled - mtctx->inBuff.prefixSize);
         {   unsigned jobNb;
             unsigned lastJobNb = mtctx->nextJobID + mtctx->jobReady; assert(mtctx->jobReady <= 1);
             DEBUGLOG(6, "ZSTDMT_getFrameProgression: jobs: from %u to <%u (jobReady:%u)",
    @@ -678,13 +678,13 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
                 unsigned const wJobID = jobNb & mtctx->jobIDMask;
                 size_t const cResult = mtctx->jobs[wJobID].cSize;
                 size_t const produced = ZSTD_isError(cResult) ? 0 : cResult;
    -            fs.consumed += mtctx->jobs[wJobID].consumed;
    -            fs.ingested += mtctx->jobs[wJobID].srcSize;
    -            fs.produced += produced;
    +            fps.consumed += mtctx->jobs[wJobID].consumed;
    +            fps.ingested += mtctx->jobs[wJobID].srcSize;
    +            fps.produced += produced;
             }
         }
         ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
    -    return fs;
    +    return fps;
     }
     
     
    @@ -928,11 +928,11 @@ size_t ZSTDMT_initCStream_internal(
         if (mtctx->targetSectionSize < ZSTDMT_JOBSIZE_MIN) mtctx->targetSectionSize = ZSTDMT_JOBSIZE_MIN;
         if (mtctx->targetSectionSize < mtctx->targetPrefixSize) mtctx->targetSectionSize = mtctx->targetPrefixSize;  /* job size must be >= overlap size */
         DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(mtctx->targetSectionSize>>10), params.jobSize);
    -    mtctx->inBuffSize = mtctx->targetPrefixSize + mtctx->targetSectionSize;
    -    DEBUGLOG(4, "inBuff Size : %u KB", (U32)(mtctx->inBuffSize>>10));
    -    ZSTDMT_setBufferSize(mtctx->bufPool, MAX(mtctx->inBuffSize, ZSTD_compressBound(mtctx->targetSectionSize)) );
    +    mtctx->inBuff.targetCapacity = mtctx->targetPrefixSize + mtctx->targetSectionSize;
    +    DEBUGLOG(4, "inBuff Size : %u KB", (U32)(mtctx->inBuff.targetCapacity>>10));
    +    ZSTDMT_setBufferSize(mtctx->bufPool, MAX(mtctx->inBuff.targetCapacity, ZSTD_compressBound(mtctx->targetSectionSize)) );
         mtctx->inBuff.buffer = g_nullBuffer;
    -    mtctx->prefixSize = 0;
    +    mtctx->inBuff.prefixSize = 0;
         mtctx->doneJobID = 0;
         mtctx->nextJobID = 0;
         mtctx->frameEnded = 0;
    @@ -1009,10 +1009,10 @@ static void ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job)
          * This shortcut will simply switch srcBuffer for dstBuffer, providing same outcome as a normal job */
         assert(job->dstBuff.start == NULL);   /* invoked from streaming variant only (otherwise, dstBuff might be user's output) */
         assert(job->srcBuff.start != NULL);   /* invoked from streaming variant only (otherwise, srcBuff might be user's input) */
    -    assert(job->srcBuff.size >= ZSTD_blockHeaderSize);   /* no buffer should ever be that small */
    +    assert(job->srcBuff.capacity >= ZSTD_blockHeaderSize);   /* no buffer should ever be that small */
         job->dstBuff = job->srcBuff;
         job->srcBuff = g_nullBuffer;
    -    job->cSize = ZSTD_writeLastEmptyBlock(job->dstBuff.start, job->dstBuff.size);
    +    job->cSize = ZSTD_writeLastEmptyBlock(job->dstBuff.start, job->dstBuff.capacity);
         assert(!ZSTD_isError(job->cSize));
         assert(job->consumed == 0);
     }
    @@ -1030,15 +1030,15 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
     
         if (!mtctx->jobReady) {
             DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
    -                    mtctx->nextJobID, (U32)srcSize, (U32)mtctx->prefixSize);
    +                    mtctx->nextJobID, (U32)srcSize, (U32)mtctx->inBuff.prefixSize);
             assert(mtctx->jobs[jobID].srcBuff.start == NULL);   /* no buffer left : supposed already released */
             mtctx->jobs[jobID].srcBuff = mtctx->inBuff.buffer;
             mtctx->jobs[jobID].prefixStart = mtctx->inBuff.buffer.start;
    -        mtctx->jobs[jobID].prefixSize = mtctx->prefixSize;
    +        mtctx->jobs[jobID].prefixSize = mtctx->inBuff.prefixSize;
             mtctx->jobs[jobID].srcSize = srcSize;
    +        assert(mtctx->inBuff.filled >= srcSize + mtctx->inBuff.prefixSize);
             mtctx->jobs[jobID].consumed = 0;
             mtctx->jobs[jobID].cSize = 0;
    -        assert(mtctx->inBuff.filled >= srcSize + mtctx->prefixSize);
             mtctx->jobs[jobID].params = mtctx->params;
             /* do not calculate checksum within sections, but write it in header for first section */
             if (mtctx->nextJobID) mtctx->jobs[jobID].params.fParams.checksumFlag = 0;
    @@ -1055,28 +1055,28 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
             mtctx->jobs[jobID].mtctx_cond = &mtctx->mtctx_cond;
     
             if (mtctx->params.fParams.checksumFlag)
    -            XXH64_update(&mtctx->xxhState, (const char*)mtctx->inBuff.buffer.start + mtctx->prefixSize, srcSize);
    +            XXH64_update(&mtctx->xxhState, (const char*)mtctx->inBuff.buffer.start + mtctx->inBuff.prefixSize, srcSize);
     
             /* get a new buffer for next input */
             if (!endFrame) {
    -            size_t const newPrefixSize = MIN(srcSize + mtctx->prefixSize, mtctx->targetPrefixSize);
    +            size_t const newPrefixSize = MIN(mtctx->inBuff.filled, mtctx->targetPrefixSize);
                 mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool);
    -            if (mtctx->inBuff.buffer.start == NULL) {   /* not enough memory to allocate next input buffer */
    +            if (mtctx->inBuff.buffer.start == NULL) {    /* not enough memory to allocate a new input buffer */
                     mtctx->jobs[jobID].srcSize = mtctx->jobs[jobID].consumed = 0;
                     mtctx->nextJobID++;
                     ZSTDMT_waitForAllJobsCompleted(mtctx);
                     ZSTDMT_releaseAllJobResources(mtctx);
                     return ERROR(memory_allocation);
                 }
    -            mtctx->inBuff.filled -= srcSize + mtctx->prefixSize - newPrefixSize;
    +            mtctx->inBuff.filled -= (mtctx->inBuff.prefixSize + srcSize) - newPrefixSize;
                 memmove(mtctx->inBuff.buffer.start,   /* copy end of current job into next job, as "prefix" */
    -                (const char*)mtctx->jobs[jobID].prefixStart + mtctx->prefixSize + srcSize - newPrefixSize,
    +                (const char*)mtctx->jobs[jobID].prefixStart + mtctx->inBuff.prefixSize + srcSize - newPrefixSize,
                     mtctx->inBuff.filled);
    -            mtctx->prefixSize = newPrefixSize;
    +            mtctx->inBuff.prefixSize = newPrefixSize;
             } else {   /* endFrame==1 => no need for another input buffer */
                 mtctx->inBuff.buffer = g_nullBuffer;
                 mtctx->inBuff.filled = 0;
    -            mtctx->prefixSize = 0;
    +            mtctx->inBuff.prefixSize = 0;
                 mtctx->frameEnded = endFrame;
                 if (mtctx->nextJobID == 0) {
                     /* single chunk exception : checksum is already calculated directly within worker thread */
    @@ -1202,7 +1202,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
                                          ZSTD_inBuffer* input,
                                          ZSTD_EndDirective endOp)
     {
    -    size_t const newJobThreshold = mtctx->prefixSize + mtctx->targetSectionSize;
    +    size_t const newJobThreshold = mtctx->inBuff.prefixSize + mtctx->targetSectionSize;
         unsigned forwardInputProgress = 0;
         DEBUGLOG(5, "ZSTDMT_compressStream_generic (endOp=%u)", (U32)endOp);
         assert(output->pos <= output->size);
    @@ -1240,16 +1240,18 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
         if ( (!mtctx->jobReady)
           && (input->size > input->pos) ) {   /* support NULL input */
             if (mtctx->inBuff.buffer.start == NULL) {
    -            mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool);  /* note : allocation can fail, in which case, no forward input progress */
    +            mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool);  /* note : allocation can fail, in which case, buffer.start==NULL */
                 mtctx->inBuff.filled = 0;
    -            if ( (mtctx->inBuff.buffer.start == NULL)    /* allocation failure */
    +            if ( (mtctx->inBuff.buffer.start == NULL)        /* allocation failure */
                   && (mtctx->doneJobID == mtctx->nextJobID) ) {  /* and nothing to flush */
    -                return ERROR(memory_allocation);   /* no forward progress possible => output an error */
    -        }   }
    -        if (mtctx->inBuff.buffer.start != NULL) {
    -            size_t const toLoad = MIN(input->size - input->pos, mtctx->inBuffSize - mtctx->inBuff.filled);
    +                return ERROR(memory_allocation);    /* no forward progress possible => output an error */
    +            }
    +            assert(mtctx->inBuff.buffer.capacity >= mtctx->inBuff.targetCapacity);  /* pool must provide a buffer >= targetCapacity */
    +        }
    +        if (mtctx->inBuff.buffer.start != NULL) {   /* no buffer for input, but it's possible to flush, and then reclaim the buffer */
    +            size_t const toLoad = MIN(input->size - input->pos, mtctx->inBuff.targetCapacity - mtctx->inBuff.filled);
                 DEBUGLOG(5, "ZSTDMT_compressStream_generic: adding %u bytes on top of %u to buffer of size %u",
    -                        (U32)toLoad, (U32)mtctx->inBuff.filled, (U32)mtctx->inBuffSize);
    +                        (U32)toLoad, (U32)mtctx->inBuff.filled, (U32)mtctx->inBuff.targetCapacity);
                 memcpy((char*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, (const char*)input->src + input->pos, toLoad);
                 input->pos += toLoad;
                 mtctx->inBuff.filled += toLoad;
    @@ -1263,7 +1265,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
           || (mtctx->inBuff.filled >= newJobThreshold)  /* filled enough : let's compress */
           || ((endOp != ZSTD_e_continue) && (mtctx->inBuff.filled > 0))  /* something to flush : let's go */
           || ((endOp == ZSTD_e_end) && (!mtctx->frameEnded)) ) {   /* must finish the frame with a zero-size block */
    -        size_t const jobSize = MIN(mtctx->inBuff.filled - mtctx->prefixSize, mtctx->targetSectionSize);
    +        size_t const jobSize = MIN(mtctx->inBuff.filled - mtctx->inBuff.prefixSize, mtctx->targetSectionSize);
             CHECK_F( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp) );
         }
     
    @@ -1280,13 +1282,13 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_in
         CHECK_F( ZSTDMT_compressStream_generic(mtctx, output, input, ZSTD_e_continue) );
     
         /* recommended next input size : fill current input buffer */
    -    return mtctx->inBuffSize - mtctx->inBuff.filled;   /* note : could be zero when input buffer is fully filled and no more availability to create new job */
    +    return mtctx->inBuff.targetCapacity - mtctx->inBuff.filled;   /* note : could be zero when input buffer is fully filled and no more availability to create new job */
     }
     
     
     static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_EndDirective endFrame)
     {
    -    size_t const srcSize = mtctx->inBuff.filled - mtctx->prefixSize;
    +    size_t const srcSize = mtctx->inBuff.filled - mtctx->inBuff.prefixSize;
         DEBUGLOG(5, "ZSTDMT_flushStream_internal");
     
         if ( mtctx->jobReady     /* one job ready for a worker to pick up */
    
    From 27c5853c4285fa678fb9f606a87d21852b533ac0 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 26 Jan 2018 14:35:54 -0800
    Subject: [PATCH 092/255] zstdmt: job table correctly cleaned after synchronous
     ZSTDMT_compress()
    
    ---
     lib/compress/zstdmt_compress.c | 13 +++++++++----
     1 file changed, 9 insertions(+), 4 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 3bf585d49..d78257b2e 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -559,6 +559,7 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx)
             DEBUGLOG(4, "job%02u: release dst address %08X", jobID, (U32)(size_t)mtctx->jobs[jobID].dstBuff.start);
             ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);
             mtctx->jobs[jobID].dstBuff = g_nullBuffer;
    +        mtctx->jobs[jobID].cSize = 0;
             DEBUGLOG(4, "job%02u: release src address %08X", jobID, (U32)(size_t)mtctx->jobs[jobID].srcBuff.start);
             ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].srcBuff);
             mtctx->jobs[jobID].srcBuff = g_nullBuffer;
    @@ -816,6 +817,7 @@ static size_t ZSTDMT_compress_advanced_internal(
                             ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[chunkID].dstBuff);
                     }   }
                     mtctx->jobs[chunkID].dstBuff = g_nullBuffer;
    +                mtctx->jobs[chunkID].cSize = 0;
                     dstPos += cSize ;
                 }
             }  /* for (chunkID=0; chunkIDjobs[wJobID].cSize += 4;  /* can write this shared value, as worker is no longer active */
                 mtctx->jobs[wJobID].frameChecksumNeeded = 0;
             }
    -        if (cSize > 0) {  /* compression is ongoing or completed */
    +        if (cSize > 0) {   /* compression is ongoing or completed */
                 size_t const toWrite = MIN(cSize - mtctx->jobs[wJobID].dstFlushed, output->size - output->pos);
    -            DEBUGLOG(5, "ZSTDMT_flushProduced: Flushing %u bytes from job %u (completion:%u/%u)",
    -                        (U32)toWrite, mtctx->doneJobID, (U32)srcConsumed, (U32)mtctx->jobs[wJobID].srcSize);
    +            DEBUGLOG(5, "ZSTDMT_flushProduced: Flushing %u bytes from job %u (completion:%u/%u, generated:%u)",
    +                        (U32)toWrite, mtctx->doneJobID, (U32)srcConsumed, (U32)mtctx->jobs[wJobID].srcSize, (U32)cSize);
    +            assert(mtctx->doneJobID < mtctx->nextJobID);
                 assert(cSize >= mtctx->jobs[wJobID].dstFlushed);
    +            assert(mtctx->jobs[wJobID].dstBuff.start != NULL);
                 memcpy((char*)output->dst + output->pos, (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed, toWrite);
                 output->pos += toWrite;
                 mtctx->jobs[wJobID].dstFlushed += toWrite;  /* can write : this value is only used by mtctx */
    @@ -1204,7 +1208,8 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
     {
         size_t const newJobThreshold = mtctx->inBuff.prefixSize + mtctx->targetSectionSize;
         unsigned forwardInputProgress = 0;
    -    DEBUGLOG(5, "ZSTDMT_compressStream_generic (endOp=%u)", (U32)endOp);
    +    DEBUGLOG(5, "ZSTDMT_compressStream_generic (endOp=%u, srcSize=%u)",
    +                (U32)endOp, (U32)(input->size - input->pos));
         assert(output->pos <= output->size);
         assert(input->pos  <= input->size);
     
    
    From 77e36273de31ded4fc35703514175af07bdf2fb5 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 26 Jan 2018 17:08:58 -0800
    Subject: [PATCH 093/255] zstdmt: minor code refactor for clarity
    
    ---
     lib/compress/zstdmt_compress.c | 80 +++++++++++++++++++---------------
     1 file changed, 44 insertions(+), 36 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index d78257b2e..e04864507 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -379,35 +379,35 @@ void ZSTDMT_compressChunk(void* jobDescription)
         }
     
         /* compress */
    -    if (sizeof(size_t) > sizeof(int))
    -        assert(job->srcSize < ((size_t)INT_MAX) * ZSTD_BLOCKSIZE_MAX);   /* check overflow */
    -
    -    {   int const nbBlocks = (int)((job->srcSize + (ZSTD_BLOCKSIZE_MAX-1)) / ZSTD_BLOCKSIZE_MAX);
    +    {   size_t const blockSize = ZSTD_BLOCKSIZE_MAX;
    +        int const nbBlocks = (int)((job->srcSize + (blockSize-1)) / blockSize);
             const BYTE* ip = (const BYTE*) src;
             BYTE* const ostart = (BYTE*)dstBuff.start;
             BYTE* op = ostart;
             BYTE* oend = op + dstBuff.capacity;
             int blockNb;
    +        if (sizeof(size_t) > sizeof(int)) assert(job->srcSize < ((size_t)INT_MAX) * blockSize);   /* check overflow */
             DEBUGLOG(5, "ZSTDMT_compressChunk: compress %u bytes in %i blocks", (U32)job->srcSize, nbBlocks);
             assert(job->cSize == 0);
             for (blockNb = 1; blockNb < nbBlocks; blockNb++) {
    -            size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, ZSTD_BLOCKSIZE_MAX);
    +            size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, blockSize);
                 if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; }
    -            ip += ZSTD_BLOCKSIZE_MAX;
    +            ip += blockSize;
                 op += cSize; assert(op < oend);
                 /* stats */
                 ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);   /* note : it's a mtctx mutex */
                 job->cSize += cSize;
    -            job->consumed = ZSTD_BLOCKSIZE_MAX * blockNb;
    +            job->consumed = blockSize * blockNb;
                 DEBUGLOG(5, "ZSTDMT_compressChunk: compress new block : cSize==%u bytes (total: %u)",
                             (U32)cSize, (U32)job->cSize);
                 ZSTD_pthread_cond_signal(job->mtctx_cond);   /* warns some more data is ready to be flushed */
                 ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
             }
             /* last block */
    +        assert(blockSize > 0); assert((blockSize & (blockSize - 1)) == 0);  /* blockSize must be power of 2 for mask==(blockSize-1) to work */
             if ((nbBlocks > 0) | job->lastChunk /*must output a "last block" flag*/ ) {
    -            size_t const lastBlockSize1 = job->srcSize & (ZSTD_BLOCKSIZE_MAX-1);
    -            size_t const lastBlockSize = ((lastBlockSize1==0) & (job->srcSize>=ZSTD_BLOCKSIZE_MAX)) ? ZSTD_BLOCKSIZE_MAX : lastBlockSize1;
    +            size_t const lastBlockSize1 = job->srcSize & (blockSize-1);
    +            size_t const lastBlockSize = ((lastBlockSize1==0) & (job->srcSize>=blockSize)) ? blockSize : lastBlockSize1;
                 size_t const cSize = (job->lastChunk) ?
                      ZSTD_compressEnd     (cctx, op, oend-op, ip, lastBlockSize) :
                      ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize);
    @@ -469,21 +469,10 @@ struct ZSTDMT_CCtx_s {
         const ZSTD_CDict* cdict;
     };
     
    -/* Sets parameters relevant to the compression job, initializing others to
    - * default values. Notably, nbThreads should probably be zero. */
    -static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params)
    -{
    -    ZSTD_CCtx_params jobParams;
    -    memset(&jobParams, 0, sizeof(jobParams));
    -
    -    jobParams.cParams = params.cParams;
    -    jobParams.fParams = params.fParams;
    -    jobParams.compressionLevel = params.compressionLevel;
    -
    -    jobParams.ldmParams = params.ldmParams;
    -    return jobParams;
    -}
    -
    +/* ZSTDMT_allocJobsTable()
    + * allocate, and just init to zero, a job table.
    + * update *nbJobsPtr to next power of 2 value, as size of table
    + * No reverse free() function is provided : just use ZSTD_free() */
     static ZSTDMT_jobDescription* ZSTDMT_allocJobsTable(U32* nbJobsPtr, ZSTD_customMem cMem)
     {
         U32 const nbJobsLog2 = ZSTD_highbit32(*nbJobsPtr) + 1;
    @@ -524,6 +513,7 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem)
         mtctx->allJobsCompleted = 1;
         mtctx->factory = POOL_create_advanced(nbThreads, 0, cMem);
         mtctx->jobs = ZSTDMT_allocJobsTable(&nbJobs, cMem);
    +    assert(nbJobs > 0); assert((nbJobs & (nbJobs - 1)) == 0);  /* ensure nbJobs is a power of 2 */
         mtctx->jobIDMask = nbJobs - 1;
         mtctx->bufPool = ZSTDMT_createBufferPool(nbThreads, cMem);
         mtctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads, cMem);
    @@ -649,6 +639,21 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter,
         }
     }
     
    +/* Sets parameters relevant to the compression job, initializing others to
    + * default values. Notably, nbThreads should probably be zero. */
    +static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params)
    +{
    +    ZSTD_CCtx_params jobParams;
    +    memset(&jobParams, 0, sizeof(jobParams));
    +
    +    jobParams.cParams = params.cParams;
    +    jobParams.fParams = params.fParams;
    +    jobParams.compressionLevel = params.compressionLevel;
    +
    +    jobParams.ldmParams = params.ldmParams;
    +    return jobParams;
    +}
    +
     /* ZSTDMT_getNbThreads():
      * @return nb threads currently active in mtctx.
      * mtctx must be valid */
    @@ -1139,8 +1144,9 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
         }   }
     
         /* try to flush something */
    -    {   size_t cSize = mtctx->jobs[wJobID].cSize;
    -        size_t const srcConsumed = mtctx->jobs[wJobID].consumed;
    +    {   size_t cSize = mtctx->jobs[wJobID].cSize;                  /* shared */
    +        size_t const srcConsumed = mtctx->jobs[wJobID].consumed;   /* shared */
    +        size_t const srcSize = mtctx->jobs[wJobID].srcSize;        /* read-only, could be done after mutex lock, but no-declaration-after-statement */
             ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
             if (ZSTD_isError(cSize)) {
                 DEBUGLOG(5, "ZSTDMT_flushProduced: job %u : compression error detected : %s",
    @@ -1150,8 +1156,8 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
                 return cSize;
             }
             /* add frame checksum if necessary (can only happen once) */
    -        assert(srcConsumed <= mtctx->jobs[wJobID].srcSize);
    -        if ( (srcConsumed == mtctx->jobs[wJobID].srcSize)   /* job completed -> worker no longer active */
    +        assert(srcConsumed <= srcSize);
    +        if ( (srcConsumed == srcSize)   /* job completed -> worker no longer active */
               && mtctx->jobs[wJobID].frameChecksumNeeded ) {
                 U32 const checksum = (U32)XXH64_digest(&mtctx->xxhState);
                 DEBUGLOG(4, "ZSTDMT_flushProduced: writing checksum : %08X \n", checksum);
    @@ -1161,17 +1167,19 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
                 mtctx->jobs[wJobID].frameChecksumNeeded = 0;
             }
             if (cSize > 0) {   /* compression is ongoing or completed */
    -            size_t const toWrite = MIN(cSize - mtctx->jobs[wJobID].dstFlushed, output->size - output->pos);
    +            size_t const toFlush = MIN(cSize - mtctx->jobs[wJobID].dstFlushed, output->size - output->pos);
                 DEBUGLOG(5, "ZSTDMT_flushProduced: Flushing %u bytes from job %u (completion:%u/%u, generated:%u)",
    -                        (U32)toWrite, mtctx->doneJobID, (U32)srcConsumed, (U32)mtctx->jobs[wJobID].srcSize, (U32)cSize);
    +                        (U32)toFlush, mtctx->doneJobID, (U32)srcConsumed, (U32)srcSize, (U32)cSize);
                 assert(mtctx->doneJobID < mtctx->nextJobID);
                 assert(cSize >= mtctx->jobs[wJobID].dstFlushed);
                 assert(mtctx->jobs[wJobID].dstBuff.start != NULL);
    -            memcpy((char*)output->dst + output->pos, (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed, toWrite);
    -            output->pos += toWrite;
    -            mtctx->jobs[wJobID].dstFlushed += toWrite;  /* can write : this value is only used by mtctx */
    +            memcpy((char*)output->dst + output->pos,
    +                   (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed,
    +                   toFlush);
    +            output->pos += toFlush;
    +            mtctx->jobs[wJobID].dstFlushed += toFlush;  /* can write : this value is only used by mtctx */
     
    -            if ( (srcConsumed == mtctx->jobs[wJobID].srcSize)    /* job completed */
    +            if ( (srcConsumed == srcSize)    /* job completed */
                   && (mtctx->jobs[wJobID].dstFlushed == cSize) ) {   /* output buffer fully flushed => free this job position */
                     DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one",
                             mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed);
    @@ -1179,14 +1187,14 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
                     ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[wJobID].dstBuff);
                     mtctx->jobs[wJobID].dstBuff = g_nullBuffer;
                     mtctx->jobs[wJobID].cSize = 0;   /* ensure this job slot is considered "not started" in future check */
    -                mtctx->consumed += mtctx->jobs[wJobID].srcSize;
    +                mtctx->consumed += srcSize;
                     mtctx->produced += cSize;
                     mtctx->doneJobID++;
             }   }
     
             /* return value : how many bytes left in buffer ; fake it to 1 when unknown but >0 */
             if (cSize > mtctx->jobs[wJobID].dstFlushed) return (cSize - mtctx->jobs[wJobID].dstFlushed);
    -        if (mtctx->jobs[wJobID].srcSize > srcConsumed) return 1;   /* current job not completely compressed */
    +        if (srcSize > srcConsumed) return 1;   /* current job not completely compressed */
         }
         if (mtctx->doneJobID < mtctx->nextJobID) return 1;   /* some more jobs ongoing */
         if (mtctx->jobReady) return 1;      /* one job is ready to push, just not yet in the list */
    
    From 9c40ae7ff16a6561914092e130b47c7a45f7914b Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 26 Jan 2018 17:48:33 -0800
    Subject: [PATCH 094/255] zstdmt: there is now one mutex/cond per job
    
    ---
     lib/compress/zstdmt_compress.c | 103 +++++++++++++++++----------------
     tests/zstreamtest.c            |   4 +-
     2 files changed, 55 insertions(+), 52 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index e04864507..22560bfb1 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -310,8 +310,8 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx)
     typedef struct {
         size_t   consumed;                   /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx */
         size_t   cSize;                      /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx, then set0 by mtctx */
    -    ZSTD_pthread_mutex_t* mtctx_mutex;   /* Thread-safe - used by mtctx and (all) workers */
    -    ZSTD_pthread_cond_t* mtctx_cond;     /* Thread-safe - used by mtctx and (all) workers */
    +    ZSTD_pthread_mutex_t job_mutex;      /* Thread-safe - used by mtctx and worker */
    +    ZSTD_pthread_cond_t job_cond;        /* Thread-safe - used by mtctx and worker */
         ZSTDMT_CCtxPool* cctxPool;           /* Thread-safe - used by mtctx and (all) workers */
         ZSTDMT_bufferPool* bufPool;          /* Thread-safe - used by mtctx and (all) workers */
         buffer_t dstBuff;                    /* set by worker (or mtctx), then read by worker & mtctx, then modified by mtctx => no barrier */
    @@ -395,13 +395,13 @@ void ZSTDMT_compressChunk(void* jobDescription)
                 ip += blockSize;
                 op += cSize; assert(op < oend);
                 /* stats */
    -            ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);   /* note : it's a mtctx mutex */
    +            ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex);   /* note : it's a mtctx mutex */
                 job->cSize += cSize;
                 job->consumed = blockSize * blockNb;
                 DEBUGLOG(5, "ZSTDMT_compressChunk: compress new block : cSize==%u bytes (total: %u)",
                             (U32)cSize, (U32)job->cSize);
    -            ZSTD_pthread_cond_signal(job->mtctx_cond);   /* warns some more data is ready to be flushed */
    -            ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
    +            ZSTD_pthread_cond_signal(&job->job_cond);   /* warns some more data is ready to be flushed */
    +            ZSTD_pthread_mutex_unlock(&job->job_mutex);
             }
             /* last block */
             assert(blockSize > 0); assert((blockSize & (blockSize - 1)) == 0);  /* blockSize must be power of 2 for mask==(blockSize-1) to work */
    @@ -413,9 +413,9 @@ void ZSTDMT_compressChunk(void* jobDescription)
                      ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize);
                 if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; }
                 /* stats */
    -            ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);
    +            ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex);
                 job->cSize += cSize;
    -            ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
    +            ZSTD_pthread_mutex_unlock(&job->job_mutex);
         }   }
     
     _endJob:
    @@ -424,10 +424,10 @@ _endJob:
         ZSTDMT_releaseBuffer(job->bufPool, job->srcBuff);
         job->srcBuff = g_nullBuffer; job->prefixStart = NULL;
         /* report */
    -    ZSTD_PTHREAD_MUTEX_LOCK(job->mtctx_mutex);
    +    ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex);
         job->consumed = job->srcSize;
    -    ZSTD_pthread_cond_signal(job->mtctx_cond);
    -    ZSTD_pthread_mutex_unlock(job->mtctx_mutex);
    +    ZSTD_pthread_cond_signal(&job->job_cond);
    +    ZSTD_pthread_mutex_unlock(&job->job_mutex);
     }
     
     
    @@ -447,8 +447,6 @@ struct ZSTDMT_CCtx_s {
         ZSTDMT_jobDescription* jobs;
         ZSTDMT_bufferPool* bufPool;
         ZSTDMT_CCtxPool* cctxPool;
    -    ZSTD_pthread_mutex_t mtctx_mutex;
    -    ZSTD_pthread_cond_t mtctx_cond;
         ZSTD_CCtx_params params;
         size_t targetSectionSize;
         size_t targetPrefixSize;
    @@ -470,16 +468,34 @@ struct ZSTDMT_CCtx_s {
     };
     
     /* ZSTDMT_allocJobsTable()
    - * allocate, and just init to zero, a job table.
    + * allocate and init a job table.
      * update *nbJobsPtr to next power of 2 value, as size of table
      * No reverse free() function is provided : just use ZSTD_free() */
    -static ZSTDMT_jobDescription* ZSTDMT_allocJobsTable(U32* nbJobsPtr, ZSTD_customMem cMem)
    +static ZSTDMT_jobDescription* ZSTDMT_createJobsTable(U32* nbJobsPtr, ZSTD_customMem cMem)
     {
         U32 const nbJobsLog2 = ZSTD_highbit32(*nbJobsPtr) + 1;
         U32 const nbJobs = 1 << nbJobsLog2;
    -    *nbJobsPtr = nbJobs;
    -    return (ZSTDMT_jobDescription*) ZSTD_calloc(
    +    ZSTDMT_jobDescription* const jobTable = ZSTD_calloc(
                                 nbJobs * sizeof(ZSTDMT_jobDescription), cMem);
    +    U32 jobNb;
    +    if (jobTable==NULL) return NULL;
    +    *nbJobsPtr = nbJobs;
    +    for (jobNb=0; jobNbcMem = cMem;
         mtctx->allJobsCompleted = 1;
         mtctx->factory = POOL_create_advanced(nbThreads, 0, cMem);
    -    mtctx->jobs = ZSTDMT_allocJobsTable(&nbJobs, cMem);
    +    mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, cMem);
         assert(nbJobs > 0); assert((nbJobs & (nbJobs - 1)) == 0);  /* ensure nbJobs is a power of 2 */
         mtctx->jobIDMask = nbJobs - 1;
         mtctx->bufPool = ZSTDMT_createBufferPool(nbThreads, cMem);
    @@ -521,14 +537,6 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem)
             ZSTDMT_freeCCtx(mtctx);
             return NULL;
         }
    -    if (ZSTD_pthread_mutex_init(&mtctx->mtctx_mutex, NULL)) {
    -        ZSTDMT_freeCCtx(mtctx);
    -        return NULL;
    -    }
    -    if (ZSTD_pthread_cond_init(&mtctx->mtctx_cond, NULL)) {
    -        ZSTDMT_freeCCtx(mtctx);
    -        return NULL;
    -    }
         DEBUGLOG(3, "mt_cctx created, for %u threads", nbThreads);
         return mtctx;
     }
    @@ -566,12 +574,12 @@ static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* mtctx)
         DEBUGLOG(4, "ZSTDMT_waitForAllJobsCompleted");
         while (mtctx->doneJobID < mtctx->nextJobID) {
             unsigned const jobID = mtctx->doneJobID & mtctx->jobIDMask;
    -        ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->mtctx_mutex);
    +        ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[jobID].job_mutex);
             while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].srcSize) {
                 DEBUGLOG(5, "waiting for jobCompleted signal from chunk %u", mtctx->doneJobID);   /* we want to block when waiting for data to flush */
    -            ZSTD_pthread_cond_wait(&mtctx->mtctx_cond, &mtctx->mtctx_mutex);
    +            ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex);
             }
    -        ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
    +        ZSTD_pthread_mutex_unlock(&mtctx->jobs[jobID].job_mutex);
             mtctx->doneJobID++;
         }
     }
    @@ -581,12 +589,10 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx)
         if (mtctx==NULL) return 0;   /* compatible with free on NULL */
         POOL_free(mtctx->factory);   /* stop and free worker threads */
         ZSTDMT_releaseAllJobResources(mtctx);  /* release job resources into pools first */
    -    ZSTD_free(mtctx->jobs, mtctx->cMem);
    +    ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem);
         ZSTDMT_freeBufferPool(mtctx->bufPool);
         ZSTDMT_freeCCtxPool(mtctx->cctxPool);
         ZSTD_freeCDict(mtctx->cdictLocal);
    -    ZSTD_pthread_mutex_destroy(&mtctx->mtctx_mutex);
    -    ZSTD_pthread_cond_destroy(&mtctx->mtctx_cond);
         ZSTD_free(mtctx, mtctx->cMem);
         return 0;
     }
    @@ -671,7 +677,6 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
     {
         ZSTD_frameProgression fps;
         DEBUGLOG(6, "ZSTDMT_getFrameProgression");
    -    ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->mtctx_mutex);
         fps.consumed = mtctx->consumed;
         fps.produced = mtctx->produced;
         assert(mtctx->inBuff.filled >= mtctx->inBuff.prefixSize);
    @@ -682,14 +687,16 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
                         mtctx->doneJobID, lastJobNb, mtctx->jobReady)
             for (jobNb = mtctx->doneJobID ; jobNb < lastJobNb ; jobNb++) {
                 unsigned const wJobID = jobNb & mtctx->jobIDMask;
    -            size_t const cResult = mtctx->jobs[wJobID].cSize;
    -            size_t const produced = ZSTD_isError(cResult) ? 0 : cResult;
    -            fps.consumed += mtctx->jobs[wJobID].consumed;
    -            fps.ingested += mtctx->jobs[wJobID].srcSize;
    -            fps.produced += produced;
    +            ZSTD_pthread_mutex_lock(&mtctx->jobs[wJobID].job_mutex);
    +            {   size_t const cResult = mtctx->jobs[wJobID].cSize;
    +                size_t const produced = ZSTD_isError(cResult) ? 0 : cResult;
    +                fps.consumed += mtctx->jobs[wJobID].consumed;
    +                fps.ingested += mtctx->jobs[wJobID].srcSize;
    +                fps.produced += produced;
    +            }
    +            ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
             }
         }
    -    ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
         return fps;
     }
     
    @@ -749,9 +756,9 @@ static size_t ZSTDMT_compress_advanced_internal(
     
         if (nbChunks > mtctx->jobIDMask+1) {  /* enlarge job table */
             U32 nbJobs = nbChunks;
    -        ZSTD_free(mtctx->jobs, mtctx->cMem);
    +        ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem);
             mtctx->jobIDMask = 0;
    -        mtctx->jobs = ZSTDMT_allocJobsTable(&nbJobs, mtctx->cMem);
    +        mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, mtctx->cMem);
             if (mtctx->jobs==NULL) return ERROR(memory_allocation);
             assert((nbJobs != 0) && ((nbJobs & (nbJobs - 1)) == 0));  /* ensure nbJobs is a power of 2 */
             mtctx->jobIDMask = nbJobs - 1;
    @@ -781,8 +788,6 @@ static size_t ZSTDMT_compress_advanced_internal(
                 mtctx->jobs[u].bufPool = mtctx->bufPool;
                 mtctx->jobs[u].firstChunk = (u==0);
                 mtctx->jobs[u].lastChunk = (u==nbChunks-1);
    -            mtctx->jobs[u].mtctx_mutex = &mtctx->mtctx_mutex;
    -            mtctx->jobs[u].mtctx_cond = &mtctx->mtctx_cond;
     
                 if (params.fParams.checksumFlag) {
                     XXH64_update(&xxh64, srcStart + frameStartPos, chunkSize);
    @@ -802,12 +807,12 @@ static size_t ZSTDMT_compress_advanced_internal(
             unsigned chunkID;
             for (chunkID=0; chunkIDmtctx_mutex);
    +            ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[chunkID].job_mutex);
                 while (mtctx->jobs[chunkID].consumed < mtctx->jobs[chunkID].srcSize) {
                     DEBUGLOG(5, "waiting for jobCompleted signal from chunk %u", chunkID);
    -                ZSTD_pthread_cond_wait(&mtctx->mtctx_cond, &mtctx->mtctx_mutex);
    +                ZSTD_pthread_cond_wait(&mtctx->jobs[chunkID].job_cond, &mtctx->jobs[chunkID].job_mutex);
                 }
    -            ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
    +            ZSTD_pthread_mutex_unlock(&mtctx->jobs[chunkID].job_mutex);
                 DEBUGLOG(5, "ready to write chunk %u ", chunkID);
     
                 mtctx->jobs[chunkID].prefixStart = NULL;
    @@ -1058,8 +1063,6 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
             mtctx->jobs[jobID].lastChunk = endFrame;
             mtctx->jobs[jobID].frameChecksumNeeded = endFrame && (mtctx->nextJobID>0) && mtctx->params.fParams.checksumFlag;
             mtctx->jobs[jobID].dstFlushed = 0;
    -        mtctx->jobs[jobID].mtctx_mutex = &mtctx->mtctx_mutex;
    -        mtctx->jobs[jobID].mtctx_cond = &mtctx->mtctx_cond;
     
             if (mtctx->params.fParams.checksumFlag)
                 XXH64_update(&mtctx->xxhState, (const char*)mtctx->inBuff.buffer.start + mtctx->inBuff.prefixSize, srcSize);
    @@ -1128,7 +1131,7 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
                     blockToFlush, mtctx->doneJobID, mtctx->nextJobID);
         assert(output->size >= output->pos);
     
    -    ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->mtctx_mutex);
    +    ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[wJobID].job_mutex);
         if (  blockToFlush
           && (mtctx->doneJobID < mtctx->nextJobID) ) {
             assert(mtctx->jobs[wJobID].dstFlushed <= mtctx->jobs[wJobID].cSize);
    @@ -1140,14 +1143,14 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
                 }
                 DEBUGLOG(5, "waiting for something to flush from job %u (currently flushed: %u bytes)",
                             mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed);
    -            ZSTD_pthread_cond_wait(&mtctx->mtctx_cond, &mtctx->mtctx_mutex);  /* block when nothing available to flush but more to come */
    +            ZSTD_pthread_cond_wait(&mtctx->jobs[wJobID].job_cond, &mtctx->jobs[wJobID].job_mutex);  /* block when nothing to flush but some to come */
         }   }
     
         /* try to flush something */
         {   size_t cSize = mtctx->jobs[wJobID].cSize;                  /* shared */
             size_t const srcConsumed = mtctx->jobs[wJobID].consumed;   /* shared */
             size_t const srcSize = mtctx->jobs[wJobID].srcSize;        /* read-only, could be done after mutex lock, but no-declaration-after-statement */
    -        ZSTD_pthread_mutex_unlock(&mtctx->mtctx_mutex);
    +        ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
             if (ZSTD_isError(cSize)) {
                 DEBUGLOG(5, "ZSTDMT_flushProduced: job %u : compression error detected : %s",
                             mtctx->doneJobID, ZSTD_getErrorName(cSize));
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index 16dcba73a..ecc477c71 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -969,9 +969,9 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres
             FUZ_rand(&coreSeed);
             lseed = coreSeed ^ prime32;
             if (nbTests >= testNb) {
    -            DISPLAYUPDATE(2, "\r%6u/%6u (%08X)   ", testNb, nbTests, lseed);
    +            DISPLAYUPDATE(2, "\r%6u/%6u    ", testNb, nbTests);
             } else {
    -            DISPLAYUPDATE(2, "\r%6u  (%08X)        ", testNb, lseed);
    +            DISPLAYUPDATE(2, "\r%6u        ", testNb);
             }
     
             /* states full reset (deliberately not synchronized) */
    
    From caf9e96dc3958850f14b3ddba31003e5f2f2a700 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 26 Jan 2018 18:09:25 -0800
    Subject: [PATCH 095/255] job mutex creation is checked
    
    ---
     lib/compress/zstdmt_compress.c | 44 ++++++++++++++++++----------------
     1 file changed, 24 insertions(+), 20 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 22560bfb1..865035691 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -467,26 +467,6 @@ struct ZSTDMT_CCtx_s {
         const ZSTD_CDict* cdict;
     };
     
    -/* ZSTDMT_allocJobsTable()
    - * allocate and init a job table.
    - * update *nbJobsPtr to next power of 2 value, as size of table
    - * No reverse free() function is provided : just use ZSTD_free() */
    -static ZSTDMT_jobDescription* ZSTDMT_createJobsTable(U32* nbJobsPtr, ZSTD_customMem cMem)
    -{
    -    U32 const nbJobsLog2 = ZSTD_highbit32(*nbJobsPtr) + 1;
    -    U32 const nbJobs = 1 << nbJobsLog2;
    -    ZSTDMT_jobDescription* const jobTable = ZSTD_calloc(
    -                            nbJobs * sizeof(ZSTDMT_jobDescription), cMem);
    -    U32 jobNb;
    -    if (jobTable==NULL) return NULL;
    -    *nbJobsPtr = nbJobs;
    -    for (jobNb=0; jobNb
    Date: Fri, 26 Jan 2018 18:18:42 -0800
    Subject: [PATCH 096/255] fixed minor conversion warning for C++ compilation
     mode
    
    ---
     lib/compress/zstdmt_compress.c | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 865035691..72515edb8 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -486,8 +486,8 @@ static ZSTDMT_jobDescription* ZSTDMT_createJobsTable(U32* nbJobsPtr, ZSTD_custom
         U32 const nbJobsLog2 = ZSTD_highbit32(*nbJobsPtr) + 1;
         U32 const nbJobs = 1 << nbJobsLog2;
         U32 jobNb;
    -    ZSTDMT_jobDescription* const jobTable = ZSTD_calloc(
    -                            nbJobs * sizeof(ZSTDMT_jobDescription), cMem);
    +    ZSTDMT_jobDescription* const jobTable = (ZSTDMT_jobDescription*)
    +                ZSTD_calloc(nbJobs * sizeof(ZSTDMT_jobDescription), cMem);
         int initError = 0;
         if (jobTable==NULL) return NULL;
         *nbJobsPtr = nbJobs;
    
    From 9f8ed23b5bb95786bb7fafb0db213136ea14732f Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Sat, 27 Jan 2018 22:23:26 -0800
    Subject: [PATCH 097/255] bumped version number to v1.3.4
    
    also added a paragraph on using compression level with training mode
    as this is a recurrent question (see for example #1004)
    ---
     doc/zstd_manual.html    | 12 +++++++++---
     lib/dictBuilder/zdict.h |  8 ++++----
     lib/zstd.h              |  2 +-
     programs/zstd.1         | 10 +++++++---
     programs/zstd.1.md      | 14 +++++++++-----
     5 files changed, 30 insertions(+), 16 deletions(-)
    
    diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
    index 473d5be8b..4dcf5a58a 100644
    --- a/doc/zstd_manual.html
    +++ b/doc/zstd_manual.html
    @@ -1,10 +1,10 @@
     
     
     
    -zstd 1.3.3 Manual
    +zstd 1.3.4 Manual
     
     
    -

    zstd 1.3.3 Manual

    +

    zstd 1.3.4 Manual


    Contents

      @@ -582,10 +582,16 @@ size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* If pledgedSrcSize is not known at reset time, use macro ZSTD_CONTENTSIZE_UNKNOWN. If pledgedSrcSize > 0, its value must be correct, as it will be written in header, and controlled at the end. For the time being, pledgedSrcSize==0 is interpreted as "srcSize unknown" for compatibility with older programs, - but it may change to mean "empty" in some future version, so prefer using macro ZSTD_CONTENTSIZE_UNKNOWN. + but it will change to mean "empty" in future version, so use macro ZSTD_CONTENTSIZE_UNKNOWN instead. @return : 0, or an error code (which can be tested using ZSTD_isError())


    +
    typedef struct {
    +    unsigned long long ingested;
    +    unsigned long long consumed;
    +    unsigned long long produced;
    +} ZSTD_frameProgression;
    +

    Advanced Streaming decompression functions

    typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e;
     size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue);   /* obsolete : this API will be removed in a future version */
     size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: no dictionary will be used if dict == NULL or dictSize < 8 */
    diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h
    index 92f66415f..ad459c2d7 100644
    --- a/lib/dictBuilder/zdict.h
    +++ b/lib/dictBuilder/zdict.h
    @@ -104,9 +104,9 @@ typedef struct {
      *        It's recommended that total size of all samples be about ~x100 times the target size of dictionary.
      */
     ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover(
    -    void *dictBuffer, size_t dictBufferCapacity,
    +          void *dictBuffer, size_t dictBufferCapacity,
         const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples,
    -    ZDICT_cover_params_t parameters);
    +          ZDICT_cover_params_t parameters);
     
     /*! ZDICT_optimizeTrainFromBuffer_cover():
      * The same requirements as above hold for all the parameters except `parameters`.
    @@ -125,9 +125,9 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover(
      * Note: ZDICT_optimizeTrainFromBuffer_cover() requires about 8 bytes of memory for each input byte and additionally another 5 bytes of memory for each byte of memory for each thread.
      */
     ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover(
    -    void* dictBuffer, size_t dictBufferCapacity,
    +          void* dictBuffer, size_t dictBufferCapacity,
         const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
    -    ZDICT_cover_params_t* parameters);
    +          ZDICT_cover_params_t* parameters);
     
     /*! ZDICT_finalizeDictionary():
      * Given a custom content as a basis for dictionary, and a set of samples,
    diff --git a/lib/zstd.h b/lib/zstd.h
    index a1b8ea19b..5b0ce7e87 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -59,7 +59,7 @@ extern "C" {
     /*------   Version   ------*/
     #define ZSTD_VERSION_MAJOR    1
     #define ZSTD_VERSION_MINOR    3
    -#define ZSTD_VERSION_RELEASE  3
    +#define ZSTD_VERSION_RELEASE  4
     
     #define ZSTD_VERSION_NUMBER  (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE)
     ZSTDLIB_API unsigned ZSTD_versionNumber(void);   /**< useful to check dll version */
    diff --git a/programs/zstd.1 b/programs/zstd.1
    index d253124fd..6d772d32e 100644
    --- a/programs/zstd.1
    +++ b/programs/zstd.1
    @@ -1,5 +1,5 @@
     .
    -.TH "ZSTD" "1" "December 2017" "zstd 1.3.3" "User Commands"
    +.TH "ZSTD" "1" "2018-01-27" "zstd 1.3.3" "User Commands"
     .
     .SH "NAME"
     \fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files
    @@ -136,7 +136,7 @@ force write to standard output, even if it is the console
     .
     .TP
     \fB\-\-[no\-]sparse\fR
    -enable / disable sparse FS support, to make files with many zeroes smaller on disk\. Creating sparse files may save disk space and speed up decompression by reducing the amount of disk I/O\. default : enabled when output is into a file, and disabled when output is stdout\. This setting overrides default and can force sparse mode over stdout\.
    +enable / disable sparse FS support, to make files with many zeroes smaller on disk\. Creating sparse files may save disk space and speed up decompression by reducing the amount of disk I/O\. default: enabled when output is into a file, and disabled when output is stdout\. This setting overrides default and can force sparse mode over stdout\.
     .
     .TP
     \fB\-\-rm\fR
    @@ -179,7 +179,7 @@ add integrity check computed from uncompressed data (default: enabled)
     All arguments after \fB\-\-\fR are treated as files
     .
     .SH "DICTIONARY BUILDER"
    -\fBzstd\fR offers \fIdictionary\fR compression, useful for very small files and messages\. It\'s possible to train \fBzstd\fR with some samples, the result of which is saved into a file called a \fBdictionary\fR\. Then during compression and decompression, reference the same dictionary\. It will improve compression ratio of small files\. Typical gains range from 10% (at 64KB) to x5 better (at <1KB)\.
    +\fBzstd\fR offers \fIdictionary\fR compression, which greatly improves efficiency on small files and messages\. It\'s possible to train \fBzstd\fR with a set of samples, the result of which is saved into a file called a \fBdictionary\fR\. Then during compression and decompression, reference the same dictionary, using command \fB\-D dictionaryFileName\fR\. Compression of small files similar to the sample set will be greatly improved\.
     .
     .TP
     \fB\-\-train FILEs\fR
    @@ -197,6 +197,10 @@ Dictionary saved into \fBfile\fR (default name: dictionary)\.
     Limit dictionary to specified size (default: 112640)\.
     .
     .TP
    +\fB\-#\fR
    +Use \fB#\fR compression level during training (optional)\. Will generate statistics more tuned for selected compression level, resulting in a \fIsmall\fR compression ratio improvement for this level\.
    +.
    +.TP
     \fB\-B#\fR
     Split input files in blocks of size # (default: no split)
     .
    diff --git a/programs/zstd.1.md b/programs/zstd.1.md
    index c5ff53567..4d9ec2fce 100644
    --- a/programs/zstd.1.md
    +++ b/programs/zstd.1.md
    @@ -171,12 +171,12 @@ the last one takes effect.
     DICTIONARY BUILDER
     ------------------
     `zstd` offers _dictionary_ compression,
    -useful for very small files and messages.
    -It's possible to train `zstd` with some samples,
    +which greatly improves efficiency on small files and messages.
    +It's possible to train `zstd` with a set of samples,
     the result of which is saved into a file called a `dictionary`.
    -Then during compression and decompression, reference the same dictionary.
    -It will improve compression ratio of small files.
    -Typical gains range from 10% (at 64KB) to x5 better (at <1KB).
    +Then during compression and decompression, reference the same dictionary,
    +using command `-D dictionaryFileName`.
    +Compression of small files similar to the sample set will be greatly improved.
     
     * `--train FILEs`:
         Use FILEs as training set to create a dictionary.
    @@ -192,6 +192,10 @@ Typical gains range from 10% (at 64KB) to x5 better (at <1KB).
         Dictionary saved into `file` (default name: dictionary).
     * `--maxdict=#`:
         Limit dictionary to specified size (default: 112640).
    +* `-#`:
    +    Use `#` compression level during training (optional).
    +    Will generate statistics more tuned for selected compression level,
    +    resulting in a _small_ compression ratio improvement for this level.
     * `-B#`:
         Split input files in blocks of size # (default: no split)
     * `--dictID=#`:
    
    From d13a75c969d2c48a2e47e6eca1acd9af5abbc92c Mon Sep 17 00:00:00 2001
    From: systemcrash 
    Date: Mon, 29 Jan 2018 18:38:02 +0100
    Subject: [PATCH 098/255] Update zstd.1
    
    ---
     programs/zstd.1 | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/programs/zstd.1 b/programs/zstd.1
    index 6d772d32e..8e9e83745 100644
    --- a/programs/zstd.1
    +++ b/programs/zstd.1
    @@ -1,5 +1,5 @@
     .
    -.TH "ZSTD" "1" "2018-01-27" "zstd 1.3.3" "User Commands"
    +.TH "ZSTD" "1" "2018-01-27" "zstd 1.3.4" "User Commands"
     .
     .SH "NAME"
     \fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files
    
    From 6b5738772830a42ee1cfecb6c6dea2ed38dbdd0c Mon Sep 17 00:00:00 2001
    From: systemcrash 
    Date: Mon, 29 Jan 2018 18:42:20 +0100
    Subject: [PATCH 099/255] Update README.md
    
    spelling
    ---
     lib/README.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/lib/README.md b/lib/README.md
    index 8a13b4e3d..95196e467 100644
    --- a/lib/README.md
    +++ b/lib/README.md
    @@ -31,7 +31,7 @@ Optional advanced features are exposed via :
                               it unlocks access to advanced experimental API,
                               exposed in second part of `zstd.h`.
                               These APIs are not "stable", their definition may change in the future.
    -                          As a consequencem, it shall ___never be used with dynamic library___ !
    +                          As a consequence, it shall ___never be used with dynamic library___ !
                               Only static linking is allowed.
     
     
    
    From 2cb0740b6b24b42d0255046e5692539c7b8534f8 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Tue, 30 Jan 2018 14:43:36 -0800
    Subject: [PATCH 100/255] zstdmt: changed naming convention
    
    to avoid confusion with blocks.
    
    also:
    - jobs are cut into chunks of 512KB now, to reduce nb of mutex calls.
    - fix function declaration ZSTD_getBlockSizeMax()
    - fix outdated comment
    ---
     doc/zstd_manual.html           |   2 +-
     lib/compress/zstdmt_compress.c | 176 ++++++++++++++++-----------------
     lib/zstd.h                     |   2 +-
     3 files changed, 90 insertions(+), 90 deletions(-)
    
    diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
    index b4aed91e5..debd37f7a 100644
    --- a/doc/zstd_manual.html
    +++ b/doc/zstd_manual.html
    @@ -1153,7 +1153,7 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t
             Use ZSTD_insertBlock() for such a case.
     


    -

    Raw zstd block functions

    size_t ZSTD_getBlockSize   (const ZSTD_CCtx* cctx);
    +

    Raw zstd block functions

    size_t ZSTD_getBlockSizeMax(const ZSTD_CCtx* cctx);
     size_t ZSTD_compressBlock  (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
     size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
     size_t ZSTD_insertBlock    (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize);  /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 72515edb8..ec5f0bbbd 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -319,8 +319,8 @@ typedef struct {
         const void* prefixStart;             /* set by mtctx, then read and set0 by worker => no barrier */
         size_t   prefixSize;                 /* set by mtctx, then read by worker => no barrier */
         size_t   srcSize;                    /* set by mtctx, then read by worker & mtctx => no barrier */
    -    unsigned firstChunk;                 /* set by mtctx, then read by worker => no barrier */
    -    unsigned lastChunk;                  /* set by mtctx, then read by worker => no barrier */
    +    unsigned firstJob;                   /* set by mtctx, then read by worker => no barrier */
    +    unsigned lastJob;                    /* set by mtctx, then read by worker => no barrier */
         ZSTD_CCtx_params params;             /* set by mtctx, then read by worker => no barrier */
         const ZSTD_CDict* cdict;             /* set by mtctx, then read by worker => no barrier */
         unsigned long long fullFrameSize;    /* set by mtctx, then read by worker => no barrier */
    @@ -328,8 +328,8 @@ typedef struct {
         unsigned frameChecksumNeeded;        /* used only by mtctx */
     } ZSTDMT_jobDescription;
     
    -/* ZSTDMT_compressChunk() is a POOL_function type */
    -void ZSTDMT_compressChunk(void* jobDescription)
    +/* ZSTDMT_compressionJob() is a POOL_function type */
    +void ZSTDMT_compressionJob(void* jobDescription)
     {
         ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription;
         ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool);
    @@ -353,12 +353,12 @@ void ZSTDMT_compressChunk(void* jobDescription)
         /* init */
         if (job->cdict) {
             size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dm_auto, job->cdict, job->params, job->fullFrameSize);
    -        assert(job->firstChunk);  /* only allowed for first job */
    +        assert(job->firstJob);  /* only allowed for first job */
             if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; }
         } else {  /* srcStart points at reloaded section */
    -        U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : job->srcSize;
    +        U64 const pledgedSrcSize = job->firstJob ? job->fullFrameSize : job->srcSize;
             ZSTD_CCtx_params jobParams = job->params;   /* do not modify job->params ! copy it, modify the copy */
    -        {   size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk);
    +        {   size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstJob);
                 if (ZSTD_isError(forceWindowError)) {
                     job->cSize = forceWindowError;
                     goto _endJob;
    @@ -371,44 +371,44 @@ void ZSTDMT_compressChunk(void* jobDescription)
                     job->cSize = initError;
                     goto _endJob;
         }   }   }
    -    if (!job->firstChunk) {  /* flush and overwrite frame header when it's not first job */
    +    if (!job->firstJob) {  /* flush and overwrite frame header when it's not first job */
             size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, src, 0);
             if (ZSTD_isError(hSize)) { job->cSize = hSize; /* save error code */ goto _endJob; }
    -        DEBUGLOG(5, "ZSTDMT_compressChunk: flush and overwrite %u bytes of frame header (not first chunk)", (U32)hSize);
    +        DEBUGLOG(5, "ZSTDMT_compressionJob: flush and overwrite %u bytes of frame header (not first job)", (U32)hSize);
             ZSTD_invalidateRepCodes(cctx);
         }
     
         /* compress */
    -    {   size_t const blockSize = ZSTD_BLOCKSIZE_MAX;
    -        int const nbBlocks = (int)((job->srcSize + (blockSize-1)) / blockSize);
    +    {   size_t const chunkSize = 4*ZSTD_BLOCKSIZE_MAX;
    +        int const nbChunks = (int)((job->srcSize + (chunkSize-1)) / chunkSize);
             const BYTE* ip = (const BYTE*) src;
             BYTE* const ostart = (BYTE*)dstBuff.start;
             BYTE* op = ostart;
             BYTE* oend = op + dstBuff.capacity;
    -        int blockNb;
    -        if (sizeof(size_t) > sizeof(int)) assert(job->srcSize < ((size_t)INT_MAX) * blockSize);   /* check overflow */
    -        DEBUGLOG(5, "ZSTDMT_compressChunk: compress %u bytes in %i blocks", (U32)job->srcSize, nbBlocks);
    +        int chunkNb;
    +        if (sizeof(size_t) > sizeof(int)) assert(job->srcSize < ((size_t)INT_MAX) * chunkSize);   /* check overflow */
    +        DEBUGLOG(5, "ZSTDMT_compressionJob: compress %u bytes in %i blocks", (U32)job->srcSize, nbChunks);
             assert(job->cSize == 0);
    -        for (blockNb = 1; blockNb < nbBlocks; blockNb++) {
    -            size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, blockSize);
    +        for (chunkNb = 1; chunkNb < nbChunks; chunkNb++) {
    +            size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, chunkSize);
                 if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; }
    -            ip += blockSize;
    +            ip += chunkSize;
                 op += cSize; assert(op < oend);
                 /* stats */
    -            ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex);   /* note : it's a mtctx mutex */
    +            ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex);
                 job->cSize += cSize;
    -            job->consumed = blockSize * blockNb;
    -            DEBUGLOG(5, "ZSTDMT_compressChunk: compress new block : cSize==%u bytes (total: %u)",
    +            job->consumed = chunkSize * chunkNb;
    +            DEBUGLOG(5, "ZSTDMT_compressionJob: compress new block : cSize==%u bytes (total: %u)",
                             (U32)cSize, (U32)job->cSize);
                 ZSTD_pthread_cond_signal(&job->job_cond);   /* warns some more data is ready to be flushed */
                 ZSTD_pthread_mutex_unlock(&job->job_mutex);
             }
             /* last block */
    -        assert(blockSize > 0); assert((blockSize & (blockSize - 1)) == 0);  /* blockSize must be power of 2 for mask==(blockSize-1) to work */
    -        if ((nbBlocks > 0) | job->lastChunk /*must output a "last block" flag*/ ) {
    -            size_t const lastBlockSize1 = job->srcSize & (blockSize-1);
    -            size_t const lastBlockSize = ((lastBlockSize1==0) & (job->srcSize>=blockSize)) ? blockSize : lastBlockSize1;
    -            size_t const cSize = (job->lastChunk) ?
    +        assert(chunkSize > 0); assert((chunkSize & (chunkSize - 1)) == 0);  /* chunkSize must be power of 2 for mask==(chunkSize-1) to work */
    +        if ((nbChunks > 0) | job->lastJob /*must output a "last block" flag*/ ) {
    +            size_t const lastBlockSize1 = job->srcSize & (chunkSize-1);
    +            size_t const lastBlockSize = ((lastBlockSize1==0) & (job->srcSize>=chunkSize)) ? chunkSize : lastBlockSize1;
    +            size_t const cSize = (job->lastJob) ?
                      ZSTD_compressEnd     (cctx, op, oend-op, ip, lastBlockSize) :
                      ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize);
                 if (ZSTD_isError(cSize)) { job->cSize = cSize; goto _endJob; }
    @@ -580,7 +580,7 @@ static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* mtctx)
             unsigned const jobID = mtctx->doneJobID & mtctx->jobIDMask;
             ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[jobID].job_mutex);
             while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].srcSize) {
    -            DEBUGLOG(5, "waiting for jobCompleted signal from chunk %u", mtctx->doneJobID);   /* we want to block when waiting for data to flush */
    +            DEBUGLOG(5, "waiting for jobCompleted signal from job %u", mtctx->doneJobID);   /* we want to block when waiting for data to flush */
                 ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex);
             }
             ZSTD_pthread_mutex_unlock(&mtctx->jobs[jobID].job_mutex);
    @@ -709,16 +709,16 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
     /* =====   Multi-threaded compression   ===== */
     /* ------------------------------------------ */
     
    -static unsigned ZSTDMT_computeNbChunks(size_t srcSize, unsigned windowLog, unsigned nbThreads) {
    +static unsigned ZSTDMT_computeNbJobs(size_t srcSize, unsigned windowLog, unsigned nbThreads) {
         assert(nbThreads>0);
    -    {   size_t const chunkSizeTarget = (size_t)1 << (windowLog + 2);
    -        size_t const chunkMaxSize = chunkSizeTarget << 2;
    -        size_t const passSizeMax = chunkMaxSize * nbThreads;
    +    {   size_t const jobSizeTarget = (size_t)1 << (windowLog + 2);
    +        size_t const jobMaxSize = jobSizeTarget << 2;
    +        size_t const passSizeMax = jobMaxSize * nbThreads;
             unsigned const multiplier = (unsigned)(srcSize / passSizeMax) + 1;
    -        unsigned const nbChunksLarge = multiplier * nbThreads;
    -        unsigned const nbChunksMax = (unsigned)(srcSize / chunkSizeTarget) + 1;
    -        unsigned const nbChunksSmall = MIN(nbChunksMax, nbThreads);
    -        return (multiplier>1) ? nbChunksLarge : nbChunksSmall;
    +        unsigned const nbJobsLarge = multiplier * nbThreads;
    +        unsigned const nbJobsMax = (unsigned)(srcSize / jobSizeTarget) + 1;
    +        unsigned const nbJobsSmall = MIN(nbJobsMax, nbThreads);
    +        return (multiplier>1) ? nbJobsLarge : nbJobsSmall;
     }   }
     
     /* ZSTDMT_compress_advanced_internal() :
    @@ -734,44 +734,44 @@ static size_t ZSTDMT_compress_advanced_internal(
         ZSTD_CCtx_params const jobParams = ZSTDMT_initJobCCtxParams(params);
         unsigned const overlapRLog = (params.overlapSizeLog>9) ? 0 : 9-params.overlapSizeLog;
         size_t const overlapSize = (overlapRLog>=9) ? 0 : (size_t)1 << (params.cParams.windowLog - overlapRLog);
    -    unsigned nbChunks = ZSTDMT_computeNbChunks(srcSize, params.cParams.windowLog, params.nbThreads);
    -    size_t const proposedChunkSize = (srcSize + (nbChunks-1)) / nbChunks;
    -    size_t const avgChunkSize = (((proposedChunkSize-1) & 0x1FFFF) < 0x7FFF) ? proposedChunkSize + 0xFFFF : proposedChunkSize;   /* avoid too small last block */
    +    unsigned const nbJobs = ZSTDMT_computeNbJobs(srcSize, params.cParams.windowLog, params.nbThreads);
    +    size_t const proposedJobSize = (srcSize + (nbJobs-1)) / nbJobs;
    +    size_t const avgJobSize = (((proposedJobSize-1) & 0x1FFFF) < 0x7FFF) ? proposedJobSize + 0xFFFF : proposedJobSize;   /* avoid too small last block */
         const char* const srcStart = (const char*)src;
         size_t remainingSrcSize = srcSize;
    -    unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbChunks : (unsigned)(dstCapacity / ZSTD_compressBound(avgChunkSize));  /* presumes avgChunkSize >= 256 KB, which should be the case */
    +    unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbJobs : (unsigned)(dstCapacity / ZSTD_compressBound(avgJobSize));  /* presumes avgJobSize >= 256 KB, which should be the case */
         size_t frameStartPos = 0, dstBufferPos = 0;
         XXH64_state_t xxh64;
         assert(jobParams.nbThreads == 0);
         assert(mtctx->cctxPool->totalCCtx == params.nbThreads);
     
    -    DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbChunks=%2u (rawSize=%u bytes; fixedSize=%u) ",
    -                nbChunks, (U32)proposedChunkSize, (U32)avgChunkSize);
    +    DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbJobs=%2u (rawSize=%u bytes; fixedSize=%u) ",
    +                nbJobs, (U32)proposedJobSize, (U32)avgJobSize);
     
    -    if ((nbChunks==1) | (params.nbThreads<=1)) {   /* fallback to single-thread mode : this is a blocking invocation anyway */
    +    if ((nbJobs==1) | (params.nbThreads<=1)) {   /* fallback to single-thread mode : this is a blocking invocation anyway */
             ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0];
             if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams);
             return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, NULL, 0, jobParams);
         }
     
    -    assert(avgChunkSize >= 256 KB);  /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), required to compress directly into Dst (no additional buffer) */
    -    ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgChunkSize) );
    +    assert(avgJobSize >= 256 KB);  /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), required to compress directly into Dst (no additional buffer) */
    +    ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgJobSize) );
         XXH64_reset(&xxh64, 0);
     
    -    if (nbChunks > mtctx->jobIDMask+1) {  /* enlarge job table */
    -        U32 nbJobs = nbChunks;
    +    if (nbJobs > mtctx->jobIDMask+1) {  /* enlarge job table */
    +        U32 jobsTableSize = nbJobs;
             ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem);
             mtctx->jobIDMask = 0;
    -        mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, mtctx->cMem);
    +        mtctx->jobs = ZSTDMT_createJobsTable(&jobsTableSize, mtctx->cMem);
             if (mtctx->jobs==NULL) return ERROR(memory_allocation);
    -        assert((nbJobs != 0) && ((nbJobs & (nbJobs - 1)) == 0));  /* ensure nbJobs is a power of 2 */
    -        mtctx->jobIDMask = nbJobs - 1;
    +        assert((jobsTableSize != 0) && ((jobsTableSize & (jobsTableSize - 1)) == 0));  /* ensure jobsTableSize is a power of 2 */
    +        mtctx->jobIDMask = jobsTableSize - 1;
         }
     
         {   unsigned u;
    -        for (u=0; ujobs[u].srcBuff = g_nullBuffer;
                 mtctx->jobs[u].prefixStart = srcStart + frameStartPos - dictSize;
                 mtctx->jobs[u].prefixSize = dictSize;
    -            mtctx->jobs[u].srcSize = chunkSize; assert(chunkSize > 0);  /* avoid job.srcSize == 0 */
    +            mtctx->jobs[u].srcSize = jobSize; assert(jobSize > 0);  /* avoid job.srcSize == 0 */
                 mtctx->jobs[u].consumed = 0;
                 mtctx->jobs[u].cSize = 0;
                 mtctx->jobs[u].cdict = (u==0) ? cdict : NULL;
    @@ -790,51 +790,51 @@ static size_t ZSTDMT_compress_advanced_internal(
                 mtctx->jobs[u].dstBuff = dstBuffer;
                 mtctx->jobs[u].cctxPool = mtctx->cctxPool;
                 mtctx->jobs[u].bufPool = mtctx->bufPool;
    -            mtctx->jobs[u].firstChunk = (u==0);
    -            mtctx->jobs[u].lastChunk = (u==nbChunks-1);
    +            mtctx->jobs[u].firstJob = (u==0);
    +            mtctx->jobs[u].lastJob = (u==nbJobs-1);
     
                 if (params.fParams.checksumFlag) {
    -                XXH64_update(&xxh64, srcStart + frameStartPos, chunkSize);
    +                XXH64_update(&xxh64, srcStart + frameStartPos, jobSize);
                 }
     
    -            DEBUGLOG(5, "ZSTDMT_compress_advanced_internal: posting job %u  (%u bytes)", u, (U32)chunkSize);
    +            DEBUGLOG(5, "ZSTDMT_compress_advanced_internal: posting job %u  (%u bytes)", u, (U32)jobSize);
                 DEBUG_PRINTHEX(6, mtctx->jobs[u].prefixStart, 12);
    -            POOL_add(mtctx->factory, ZSTDMT_compressChunk, &mtctx->jobs[u]);
    +            POOL_add(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[u]);
     
    -            frameStartPos += chunkSize;
    +            frameStartPos += jobSize;
                 dstBufferPos += dstBufferCapacity;
    -            remainingSrcSize -= chunkSize;
    +            remainingSrcSize -= jobSize;
         }   }
     
         /* collect result */
         {   size_t error = 0, dstPos = 0;
    -        unsigned chunkID;
    -        for (chunkID=0; chunkIDjobs[chunkID].job_mutex);
    -            while (mtctx->jobs[chunkID].consumed < mtctx->jobs[chunkID].srcSize) {
    -                DEBUGLOG(5, "waiting for jobCompleted signal from chunk %u", chunkID);
    -                ZSTD_pthread_cond_wait(&mtctx->jobs[chunkID].job_cond, &mtctx->jobs[chunkID].job_mutex);
    +        unsigned jobID;
    +        for (jobID=0; jobIDjobs[jobID].job_mutex);
    +            while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].srcSize) {
    +                DEBUGLOG(5, "waiting for jobCompleted signal from job %u", jobID);
    +                ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex);
                 }
    -            ZSTD_pthread_mutex_unlock(&mtctx->jobs[chunkID].job_mutex);
    -            DEBUGLOG(5, "ready to write chunk %u ", chunkID);
    +            ZSTD_pthread_mutex_unlock(&mtctx->jobs[jobID].job_mutex);
    +            DEBUGLOG(5, "ready to write job %u ", jobID);
     
    -            mtctx->jobs[chunkID].prefixStart = NULL;
    -            {   size_t const cSize = mtctx->jobs[chunkID].cSize;
    +            mtctx->jobs[jobID].prefixStart = NULL;
    +            {   size_t const cSize = mtctx->jobs[jobID].cSize;
                     if (ZSTD_isError(cSize)) error = cSize;
                     if ((!error) && (dstPos + cSize > dstCapacity)) error = ERROR(dstSize_tooSmall);
    -                if (chunkID) {   /* note : chunk 0 is written directly at dst, which is correct position */
    +                if (jobID) {   /* note : job 0 is written directly at dst, which is correct position */
                         if (!error)
    -                        memmove((char*)dst + dstPos, mtctx->jobs[chunkID].dstBuff.start, cSize);  /* may overlap when chunk compressed within dst */
    -                    if (chunkID >= compressWithinDst) {  /* chunk compressed into its own buffer, which must be released */
    -                        DEBUGLOG(5, "releasing buffer %u>=%u", chunkID, compressWithinDst);
    -                        ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[chunkID].dstBuff);
    +                        memmove((char*)dst + dstPos, mtctx->jobs[jobID].dstBuff.start, cSize);  /* may overlap when job compressed within dst */
    +                    if (jobID >= compressWithinDst) {  /* job compressed into its own buffer, which must be released */
    +                        DEBUGLOG(5, "releasing buffer %u>=%u", jobID, compressWithinDst);
    +                        ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);
                     }   }
    -                mtctx->jobs[chunkID].dstBuff = g_nullBuffer;
    -                mtctx->jobs[chunkID].cSize = 0;
    +                mtctx->jobs[jobID].dstBuff = g_nullBuffer;
    +                mtctx->jobs[jobID].cSize = 0;
                     dstPos += cSize ;
                 }
    -        }  /* for (chunkID=0; chunkIDlastChunk == 1);
    -    assert(job->srcSize == 0);      /* last chunk is empty -> will be simplified into a last empty block */
    -    assert(job->firstChunk == 0);   /* cannot be first chunk, as it also needs to create frame header */
    +    assert(job->lastJob == 1);
    +    assert(job->srcSize == 0);    /* last job is empty -> will be simplified into a last empty block */
    +    assert(job->firstJob == 0);   /* cannot be first job, as it also needs to create frame header */
         /* A job created by streaming variant starts with a src buffer, but no dst buffer.
          * It summons a dstBuffer itself, compresses into it, then releases srcBuffer, and gives result to mtctx.
          * When done, srcBuffer is empty, while dstBuffer is filled, and will be released by mtctx.
    @@ -1063,8 +1063,8 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
             mtctx->jobs[jobID].dstBuff = g_nullBuffer;
             mtctx->jobs[jobID].cctxPool = mtctx->cctxPool;
             mtctx->jobs[jobID].bufPool = mtctx->bufPool;
    -        mtctx->jobs[jobID].firstChunk = (mtctx->nextJobID==0);
    -        mtctx->jobs[jobID].lastChunk = endFrame;
    +        mtctx->jobs[jobID].firstJob = (mtctx->nextJobID==0);
    +        mtctx->jobs[jobID].lastJob = endFrame;
             mtctx->jobs[jobID].frameChecksumNeeded = endFrame && (mtctx->nextJobID>0) && mtctx->params.fParams.checksumFlag;
             mtctx->jobs[jobID].dstFlushed = 0;
     
    @@ -1093,12 +1093,12 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
                 mtctx->inBuff.prefixSize = 0;
                 mtctx->frameEnded = endFrame;
                 if (mtctx->nextJobID == 0) {
    -                /* single chunk exception : checksum is already calculated directly within worker thread */
    +                /* single job exception : checksum is already calculated directly within worker thread */
                     mtctx->params.fParams.checksumFlag = 0;
             }   }
     
             if ( (srcSize == 0)
    -          && (mtctx->nextJobID>0)/*single chunk must also write frame header*/ ) {
    +          && (mtctx->nextJobID>0)/*single job must also write frame header*/ ) {
                 DEBUGLOG(5, "ZSTDMT_createCompressionJob: creating a last empty block to end frame");
                 assert(endOp == ZSTD_e_end);  /* only possible case : need to end the frame with an empty last block */
                 ZSTDMT_writeLastEmptyBlock(mtctx->jobs + jobID);
    @@ -1110,10 +1110,10 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
         DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes  (end:%u, jobNb == %u (mod:%u))",
                     mtctx->nextJobID,
                     (U32)mtctx->jobs[jobID].srcSize,
    -                mtctx->jobs[jobID].lastChunk,
    +                mtctx->jobs[jobID].lastJob,
                     mtctx->nextJobID,
                     jobID);
    -    if (POOL_tryAdd(mtctx->factory, ZSTDMT_compressChunk, &mtctx->jobs[jobID])) {
    +    if (POOL_tryAdd(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[jobID])) {
             mtctx->nextJobID++;
             mtctx->jobReady = 0;
         } else {
    @@ -1206,7 +1206,7 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
         if (mtctx->doneJobID < mtctx->nextJobID) return 1;   /* some more jobs ongoing */
         if (mtctx->jobReady) return 1;      /* one job is ready to push, just not yet in the list */
         if (mtctx->inBuff.filled > 0) return 1;   /* input is not empty, and still needs to be converted into a job */
    -    mtctx->allJobsCompleted = mtctx->frameEnded;   /* all chunks are entirely flushed => if this one is last one, frame is completed */
    +    mtctx->allJobsCompleted = mtctx->frameEnded;   /* all jobs are entirely flushed => if this one is last one, frame is completed */
         if (end == ZSTD_e_end) return !mtctx->frameEnded;  /* for ZSTD_e_end, question becomes : is frame completed ? instead of : are internal buffers fully flushed ? */
         return 0;   /* internal buffers fully flushed */
     }
    diff --git a/lib/zstd.h b/lib/zstd.h
    index e573daf5b..4c77197d1 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -1373,7 +1373,7 @@ ZSTDLIB_API void ZSTD_DCtx_reset(ZSTD_DCtx* dctx);
     #define ZSTD_BLOCKSIZELOG_MAX 17
     #define ZSTD_BLOCKSIZE_MAX   (1<
    Date: Tue, 30 Jan 2018 15:03:39 -0800
    Subject: [PATCH 101/255] fixed function declaration ZSTD_getBlockSize()
    
    ---
     lib/zstd.h | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/lib/zstd.h b/lib/zstd.h
    index 4c77197d1..e573daf5b 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -1373,7 +1373,7 @@ ZSTDLIB_API void ZSTD_DCtx_reset(ZSTD_DCtx* dctx);
     #define ZSTD_BLOCKSIZELOG_MAX 17
     #define ZSTD_BLOCKSIZE_MAX   (1<
    Date: Tue, 30 Jan 2018 15:05:12 -0800
    Subject: [PATCH 102/255] updated zstd api manual
    
    ---
     doc/zstd_manual.html | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
    index debd37f7a..b4aed91e5 100644
    --- a/doc/zstd_manual.html
    +++ b/doc/zstd_manual.html
    @@ -1153,7 +1153,7 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t
             Use ZSTD_insertBlock() for such a case.
     


    -

    Raw zstd block functions

    size_t ZSTD_getBlockSizeMax(const ZSTD_CCtx* cctx);
    +

    Raw zstd block functions

    size_t ZSTD_getBlockSize   (const ZSTD_CCtx* cctx);
     size_t ZSTD_compressBlock  (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
     size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
     size_t ZSTD_insertBlock    (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize);  /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */
    
    From ab3346af0723cc59601eca72d6df921c86ca5db3 Mon Sep 17 00:00:00 2001
    From: Nick Terrell 
    Date: Tue, 30 Jan 2018 13:30:30 -0800
    Subject: [PATCH 103/255] Fix hashLog3 size when copying cdict tables
    
    ---
     lib/compress/zstd_compress.c | 95 ++++++++++++++++++------------------
     lib/compress/zstd_opt.c      |  1 +
     tests/zstreamtest.c          | 30 ++++++++++++
     3 files changed, 78 insertions(+), 48 deletions(-)
    
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index 5211384e0..19589553f 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -659,6 +659,24 @@ ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, u
         return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize);
     }
     
    +static size_t ZSTD_sizeof_matchState(ZSTD_compressionParameters const* cParams, const U32 forCCtx)
    +{
    +    size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);
    +    size_t const hSize = ((size_t)1) << cParams->hashLog;
    +    U32    const hashLog3 = (forCCtx && cParams->searchLength==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;
    +    size_t const h3Size = ((size_t)1) << hashLog3;
    +    size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
    +    size_t const optPotentialSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<strategy == ZSTD_btopt) ||
    +                                         (cParams->strategy == ZSTD_btultra)))
    +                                ? optPotentialSpace
    +                                : 0;
    +    DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u",
    +                (U32)chainSize, (U32)hSize, (U32)h3Size);
    +    return tableSpace + optSpace;
    +}
    +
     size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params)
     {
         /* Estimate CCtx size is supported for single-threaded compression only. */
    @@ -669,27 +687,16 @@ size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params)
             U32    const divider = (cParams.searchLength==3) ? 3 : 4;
             size_t const maxNbSeq = blockSize / divider;
             size_t const tokenSpace = blockSize + 11*maxNbSeq;
    -        size_t const chainSize =
    -                (cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams.chainLog);
    -        size_t const hSize = ((size_t)1) << cParams.hashLog;
    -        U32    const hashLog3 = (cParams.searchLength>3) ?
    -                                0 : MIN(ZSTD_HASHLOG3_MAX, cParams.windowLog);
    -        size_t const h3Size = ((size_t)1) << hashLog3;
             size_t const entropySpace = HUF_WORKSPACE_SIZE;
             size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t);
    -        size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
    -
    -        size_t const optBudget =
    -                ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<cParams, /* forCCtx */ 1);
     
             size_t const ldmSpace = params->ldmParams.enableLdm ?
                 ZSTD_ldm_getTableSize(params->ldmParams.hashLog,
                                       params->ldmParams.bucketSizeLog) : 0;
     
    -        size_t const neededSpace = entropySpace + blockStateSpace + tableSpace + tokenSpace +
    -                                   optSpace + ldmSpace;
    +        size_t const neededSpace = entropySpace + blockStateSpace + tokenSpace +
    +                                   matchStateSize + ldmSpace;
     
             DEBUGLOG(5, "sizeof(ZSTD_CCtx) : %u", (U32)sizeof(ZSTD_CCtx));
             DEBUGLOG(5, "estimate workSpace : %u", (U32)neededSpace);
    @@ -888,29 +895,11 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pl
     
     typedef enum { ZSTDcrp_continue, ZSTDcrp_noMemset } ZSTD_compResetPolicy_e;
     
    -static size_t ZSTD_sizeof_matchState(ZSTD_compressionParameters const* cParams, const U32 opt)
    +static void* ZSTD_reset_matchState(ZSTD_matchState_t* ms, void* ptr, ZSTD_compressionParameters const* cParams, ZSTD_compResetPolicy_e const crp, U32 const forCCtx)
     {
         size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);
         size_t const hSize = ((size_t)1) << cParams->hashLog;
    -    U32    const hashLog3 = (cParams->searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog);
    -    size_t const h3Size = ((size_t)1) << hashLog3;
    -    size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
    -    size_t const optPotentialSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<strategy == ZSTD_btopt) ||
    -                                     (cParams->strategy == ZSTD_btultra)))
    -                                ? optPotentialSpace
    -                                : 0;
    -    DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u",
    -                (U32)chainSize, (U32)hSize, (U32)h3Size);
    -    return tableSpace + optSpace;
    -}
    -
    -static void* ZSTD_reset_matchState(ZSTD_matchState_t* ms, void* ptr, ZSTD_compressionParameters const* cParams, ZSTD_compResetPolicy_e const crp, U32 const opt)
    -{
    -    size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);
    -    size_t const hSize = ((size_t)1) << cParams->hashLog;
    -    U32    const hashLog3 = (cParams->searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog);
    +    U32    const hashLog3 = (forCCtx && cParams->searchLength==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;
         size_t const h3Size = ((size_t)1) << hashLog3;
         size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
     
    @@ -923,7 +912,7 @@ static void* ZSTD_reset_matchState(ZSTD_matchState_t* ms, void* ptr, ZSTD_compre
         ZSTD_invalidateMatchState(ms);
     
         /* opt parser space */
    -    if (opt && ((cParams->strategy == ZSTD_btopt) | (cParams->strategy == ZSTD_btultra))) {
    +    if (forCCtx && ((cParams->strategy == ZSTD_btopt) | (cParams->strategy == ZSTD_btultra))) {
             DEBUGLOG(4, "reserving optimal parser space");
             ms->opt.litFreq = (U32*)ptr;
             ms->opt.litLengthFreq = ms->opt.litFreq + (1<ldmState.hashTable + ldmHSize;
             }
     
    -        ptr = ZSTD_reset_matchState(&zc->blockState.matchState, ptr, ¶ms.cParams, crp, /* opt */ 1);
    +        ptr = ZSTD_reset_matchState(&zc->blockState.matchState, ptr, ¶ms.cParams, crp, /* forCCtx */ 1);
     
             /* sequences storage */
             zc->seqStore.sequencesStart = (seqDef*)ptr;
    @@ -1111,19 +1100,26 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx,
             params.fParams = fParams;
             ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,
                                     ZSTDcrp_noMemset, zbuff);
    +        assert(cctx->appliedParams.cParams.strategy == cdict->cParams.strategy);
    +        assert(cctx->appliedParams.cParams.hashLog == cdict->cParams.hashLog);
    +        assert(cctx->appliedParams.cParams.chainLog == cdict->cParams.chainLog);
         }
     
         /* copy tables */
    -    {   size_t const chainSize = (cctx->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << cctx->appliedParams.cParams.chainLog);
    -        size_t const hSize =  (size_t)1 << cctx->appliedParams.cParams.hashLog;
    -        size_t const h3Size = (size_t)1 << cctx->blockState.matchState.hashLog3;
    -        size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
    +    {   size_t const chainSize = (cdict->cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << cdict->cParams.chainLog);
    +        size_t const hSize =  (size_t)1 << cdict->cParams.hashLog;
    +        size_t const tableSpace = (chainSize + hSize) * sizeof(U32);
             assert((U32*)cctx->blockState.matchState.chainTable == (U32*)cctx->blockState.matchState.hashTable + hSize);  /* chainTable must follow hashTable */
             assert((U32*)cctx->blockState.matchState.hashTable3 == (U32*)cctx->blockState.matchState.chainTable + chainSize);
             assert((U32*)cdict->matchState.chainTable == (U32*)cdict->matchState.hashTable + hSize);  /* chainTable must follow hashTable */
             assert((U32*)cdict->matchState.hashTable3 == (U32*)cdict->matchState.chainTable + chainSize);
             memcpy(cctx->blockState.matchState.hashTable, cdict->matchState.hashTable, tableSpace);   /* presumes all tables follow each other */
         }
    +    /* Zero the hashTable3, since the cdict never fills it */
    +    {   size_t const h3Size = (size_t)1 << cctx->blockState.matchState.hashLog3;
    +        assert(cdict->matchState.hashLog3 == 0);
    +        memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32));
    +    }
     
         /* copy dictionary offsets */
         {
    @@ -1155,7 +1151,6 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx,
      * @return : 0, or an error code */
     static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx,
                                 const ZSTD_CCtx* srcCCtx,
    -                            unsigned windowLog,
                                 ZSTD_frameParameters fParams,
                                 U64 pledgedSrcSize,
                                 ZSTD_buffered_policy_e zbuff)
    @@ -1167,10 +1162,14 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx,
         {   ZSTD_CCtx_params params = dstCCtx->requestedParams;
             /* Copy only compression parameters related to tables. */
             params.cParams = srcCCtx->appliedParams.cParams;
    -        if (windowLog) params.cParams.windowLog = windowLog;
             params.fParams = fParams;
             ZSTD_resetCCtx_internal(dstCCtx, params, pledgedSrcSize,
                                     ZSTDcrp_noMemset, zbuff);
    +        assert(dstCCtx->appliedParams.cParams.windowLog == srcCCtx->appliedParams.cParams.windowLog);
    +        assert(dstCCtx->appliedParams.cParams.strategy == srcCCtx->appliedParams.cParams.strategy);
    +        assert(dstCCtx->appliedParams.cParams.hashLog == srcCCtx->appliedParams.cParams.hashLog);
    +        assert(dstCCtx->appliedParams.cParams.chainLog == srcCCtx->appliedParams.cParams.chainLog);
    +        assert(dstCCtx->blockState.matchState.hashLog3 == srcCCtx->blockState.matchState.hashLog3);
         }
     
         /* copy tables */
    @@ -1218,7 +1217,7 @@ size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long
         fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN);
     
         return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx,
    -                                0 /*windowLog from srcCCtx*/, fParams, pledgedSrcSize,
    +                                fParams, pledgedSrcSize,
                                     zbuff);
     }
     
    @@ -2528,7 +2527,7 @@ size_t ZSTD_estimateCDictSize_advanced(
             ZSTD_dictLoadMethod_e dictLoadMethod)
     {
         DEBUGLOG(5, "sizeof(ZSTD_CDict) : %u", (U32)sizeof(ZSTD_CDict));
    -    return sizeof(ZSTD_CDict) + HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* opt */ 0)
    +    return sizeof(ZSTD_CDict) + HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0)
                + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize);
     }
     
    @@ -2572,7 +2571,7 @@ static size_t ZSTD_initCDict_internal(
             void* const end = ZSTD_reset_matchState(
                     &cdict->matchState,
                     (U32*)cdict->workspace + HUF_WORKSPACE_SIZE_U32,
    -                &cParams, ZSTDcrp_continue, /* opt */ 0);
    +                &cParams, ZSTDcrp_continue, /* forCCtx */ 0);
             assert(end == (char*)cdict->workspace + cdict->workspaceSize);
             (void)end;
         }
    @@ -2608,7 +2607,7 @@ ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize,
         if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
     
         {   ZSTD_CDict* const cdict = (ZSTD_CDict*)ZSTD_malloc(sizeof(ZSTD_CDict), customMem);
    -        size_t const workspaceSize = HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* opt */ 0);
    +        size_t const workspaceSize = HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0);
             void* const workspace = ZSTD_malloc(workspaceSize, customMem);
     
             if (!cdict || !workspace) {
    @@ -2678,7 +2677,7 @@ const ZSTD_CDict* ZSTD_initStaticCDict(
                                      ZSTD_dictMode_e dictMode,
                                      ZSTD_compressionParameters cParams)
     {
    -    size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* opt */ 0);
    +    size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0);
         size_t const neededSize = sizeof(ZSTD_CDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize)
                                 + HUF_WORKSPACE_SIZE + matchStateSize;
         ZSTD_CDict* const cdict = (ZSTD_CDict*) workspace;
    diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c
    index 1c56ff32d..149e63687 100644
    --- a/lib/compress/zstd_opt.c
    +++ b/lib/compress/zstd_opt.c
    @@ -251,6 +251,7 @@ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE*
         U32 idx = ms->nextToUpdate3;
         U32 const target = ms->nextToUpdate3 = (U32)(ip - base);
         size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3);
    +    assert(hashLog3 > 0);
     
         while(idx < target) {
             hashTable3[ZSTD_hash3Ptr(base+idx, hashLog3)] = idx;
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index 5cd1ea0fb..6147168d8 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -558,6 +558,36 @@ static int basicUnitTests(U32 seed, double compressibility)
           if (!ZSTD_isError(r)) goto _output_error;  /* must fail : frame requires > 100 bytes */
           DISPLAYLEVEL(3, "OK (%s)\n", ZSTD_getErrorName(r)); }
     
    +    DISPLAYLEVEL(3, "test%3i : dictionary source size and level : ", testNb++);
    +    {   ZSTD_DCtx* const dctx = ZSTD_createDCtx();
    +        ZSTD_DDict* const ddict = ZSTD_createDDict(dictionary.start, dictionary.filled);
    +        int const maxLevel = ZSTD_maxCLevel();
    +        int level;
    +        for (level = 1; level <= maxLevel; ++level) {
    +            ZSTD_CDict* const cdict = ZSTD_createCDict(dictionary.start, dictionary.filled, level);
    +            int const maxSize = MIN(1 MB, CNBufferSize);
    +            int size;
    +            for (size = 512; size <= maxSize; size <<= 1) {
    +                ZSTD_CCtx* const cctx = ZSTD_createCCtx();
    +                outBuff.dst = compressedBuffer;
    +                outBuff.size = compressedBufferSize;
    +                outBuff.pos = 0;
    +                inBuff.src = CNBuffer;
    +                inBuff.size = size;
    +                inBuff.pos = 0;
    +                CHECK_Z(ZSTD_CCtx_refCDict(cctx, cdict));
    +                CHECK_Z(ZSTD_compress_generic(cctx, &outBuff, &inBuff, ZSTD_e_end));
    +                if (inBuff.pos != inBuff.size) goto _output_error;
    +                CHECK_Z(ZSTD_decompress_usingDDict(dctx, decodedBuffer, size, outBuff.dst, outBuff.pos, ddict));
    +                ZSTD_freeCCtx(cctx);
    +            }
    +            ZSTD_freeCDict(cdict);
    +        }
    +        ZSTD_freeDDict(ddict);
    +        ZSTD_freeDCtx(dctx);
    +    }
    +    DISPLAYLEVEL(3, "OK\n");
    +
         DISPLAYLEVEL(3, "test%3i : ZSTD_initCStream_usingCDict_advanced with masked dictID : ", testNb++);
         {   ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBufferSize, dictionary.filled);
             ZSTD_frameParameters const fParams = { 1 /* contentSize */, 1 /* checksum */, 1 /* noDictID */};
    
    From 9c8c69e41a6c5520c0008535c53fb43f5158bafc Mon Sep 17 00:00:00 2001
    From: Nick Terrell 
    Date: Wed, 31 Jan 2018 11:12:54 -0800
    Subject: [PATCH 104/255] [fuzzer] Check ZSTD_initStaticCDict() for every level
    
    ---
     tests/fuzzer.c | 47 +++++++++++++++++++++++++----------------------
     1 file changed, 25 insertions(+), 22 deletions(-)
    
    diff --git a/tests/fuzzer.c b/tests/fuzzer.c
    index 0a6cf61d2..3982f8bbc 100644
    --- a/tests/fuzzer.c
    +++ b/tests/fuzzer.c
    @@ -763,28 +763,31 @@ static int basicUnitTests(U32 seed, double compressibility)
             DISPLAYLEVEL(3, "OK \n");
     
             DISPLAYLEVEL(3, "test%3i : compress with static CDict : ", testNb++);
    -        {   ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBuffSize, dictSize);
    -            size_t const cdictSize = ZSTD_estimateCDictSize_advanced(dictSize, cParams, ZSTD_dlm_byCopy);
    -            void* const cdictBuffer = malloc(cdictSize);
    -            if (cdictBuffer==NULL) goto _output_error;
    -            {   const ZSTD_CDict* const cdict = ZSTD_initStaticCDict(
    -                                            cdictBuffer, cdictSize,
    -                                            dictBuffer, dictSize,
    -                                            ZSTD_dlm_byCopy, ZSTD_dm_auto,
    -                                            cParams);
    -                if (cdict == NULL) {
    -                    DISPLAY("ZSTD_initStaticCDict failed ");
    -                    goto _output_error;
    -                }
    -                cSize = ZSTD_compress_usingCDict(cctx,
    -                                compressedBuffer, compressedBufferSize,
    -                                CNBuffer, CNBuffSize, cdict);
    -                if (ZSTD_isError(cSize)) {
    -                    DISPLAY("ZSTD_compress_usingCDict failed ");
    -                    goto _output_error;
    -            }   }
    -            free(cdictBuffer);
    -        }
    +        {   int const maxLevel = ZSTD_maxCLevel();
    +            int level;
    +            for (level = 1; level <= maxLevel; ++level) {
    +                ZSTD_compressionParameters const cParams = ZSTD_getCParams(level, CNBuffSize, dictSize);
    +                size_t const cdictSize = ZSTD_estimateCDictSize_advanced(dictSize, cParams, ZSTD_dlm_byCopy);
    +                void* const cdictBuffer = malloc(cdictSize);
    +                if (cdictBuffer==NULL) goto _output_error;
    +                {   const ZSTD_CDict* const cdict = ZSTD_initStaticCDict(
    +                                                cdictBuffer, cdictSize,
    +                                                dictBuffer, dictSize,
    +                                                ZSTD_dlm_byCopy, ZSTD_dm_auto,
    +                                                cParams);
    +                    if (cdict == NULL) {
    +                        DISPLAY("ZSTD_initStaticCDict failed ");
    +                        goto _output_error;
    +                    }
    +                    cSize = ZSTD_compress_usingCDict(cctx,
    +                                    compressedBuffer, compressedBufferSize,
    +                                    CNBuffer, MIN(10 KB, CNBuffSize), cdict);
    +                    if (ZSTD_isError(cSize)) {
    +                        DISPLAY("ZSTD_compress_usingCDict failed ");
    +                        goto _output_error;
    +                }   }
    +                free(cdictBuffer);
    +        }   }
             DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
     
             DISPLAYLEVEL(3, "test%3i : ZSTD_compress_usingCDict_advanced, no contentSize, no dictID : ", testNb++);
    
    From 48acaddff9bba40656a63fe7cd2577ac7ccdfe56 Mon Sep 17 00:00:00 2001
    From: Nick Terrell 
    Date: Thu, 1 Feb 2018 12:04:05 -0800
    Subject: [PATCH 105/255] Test for incorrect pledgeSrcSize earlier
    
    ---
     lib/compress/zstd_compress.c |  7 +++++++
     tests/zstreamtest.c          | 18 ++++++++++++++++--
     2 files changed, 23 insertions(+), 2 deletions(-)
    
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index 14bc3604b..4a825f65f 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -2096,6 +2096,13 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,
             if (ZSTD_isError(cSize)) return cSize;
             cctx->consumedSrcSize += srcSize;
             cctx->producedCSize += (cSize + fhSize);
    +        if (cctx->appliedParams.fParams.contentSizeFlag) {  /* control src size */
    +            if (cctx->consumedSrcSize+1 > cctx->pledgedSrcSizePlusOne) {
    +                DEBUGLOG(4, "error : pledgedSrcSize = %u, while realSrcSize >= %u",
    +                    (U32)cctx->pledgedSrcSizePlusOne-1, (U32)cctx->consumedSrcSize);
    +                return ERROR(srcSize_wrong);
    +            }
    +        }
             return cSize + fhSize;
         }
     }
    diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
    index 94b1a1917..a4fb5589e 100644
    --- a/tests/zstreamtest.c
    +++ b/tests/zstreamtest.c
    @@ -432,8 +432,8 @@ static int basicUnitTests(U32 seed, double compressibility)
         DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/COMPRESSIBLE_NOISE_LENGTH*100);
     
         /* wrong _srcSize compression test */
    -    DISPLAYLEVEL(3, "test%3i : wrong srcSize : %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH-1);
    -    ZSTD_initCStream_srcSize(zc, 1, CNBufferSize-1);
    +    DISPLAYLEVEL(3, "test%3i : too large srcSize : %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH-1);
    +    ZSTD_initCStream_srcSize(zc, 1, CNBufferSize+1);
         outBuff.dst = (char*)(compressedBuffer);
         outBuff.size = compressedBufferSize;
         outBuff.pos = 0;
    @@ -446,6 +446,20 @@ static int basicUnitTests(U32 seed, double compressibility)
           if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error;    /* must fail : wrong srcSize */
           DISPLAYLEVEL(3, "OK (error detected : %s) \n", ZSTD_getErrorName(r)); }
     
    +    /* wrong _srcSize compression test */
    +    DISPLAYLEVEL(3, "test%3i : too small srcSize : %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH-1);
    +    ZSTD_initCStream_srcSize(zc, 1, CNBufferSize-1);
    +    outBuff.dst = (char*)(compressedBuffer);
    +    outBuff.size = compressedBufferSize;
    +    outBuff.pos = 0;
    +    inBuff.src = CNBuffer;
    +    inBuff.size = CNBufferSize;
    +    inBuff.pos = 0;
    +    {   size_t const r = ZSTD_compressStream(zc, &outBuff, &inBuff);
    +        if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error;    /* must fail : wrong srcSize */
    +        DISPLAYLEVEL(3, "OK (error detected : %s) \n", ZSTD_getErrorName(r));
    +    }
    +
         /* Complex context re-use scenario */
         DISPLAYLEVEL(3, "test%3i : context re-use : ", testNb++);
         ZSTD_freeCStream(zc);
    
    From 2bfc79ab8d480e7556ee75135407cc8aa0557b60 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 1 Feb 2018 16:13:04 -0800
    Subject: [PATCH 106/255] removed bitstream.h dependency
    
    ---
     programs/fileio.c | 48 +++++++++++++++++++++++++++++++----------------
     1 file changed, 32 insertions(+), 16 deletions(-)
    
    diff --git a/programs/fileio.c b/programs/fileio.c
    index 6039a75a2..4e5d7c411 100644
    --- a/programs/fileio.c
    +++ b/programs/fileio.c
    @@ -36,18 +36,21 @@
     #  include 
     #endif
     
    -#include "bitstream.h"
     #include "mem.h"
     #include "fileio.h"
     #include "util.h"
    +
     #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */
     #include "zstd.h"
    +#include "zstd_errors.h"           /* ZSTD_error_frameParameter_windowTooLarge */
    +
     #if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS)
     #  include 
     #  if !defined(z_const)
     #    define z_const
     #  endif
     #endif
    +
     #if defined(ZSTD_LZMACOMPRESS) || defined(ZSTD_LZMADECOMPRESS)
     #  include 
     #endif
    @@ -1142,33 +1145,46 @@ static unsigned FIO_passThrough(FILE* foutput, FILE* finput, void* buffer, size_
         return 0;
     }
     
    -static void FIO_zstdErrorHelp(dRess_t* ress, size_t ret, char const* srcFileName)
    +/* FIO_highbit64() :
    + * gives position of highest bit.
    + * note : only works for v > 0 !
    + */
    +static unsigned FIO_highbit64(unsigned long long v)
    +{
    +    unsigned count = 0;
    +    assert(v != 0);
    +    v >>= 1;
    +    while (v) { v >>= 1; count++; }
    +    return count;
    +}
    +
    +/* FIO_zstdErrorHelp() :
    + * detailed error message when requested window size is too large */
    +static void FIO_zstdErrorHelp(dRess_t* ress, size_t err, char const* srcFileName)
     {
         ZSTD_frameHeader header;
    -    /* No special help for these errors */
    -    if (ZSTD_getErrorCode(ret) != ZSTD_error_frameParameter_windowTooLarge)
    +
    +    /* Help message only for one specific error */
    +    if (ZSTD_getErrorCode(err) != ZSTD_error_frameParameter_windowTooLarge)
             return;
    +
         /* Try to decode the frame header */
    -    ret = ZSTD_getFrameHeader(&header, ress->srcBuffer, ress->srcBufferLoaded);
    -    if (ret == 0) {
    -        U32 const windowSize = (U32)header.windowSize;
    -        U32 const windowLog = BIT_highbit32(windowSize) + ((windowSize & (windowSize - 1)) != 0);
    -        U32 const windowMB = (windowSize >> 20) + ((windowSize & ((1 MB) - 1)) != 0);
    -        assert(header.windowSize <= (U64)((U32)-1));
    +    err = ZSTD_getFrameHeader(&header, ress->srcBuffer, ress->srcBufferLoaded);
    +    if (err == 0) {
    +        U64 const windowSize = header.windowSize;
    +        U32 const windowLog = FIO_highbit64(windowSize) + ((windowSize & (windowSize - 1)) != 0);
    +        U32 const windowMB = (U32)((windowSize >> 20) + ((windowSize & ((1 MB) - 1)) != 0));
    +        assert(windowSize < (U64)(1ULL << 52));
             assert(g_memLimit > 0);
             DISPLAYLEVEL(1, "%s : Window size larger than maximum : %llu > %u\n",
    -                        srcFileName, header.windowSize, g_memLimit);
    +                        srcFileName, windowSize, g_memLimit);
             if (windowLog <= ZSTD_WINDOWLOG_MAX) {
                 DISPLAYLEVEL(1, "%s : Use --long=%u or --memory=%uMB\n",
                                 srcFileName, windowLog, windowMB);
                 return;
             }
    -    } else if (ZSTD_getErrorCode(ret) != ZSTD_error_frameParameter_windowTooLarge) {
    -        DISPLAYLEVEL(1, "%s : Error decoding frame header to read window size : %s\n",
    -                        srcFileName, ZSTD_getErrorName(ret));
    -        return;
         }
    -    DISPLAYLEVEL(1, "%s : Window log larger than ZSTD_WINDOWLOG_MAX=%u not supported\n",
    +    DISPLAYLEVEL(1, "%s : Window log larger than ZSTD_WINDOWLOG_MAX=%u; not supported\n",
                         srcFileName, ZSTD_WINDOWLOG_MAX);
     }
     
    
    From 60fa90b6c04c69cbd9312155f70dad46f2ce92b5 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 1 Feb 2018 16:13:31 -0800
    Subject: [PATCH 107/255] zstdmt: added ability to change compression
     parameters during compression
    
    ---
     lib/compress/zstd_compress.c   |  7 +++++++
     lib/compress/zstdmt_compress.c | 21 ++++++++++++++++++++-
     lib/compress/zstdmt_compress.h | 27 +++++++++++++++++++++------
     lib/zstd.h                     |  2 +-
     4 files changed, 49 insertions(+), 8 deletions(-)
    
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index 4a825f65f..18da77f8d 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -476,6 +476,9 @@ size_t ZSTD_CCtxParam_setParameter(
     /** ZSTD_CCtx_setParametersUsingCCtxParams() :
      *  just applies `params` into `cctx`
      *  no action is performed, parameters are merely stored.
    + *  If ZSTDMT is enabled, parameters are pushed to cctx->mtctx.
    + *    This is possible even if a compression is ongoing.
    + *    In which case, new parameters will be applied on the fly, starting with next compression job.
      */
     size_t ZSTD_CCtx_setParametersUsingCCtxParams(
             ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params)
    @@ -484,6 +487,10 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams(
         if (cctx->cdict) return ERROR(stage_wrong);
     
         cctx->requestedParams = *params;
    +#ifdef ZSTD_MULTITHREAD
    +    if (cctx->mtctx)
    +        ZSTDMT_MTCtx_setParametersUsingCCtxParams(cctx->mtctx, params);
    +#endif
     
         return 0;
     }
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index ec5f0bbbd..32390a61c 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -664,6 +664,25 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params)
         return jobParams;
     }
     
    +/*! ZSTDMT_MTCtx_setParametersUsingCCtxParams() :
    + *  Apply a ZSTD_CCtx_params to the compression context.
    + *  This entry point is accessed while compression is ongoing,
    + *  new parameters will be applied to next compression job.
    + *  However, following parameters are NOT updated :
    + *  - window size
    + *  - pledgedSrcSize
    + *  - nb threads
    + *  - job size
    + *  - overlap size
    + */
    +void ZSTDMT_MTCtx_setParametersUsingCCtxParams(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* params)
    +{
    +    U32 const wlog = mtctx->params.cParams.windowLog;
    +    mtctx->params = *params;
    +    mtctx->params.cParams.windowLog = wlog;  /* Do not modify windowLog ! */
    +    /* note : other parameters not updated are simply not used beyond initialization */
    +}
    +
     /* ZSTDMT_getNbThreads():
      * @return nb threads currently active in mtctx.
      * mtctx must be valid */
    @@ -856,7 +875,7 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
                                    void* dst, size_t dstCapacity,
                              const void* src, size_t srcSize,
                              const ZSTD_CDict* cdict,
    -                               ZSTD_parameters const params,
    +                               ZSTD_parameters params,
                                    unsigned overlapLog)
     {
         ZSTD_CCtx_params cctxParams = mtctx->params;
    diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h
    index 34dfc488f..dcff1138a 100644
    --- a/lib/compress/zstdmt_compress.h
    +++ b/lib/compress/zstdmt_compress.h
    @@ -38,7 +38,7 @@ ZSTDLIB_API size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx);
     ZSTDLIB_API size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx);
     
     
    -/* ===   Simple buffer-to-butter one-pass function   === */
    +/* ===   Simple one-pass compression function   === */
     
     ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx,
                                            void* dst, size_t dstCapacity,
    @@ -50,7 +50,7 @@ ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx,
     /* ===   Streaming functions   === */
     
     ZSTDLIB_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel);
    -ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize);  /**< if srcSize is not known at reset time, use ZSTD_CONTENTSIZE_UNKNOWN. Note: for compatibility with older programs, 0 means the same as ZSTD_CONTENTSIZE_UNKNOWN, but it may change in the future, to mean "empty" */
    +ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize);  /**< if srcSize is not known at reset time, use ZSTD_CONTENTSIZE_UNKNOWN. Note: for compatibility with older programs, 0 means the same as ZSTD_CONTENTSIZE_UNKNOWN, but it will change in the future to mean "empty" */
     
     ZSTDLIB_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
     
    @@ -68,7 +68,7 @@ ZSTDLIB_API size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
                                                void* dst, size_t dstCapacity,
                                          const void* src, size_t srcSize,
                                          const ZSTD_CDict* cdict,
    -                                           ZSTD_parameters const params,
    +                                           ZSTD_parameters params,
                                                unsigned overlapLog);
     
     ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx,
    @@ -109,15 +109,30 @@ ZSTDLIB_API size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
                                                     ZSTD_EndDirective endOp);
     
     
    -/* ===   Private definitions; never ever use directly  === */
    +/* ========================================================
    + * ===  Private interface, for use by ZSTD_compress.c   ===
    + * ===  Not exposed in libzstd. Never invoke directly   ===
    + * ======================================================== */
     
     size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_parameter parameter, unsigned value);
     
     /* ZSTDMT_CCtxParam_setNbThreads()
    - * Set nbThreads, and clamp it correctly,
    - * also reset jobSize and overlapLog */
    + * Set nbThreads, and clamp it.
    + * Also reset jobSize and overlapLog */
     size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThreads);
     
    +/*! ZSTDMT_MTCtx_setParametersUsingCCtxParams() :
    + *  Apply a ZSTD_CCtx_params to the compression context.
    + *  This works even during compression, and will be applied to next compression job.
    + *  However, the following parameters will NOT be updated after compression has been started :
    + *  - window size
    + *  - pledgedSrcSize
    + *  - nb threads
    + *  - job size
    + *  - overlap size
    + */
    +void ZSTDMT_MTCtx_setParametersUsingCCtxParams(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* params);
    +
     /* ZSTDMT_getNbThreads():
      * @return nb threads currently active in mtctx.
      * mtctx must be valid */
    diff --git a/lib/zstd.h b/lib/zstd.h
    index 29c3ced18..be73d25b4 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -1198,7 +1198,7 @@ ZSTDLIB_API size_t ZSTD_compress_generic_simpleArgs (
     ZSTDLIB_API ZSTD_CCtx_params* ZSTD_createCCtxParams(void);
     
     /*! ZSTD_resetCCtxParams() :
    - *  Reset params to default, with the default compression level.
    + *  Reset params to default values.
      */
     ZSTDLIB_API size_t ZSTD_resetCCtxParams(ZSTD_CCtx_params* params);
     
    
    From 4b6a94f0ccb3de6ba0ff3b840febecefb6285d81 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 1 Feb 2018 17:07:27 -0800
    Subject: [PATCH 108/255] clarified comments on LDM parameters
    
    ---
     lib/zstd.h        | 34 +++++++++++++++++++---------------
     programs/fileio.c |  9 ++++-----
     2 files changed, 23 insertions(+), 20 deletions(-)
    
    diff --git a/lib/zstd.h b/lib/zstd.h
    index be73d25b4..c3630eb00 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -1015,7 +1015,7 @@ typedef enum {
         /* advanced parameters - may not remain available after API update */
         ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize,
                                   * even when referencing into Dictionary content (default:0) */
    -    ZSTD_p_enableLongDistanceMatching=1200,  /* Enable long distance matching.
    +    ZSTD_p_enableLongDistanceMatching=1200, /* Enable long distance matching.
                                              * This parameter is designed to improve the compression
                                              * ratio for large inputs with long distance matches.
                                              * This increases the memory usage as well as window size.
    @@ -1025,25 +1025,29 @@ typedef enum {
                                              * other LDM parameters. Setting the compression level
                                              * after this parameter overrides the window log, though LDM
                                              * will remain enabled until explicitly disabled. */
    -    ZSTD_p_ldmHashLog,   /* Size of the table for long distance matching, as a power of 2.
    -                          * Larger values increase memory usage and compression ratio, but decrease
    -                          * compression speed.
    -                          * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
    -                          * (default: windowlog - 7). */
    -    ZSTD_p_ldmMinMatch,  /* Minimum size of searched matches for long distance matcher.
    -                          * Larger/too small values usually decrease compression ratio.
    -                          * Must be clamped between ZSTD_LDM_MINMATCH_MIN
    -                          * and ZSTD_LDM_MINMATCH_MAX (default: 64). */
    -    ZSTD_p_ldmBucketSizeLog,  /* Log size of each bucket in the LDM hash table for collision resolution.
    -                               * Larger values usually improve collision resolution but may decrease
    -                               * compression speed.
    -                               * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX (default: 3). */
    +    ZSTD_p_ldmHashLog,       /* Size of the table for long distance matching, as a power of 2.
    +                              * Larger values increase memory usage and compression ratio, but decrease
    +                              * compression speed.
    +                              * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
    +                              * (default: windowlog - 7).
    +                              * Special: value 0 means "do not change ldmHashLog". */
    +    ZSTD_p_ldmMinMatch,      /* Minimum size of searched matches for long distance matcher.
    +                              * Larger/too small values usually decrease compression ratio.
    +                              * Must be clamped between ZSTD_LDM_MINMATCH_MIN
    +                              * and ZSTD_LDM_MINMATCH_MAX (default: 64).
    +                              * Special: value 0 means "do not change ldmMinMatch". */
    +    ZSTD_p_ldmBucketSizeLog, /* Log size of each bucket in the LDM hash table for collision resolution.
    +                              * Larger values usually improve collision resolution but may decrease
    +                              * compression speed.
    +                              * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX (default: 3).
    +                              * note : 0 is a valid value */
         ZSTD_p_ldmHashEveryLog,  /* Frequency of inserting/looking up entries in the LDM hash table.
                                   * The default is MAX(0, (windowLog - ldmHashLog)) to
                                   * optimize hash table usage.
                                   * Larger values improve compression speed. Deviating far from the
                                   * default value will likely result in a decrease in compression ratio.
    -                              * Must be clamped between 0 and ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN. */
    +                              * Must be clamped between 0 and ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN.
    +                              * note : 0 is a valid value */
     
     } ZSTD_cParameter;
     
    diff --git a/programs/fileio.c b/programs/fileio.c
    index 4e5d7c411..8bfdc20ee 100644
    --- a/programs/fileio.c
    +++ b/programs/fileio.c
    @@ -430,7 +430,7 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel,
         if (!ress.srcBuffer || !ress.dstBuffer)
             EXM_THROW(31, "allocation error : not enough memory");
     
    -    /* Advances parameters, including dictionary */
    +    /* Advanced parameters, including dictionary */
         {   void* dictBuffer;
             size_t const dictBuffSize = FIO_createDictBuffer(&dictBuffer, dictFileName);   /* works with dictFileName==NULL */
             if (dictFileName && (dictBuffer==NULL))
    @@ -442,8 +442,7 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel,
             /* compression level */
             CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionLevel, cLevel) );
             /* long distance matching */
    -        CHECK( ZSTD_CCtx_setParameter(
    -                      ress.cctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag) );
    +        CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag) );
             CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_ldmHashLog, g_ldmHashLog) );
             CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_ldmMinMatch, g_ldmMinMatch) );
             if (g_ldmBucketSizeLog != FIO_LDM_PARAM_NOTSET) {
    @@ -461,13 +460,13 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel,
             CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_targetLength, comprParams->targetLength) );
             CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionStrategy, (U32)comprParams->strategy) );
             /* multi-threading */
    +#ifdef ZSTD_MULTITHREAD
             DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbThreads);
             CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbThreads, g_nbThreads) );
    -#ifdef ZSTD_MULTITHREAD
             CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nonBlockingMode, 1) );
     #endif
             /* dictionary */
    -        CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) );  /* just for dictionary loading, for compression parameters adaptation */
    +        CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) );  /* set the value temporarily for dictionary loading, to adapt compression parameters */
             CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, dictBuffer, dictBuffSize) );
             CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, ZSTD_CONTENTSIZE_UNKNOWN) );  /* reset */
     
    
    From 209df52ba29a0f1eacd9899dda5fcc0c0a1c313f Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 1 Feb 2018 19:29:30 -0800
    Subject: [PATCH 109/255] Changed nbThreads for nbWorkers
    
    This makes it easier to explain that nbWorkers=0 --> single-threaded mode,
    while nbWorkers=1 --> asynchronous mode (one mode thread on top of the "main" caller thread).
    No need for an additional asynchronous mode flag.
    nbWorkers>=2 works the same as nbThreads>=2 previously.
    ---
     doc/zstd_manual.html                  |  57 +++++++------
     lib/compress/zstd_compress.c          |  56 +++++--------
     lib/compress/zstd_compress_internal.h |   5 +-
     lib/compress/zstdmt_compress.c        | 111 +++++++++++++-------------
     lib/compress/zstdmt_compress.h        |  18 ++---
     lib/zstd.h                            |  30 +++----
     programs/bench.c                      |  10 +--
     programs/bench.h                      |   2 +-
     programs/fileio.c                     |  17 ++--
     programs/fileio.h                     |   2 +-
     programs/zstdcli.c                    |  22 ++---
     tests/fullbench.c                     |   4 +-
     tests/fuzzer.c                        |   4 +-
     tests/roundTripCrash.c                |   2 +-
     tests/zstreamtest.c                   |   8 +-
     15 files changed, 165 insertions(+), 183 deletions(-)
    
    diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
    index 1dea249d4..748568237 100644
    --- a/doc/zstd_manual.html
    +++ b/doc/zstd_manual.html
    @@ -416,7 +416,7 @@ size_t ZSTD_estimateDCtxSize(void);
       It will also consider src size to be arbitrarily "large", which is worst case.
       If srcSize is known to always be small, ZSTD_estimateCCtxSize_usingCParams() can provide a tighter estimation.
       ZSTD_estimateCCtxSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.
    -  ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is > 1.
    +  ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbWorkers is >= 1.
       Note : CCtx size estimation is only correct for single-threaded compression. 
     


    @@ -429,7 +429,7 @@ size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize); It will also consider src size to be arbitrarily "large", which is worst case. If srcSize is known to always be small, ZSTD_estimateCStreamSize_usingCParams() can provide a tighter estimation. ZSTD_estimateCStreamSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel. - ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is set to a value > 1. + ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbWorkers is >= 1. Note : CStream size estimation is only correct for single-threaded compression. ZSTD_DStream memory budget depends on window Size. This information can be passed manually, using ZSTD_estimateDStreamSize, @@ -800,18 +800,11 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
    /* multi-threading parameters */ /* These parameters are only useful if multi-threading is enabled (ZSTD_MULTITHREAD). * They return an error otherwise. */ - ZSTD_p_nbThreads=400, /* Select how many threads a compression job can spawn (default:1) + ZSTD_p_nbWorkers=400, /* Select how many threads will be spawned to compress in parallel. + * Triggers asynchronous mode, even with nbWorkers = 1. + * Can only be set to a value >= 1 if ZSTD_MULTITHREAD is enabled. * More threads improve speed, but also increase memory usage. - * Can only receive a value > 1 if ZSTD_MULTITHREAD is enabled. - * Special: value 0 means "do not change nbThreads" */ - ZSTD_p_nonBlockingMode, /* Single thread mode is by default "blocking" : - * it finishes its job as much as possible, and only then gives back control to caller. - * In contrast, multi-thread is by default "non-blocking" : - * it takes some input, flush some output if available, and immediately gives back control to caller. - * Compression work is performed in parallel, within worker threads. - * (note : a strong exception to this rule is when first job is called with ZSTD_e_end : it becomes blocking) - * Setting this parameter to 1 will enforce non-blocking mode even when only 1 thread is selected. - * It allows the caller to do other tasks while the worker thread compresses in parallel. */ + * Default value is `0`, aka "blocking mode" : no worker is spawned, compression is performed inside Caller's thread */ ZSTD_p_jobSize, /* Size of a compression job. This value is only enforced in streaming (non-blocking) mode. * Each compression job is completed in parallel, so indirectly controls the nb of active threads. * 0 means default, which is dynamically determined based on compression parameters. @@ -823,7 +816,7 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long /* advanced parameters - may not remain available after API update */ ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize, * even when referencing into Dictionary content (default:0) */ - ZSTD_p_enableLongDistanceMatching=1200, /* Enable long distance matching. + ZSTD_p_enableLongDistanceMatching=1200, /* Enable long distance matching. * This parameter is designed to improve the compression * ratio for large inputs with long distance matches. * This increases the memory usage as well as window size. @@ -833,25 +826,29 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long * other LDM parameters. Setting the compression level * after this parameter overrides the window log, though LDM * will remain enabled until explicitly disabled. */ - ZSTD_p_ldmHashLog, /* Size of the table for long distance matching, as a power of 2. - * Larger values increase memory usage and compression ratio, but decrease - * compression speed. - * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX - * (default: windowlog - 7). */ - ZSTD_p_ldmMinMatch, /* Minimum size of searched matches for long distance matcher. - * Larger/too small values usually decrease compression ratio. - * Must be clamped between ZSTD_LDM_MINMATCH_MIN - * and ZSTD_LDM_MINMATCH_MAX (default: 64). */ - ZSTD_p_ldmBucketSizeLog, /* Log size of each bucket in the LDM hash table for collision resolution. - * Larger values usually improve collision resolution but may decrease - * compression speed. - * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX (default: 3). */ + ZSTD_p_ldmHashLog, /* Size of the table for long distance matching, as a power of 2. + * Larger values increase memory usage and compression ratio, but decrease + * compression speed. + * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX + * (default: windowlog - 7). + * Special: value 0 means "do not change ldmHashLog". */ + ZSTD_p_ldmMinMatch, /* Minimum size of searched matches for long distance matcher. + * Larger/too small values usually decrease compression ratio. + * Must be clamped between ZSTD_LDM_MINMATCH_MIN + * and ZSTD_LDM_MINMATCH_MAX (default: 64). + * Special: value 0 means "do not change ldmMinMatch". */ + ZSTD_p_ldmBucketSizeLog, /* Log size of each bucket in the LDM hash table for collision resolution. + * Larger values usually improve collision resolution but may decrease + * compression speed. + * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX (default: 3). + * note : 0 is a valid value */ ZSTD_p_ldmHashEveryLog, /* Frequency of inserting/looking up entries in the LDM hash table. * The default is MAX(0, (windowLog - ldmHashLog)) to * optimize hash table usage. * Larger values improve compression speed. Deviating far from the * default value will likely result in a decrease in compression ratio. - * Must be clamped between 0 and ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN. */ + * Must be clamped between 0 and ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN. + * note : 0 is a valid value */ } ZSTD_cParameter;

    @@ -1000,7 +997,7 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t


    size_t ZSTD_resetCCtxParams(ZSTD_CCtx_params* params);
    -

    Reset params to default, with the default compression level. +

    Reset params to default values.


    @@ -1030,7 +1027,7 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t

    Apply a set of ZSTD_CCtx_params to the compression context. This must be done before the dictionary is loaded. The pledgedSrcSize is treated as unknown. - Multithreading parameters are applied only if nbThreads > 1. + Multithreading parameters are applied only if nbWorkers >= 1.


    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 18da77f8d..e66b4df8a 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -281,13 +281,12 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v * default : 0 when using a CDict, 1 when using a Prefix */ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); - case ZSTD_p_nbThreads: - if ((value > 1) && cctx->staticSize) { + case ZSTD_p_nbWorkers: + if ((value>0) && cctx->staticSize) { return ERROR(parameter_unsupported); /* MT not compatible with static alloc */ } return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); - case ZSTD_p_nonBlockingMode: case ZSTD_p_jobSize: case ZSTD_p_overlapSizeLog: return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); @@ -403,21 +402,12 @@ size_t ZSTD_CCtxParam_setParameter( CCtxParams->forceWindow = (value > 0); return CCtxParams->forceWindow; - case ZSTD_p_nbThreads : - if (value == 0) return CCtxParams->nbThreads; + case ZSTD_p_nbWorkers : #ifndef ZSTD_MULTITHREAD - if (value > 1) return ERROR(parameter_unsupported); - return 1; + if (value > 0) return ERROR(parameter_unsupported); + return 0; #else - return ZSTDMT_CCtxParam_setNbThreads(CCtxParams, value); -#endif - - case ZSTD_p_nonBlockingMode : -#ifndef ZSTD_MULTITHREAD - return ERROR(parameter_unsupported); -#else - CCtxParams->nonBlockingMode = (value>0); - return CCtxParams->nonBlockingMode; + return ZSTDMT_CCtxParam_setNbWorkers(CCtxParams, value); #endif case ZSTD_p_jobSize : @@ -489,7 +479,7 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( cctx->requestedParams = *params; #ifdef ZSTD_MULTITHREAD if (cctx->mtctx) - ZSTDMT_MTCtx_setParametersUsingCCtxParams(cctx->mtctx, params); + ZSTDMT_MTCtx_setParametersUsingCCtxParams_whileCompressing(cctx->mtctx, params); #endif return 0; @@ -687,7 +677,7 @@ static size_t ZSTD_sizeof_matchState(ZSTD_compressionParameters const* cParams, size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params) { /* Estimate CCtx size is supported for single-threaded compression only. */ - if (params->nbThreads > 1) { return ERROR(GENERIC); } + if (params->nbWorkers > 0) { return ERROR(GENERIC); } { ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams(*params, 0, 0); size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog); @@ -736,7 +726,7 @@ size_t ZSTD_estimateCCtxSize(int compressionLevel) size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params) { - if (params->nbThreads > 1) { return ERROR(GENERIC); } + if (params->nbWorkers > 0) { return ERROR(GENERIC); } { size_t const CCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(params); size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << params->cParams.windowLog); size_t const inBuffSize = ((size_t)1 << params->cParams.windowLog) + blockSize; @@ -775,7 +765,7 @@ size_t ZSTD_estimateCStreamSize(int compressionLevel) { ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx) { #ifdef ZSTD_MULTITHREAD - if ((cctx->appliedParams.nbThreads > 1) || (cctx->appliedParams.nonBlockingMode)) { + if (cctx->appliedParams.nbWorkers > 0) { return ZSTDMT_getFrameProgression(cctx->mtctx); } #endif @@ -3166,28 +3156,26 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, #ifdef ZSTD_MULTITHREAD if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) { - params.nbThreads = 1; /* do not invoke multi-threading when src size is too small */ - params.nonBlockingMode = 0; + params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */ } - if ((params.nbThreads > 1) | (params.nonBlockingMode == 1)) { - if (cctx->mtctx == NULL || (params.nbThreads != ZSTDMT_getNbThreads(cctx->mtctx))) { - DEBUGLOG(4, "ZSTD_compress_generic: creating new mtctx for nbThreads=%u", - params.nbThreads); + if (params.nbWorkers > 0) { + if (cctx->mtctx == NULL || (params.nbWorkers != ZSTDMT_getNbWorkers(cctx->mtctx))) { + DEBUGLOG(4, "ZSTD_compress_generic: creating new mtctx for nbWorkers=%u", + params.nbWorkers); if (cctx->mtctx != NULL) - DEBUGLOG(4, "ZSTD_compress_generic: previous nbThreads was %u", - ZSTDMT_getNbThreads(cctx->mtctx)); + DEBUGLOG(4, "ZSTD_compress_generic: previous nbWorkers was %u", + ZSTDMT_getNbWorkers(cctx->mtctx)); ZSTDMT_freeCCtx(cctx->mtctx); - cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbThreads, cctx->customMem); + cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbWorkers, cctx->customMem); if (cctx->mtctx == NULL) return ERROR(memory_allocation); } - DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbThreads=%u", params.nbThreads); + DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbWorkers=%u", params.nbWorkers); CHECK_F( ZSTDMT_initCStream_internal( cctx->mtctx, prefixDict.dict, prefixDict.dictSize, ZSTD_dm_rawContent, cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) ); cctx->streamStage = zcss_load; - cctx->appliedParams.nbThreads = params.nbThreads; - cctx->appliedParams.nonBlockingMode = params.nonBlockingMode; + cctx->appliedParams.nbWorkers = params.nbWorkers; } else #endif { CHECK_F( ZSTD_resetCStream_internal( @@ -3195,12 +3183,12 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, prefixDict.dictMode, cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) ); assert(cctx->streamStage == zcss_load); - assert(cctx->appliedParams.nbThreads <= 1); + assert(cctx->appliedParams.nbWorkers == 0); } } /* compression stage */ #ifdef ZSTD_MULTITHREAD - if ((cctx->appliedParams.nbThreads > 1) | (cctx->appliedParams.nonBlockingMode==1)) { + if (cctx->appliedParams.nbWorkers > 0) { size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp); if ( ZSTD_isError(flushMin) || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */ diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index cf5936587..eb651fb5e 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -145,12 +145,11 @@ struct ZSTD_CCtx_params_s { ZSTD_frameParameters fParams; int compressionLevel; - U32 forceWindow; /* force back-references to respect limit of + int forceWindow; /* force back-references to respect limit of * 1<= 1 , checked by caller ZSTDMT_createCCtx() */ -static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads, + * implies nbWorkers >= 1 , checked by caller ZSTDMT_createCCtx() */ +static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbWorkers, ZSTD_customMem cMem) { ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) ZSTD_calloc( - sizeof(ZSTDMT_CCtxPool) + (nbThreads-1)*sizeof(ZSTD_CCtx*), cMem); + sizeof(ZSTDMT_CCtxPool) + (nbWorkers-1)*sizeof(ZSTD_CCtx*), cMem); + assert(nbWorkers > 0); if (!cctxPool) return NULL; if (ZSTD_pthread_mutex_init(&cctxPool->poolMutex, NULL)) { ZSTD_free(cctxPool, cMem); return NULL; } cctxPool->cMem = cMem; - cctxPool->totalCCtx = nbThreads; + cctxPool->totalCCtx = nbWorkers; cctxPool->availCCtx = 1; /* at least one cctx for single-thread mode */ cctxPool->cctx[0] = ZSTD_createCCtx_advanced(cMem); if (!cctxPool->cctx[0]) { ZSTDMT_freeCCtxPool(cctxPool); return NULL; } - DEBUGLOG(3, "cctxPool created, with %u threads", nbThreads); + DEBUGLOG(3, "cctxPool created, with %u workers", nbWorkers); return cctxPool; } @@ -260,15 +261,16 @@ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads, static size_t ZSTDMT_sizeof_CCtxPool(ZSTDMT_CCtxPool* cctxPool) { ZSTD_pthread_mutex_lock(&cctxPool->poolMutex); - { unsigned const nbThreads = cctxPool->totalCCtx; + { unsigned const nbWorkers = cctxPool->totalCCtx; size_t const poolSize = sizeof(*cctxPool) - + (nbThreads-1)*sizeof(ZSTD_CCtx*); + + (nbWorkers-1) * sizeof(ZSTD_CCtx*); unsigned u; size_t totalCCtxSize = 0; - for (u=0; ucctx[u]); } ZSTD_pthread_mutex_unlock(&cctxPool->poolMutex); + assert(nbWorkers > 0); return poolSize + totalCCtxSize; } } @@ -295,8 +297,8 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) if (pool->availCCtx < pool->totalCCtx) pool->cctx[pool->availCCtx++] = cctx; else { - /* pool overflow : should not happen, since totalCCtx==nbThreads */ - DEBUGLOG(5, "CCtx pool overflow : free cctx"); + /* pool overflow : should not happen, since totalCCtx==nbWorkers */ + DEBUGLOG(4, "CCtx pool overflow : free cctx"); ZSTD_freeCCtx(cctx); } ZSTD_pthread_mutex_unlock(&pool->poolMutex); @@ -502,52 +504,52 @@ static ZSTDMT_jobDescription* ZSTDMT_createJobsTable(U32* nbJobsPtr, ZSTD_custom return jobTable; } -/* ZSTDMT_CCtxParam_setNbThreads(): +/* ZSTDMT_CCtxParam_setNbWorkers(): * Internal use only */ -size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThreads) +size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers) { - if (nbThreads > ZSTDMT_NBTHREADS_MAX) nbThreads = ZSTDMT_NBTHREADS_MAX; - if (nbThreads < 1) nbThreads = 1; - params->nbThreads = nbThreads; + if (nbWorkers > ZSTDMT_NBWORKERS_MAX) nbWorkers = ZSTDMT_NBWORKERS_MAX; + if (nbWorkers < 1) nbWorkers = 1; + params->nbWorkers = nbWorkers; params->overlapSizeLog = ZSTDMT_OVERLAPLOG_DEFAULT; params->jobSize = 0; - return nbThreads; + return nbWorkers; } -ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem) +ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem) { ZSTDMT_CCtx* mtctx; - U32 nbJobs = nbThreads + 2; - DEBUGLOG(3, "ZSTDMT_createCCtx_advanced (nbThreads = %u)", nbThreads); + U32 nbJobs = nbWorkers + 2; + DEBUGLOG(3, "ZSTDMT_createCCtx_advanced (nbWorkers = %u)", nbWorkers); - if (nbThreads < 1) return NULL; - nbThreads = MIN(nbThreads , ZSTDMT_NBTHREADS_MAX); + if (nbWorkers < 1) return NULL; + nbWorkers = MIN(nbWorkers , ZSTDMT_NBWORKERS_MAX); if ((cMem.customAlloc!=NULL) ^ (cMem.customFree!=NULL)) /* invalid custom allocator */ return NULL; mtctx = (ZSTDMT_CCtx*) ZSTD_calloc(sizeof(ZSTDMT_CCtx), cMem); if (!mtctx) return NULL; - ZSTDMT_CCtxParam_setNbThreads(&mtctx->params, nbThreads); + ZSTDMT_CCtxParam_setNbWorkers(&mtctx->params, nbWorkers); mtctx->cMem = cMem; mtctx->allJobsCompleted = 1; - mtctx->factory = POOL_create_advanced(nbThreads, 0, cMem); + mtctx->factory = POOL_create_advanced(nbWorkers, 0, cMem); mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, cMem); assert(nbJobs > 0); assert((nbJobs & (nbJobs - 1)) == 0); /* ensure nbJobs is a power of 2 */ mtctx->jobIDMask = nbJobs - 1; - mtctx->bufPool = ZSTDMT_createBufferPool(nbThreads, cMem); - mtctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads, cMem); + mtctx->bufPool = ZSTDMT_createBufferPool(nbWorkers, cMem); + mtctx->cctxPool = ZSTDMT_createCCtxPool(nbWorkers, cMem); if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool) { ZSTDMT_freeCCtx(mtctx); return NULL; } - DEBUGLOG(3, "mt_cctx created, for %u threads", nbThreads); + DEBUGLOG(3, "mt_cctx created, for %u threads", nbWorkers); return mtctx; } -ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbThreads) +ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers) { - return ZSTDMT_createCCtx_advanced(nbThreads, ZSTD_defaultCMem); + return ZSTDMT_createCCtx_advanced(nbWorkers, ZSTD_defaultCMem); } @@ -649,8 +651,8 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, } } -/* Sets parameters relevant to the compression job, initializing others to - * default values. Notably, nbThreads should probably be zero. */ +/* Sets parameters relevant to the compression job, + * initializing others to default values. */ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) { ZSTD_CCtx_params jobParams; @@ -664,7 +666,7 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) return jobParams; } -/*! ZSTDMT_MTCtx_setParametersUsingCCtxParams() : +/*! ZSTDMT_MTCtx_setParametersUsingCCtxParams_whileCompressing() : * Apply a ZSTD_CCtx_params to the compression context. * This entry point is accessed while compression is ongoing, * new parameters will be applied to next compression job. @@ -675,21 +677,23 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) * - job size * - overlap size */ -void ZSTDMT_MTCtx_setParametersUsingCCtxParams(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* params) +void ZSTDMT_MTCtx_setParametersUsingCCtxParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* params) { U32 const wlog = mtctx->params.cParams.windowLog; + U32 const nbWorkers = mtctx->params.nbWorkers; mtctx->params = *params; - mtctx->params.cParams.windowLog = wlog; /* Do not modify windowLog ! */ + mtctx->params.cParams.windowLog = wlog; /* Do not modify windowLog ! Frame must keep same wlog during the whole process ! */ + mtctx->params.nbWorkers = nbWorkers; /* Do not modify nbWorkers, it must remain synchronized with CCtx Pool ! */ /* note : other parameters not updated are simply not used beyond initialization */ } -/* ZSTDMT_getNbThreads(): +/* ZSTDMT_getNbWorkers(): * @return nb threads currently active in mtctx. * mtctx must be valid */ -unsigned ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx) +unsigned ZSTDMT_getNbWorkers(const ZSTDMT_CCtx* mtctx) { assert(mtctx != NULL); - return mtctx->params.nbThreads; + return mtctx->params.nbWorkers; } /* ZSTDMT_getFrameProgression(): @@ -728,15 +732,15 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx) /* ===== Multi-threaded compression ===== */ /* ------------------------------------------ */ -static unsigned ZSTDMT_computeNbJobs(size_t srcSize, unsigned windowLog, unsigned nbThreads) { - assert(nbThreads>0); +static unsigned ZSTDMT_computeNbJobs(size_t srcSize, unsigned windowLog, unsigned nbWorkers) { + assert(nbWorkers>0); { size_t const jobSizeTarget = (size_t)1 << (windowLog + 2); size_t const jobMaxSize = jobSizeTarget << 2; - size_t const passSizeMax = jobMaxSize * nbThreads; + size_t const passSizeMax = jobMaxSize * nbWorkers; unsigned const multiplier = (unsigned)(srcSize / passSizeMax) + 1; - unsigned const nbJobsLarge = multiplier * nbThreads; + unsigned const nbJobsLarge = multiplier * nbWorkers; unsigned const nbJobsMax = (unsigned)(srcSize / jobSizeTarget) + 1; - unsigned const nbJobsSmall = MIN(nbJobsMax, nbThreads); + unsigned const nbJobsSmall = MIN(nbJobsMax, nbWorkers); return (multiplier>1) ? nbJobsLarge : nbJobsSmall; } } @@ -753,7 +757,7 @@ static size_t ZSTDMT_compress_advanced_internal( ZSTD_CCtx_params const jobParams = ZSTDMT_initJobCCtxParams(params); unsigned const overlapRLog = (params.overlapSizeLog>9) ? 0 : 9-params.overlapSizeLog; size_t const overlapSize = (overlapRLog>=9) ? 0 : (size_t)1 << (params.cParams.windowLog - overlapRLog); - unsigned const nbJobs = ZSTDMT_computeNbJobs(srcSize, params.cParams.windowLog, params.nbThreads); + unsigned const nbJobs = ZSTDMT_computeNbJobs(srcSize, params.cParams.windowLog, params.nbWorkers); size_t const proposedJobSize = (srcSize + (nbJobs-1)) / nbJobs; size_t const avgJobSize = (((proposedJobSize-1) & 0x1FFFF) < 0x7FFF) ? proposedJobSize + 0xFFFF : proposedJobSize; /* avoid too small last block */ const char* const srcStart = (const char*)src; @@ -761,13 +765,13 @@ static size_t ZSTDMT_compress_advanced_internal( unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbJobs : (unsigned)(dstCapacity / ZSTD_compressBound(avgJobSize)); /* presumes avgJobSize >= 256 KB, which should be the case */ size_t frameStartPos = 0, dstBufferPos = 0; XXH64_state_t xxh64; - assert(jobParams.nbThreads == 0); - assert(mtctx->cctxPool->totalCCtx == params.nbThreads); + assert(jobParams.nbWorkers == 0); + assert(mtctx->cctxPool->totalCCtx == params.nbWorkers); DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbJobs=%2u (rawSize=%u bytes; fixedSize=%u) ", nbJobs, (U32)proposedJobSize, (U32)avgJobSize); - if ((nbJobs==1) | (params.nbThreads<=1)) { /* fallback to single-thread mode : this is a blocking invocation anyway */ + if ((nbJobs==1) | (params.nbWorkers<=1)) { /* fallback to single-thread mode : this is a blocking invocation anyway */ ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0]; if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams); return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, NULL, 0, jobParams); @@ -911,11 +915,12 @@ size_t ZSTDMT_initCStream_internal( const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize) { - DEBUGLOG(4, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u)", (U32)pledgedSrcSize); + DEBUGLOG(2, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u, nbWorkers=%u, cctxPool=%u)", + (U32)pledgedSrcSize, params.nbWorkers, mtctx->cctxPool->totalCCtx); /* params are supposed to be fully validated at this point */ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ - assert(mtctx->cctxPool->totalCCtx == params.nbThreads); + assert(mtctx->cctxPool->totalCCtx == params.nbWorkers); mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */ if (params.jobSize == 0) { if (params.cParams.windowLog >= 29) @@ -928,12 +933,12 @@ size_t ZSTDMT_initCStream_internal( if (mtctx->singleBlockingThread) { ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(params); DEBUGLOG(4, "ZSTDMT_initCStream_internal: switch to single blocking thread mode"); - assert(singleThreadParams.nbThreads == 0); + assert(singleThreadParams.nbWorkers == 0); return ZSTD_initCStream_internal(mtctx->cctxPool->cctx[0], dict, dictSize, cdict, singleThreadParams, pledgedSrcSize); } - DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u threads", params.nbThreads); + DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u workers", params.nbWorkers); if (mtctx->allJobsCompleted == 0) { /* previous compression not correctly finished */ ZSTDMT_waitForAllJobsCompleted(mtctx); @@ -1012,8 +1017,6 @@ size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize) { if (!pledgedSrcSize) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; - if (mtctx->params.nbThreads==1) - return ZSTD_resetCStream(mtctx->cctxPool->cctx[0], pledgedSrcSize); return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dm_auto, 0, mtctx->params, pledgedSrcSize); } diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index dcff1138a..3fef2ed6a 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -30,8 +30,8 @@ /* === Memory management === */ typedef struct ZSTDMT_CCtx_s ZSTDMT_CCtx; -ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbThreads); -ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, +ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers); +ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem); ZSTDLIB_API size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx); @@ -116,12 +116,12 @@ ZSTDLIB_API size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_parameter parameter, unsigned value); -/* ZSTDMT_CCtxParam_setNbThreads() - * Set nbThreads, and clamp it. +/* ZSTDMT_CCtxParam_setNbWorkers() + * Set nbWorkers, and clamp it. * Also reset jobSize and overlapLog */ -size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThreads); +size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers); -/*! ZSTDMT_MTCtx_setParametersUsingCCtxParams() : +/*! ZSTDMT_MTCtx_setParametersUsingCCtxParams_whileCompressing() : * Apply a ZSTD_CCtx_params to the compression context. * This works even during compression, and will be applied to next compression job. * However, the following parameters will NOT be updated after compression has been started : @@ -131,12 +131,12 @@ size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThread * - job size * - overlap size */ -void ZSTDMT_MTCtx_setParametersUsingCCtxParams(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* params); +void ZSTDMT_MTCtx_setParametersUsingCCtxParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* params); -/* ZSTDMT_getNbThreads(): +/* ZSTDMT_getNbWorkers(): * @return nb threads currently active in mtctx. * mtctx must be valid */ -unsigned ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx); +unsigned ZSTDMT_getNbWorkers(const ZSTDMT_CCtx* mtctx); /* ZSTDMT_getFrameProgression(): * tells how much data has been consumed (input) and produced (output) for current frame. diff --git a/lib/zstd.h b/lib/zstd.h index c3630eb00..be93a7eeb 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -506,7 +506,7 @@ ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict); * It will also consider src size to be arbitrarily "large", which is worst case. * If srcSize is known to always be small, ZSTD_estimateCCtxSize_usingCParams() can provide a tighter estimation. * ZSTD_estimateCCtxSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel. - * ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is > 1. + * ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbWorkers is >= 1. * Note : CCtx size estimation is only correct for single-threaded compression. */ ZSTDLIB_API size_t ZSTD_estimateCCtxSize(int compressionLevel); ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams); @@ -518,7 +518,7 @@ ZSTDLIB_API size_t ZSTD_estimateDCtxSize(void); * It will also consider src size to be arbitrarily "large", which is worst case. * If srcSize is known to always be small, ZSTD_estimateCStreamSize_usingCParams() can provide a tighter estimation. * ZSTD_estimateCStreamSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel. - * ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbThreads is set to a value > 1. + * ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_p_nbWorkers is >= 1. * Note : CStream size estimation is only correct for single-threaded compression. * ZSTD_DStream memory budget depends on window Size. * This information can be passed manually, using ZSTD_estimateDStreamSize, @@ -992,18 +992,13 @@ typedef enum { /* multi-threading parameters */ /* These parameters are only useful if multi-threading is enabled (ZSTD_MULTITHREAD). * They return an error otherwise. */ - ZSTD_p_nbThreads=400, /* Select how many threads a compression job can spawn (default:1) - * More threads improve speed, but also increase memory usage. - * Can only receive a value > 1 if ZSTD_MULTITHREAD is enabled. - * Special: value 0 means "do not change nbThreads" */ - ZSTD_p_nonBlockingMode, /* Single thread mode is by default "blocking" : - * it finishes its job as much as possible, and only then gives back control to caller. - * In contrast, multi-thread is by default "non-blocking" : - * it takes some input, flush some output if available, and immediately gives back control to caller. - * Compression work is performed in parallel, within worker threads. - * (note : a strong exception to this rule is when first job is called with ZSTD_e_end : it becomes blocking) - * Setting this parameter to 1 will enforce non-blocking mode even when only 1 thread is selected. - * It allows the caller to do other tasks while the worker thread compresses in parallel. */ + ZSTD_p_nbWorkers=400, /* Select how many threads will be spawned to compress in parallel. + * When nbWorkers >= 1, triggers asynchronous mode : + * ZSTD_compress_generic() consumes some input, flush some output if possible, and immediately gives back control to caller, + * while compression work is performed in parallel, within worker threads. + * (note : a strong exception to this rule is when first invocation sets ZSTD_e_end : it becomes a blocking call). + * More workers improve speed, but also increase memory usage. + * Default value is `0`, aka "single-threaded mode" : no worker is spawned, compression is performed inside Caller's thread, all invocations are blocking */ ZSTD_p_jobSize, /* Size of a compression job. This value is only enforced in streaming (non-blocking) mode. * Each compression job is completed in parallel, so indirectly controls the nb of active threads. * 0 means default, which is dynamically determined based on compression parameters. @@ -1231,9 +1226,10 @@ ZSTDLIB_API size_t ZSTD_CCtxParam_setParameter(ZSTD_CCtx_params* params, ZSTD_cP /*! ZSTD_CCtx_setParametersUsingCCtxParams() : * Apply a set of ZSTD_CCtx_params to the compression context. - * This must be done before the dictionary is loaded. - * The pledgedSrcSize is treated as unknown. - * Multithreading parameters are applied only if nbThreads > 1. + * This can be done even after compression is started, + * if nbWorkers==0, this will have no impact until a new compression is started. + * if nbWorkers>=1, new parameters will be picked up at next job, + * with a few restrictions (windowLog, pledgedSrcSize, nbWorkers, jobSize, and overlapLog are not updated). */ ZSTDLIB_API size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params); diff --git a/programs/bench.c b/programs/bench.c index 843920c89..bf3dcb47d 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -122,12 +122,12 @@ void BMK_setBlockSize(size_t blockSize) void BMK_setDecodeOnlyMode(unsigned decodeFlag) { g_decodeOnly = (decodeFlag>0); } -static U32 g_nbThreads = 1; -void BMK_setNbThreads(unsigned nbThreads) { +static U32 g_nbWorkers = 0; +void BMK_setNbWorkers(unsigned nbWorkers) { #ifndef ZSTD_MULTITHREAD - if (nbThreads > 1) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n"); + if (nbWorkers > 0) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n"); #endif - g_nbThreads = nbThreads; + g_nbWorkers = nbWorkers; } static U32 g_realTime = 0; @@ -295,7 +295,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, if (!cCompleted) { /* still some time to do compression tests */ U64 const clockLoop = g_nbSeconds ? TIMELOOP_MICROSEC : 1; U32 nbLoops = 0; - ZSTD_CCtx_setParameter(ctx, ZSTD_p_nbThreads, g_nbThreads); + ZSTD_CCtx_setParameter(ctx, ZSTD_p_nbWorkers, g_nbWorkers); ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionLevel, cLevel); ZSTD_CCtx_setParameter(ctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag); ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmMinMatch, g_ldmMinMatch); diff --git a/programs/bench.h b/programs/bench.h index d2ec94ee3..bf1087013 100644 --- a/programs/bench.h +++ b/programs/bench.h @@ -22,7 +22,7 @@ int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, const char* di /* Set Parameters */ void BMK_setNbSeconds(unsigned nbLoops); void BMK_setBlockSize(size_t blockSize); -void BMK_setNbThreads(unsigned nbThreads); +void BMK_setNbWorkers(unsigned nbWorkers); void BMK_setRealTime(unsigned priority); void BMK_setNotificationLevel(unsigned level); void BMK_setSeparateFiles(unsigned separate); diff --git a/programs/fileio.c b/programs/fileio.c index 8bfdc20ee..bd880f30a 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -218,23 +218,23 @@ static U32 g_removeSrcFile = 0; void FIO_setRemoveSrcFile(unsigned flag) { g_removeSrcFile = (flag>0); } static U32 g_memLimit = 0; void FIO_setMemLimit(unsigned memLimit) { g_memLimit = memLimit; } -static U32 g_nbThreads = 1; -void FIO_setNbThreads(unsigned nbThreads) { +static U32 g_nbWorkers = 1; +void FIO_setNbWorkers(unsigned nbWorkers) { #ifndef ZSTD_MULTITHREAD - if (nbThreads > 1) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n"); + if (nbWorkers > 0) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n"); #endif - g_nbThreads = nbThreads; + g_nbWorkers = nbWorkers; } static U32 g_blockSize = 0; void FIO_setBlockSize(unsigned blockSize) { - if (blockSize && g_nbThreads==1) + if (blockSize && g_nbWorkers==0) DISPLAYLEVEL(2, "Setting block size is useless in single-thread mode \n"); g_blockSize = blockSize; } #define FIO_OVERLAP_LOG_NOTSET 9999 static U32 g_overlapLog = FIO_OVERLAP_LOG_NOTSET; void FIO_setOverlapLog(unsigned overlapLog){ - if (overlapLog && g_nbThreads==1) + if (overlapLog && g_nbWorkers==0) DISPLAYLEVEL(2, "Setting overlapLog is useless in single-thread mode \n"); g_overlapLog = overlapLog; } @@ -461,9 +461,8 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionStrategy, (U32)comprParams->strategy) ); /* multi-threading */ #ifdef ZSTD_MULTITHREAD - DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbThreads); - CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbThreads, g_nbThreads) ); - CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nonBlockingMode, 1) ); + DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbWorkers); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbWorkers, g_nbWorkers) ); #endif /* dictionary */ CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) ); /* set the value temporarily for dictionary loading, to adapt compression parameters */ diff --git a/programs/fileio.h b/programs/fileio.h index 9b9c7ea2f..69c83f71d 100644 --- a/programs/fileio.h +++ b/programs/fileio.h @@ -54,7 +54,7 @@ void FIO_setDictIDFlag(unsigned dictIDFlag); void FIO_setChecksumFlag(unsigned checksumFlag); void FIO_setRemoveSrcFile(unsigned flag); void FIO_setMemLimit(unsigned memLimit); -void FIO_setNbThreads(unsigned nbThreads); +void FIO_setNbWorkers(unsigned nbWorkers); void FIO_setBlockSize(unsigned blockSize); void FIO_setOverlapLog(unsigned overlapLog); void FIO_setLdmFlag(unsigned ldmFlag); diff --git a/programs/zstdcli.c b/programs/zstdcli.c index c80c36acf..015dc5e4c 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -377,7 +377,7 @@ int main(int argCount, const char* argv[]) nextArgumentsAreFiles=0, ultra=0, lastCommand = 0, - nbThreads = 1, + nbWorkers = 1, setRealTimePrio = 0, separateFiles = 0, ldmFlag = 0; @@ -422,7 +422,7 @@ int main(int argCount, const char* argv[]) programName = lastNameFromPath(programName); /* preset behaviors */ - if (exeNameMatch(programName, ZSTD_ZSTDMT)) nbThreads=0; + if (exeNameMatch(programName, ZSTD_ZSTDMT)) nbWorkers=0; if (exeNameMatch(programName, ZSTD_UNZSTD)) operation=zom_decompress; if (exeNameMatch(programName, ZSTD_CAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(); outFileName=stdoutmark; g_displayLevel=1; } /* supports multiple formats */ if (exeNameMatch(programName, ZSTD_ZCAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(); outFileName=stdoutmark; g_displayLevel=1; } /* behave like zcat, also supports multiple formats */ @@ -515,7 +515,7 @@ int main(int argCount, const char* argv[]) continue; } #endif - if (longCommandWArg(&argument, "--threads=")) { nbThreads = readU32FromChar(&argument); continue; } + if (longCommandWArg(&argument, "--threads=")) { nbWorkers = readU32FromChar(&argument); continue; } if (longCommandWArg(&argument, "--memlimit=")) { memLimit = readU32FromChar(&argument); continue; } if (longCommandWArg(&argument, "--memory=")) { memLimit = readU32FromChar(&argument); continue; } if (longCommandWArg(&argument, "--memlimit-decompress=")) { memLimit = readU32FromChar(&argument); continue; } @@ -648,7 +648,7 @@ int main(int argCount, const char* argv[]) /* nb of threads (hidden option) */ case 'T': argument++; - nbThreads = readU32FromChar(&argument); + nbWorkers = readU32FromChar(&argument); break; /* Dictionary Selection level */ @@ -716,10 +716,10 @@ int main(int argCount, const char* argv[]) /* Welcome message (if verbose) */ DISPLAYLEVEL(3, WELCOME_MESSAGE); - if (nbThreads == 0) { - /* try to guess */ - nbThreads = UTIL_countPhysicalCores(); - DISPLAYLEVEL(3, "Note: %d physical core(s) detected \n", nbThreads); + if (nbWorkers == 0) { + /* automatically set # workers based on # of reported cpus */ + nbWorkers = UTIL_countPhysicalCores(); + DISPLAYLEVEL(3, "Note: %d physical core(s) detected \n", nbWorkers); } g_utilDisplayLevel = g_displayLevel; @@ -763,7 +763,7 @@ int main(int argCount, const char* argv[]) BMK_setNotificationLevel(g_displayLevel); BMK_setSeparateFiles(separateFiles); BMK_setBlockSize(blockSize); - BMK_setNbThreads(nbThreads); + BMK_setNbWorkers(nbWorkers); BMK_setRealTime(setRealTimePrio); BMK_setNbSeconds(bench_nbSeconds); BMK_setLdmFlag(ldmFlag); @@ -791,7 +791,7 @@ int main(int argCount, const char* argv[]) zParams.dictID = dictID; if (cover) { int const optimize = !coverParams.k || !coverParams.d; - coverParams.nbThreads = nbThreads; + coverParams.nbThreads = nbWorkers; coverParams.zParams = zParams; operationResult = DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, blockSize, NULL, &coverParams, optimize); } else { @@ -835,7 +835,7 @@ int main(int argCount, const char* argv[]) FIO_setNotificationLevel(g_displayLevel); if (operation==zom_compress) { #ifndef ZSTD_NOCOMPRESS - FIO_setNbThreads(nbThreads); + FIO_setNbWorkers(nbWorkers); FIO_setBlockSize((U32)blockSize); FIO_setLdmFlag(ldmFlag); FIO_setLdmHashLog(g_ldmHashLog); diff --git a/tests/fullbench.c b/tests/fullbench.c index 2ec3ce555..199f4966b 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -181,7 +181,7 @@ static size_t local_ZSTD_compress_generic_T2_end(void* dst, size_t dstCapacity, ZSTD_inBuffer buffIn; (void)buff2; ZSTD_CCtx_setParameter(g_cstream, ZSTD_p_compressionLevel, 1); - ZSTD_CCtx_setParameter(g_cstream, ZSTD_p_nbThreads, 2); + ZSTD_CCtx_setParameter(g_cstream, ZSTD_p_nbWorkers, 2); buffOut.dst = dst; buffOut.size = dstCapacity; buffOut.pos = 0; @@ -198,7 +198,7 @@ static size_t local_ZSTD_compress_generic_T2_continue(void* dst, size_t dstCapac ZSTD_inBuffer buffIn; (void)buff2; ZSTD_CCtx_setParameter(g_cstream, ZSTD_p_compressionLevel, 1); - ZSTD_CCtx_setParameter(g_cstream, ZSTD_p_nbThreads, 2); + ZSTD_CCtx_setParameter(g_cstream, ZSTD_p_nbWorkers, 2); buffOut.dst = dst; buffOut.size = dstCapacity; buffOut.pos = 0; diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 3982f8bbc..f42b8b7a7 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -226,7 +226,7 @@ static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) ZSTD_outBuffer out = { outBuffer, outSize, 0 }; ZSTD_inBuffer in = { inBuffer, inSize, 0 }; CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel) ); - CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, nbThreads) ); + CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbWorkers, nbThreads) ); while ( ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end) ) {} ZSTD_freeCCtx(cctx); DISPLAYLEVEL(3, "compress_generic,-T%u,end level %i : ", @@ -246,7 +246,7 @@ static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) ZSTD_outBuffer out = { outBuffer, outSize, 0 }; ZSTD_inBuffer in = { inBuffer, inSize, 0 }; CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel) ); - CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, nbThreads) ); + CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbWorkers, nbThreads) ); CHECK_Z( ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_continue) ); while ( ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end) ) {} ZSTD_freeCCtx(cctx); diff --git a/tests/roundTripCrash.c b/tests/roundTripCrash.c index 180fa9b6f..7d937fcee 100644 --- a/tests/roundTripCrash.c +++ b/tests/roundTripCrash.c @@ -94,7 +94,7 @@ static size_t cctxParamRoundTripTest(void* resultBuff, size_t resultBuffCapacity /* Set parameters */ CHECK_Z( ZSTD_CCtxParam_setParameter(cctxParams, ZSTD_p_compressionLevel, cLevel) ); - CHECK_Z( ZSTD_CCtxParam_setParameter(cctxParams, ZSTD_p_nbThreads, 2) ); + CHECK_Z( ZSTD_CCtxParam_setParameter(cctxParams, ZSTD_p_nbWorkers, 2) ); CHECK_Z( ZSTD_CCtxParam_setParameter(cctxParams, ZSTD_p_overlapSizeLog, 5) ); diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index a4fb5589e..db87e2c5b 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -753,9 +753,9 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "OK \n"); /* Complex multithreading + dictionary test */ - { U32 const nbThreads = 2; + { U32 const nbWorkers = 2; size_t const jobSize = 4 * 1 MB; - size_t const srcSize = jobSize * nbThreads; /* we want each job to have predictable size */ + size_t const srcSize = jobSize * nbWorkers; /* we want each job to have predictable size */ size_t const segLength = 2 KB; size_t const offset = 600 KB; /* must be larger than window defined in cdict */ size_t const start = jobSize + (offset-1); @@ -763,7 +763,7 @@ static int basicUnitTests(U32 seed, double compressibility) BYTE* const dst = (BYTE*)CNBuffer + start - offset; DISPLAYLEVEL(3, "test%3i : compress %u bytes with multiple threads + dictionary : ", testNb++, (U32)srcSize); CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_p_compressionLevel, 3) ); - CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_p_nbThreads, 2) ); + CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_p_nbWorkers, nbWorkers) ); CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_p_jobSize, jobSize) ); assert(start > offset); assert(start + segLength < COMPRESSIBLE_NOISE_LENGTH); @@ -1672,7 +1672,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double U32 const nbThreadsAdjusted = (windowLogMalus < nbThreadsCandidate) ? nbThreadsCandidate - windowLogMalus : 1; U32 const nbThreads = MIN(nbThreadsAdjusted, nbThreadsMax); DISPLAYLEVEL(5, "t%u: nbThreads : %u \n", testNb, nbThreads); - CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_nbThreads, nbThreads, useOpaqueAPI) ); + CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_nbWorkers, nbThreads, useOpaqueAPI) ); if (nbThreads > 1) { U32 const jobLog = FUZ_rand(&lseed) % (testLog+1); CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_overlapSizeLog, FUZ_rand(&lseed) % 10, useOpaqueAPI) ); From 6c492af284230dfc1356bd686c84c017c9695cb1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 1 Feb 2018 20:16:00 -0800 Subject: [PATCH 110/255] fixed minor conversion warning --- programs/fileio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/fileio.c b/programs/fileio.c index bd880f30a..9fc0fc998 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -1169,7 +1169,7 @@ static void FIO_zstdErrorHelp(dRess_t* ress, size_t err, char const* srcFileName /* Try to decode the frame header */ err = ZSTD_getFrameHeader(&header, ress->srcBuffer, ress->srcBufferLoaded); if (err == 0) { - U64 const windowSize = header.windowSize; + unsigned long long const windowSize = header.windowSize; U32 const windowLog = FIO_highbit64(windowSize) + ((windowSize & (windowSize - 1)) != 0); U32 const windowMB = (U32)((windowSize >> 20) + ((windowSize & ((1 MB) - 1)) != 0)); assert(windowSize < (U64)(1ULL << 52)); From 90eca318a776a1e365955e8a2c4f3245e5840bd7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 2 Feb 2018 14:24:56 -0800 Subject: [PATCH 111/255] fileio: create dedicated function to generate zstd frames like other formats --- lib/compress/zstd_compress.c | 65 +++++++++++---- lib/compress/zstd_compress_internal.h | 4 +- lib/compress/zstdmt_compress.c | 27 ++---- lib/compress/zstdmt_compress.h | 16 ++-- lib/zstd.h | 4 +- programs/fileio.c | 115 +++++++++++++++----------- 6 files changed, 137 insertions(+), 94 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e66b4df8a..67cc49b7b 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -160,13 +160,6 @@ static void ZSTD_cLevelToCCtxParams_srcSize(ZSTD_CCtx_params* CCtxParams, U64 sr CCtxParams->compressionLevel = ZSTD_CLEVEL_CUSTOM; } -static void ZSTD_cLevelToCParams(ZSTD_CCtx* cctx) -{ - DEBUGLOG(4, "ZSTD_cLevelToCParams: level=%i", cctx->requestedParams.compressionLevel); - ZSTD_cLevelToCCtxParams_srcSize( - &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1); -} - static void ZSTD_cLevelToCCtxParams(ZSTD_CCtx_params* CCtxParams) { DEBUGLOG(4, "ZSTD_cLevelToCCtxParams"); @@ -246,10 +239,51 @@ static ZSTD_CCtx_params ZSTD_assignParamsToCCtxParams( return ERROR(parameter_outOfBound); \ } } + +static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param) +{ + switch(param) + { + case ZSTD_p_compressionLevel: + case ZSTD_p_hashLog: + case ZSTD_p_chainLog: + case ZSTD_p_searchLog: + case ZSTD_p_minMatch: + case ZSTD_p_targetLength: + case ZSTD_p_compressionStrategy: + return 1; + + case ZSTD_p_format : + case ZSTD_p_windowLog: + case ZSTD_p_contentSizeFlag: + case ZSTD_p_checksumFlag: + case ZSTD_p_dictIDFlag: + case ZSTD_p_forceMaxWindow : + case ZSTD_p_nbWorkers: + case ZSTD_p_jobSize: + case ZSTD_p_overlapSizeLog: + case ZSTD_p_enableLongDistanceMatching: + case ZSTD_p_ldmHashLog: + case ZSTD_p_ldmMinMatch: + case ZSTD_p_ldmBucketSizeLog: + case ZSTD_p_ldmHashEveryLog: + return 0; + + default: + assert(0); + return 0; + } +} + size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned value) { DEBUGLOG(4, "ZSTD_CCtx_setParameter (%u, %u)", (U32)param, value); - if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); + if (cctx->streamStage != zcss_init) { + if (ZSTD_isUpdateAuthorized(param)) { + cctx->cParamsChanged = 1; + } else { + return ERROR(stage_wrong); + } } switch(param) { @@ -268,7 +302,9 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v case ZSTD_p_targetLength: case ZSTD_p_compressionStrategy: if (cctx->cdict) return ERROR(stage_wrong); - if (value>0) ZSTD_cLevelToCParams(cctx); /* Can optimize if srcSize is known */ + if (value>0) { + ZSTD_cLevelToCCtxParams_srcSize(&cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1); /* Optimize cParams when srcSize is known */ + } return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); case ZSTD_p_contentSizeFlag: @@ -293,7 +329,8 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v case ZSTD_p_enableLongDistanceMatching: if (cctx->cdict) return ERROR(stage_wrong); - if (value>0) ZSTD_cLevelToCParams(cctx); + if (value>0) + ZSTD_cLevelToCCtxParams_srcSize(&cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1); /* Optimize cParams when srcSize is known */ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); case ZSTD_p_ldmHashLog: @@ -477,11 +514,6 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( if (cctx->cdict) return ERROR(stage_wrong); cctx->requestedParams = *params; -#ifdef ZSTD_MULTITHREAD - if (cctx->mtctx) - ZSTDMT_MTCtx_setParametersUsingCCtxParams_whileCompressing(cctx->mtctx, params); -#endif - return 0; } @@ -2497,7 +2529,8 @@ size_t ZSTD_compress_advanced_internal( const void* dict,size_t dictSize, ZSTD_CCtx_params params) { - DEBUGLOG(4, "ZSTD_compress_advanced_internal"); + DEBUGLOG(4, "ZSTD_compress_advanced_internal (srcSize:%u)", + (U32)srcSize); CHECK_F( ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dm_auto, NULL, params, srcSize, ZSTDb_not_buffered) ); return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index eb651fb5e..ad2a4be0d 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -158,14 +158,14 @@ struct ZSTD_CCtx_params_s { /* For use with createCCtxParams() and freeCCtxParams() only */ ZSTD_customMem customMem; - }; /* typedef'd to ZSTD_CCtx_params within "zstd.h" */ struct ZSTD_CCtx_s { ZSTD_compressionStage_e stage; - U32 dictID; + int cParamsChanged; /* == 1 if cParams(except wlog) or compression level are changed in requestedParams. Triggers transmission of new params to ZSTDMT (if available) then reset to 0. */ ZSTD_CCtx_params requestedParams; ZSTD_CCtx_params appliedParams; + U32 dictID; void* workSpace; size_t workSpaceSize; size_t blockSize; diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 90084b9b3..3542623e9 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -666,25 +666,16 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) return jobParams; } -/*! ZSTDMT_MTCtx_setParametersUsingCCtxParams_whileCompressing() : - * Apply a ZSTD_CCtx_params to the compression context. - * This entry point is accessed while compression is ongoing, - * new parameters will be applied to next compression job. - * However, following parameters are NOT updated : - * - window size - * - pledgedSrcSize - * - nb threads - * - job size - * - overlap size - */ -void ZSTDMT_MTCtx_setParametersUsingCCtxParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* params) +/*! ZSTDMT_MTCtx_updateParametersWhileCompressing() : + * Update compression level and parameters (except wlog) + * while compression is ongoing. + * New parameters will be applied to next compression job. */ +void ZSTDMT_MTCtx_updateParametersWhileCompressing(ZSTDMT_CCtx* mtctx, int compressionLevel, ZSTD_compressionParameters cParams) { - U32 const wlog = mtctx->params.cParams.windowLog; - U32 const nbWorkers = mtctx->params.nbWorkers; - mtctx->params = *params; + U32 const wlog = cParams.windowLog; + mtctx->params.cParams = cParams; mtctx->params.cParams.windowLog = wlog; /* Do not modify windowLog ! Frame must keep same wlog during the whole process ! */ - mtctx->params.nbWorkers = nbWorkers; /* Do not modify nbWorkers, it must remain synchronized with CCtx Pool ! */ - /* note : other parameters not updated are simply not used beyond initialization */ + mtctx->params.compressionLevel = compressionLevel; } /* ZSTDMT_getNbWorkers(): @@ -915,7 +906,7 @@ size_t ZSTDMT_initCStream_internal( const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize) { - DEBUGLOG(2, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u, nbWorkers=%u, cctxPool=%u)", + DEBUGLOG(4, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u, nbWorkers=%u, cctxPool=%u)", (U32)pledgedSrcSize, params.nbWorkers, mtctx->cctxPool->totalCCtx); /* params are supposed to be fully validated at this point */ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 3fef2ed6a..c25521d38 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -121,17 +121,11 @@ size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_param * Also reset jobSize and overlapLog */ size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers); -/*! ZSTDMT_MTCtx_setParametersUsingCCtxParams_whileCompressing() : - * Apply a ZSTD_CCtx_params to the compression context. - * This works even during compression, and will be applied to next compression job. - * However, the following parameters will NOT be updated after compression has been started : - * - window size - * - pledgedSrcSize - * - nb threads - * - job size - * - overlap size - */ -void ZSTDMT_MTCtx_setParametersUsingCCtxParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* params); +/*! ZSTDMT_MTCtx_updateParametersWhileCompressing() : + * Update compression level and parameters (except wlog) + * while compression is ongoing. + * New parameters will be applied to next compression job. */ +void ZSTDMT_MTCtx_updateParametersWhileCompressing(ZSTDMT_CCtx* mtctx, int compressionLevel, ZSTD_compressionParameters cParams); /* ZSTDMT_getNbWorkers(): * @return nb threads currently active in mtctx. diff --git a/lib/zstd.h b/lib/zstd.h index be93a7eeb..986b81980 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -1049,8 +1049,10 @@ typedef enum { /*! ZSTD_CCtx_setParameter() : * Set one compression parameter, selected by enum ZSTD_cParameter. + * Setting a parameter is generally only possible during frame initialization (before starting compression), + * except for a few exceptions which can be updated during compression: compressionLevel, hashLog, chainLog, searchLog, minMatch, targetLength and strategy. * Note : when `value` is an enum, cast it to unsigned for proper type checking. - * @result : informational value (typically, the one being set, possibly corrected), + * @result : informational value (typically, value being set clamped correctly), * or an error code (which can be tested with ZSTD_isError()). */ ZSTDLIB_API size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned value); diff --git a/programs/fileio.c b/programs/fileio.c index 9fc0fc998..f1c9b9224 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -736,56 +736,22 @@ static unsigned long long FIO_compressLz4Frame(cRess_t* ress, * @return : 0 : compression completed correctly, * 1 : missing or pb opening srcFileName */ -static int FIO_compressFilename_internal(cRess_t ress, - const char* dstFileName, const char* srcFileName, int compressionLevel) +static unsigned long long +FIO_compressZstdFrame(const cRess_t* ressPtr, + const char* srcFileName, U64 fileSize, + int compressionLevel, U64* readsize) { + cRess_t const ress = *ressPtr; FILE* const srcFile = ress.srcFile; FILE* const dstFile = ress.dstFile; - U64 readsize = 0; U64 compressedfilesize = 0; - U64 const fileSize = UTIL_getFileSize(srcFileName); ZSTD_EndDirective directive = ZSTD_e_continue; - DISPLAYLEVEL(5, "%s: %u bytes \n", srcFileName, (U32)fileSize); - - switch (g_compressionType) { - case FIO_zstdCompression: - break; - - case FIO_gzipCompression: -#ifdef ZSTD_GZCOMPRESS - compressedfilesize = FIO_compressGzFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize); -#else - (void)compressionLevel; - EXM_THROW(20, "zstd: %s: file cannot be compressed as gzip (zstd compiled without ZSTD_GZCOMPRESS) -- ignored \n", - srcFileName); -#endif - goto finish; - - case FIO_xzCompression: - case FIO_lzmaCompression: -#ifdef ZSTD_LZMACOMPRESS - compressedfilesize = FIO_compressLzmaFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize, g_compressionType==FIO_lzmaCompression); -#else - (void)compressionLevel; - EXM_THROW(20, "zstd: %s: file cannot be compressed as xz/lzma (zstd compiled without ZSTD_LZMACOMPRESS) -- ignored \n", - srcFileName); -#endif - goto finish; - - case FIO_lz4Compression: -#ifdef ZSTD_LZ4COMPRESS - compressedfilesize = FIO_compressLz4Frame(&ress, srcFileName, fileSize, compressionLevel, &readsize); -#else - (void)compressionLevel; - EXM_THROW(20, "zstd: %s: file cannot be compressed as lz4 (zstd compiled without ZSTD_LZ4COMPRESS) -- ignored \n", - srcFileName); -#endif - goto finish; - } + DISPLAYLEVEL(6, "compression using zstd format \n"); /* init */ if (fileSize != UTIL_FILESIZE_UNKNOWN) ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize); + (void)compressionLevel; (void)srcFileName; /* Main compression loop */ do { @@ -794,9 +760,9 @@ static int FIO_compressFilename_internal(cRess_t ress, size_t const inSize = fread(ress.srcBuffer, (size_t)1, ress.srcBufferSize, srcFile); ZSTD_inBuffer inBuff = { ress.srcBuffer, inSize, 0 }; DISPLAYLEVEL(6, "fread %u bytes from source \n", (U32)inSize); - readsize += inSize; + *readsize += inSize; - if (inSize == 0 || (fileSize != UTIL_FILESIZE_UNKNOWN && readsize == fileSize)) + if ((inSize == 0) || (*readsize == fileSize)) directive = ZSTD_e_end; result = 1; @@ -810,7 +776,7 @@ static int FIO_compressFilename_internal(cRess_t ress, if (outBuff.pos) { size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile); if (sizeCheck!=outBuff.pos) - EXM_THROW(25, "Write error : cannot write compressed block into %s", dstFileName); + EXM_THROW(25, "Write error : cannot write compressed block"); compressedfilesize += outBuff.pos; } if (READY_FOR_UPDATE()) { @@ -824,10 +790,67 @@ static int FIO_compressFilename_internal(cRess_t ress, } } while (directive != ZSTD_e_end); -finish: + return compressedfilesize; +} + +/*! FIO_compressFilename_internal() : + * same as FIO_compressFilename_extRess(), with `ress.desFile` already opened. + * @return : 0 : compression completed correctly, + * 1 : missing or pb opening srcFileName + */ +static int +FIO_compressFilename_internal(cRess_t ress, + const char* dstFileName, const char* srcFileName, + int compressionLevel) +{ + U64 readsize = 0; + U64 compressedfilesize = 0; + U64 const fileSize = UTIL_getFileSize(srcFileName); + DISPLAYLEVEL(5, "%s: %u bytes \n", srcFileName, (U32)fileSize); + + /* compression format selection */ + switch (g_compressionType) { + default: + case FIO_zstdCompression: + compressedfilesize = FIO_compressZstdFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize); + break; + + case FIO_gzipCompression: +#ifdef ZSTD_GZCOMPRESS + compressedfilesize = FIO_compressGzFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize); +#else + (void)compressionLevel; + EXM_THROW(20, "zstd: %s: file cannot be compressed as gzip (zstd compiled without ZSTD_GZCOMPRESS) -- ignored \n", + srcFileName); +#endif + break; + + case FIO_xzCompression: + case FIO_lzmaCompression: +#ifdef ZSTD_LZMACOMPRESS + compressedfilesize = FIO_compressLzmaFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize, g_compressionType==FIO_lzmaCompression); +#else + (void)compressionLevel; + EXM_THROW(20, "zstd: %s: file cannot be compressed as xz/lzma (zstd compiled without ZSTD_LZMACOMPRESS) -- ignored \n", + srcFileName); +#endif + break; + + case FIO_lz4Compression: +#ifdef ZSTD_LZ4COMPRESS + compressedfilesize = FIO_compressLz4Frame(&ress, srcFileName, fileSize, compressionLevel, &readsize); +#else + (void)compressionLevel; + EXM_THROW(20, "zstd: %s: file cannot be compressed as lz4 (zstd compiled without ZSTD_LZ4COMPRESS) -- ignored \n", + srcFileName); +#endif + break; + } + /* Status */ DISPLAYLEVEL(2, "\r%79s\r", ""); - DISPLAYLEVEL(2,"%-20s :%6.2f%% (%6llu => %6llu bytes, %s) \n", srcFileName, + DISPLAYLEVEL(2,"%-20s :%6.2f%% (%6llu => %6llu bytes, %s) \n", + srcFileName, (double)compressedfilesize / (readsize+(!readsize)/*avoid div by zero*/) * 100, (unsigned long long)readsize, (unsigned long long) compressedfilesize, dstFileName); From 4b525af53afbc6fd5f13b021b8f707ebe2ac4514 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 2 Feb 2018 15:58:13 -0800 Subject: [PATCH 112/255] zstdmt: applies new parameters on the fly when invoked from ZSTD_compress_generic() --- lib/compress/zstd_compress.c | 16 ++++++++++------ lib/compress/zstdmt_compress.c | 4 ++-- lib/compress/zstdmt_compress.h | 4 ++-- programs/fileio.c | 3 ++- tests/fuzzer.c | 4 ++-- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 67cc49b7b..fe916b50c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3222,13 +3222,17 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, /* compression stage */ #ifdef ZSTD_MULTITHREAD if (cctx->appliedParams.nbWorkers > 0) { - size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp); - if ( ZSTD_isError(flushMin) - || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */ - ZSTD_startNewCompression(cctx); + if (cctx->cParamsChanged) { + ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, cctx->requestedParams.compressionLevel, cctx->requestedParams.cParams); + cctx->cParamsChanged = 0; } - return flushMin; - } + { size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp); + if ( ZSTD_isError(flushMin) + || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */ + ZSTD_startNewCompression(cctx); + } + return flushMin; + } } #endif CHECK_F( ZSTD_compressStream_generic(cctx, output, input, endOp) ); DEBUGLOG(5, "completed ZSTD_compress_generic"); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 3542623e9..a2deac145 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -666,11 +666,11 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) return jobParams; } -/*! ZSTDMT_MTCtx_updateParametersWhileCompressing() : +/*! ZSTDMT_updateCParams_whileCompressing() : * Update compression level and parameters (except wlog) * while compression is ongoing. * New parameters will be applied to next compression job. */ -void ZSTDMT_MTCtx_updateParametersWhileCompressing(ZSTDMT_CCtx* mtctx, int compressionLevel, ZSTD_compressionParameters cParams) +void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, int compressionLevel, ZSTD_compressionParameters cParams) { U32 const wlog = cParams.windowLog; mtctx->params.cParams = cParams; diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index c25521d38..4364f100d 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -121,11 +121,11 @@ size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_param * Also reset jobSize and overlapLog */ size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers); -/*! ZSTDMT_MTCtx_updateParametersWhileCompressing() : +/*! ZSTDMT_updateCParams_whileCompressing() : * Update compression level and parameters (except wlog) * while compression is ongoing. * New parameters will be applied to next compression job. */ -void ZSTDMT_MTCtx_updateParametersWhileCompressing(ZSTDMT_CCtx* mtctx, int compressionLevel, ZSTD_compressionParameters cParams); +void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, int compressionLevel, ZSTD_compressionParameters cParams); /* ZSTDMT_getNbWorkers(): * @return nb threads currently active in mtctx. diff --git a/programs/fileio.c b/programs/fileio.c index f1c9b9224..0cc807a11 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -781,7 +781,8 @@ FIO_compressZstdFrame(const cRess_t* ressPtr, } if (READY_FOR_UPDATE()) { ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx); - DISPLAYUPDATE(2, "\rRead :%6u MB - Consumed :%6u MB - Compressed :%6u MB => %.2f%%", + DISPLAYUPDATE(2, "\r(%i) Read :%6u MB - Consumed :%6u MB - Compressed :%6u MB => %.2f%%", + compressionLevel, (U32)(zfp.ingested >> 20), (U32)(zfp.consumed >> 20), (U32)(zfp.produced >> 20), diff --git a/tests/fuzzer.c b/tests/fuzzer.c index f42b8b7a7..e7c92edc0 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -53,7 +53,7 @@ static const U32 nbTestsDefault = 30000; /*-************************************ * Display Macros **************************************/ -#define DISPLAY(...) fprintf(stdout, __VA_ARGS__) +#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } static U32 g_displayLevel = 2; @@ -63,7 +63,7 @@ static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stdout); } } + if (g_displayLevel>=4) fflush(stderr); } } #undef MIN From 5188749e1cf38c7fc1faddc5415940c0bf74c449 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 2 Feb 2018 16:31:20 -0800 Subject: [PATCH 113/255] ensure compression parameters are updated when only compression level is changed --- lib/compress/zstd_compress.c | 8 ++++---- lib/compress/zstd_compress_internal.h | 5 +++-- lib/compress/zstd_double_fast.c | 4 ++-- lib/compress/zstd_fast.c | 4 ++-- lib/compress/zstd_lazy.c | 4 ++-- lib/compress/zstdmt_compress.c | 15 +++++++++++---- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index fe916b50c..9e768edd3 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -140,8 +140,6 @@ size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs) /* private API call, for dictBuilder only */ const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStore); } -#define ZSTD_CLEVEL_CUSTOM 999 - static ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams( ZSTD_CCtx_params CCtxParams, U64 srcSizeHint, size_t dictSize) { @@ -3010,7 +3008,7 @@ MEM_STATIC size_t ZSTD_limitCopy(void* dst, size_t dstCapacity, /** ZSTD_compressStream_generic(): * internal function for all *compressStream*() variants and *compress_generic() - * non-static, because can be called from zstdmt.c + * non-static, because can be called from zstdmt_compress.c * @return : hint size for next input */ size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, ZSTD_outBuffer* output, @@ -3192,6 +3190,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */ } if (params.nbWorkers > 0) { + /* mt context creation */ if (cctx->mtctx == NULL || (params.nbWorkers != ZSTDMT_getNbWorkers(cctx->mtctx))) { DEBUGLOG(4, "ZSTD_compress_generic: creating new mtctx for nbWorkers=%u", params.nbWorkers); @@ -3202,6 +3201,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbWorkers, cctx->customMem); if (cctx->mtctx == NULL) return ERROR(memory_allocation); } + /* mt compression */ DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbWorkers=%u", params.nbWorkers); CHECK_F( ZSTDMT_initCStream_internal( cctx->mtctx, @@ -3258,7 +3258,7 @@ size_t ZSTD_compress_generic_simpleArgs ( /*====== Finalize ======*/ /*! ZSTD_flushStream() : -* @return : amount of data remaining to flush */ + * @return : amount of data remaining to flush */ size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output) { ZSTD_inBuffer input = { NULL, 0, 0 }; diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index ad2a4be0d..22213c225 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -30,8 +30,9 @@ extern "C" { /*-************************************* * Constants ***************************************/ -static const U32 g_searchStrength = 8; -#define HASH_READ_SIZE 8 +#define kSearchStrength 8 +#define HASH_READ_SIZE 8 +#define ZSTD_CLEVEL_CUSTOM 999 /*-************************************* diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 834a64b00..6415480e2 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -113,7 +113,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } } else { - ip += ((ip-anchor) >> g_searchStrength) + 1; + ip += ((ip-anchor) >> kSearchStrength) + 1; continue; } @@ -264,7 +264,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic( ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } else { - ip += ((ip-anchor) >> g_searchStrength) + 1; + ip += ((ip-anchor) >> kSearchStrength) + 1; continue; } } diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 8941326e1..b60f21580 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -79,7 +79,7 @@ size_t ZSTD_compressBlock_fast_generic( } else { U32 offset; if ( (matchIndex <= lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { - ip += ((ip-anchor) >> g_searchStrength) + 1; + ip += ((ip-anchor) >> kSearchStrength) + 1; continue; } mLength = ZSTD_count(ip+4, match+4, iend) + 4; @@ -185,7 +185,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic( } else { if ( (matchIndex < lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { - ip += ((ip-anchor) >> g_searchStrength) + 1; + ip += ((ip-anchor) >> kSearchStrength) + 1; continue; } { const BYTE* matchEnd = matchIndex < dictLimit ? dictEnd : iend; diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 7f59eb34a..7cb53b2ef 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -537,7 +537,7 @@ size_t ZSTD_compressBlock_lazy_generic( } if (matchLength < 4) { - ip += ((ip-anchor) >> g_searchStrength) + 1; /* jump faster over incompressible sections */ + ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */ continue; } @@ -710,7 +710,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( } if (matchLength < 4) { - ip += ((ip-anchor) >> g_searchStrength) + 1; /* jump faster over incompressible sections */ + ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */ continue; } diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index a2deac145..4f327c07c 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -672,10 +672,14 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) * New parameters will be applied to next compression job. */ void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, int compressionLevel, ZSTD_compressionParameters cParams) { - U32 const wlog = cParams.windowLog; - mtctx->params.cParams = cParams; - mtctx->params.cParams.windowLog = wlog; /* Do not modify windowLog ! Frame must keep same wlog during the whole process ! */ + U32 const saved_wlog = mtctx->params.cParams.windowLog; /* Do not modify windowLog while compressing */ + DEBUGLOG(5, "ZSTDMT_updateCParams_whileCompressing (level:%i)", + compressionLevel); mtctx->params.compressionLevel = compressionLevel; + if (compressionLevel != ZSTD_CLEVEL_CUSTOM) + cParams = ZSTD_getCParams(compressionLevel, mtctx->frameContentSize, 0 /* should be dictSize */ ); + cParams. windowLog = saved_wlog; + mtctx->params.cParams = cParams; } /* ZSTDMT_getNbWorkers(): @@ -912,7 +916,8 @@ size_t ZSTDMT_initCStream_internal( assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ assert(mtctx->cctxPool->totalCCtx == params.nbWorkers); - mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */ + + /* init */ if (params.jobSize == 0) { if (params.cParams.windowLog >= 29) params.jobSize = ZSTDMT_JOBSIZE_MAX; @@ -921,6 +926,7 @@ size_t ZSTDMT_initCStream_internal( } if (params.jobSize > ZSTDMT_JOBSIZE_MAX) params.jobSize = ZSTDMT_JOBSIZE_MAX; + mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */ if (mtctx->singleBlockingThread) { ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(params); DEBUGLOG(4, "ZSTDMT_initCStream_internal: switch to single blocking thread mode"); @@ -929,6 +935,7 @@ size_t ZSTDMT_initCStream_internal( dict, dictSize, cdict, singleThreadParams, pledgedSrcSize); } + DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u workers", params.nbWorkers); if (mtctx->allJobsCompleted == 0) { /* previous compression not correctly finished */ From 94efb1749d5b6fd4ce19d7299c1014999bea28cd Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 3 Feb 2018 23:54:10 -0800 Subject: [PATCH 114/255] faster decoding in 32-bits mode for long offsets (tentative) On my laptop: Before: ./zstd32 -b --zstd=wlog=27 silesia.tar enwik8 -S 3#silesia.tar : 211984896 -> 66683478 (3.179), 97.6 MB/s , 400.7 MB/s 3#enwik8 : 100000000 -> 35643153 (2.806), 76.5 MB/s , 303.2 MB/s After: ./zstd32 -b --zstd=wlog=27 silesia.tar enwik8 -S 3#silesia.tar : 211984896 -> 66683478 (3.179), 97.4 MB/s , 435.0 MB/s 3#enwik8 : 100000000 -> 35643153 (2.806), 76.2 MB/s , 338.1 MB/s Mileage vary, depending on file, and cpu type. But a generic rule is : x86 benefits less from "long-offset mode" than x64, maybe due to register pressure. On "entropy", long-mode is _never_ a win for x86. On my laptop though, it may, depending on file and compression level (enwik8 benefits more from "long-mode" than silesia). --- lib/decompress/zstd_decompress.c | 43 +++++++++++++++++--------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 6cbce9be6..1a1fb2506 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -23,7 +23,7 @@ /*! * LEGACY_SUPPORT : -* if set to 1, ZSTD_decompress() can decode older formats (v0.1+) +* if set to 1+, ZSTD_decompress() can decode older formats (v0.1+) */ #ifndef ZSTD_LEGACY_SUPPORT # define ZSTD_LEGACY_SUPPORT 0 @@ -235,8 +235,8 @@ void ZSTD_copyDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx) /*-************************************************************* -* Decompression section -***************************************************************/ + * Frame header decoding + ***************************************************************/ /*! ZSTD_isFrame() : * Tells if the content of `buffer` starts with a valid Frame Identifier. @@ -258,7 +258,7 @@ unsigned ZSTD_isFrame(const void* buffer, size_t size) /** ZSTD_frameHeaderSize_internal() : * srcSize must be large enough to reach header size fields. - * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless + * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless. * @return : size of the Frame Header * or an error code, which can be tested with ZSTD_isError() */ static size_t ZSTD_frameHeaderSize_internal(const void* src, size_t srcSize, ZSTD_format_e format) @@ -481,6 +481,10 @@ static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t he } +/*-************************************************************* + * Block decoding + ***************************************************************/ + /*! ZSTD_getcBlockSize() : * Provides the size of compressed block from block header `src` */ size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, @@ -936,13 +940,14 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e l ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); assert(ofBits <= MaxOff); - if (MEM_32bits() && longOffsets) { - U32 const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN_32-1); + if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) { + U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed); offset = OF_base[ofCode] + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); - if (MEM_32bits() || extraBits) BIT_reloadDStream(&seqState->DStream); + BIT_reloadDStream(&seqState->DStream); if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); + assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32); /* to avoid another reload */ } else { - offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ + offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); } } @@ -955,7 +960,7 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e l if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; seqState->prevOffset[1] = seqState->prevOffset[0]; seqState->prevOffset[0] = offset = temp; - } else { + } else { /* offset == 0 */ offset = seqState->prevOffset[0]; } } else { @@ -967,16 +972,16 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e l } seq.matchLength = ML_base[mlCode] - + ((mlCode>31) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ + + ((mlCode>31) ? BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/) : 0); /* <= 16 bits */ if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) BIT_reloadDStream(&seqState->DStream); if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) BIT_reloadDStream(&seqState->DStream); - /* Verify that there is enough bits to read the rest of the data in 64-bit mode. */ + /* Ensure there are enough bits to read the rest of data in 64-bit mode. */ ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); seq.litLength = LL_base[llCode] - + ((llCode>15) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ + + ((llCode>15) ? BIT_readBitsFast(&seqState->DStream, llBits/*>0*/) : 0); /* <= 16 bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); @@ -1364,13 +1369,13 @@ static size_t ZSTD_decompressSequencesLong( FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); /* prepare in advance */ - for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && seqNbfParams.windowSize > (1ULL << STREAM_ACCUMULATOR_MIN))); - /* windowSize could be any value at this point, since it is only validated - * in the streaming API. - */ DEBUGLOG(5, "ZSTD_decompressBlock_internal (size : %u)", (U32)srcSize); if (srcSize >= ZSTD_BLOCKSIZE_MAX) return ERROR(srcSize_wrong); @@ -1429,7 +1430,9 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, ip += litCSize; srcSize -= litCSize; } - if (frame && dctx->fParams.windowSize > (1<<23)) + if ( frame /* windowSize exists */ + && (dctx->fParams.windowSize > (1<<24)) + && MEM_64bits() /* x86 benefits less from long mode than x64 */ ) return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, isLongOffset); return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, isLongOffset); } From 0170cf9a7af94d9ffdcdb5cd3ebc35fcd542f2ed Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 5 Feb 2018 11:46:02 -0800 Subject: [PATCH 115/255] minor : modified ZSTD_preserveUnsortedMark() to be more vectorization friendly --- lib/common/fse_decompress.c | 4 ++-- lib/compress/zstd_lazy.c | 37 +++++++++++++++++++++----------- lib/decompress/zstd_decompress.c | 4 ++-- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/lib/common/fse_decompress.c b/lib/common/fse_decompress.c index 8e3f0035f..4c66c3b77 100644 --- a/lib/common/fse_decompress.c +++ b/lib/common/fse_decompress.c @@ -139,8 +139,8 @@ size_t FSE_buildDTable(FSE_DTable* dt, const short* normalizedCounter, unsigned { U32 u; for (u=0; usmaller:0*, meaning the next candidate will be considered smaller. + * to *sorted=>smaller:0*, meaning next candidate will be considered smaller. * This could be wrong, and result in data corruption. + * * On second thought, this corruption might be impossible, - * because unsorted elements are always at the beginning of the list, - * and squashing to zero reduce the list to a single element, + * because unsorted elements stand at the beginning of the list, + * and squashing to zero reduces the list to a single element, * which needs to be sorted anyway. * I haven't spent much thoughts into this possible scenario, - * and just felt it was safer to implement ZSTD_preserveUnsortedMark() */ + * and just felt it was safer to implement ZSTD_preserveUnsortedMark() + * + * `size` : must be a positive multiple of ZSTD_ROWSIZE */ +#define ZSTD_ROWSIZE 16 void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const reducerValue) { - U32 u; - for (u=0; udtable; return 0; case set_repeat: if (!flagRepeatTable) return ERROR(corruption_detected); From de68c2ff10f3800f5aec60abd26e63f00b5f32bc Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 7 Feb 2018 14:22:35 -0800 Subject: [PATCH 116/255] Merged ZSTD_preserveUnsortedMark() into ZSTD_reduceIndex() as it's faster, due to one memory scan instead of two (confirmed by microbenchmark). Note : as ZSTD_reduceIndex() is rarely invoked, it does not translate into a visible gain. Consider it an exercise in auto-vectorization and micro-benchmarking. --- lib/compress/zstd_compress.c | 39 ++++++++++++++++--------- lib/compress/zstd_compress_internal.h | 6 ++++ lib/compress/zstd_lazy.c | 42 --------------------------- 3 files changed, 32 insertions(+), 55 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 4a825f65f..3f7ac849a 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1223,32 +1223,44 @@ size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long #define ZSTD_ROWSIZE 16 -/*! ZSTD_reduceTable_internal() : - * reduce table indexes by `reducerValue` - * presume table size is a multiple of ZSTD_ROWSIZE. - * Helps auto-vectorization */ -static void ZSTD_reduceTable_internal (U32* const table, int const nbRows, U32 const reducerValue) +/*! ZSTD_reduceTable() : + * reduce table indexes by `reducerValue`, or squash to zero. + * PreserveMark preserves "unsorted mark" for btlazy2 strategy. + * It must be set to a clear 0/1 value, to remove branch during inlining. + * Presume table size is a multiple of ZSTD_ROWSIZE + * to help auto-vectorization */ +FORCE_INLINE_TEMPLATE void +ZSTD_reduceTable_internal (U32* const table, U32 const size, U32 const reducerValue, int const preserveMark) { + int const nbRows = (int)size / ZSTD_ROWSIZE; int cellNb = 0; int rowNb; + assert((size & (ZSTD_ROWSIZE-1)) == 0); /* multiple of ZSTD_ROWSIZE */ + assert(size < (1U<<31)); /* can be casted to int */ for (rowNb=0 ; rowNb < nbRows ; rowNb++) { int column; for (column=0; columnappliedParams.cParams.strategy != ZSTD_fast) { U32 const chainSize = (U32)1 << zc->appliedParams.cParams.chainLog; if (zc->appliedParams.cParams.strategy == ZSTD_btlazy2) - ZSTD_preserveUnsortedMark(ms->chainTable, chainSize, reducerValue); - ZSTD_reduceTable(ms->chainTable, chainSize, reducerValue); + ZSTD_reduceTable_btlazy2(ms->chainTable, chainSize, reducerValue); + else + ZSTD_reduceTable(ms->chainTable, chainSize, reducerValue); } if (ms->hashLog3) { diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index cf5936587..35b153fa3 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -32,6 +32,12 @@ extern "C" { ***************************************/ static const U32 g_searchStrength = 8; #define HASH_READ_SIZE 8 +#define ZSTD_DUBT_UNSORTED_MARK 1 /* For btlazy2 strategy, index 1 now means "unsorted". + It could be confused for a real successor at index "1", if sorted as larger than its predecessor. + It's not a big deal though : candidate will just be sorted again. + Additionnally, candidate position 1 will be lost. + But candidate 1 cannot hide a large tree of candidates, so it's a minimal loss. + The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled after table re-use with a different strategy */ /*-************************************* diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 844afc051..8252135d0 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -15,48 +15,6 @@ /*-************************************* * Binary Tree search ***************************************/ -#define ZSTD_DUBT_UNSORTED_MARK 1 /* note : index 1 will now be confused with "unsorted" if sorted as larger than its predecessor. - It's not a big deal though : the candidate will just be considered unsorted, and be sorted again. - Additionnally, candidate position 1 will be lost. - But candidate 1 cannot hide a large tree of candidates, so it's a minimal loss. - The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled after table re-use with a different strategy */ - -/*! ZSTD_preserveUnsortedMark() : - * pre-emptively increase value of ZSTD_DUBT_UNSORTED_MARK before ZSTD_reduceTable() - * so that combined operation preserves its value. - * Without it, ZSTD_DUBT_UNSORTED_MARK==1 would be squashed to 0. - * As a consequence, the list of unsorted elements would stop at first element, - * removing candidates, resulting in a very small loss to compression ratio - * (since overflow protection with ZSTD_reduceTable() is relatively rare). - * - * Another potential risk is that a position will be promoted from *unsorted* - * to *sorted=>smaller:0*, meaning next candidate will be considered smaller. - * This could be wrong, and result in data corruption. - * - * On second thought, this corruption might be impossible, - * because unsorted elements stand at the beginning of the list, - * and squashing to zero reduces the list to a single element, - * which needs to be sorted anyway. - * I haven't spent much thoughts into this possible scenario, - * and just felt it was safer to implement ZSTD_preserveUnsortedMark() - * - * `size` : must be a positive multiple of ZSTD_ROWSIZE */ -#define ZSTD_ROWSIZE 16 -void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const reducerValue) -{ - int cellNb = 0; - U32 const nbRows = size / ZSTD_ROWSIZE; - U32 rowNb; - assert((size % ZSTD_ROWSIZE) == 0); - for (rowNb=0 ; rowNb < nbRows ; rowNb++) { - int column; - for (column=0; column Date: Wed, 7 Feb 2018 23:13:19 -0800 Subject: [PATCH 117/255] fixed streaming_memory_usage example also: ensure zstd.h is read from ../lib (instead of /usr/include) --- examples/Makefile | 1 + examples/streaming_memory_usage.c | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 52470f599..2910d23b6 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -9,6 +9,7 @@ # This Makefile presumes libzstd is installed, using `sudo make install` +CPPFLAGS += -I../lib LIB = ../lib/libzstd.a .PHONY: default all clean test diff --git a/examples/streaming_memory_usage.c b/examples/streaming_memory_usage.c index b056c2a59..5e7e13e82 100644 --- a/examples/streaming_memory_usage.c +++ b/examples/streaming_memory_usage.c @@ -46,7 +46,7 @@ static unsigned readU32FromChar(const char** stringPtr) int main(int argc, char const *argv[]) { - printf("\n Zstandard (v%u) memory usage for streaming contexts : \n\n", ZSTD_versionNumber()); + printf("\n Zstandard (v%s) memory usage for streaming : \n\n", ZSTD_versionString()); unsigned wLog = 0; if (argc > 1) { @@ -69,11 +69,13 @@ int main(int argc, char const *argv[]) { /* forces compressor to use maximum memory size for given compression level, * by not providing any information on input size */ - ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, 0); + ZSTD_parameters params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0); if (wLog) { /* special mode : specific wLog */ printf("Using custom compression parameter : level 1 + wLog=%u \n", wLog); - params = ZSTD_getParams(1, 1 << wLog, 0); - size_t const error = ZSTD_initCStream_advanced(cstream, NULL, 0, params, 0); + params = ZSTD_getParams(1 /*compressionLevel*/, + 1 << wLog /*estimatedSrcSize*/, + 0 /*no dictionary*/); + size_t const error = ZSTD_initCStream_advanced(cstream, NULL, 0, params, ZSTD_CONTENTSIZE_UNKNOWN); if (ZSTD_isError(error)) { printf("ZSTD_initCStream_advanced error : %s \n", ZSTD_getErrorName(error)); return 1; From 95424409eab01b82c6dea5186e4235a219d090f2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Feb 2018 04:25:15 -0800 Subject: [PATCH 118/255] addBits and baseline into FSE decoding table note : unfinished - need new default tables - need modify long mode --- lib/common/zstd_internal.h | 7 +- lib/compress/zstd_compress.c | 6 +- lib/decompress/zstd_decompress.c | 284 ++++++++++++++++++++++++------- 3 files changed, 228 insertions(+), 69 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 49f19a010..65c08a825 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -132,14 +132,15 @@ typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingTy #define Litbits 8 #define MaxLit ((1<tableLog = 0; + DTableH->fastMode = 0; + + cell->nbBits = 0; + cell->nextState = 0; + cell->nbAdditionalBits = 0; + cell->baseValue = symbolValue; +} + + +/* ZSTD_buildFSETable() : + * generate FSE decoding table for one symbol (ll, ml or off) */ +static void +ZSTD_buildFSETable(ZSTD_seqSymbol* dt, + const short* normalizedCounter, unsigned maxSymbolValue, + const U32* baseValue, const U32* nbAdditionalBits, + unsigned tableLog) +{ + ZSTD_seqSymbol* const tableDecode = dt+1; + U16 symbolNext[MaxSeq+1]; + + U32 const maxSV1 = maxSymbolValue + 1; + U32 const tableSize = 1 << tableLog; + U32 highThreshold = tableSize-1; + + /* Sanity Checks */ + assert(maxSymbolValue <= MaxSeq); + assert(tableLog <= MaxFSELog); + + /* Init, lay down lowprob symbols */ + { ZSTD_seqSymbol_header DTableH; + DTableH.tableLog = tableLog; + DTableH.fastMode = 1; + { S16 const largeLimit= (S16)(1 << (tableLog-1)); + U32 s; + for (s=0; s= largeLimit) DTableH.fastMode=0; + symbolNext[s] = normalizedCounter[s]; + } } } + memcpy(dt, &DTableH, sizeof(DTableH)); + } + + /* Spread symbols */ + { U32 const tableMask = tableSize-1; + U32 const step = FSE_TABLESTEP(tableSize); + U32 s, position = 0; + for (s=0; s highThreshold) position = (position + step) & tableMask; /* lowprob area */ + } } + assert(position == 0); /* position must reach all cells once, otherwise normalizedCounter is incorrect */ + } + + /* Build Decoding table */ + { U32 u; + for (u=0; u max) return ERROR(corruption_detected); - FSE_buildDTable_rle(DTableSpace, *(const BYTE*)src); + ZSTD_buildSeqTable_rle(DTableSpace, *(const BYTE*)src); *DTablePtr = DTableSpace; return 1; case set_basic : + assert(0); /* need to rebuild all default tables */ *DTablePtr = &defaultTable->dtable; return 0; case set_repeat: @@ -755,12 +850,40 @@ static size_t ZSTD_buildSeqTable(FSE_DTable* DTableSpace, const FSE_DTable** DTa size_t const headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize); if (FSE_isError(headerSize)) return ERROR(corruption_detected); if (tableLog > maxLog) return ERROR(corruption_detected); - FSE_buildDTable(DTableSpace, norm, max, tableLog); + ZSTD_buildFSETable(DTableSpace, norm, max, baseValue, nbAdditionalBits, tableLog); *DTablePtr = DTableSpace; return headerSize; } } } +static const U32 LL_base[MaxLL+1] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 18, 20, 22, 24, 28, 32, 40, + 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, + 0x2000, 0x4000, 0x8000, 0x10000 }; + +static const U32 OF_base[MaxOff+1] = { + 0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, + 0xFD, 0x1FD, 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD, + 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, + 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD }; + +static const U32 OF_bits[MaxOff+1] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31 }; + +static const U32 ML_base[MaxML+1] = { + 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, + 35, 37, 39, 41, 43, 47, 51, 59, + 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, + 0x1003, 0x2003, 0x4003, 0x8003, 0x10003 }; + size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, const void* src, size_t srcSize) { @@ -797,19 +920,27 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, /* Build DTables */ { size_t const llhSize = ZSTD_buildSeqTable(dctx->entropy.LLTable, &dctx->LLTptr, LLtype, MaxLL, LLFSELog, - ip, iend-ip, LL_defaultDTable, dctx->fseEntropy); + ip, iend-ip, + LL_base, LL_bits, + LL_defaultDTable, dctx->fseEntropy); if (ZSTD_isError(llhSize)) return ERROR(corruption_detected); ip += llhSize; } + { size_t const ofhSize = ZSTD_buildSeqTable(dctx->entropy.OFTable, &dctx->OFTptr, OFtype, MaxOff, OffFSELog, - ip, iend-ip, OF_defaultDTable, dctx->fseEntropy); + ip, iend-ip, + OF_base, OF_bits, + OF_defaultDTable, dctx->fseEntropy); if (ZSTD_isError(ofhSize)) return ERROR(corruption_detected); ip += ofhSize; } + { size_t const mlhSize = ZSTD_buildSeqTable(dctx->entropy.MLTable, &dctx->MLTptr, MLtype, MaxML, MLFSELog, - ip, iend-ip, ML_defaultDTable, dctx->fseEntropy); + ip, iend-ip, + ML_base, ML_bits, + ML_defaultDTable, dctx->fseEntropy); if (ZSTD_isError(mlhSize)) return ERROR(corruption_detected); ip += mlhSize; } @@ -826,11 +957,16 @@ typedef struct { const BYTE* match; } seq_t; +typedef struct { + size_t state; + const ZSTD_seqSymbol* table; +} ZSTD_fseState; + typedef struct { BIT_DStream_t DStream; - FSE_DState_t stateLL; - FSE_DState_t stateOffb; - FSE_DState_t stateML; + ZSTD_fseState stateLL; + ZSTD_fseState stateOffb; + ZSTD_fseState stateML; size_t prevOffset[ZSTD_REP_NUM]; const BYTE* prefixStart; const BYTE* dictEnd; @@ -885,6 +1021,16 @@ size_t ZSTD_execSequenceLast7(BYTE* op, } +static void +ZSTD_updateFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD) +{ + ZSTD_seqSymbol const DInfo = DStatePtr->table[DStatePtr->state]; + U32 const nbBits = DInfo.nbBits; + size_t const lowBits = BIT_readBits(bitD, nbBits); + DStatePtr->state = DInfo.nextState + lowBits; +} + + typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset_e; /* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum @@ -900,41 +1046,17 @@ typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset static seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) { seq_t seq; - - U32 const llCode = FSE_peekSymbol(&seqState->stateLL); - U32 const mlCode = FSE_peekSymbol(&seqState->stateML); - U32 const ofCode = FSE_peekSymbol(&seqState->stateOffb); /* <= MaxOff, by table construction */ - - U32 const llBits = LL_bits[llCode]; - U32 const mlBits = ML_bits[mlCode]; - U32 const ofBits = ofCode; + U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; + U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits; + U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits; U32 const totalBits = llBits+mlBits+ofBits; - - static const U32 LL_base[MaxLL+1] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 18, 20, 22, 24, 28, 32, 40, - 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, - 0x2000, 0x4000, 0x8000, 0x10000 }; - - static const U32 ML_base[MaxML+1] = { - 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, - 35, 37, 39, 41, 43, 47, 51, 59, - 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, - 0x1003, 0x2003, 0x4003, 0x8003, 0x10003 }; - - static const U32 OF_base[MaxOff+1] = { - 0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, - 0xFD, 0x1FD, 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD, - 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, - 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD }; + U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue; + U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue; + U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue; /* sequence */ { size_t offset; - if (!ofCode) + if (!ofBits) offset = 0; else { ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); @@ -942,18 +1064,18 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e l assert(ofBits <= MaxOff); if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) { U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed); - offset = OF_base[ofCode] + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); + offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); BIT_reloadDStream(&seqState->DStream); if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32); /* to avoid another reload */ } else { - offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ + offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); } } - if (ofCode <= 1) { - offset += (llCode==0); + if (ofBits <= 1) { + offset += (llBase==0); if (offset) { size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ @@ -971,8 +1093,8 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e l seq.offset = offset; } - seq.matchLength = ML_base[mlCode] - + ((mlCode>31) ? BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/) : 0); /* <= 16 bits */ + seq.matchLength = mlBase + + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/) : 0); /* <= 16 bits */ if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) BIT_reloadDStream(&seqState->DStream); if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) @@ -980,8 +1102,8 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e l /* Ensure there are enough bits to read the rest of data in 64-bit mode. */ ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); - seq.litLength = LL_base[llCode] - + ((llCode>15) ? BIT_readBitsFast(&seqState->DStream, llBits/*>0*/) : 0); /* <= 16 bits */ + seq.litLength = llBase + + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits/*>0*/) : 0); /* <= 16 bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); @@ -989,10 +1111,10 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e l (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset); /* ANS state update */ - FSE_updateState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ - FSE_updateState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ + ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ + ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ - FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ + ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ return seq; } @@ -1079,6 +1201,18 @@ size_t ZSTD_execSequence(BYTE* op, } +static void +ZSTD_initFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, const ZSTD_seqSymbol* dt) +{ + const void* ptr = dt; + const ZSTD_seqSymbol_header* const DTableH = (const ZSTD_seqSymbol_header*)ptr; + DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); + DEBUGLOG(6, "ZSTD_initFseState : val=%u using %u bits", + (U32)DStatePtr->state, DTableH->tableLog); + BIT_reloadDStream(bitD); + DStatePtr->table = dt + 1; +} + static size_t ZSTD_decompressSequences( ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, @@ -1112,9 +1246,12 @@ static size_t ZSTD_decompressSequences( dctx->fseEntropy = 1; { U32 i; for (i=0; ientropy.rep[i]; } CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); - FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); - FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); - FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); + DEBUGLOG(6, "ZSTD_decompressSequences: init stateLL"); + ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); + DEBUGLOG(6, "ZSTD_decompressSequences: init stateOffb"); + ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); + DEBUGLOG(6, "ZSTD_decompressSequences: init stateML"); + ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) { nbSeq--; @@ -1143,6 +1280,8 @@ static size_t ZSTD_decompressSequences( } +#if 0 + HINT_INLINE seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) { @@ -1407,6 +1546,7 @@ static size_t ZSTD_decompressSequencesLong( return op-ostart; } +#endif static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, @@ -1430,10 +1570,12 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, ip += litCSize; srcSize -= litCSize; } +#if 0 if ( frame /* windowSize exists */ && (dctx->fParams.windowSize > (1<<24)) && MEM_64bits() /* x86 benefits less from long mode than x64 */ ) return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, isLongOffset); +#endif return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, isLongOffset); } @@ -1929,8 +2071,12 @@ static size_t ZSTD_loadEntropy(ZSTD_entropyDTables_t* entropy, const void* const U32 offcodeMaxValue = MaxOff, offcodeLog; size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr); if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted); + if (offcodeMaxValue > MaxOff) return ERROR(dictionary_corrupted); if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted); - CHECK_E(FSE_buildDTable(entropy->OFTable, offcodeNCount, offcodeMaxValue, offcodeLog), dictionary_corrupted); + ZSTD_buildFSETable(entropy->OFTable, + offcodeNCount, offcodeMaxValue, + OF_base, OF_bits, + offcodeLog); dictPtr += offcodeHeaderSize; } @@ -1938,8 +2084,12 @@ static size_t ZSTD_loadEntropy(ZSTD_entropyDTables_t* entropy, const void* const unsigned matchlengthMaxValue = MaxML, matchlengthLog; size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr); if (FSE_isError(matchlengthHeaderSize)) return ERROR(dictionary_corrupted); + if (matchlengthMaxValue > MaxML) return ERROR(dictionary_corrupted); if (matchlengthLog > MLFSELog) return ERROR(dictionary_corrupted); - CHECK_E(FSE_buildDTable(entropy->MLTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog), dictionary_corrupted); + ZSTD_buildFSETable(entropy->MLTable, + matchlengthNCount, matchlengthMaxValue, + ML_base, ML_bits, + matchlengthLog); dictPtr += matchlengthHeaderSize; } @@ -1947,8 +2097,12 @@ static size_t ZSTD_loadEntropy(ZSTD_entropyDTables_t* entropy, const void* const unsigned litlengthMaxValue = MaxLL, litlengthLog; size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr); if (FSE_isError(litlengthHeaderSize)) return ERROR(dictionary_corrupted); + if (litlengthMaxValue > MaxLL) return ERROR(dictionary_corrupted); if (litlengthLog > LLFSELog) return ERROR(dictionary_corrupted); - CHECK_E(FSE_buildDTable(entropy->LLTable, litlengthNCount, litlengthMaxValue, litlengthLog), dictionary_corrupted); + ZSTD_buildFSETable(entropy->LLTable, + litlengthNCount, litlengthMaxValue, + LL_base, LL_bits, + litlengthLog); dictPtr += litlengthHeaderSize; } From 421a2716d8e9c6ed796357810c8502aa4002222e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Feb 2018 04:50:58 -0800 Subject: [PATCH 119/255] fixed default fse distributions but would be better to pre-calculate tables, for speed --- lib/decompress/zstd_decompress.c | 33 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 215dfd482..0181351b1 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -82,8 +82,8 @@ typedef enum { zdss_init=0, zdss_loadHeader, typedef struct { - U32 tableLog; U32 fastMode; + U32 tableLog; } ZSTD_seqSymbol_header; typedef struct { @@ -665,28 +665,22 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, } } - -typedef union { - FSE_decode_t realData; - FSE_DTable dtable; - U32 alignedBy4; -} FSE_decode_t4; - +#if 0 /* Default FSE distribution table for Literal Lengths */ -static const FSE_decode_t4 LL_defaultDTable[(1<dtable; + //*DTablePtr = &defaultTable->dtable; + ZSTD_buildFSETable(DTableSpace, defaultNorm, max, baseValue, nbAdditionalBits, defaultLog); + *DTablePtr = DTableSpace; return 0; case set_repeat: if (!flagRepeatTable) return ERROR(corruption_detected); @@ -922,7 +919,7 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, LLtype, MaxLL, LLFSELog, ip, iend-ip, LL_base, LL_bits, - LL_defaultDTable, dctx->fseEntropy); + LL_defaultNorm, LL_defaultNormLog, dctx->fseEntropy); if (ZSTD_isError(llhSize)) return ERROR(corruption_detected); ip += llhSize; } @@ -931,7 +928,7 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, OFtype, MaxOff, OffFSELog, ip, iend-ip, OF_base, OF_bits, - OF_defaultDTable, dctx->fseEntropy); + OF_defaultNorm, OF_defaultNormLog, dctx->fseEntropy); if (ZSTD_isError(ofhSize)) return ERROR(corruption_detected); ip += ofhSize; } @@ -940,7 +937,7 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, MLtype, MaxML, MLFSELog, ip, iend-ip, ML_base, ML_bits, - ML_defaultDTable, dctx->fseEntropy); + ML_defaultNorm, ML_defaultNormLog, dctx->fseEntropy); if (ZSTD_isError(mlhSize)) return ERROR(corruption_detected); ip += mlhSize; } From ab75df21ed9ab71a521f5d7f43f9e31f3f064d24 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Feb 2018 05:12:13 -0800 Subject: [PATCH 120/255] fixed mono-symbol distribution --- lib/decompress/zstd_decompress.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 0181351b1..bc0e3fb3d 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -734,7 +734,7 @@ static const FSE_decode_t4 OF_defaultDTable[(1<nbBits = 0; cell->nextState = 0; - cell->nbAdditionalBits = 0; - cell->baseValue = symbolValue; + assert(nbAddBits < 255); + cell->nbAdditionalBits = (BYTE)nbAddBits; + cell->baseValue = baseValue; } @@ -828,12 +829,15 @@ static size_t ZSTD_buildSeqTable(ZSTD_seqSymbol* DTableSpace, const ZSTD_seqSymb case set_rle : if (!srcSize) return ERROR(srcSize_wrong); if ( (*(const BYTE*)src) > max) return ERROR(corruption_detected); - ZSTD_buildSeqTable_rle(DTableSpace, *(const BYTE*)src); + { U32 const symbol = *(const BYTE*)src; + U32 const baseline = baseValue[symbol]; + U32 const nbBits = nbAdditionalBits[symbol]; + ZSTD_buildSeqTable_rle(DTableSpace, baseline, nbBits); + } *DTablePtr = DTableSpace; return 1; case set_basic : - assert(0); /* need to rebuild all default tables */ - //*DTablePtr = &defaultTable->dtable; + //*DTablePtr = &defaultTable->dtable; // when default tables will be pre-built ZSTD_buildFSETable(DTableSpace, defaultNorm, max, baseValue, nbAdditionalBits, defaultLog); *DTablePtr = DTableSpace; return 0; From 1850597eaa42ecda915e8578844197be16e74876 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Feb 2018 06:01:02 -0800 Subject: [PATCH 121/255] pre-calculated default decoding tables --- lib/decompress/zstd_decompress.c | 166 ++++++++++++++++++------------- 1 file changed, 98 insertions(+), 68 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index bc0e3fb3d..c09d73b29 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -665,74 +665,105 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, } } -#if 0 /* Default FSE distribution table for Literal Lengths */ static const ZSTD_seqSymbol LL_defaultDTable[(1<dtable; // when default tables will be pre-built - ZSTD_buildFSETable(DTableSpace, defaultNorm, max, baseValue, nbAdditionalBits, defaultLog); - *DTablePtr = DTableSpace; + *DTablePtr = defaultTable; return 0; case set_repeat: if (!flagRepeatTable) return ERROR(corruption_detected); @@ -885,6 +914,7 @@ static const U32 ML_base[MaxML+1] = { 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, 0x1003, 0x2003, 0x4003, 0x8003, 0x10003 }; + size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, const void* src, size_t srcSize) { @@ -923,7 +953,7 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, LLtype, MaxLL, LLFSELog, ip, iend-ip, LL_base, LL_bits, - LL_defaultNorm, LL_defaultNormLog, dctx->fseEntropy); + LL_defaultDTable, dctx->fseEntropy); if (ZSTD_isError(llhSize)) return ERROR(corruption_detected); ip += llhSize; } @@ -932,7 +962,7 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, OFtype, MaxOff, OffFSELog, ip, iend-ip, OF_base, OF_bits, - OF_defaultNorm, OF_defaultNormLog, dctx->fseEntropy); + OF_defaultDTable, dctx->fseEntropy); if (ZSTD_isError(ofhSize)) return ERROR(corruption_detected); ip += ofhSize; } @@ -941,7 +971,7 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, MLtype, MaxML, MLFSELog, ip, iend-ip, ML_base, ML_bits, - ML_defaultNorm, ML_defaultNormLog, dctx->fseEntropy); + ML_defaultDTable, dctx->fseEntropy); if (ZSTD_isError(mlhSize)) return ERROR(corruption_detected); ip += mlhSize; } From 6bfe50ad480472814de2b43616e30876042fd6d6 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Feb 2018 09:14:25 -0800 Subject: [PATCH 122/255] re-enabled ZSTD_decompressSequencesLong() --- lib/decompress/zstd_decompress.c | 73 ++++++++++---------------------- 1 file changed, 22 insertions(+), 51 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index c09d73b29..d63526920 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1277,11 +1277,8 @@ static size_t ZSTD_decompressSequences( dctx->fseEntropy = 1; { U32 i; for (i=0; ientropy.rep[i]; } CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); - DEBUGLOG(6, "ZSTD_decompressSequences: init stateLL"); ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); - DEBUGLOG(6, "ZSTD_decompressSequences: init stateOffb"); ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); - DEBUGLOG(6, "ZSTD_decompressSequences: init stateML"); ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) { @@ -1294,7 +1291,7 @@ static size_t ZSTD_decompressSequences( } } /* check if reached exact end */ - DEBUGLOG(5, "after decode loop, remaining nbSeq : %i", nbSeq); + DEBUGLOG(5, "ZSTD_decompressSequences: after decode loop, remaining nbSeq : %i", nbSeq); if (nbSeq) return ERROR(corruption_detected); /* save reps for next block */ { U32 i; for (i=0; ientropy.rep[i] = (U32)(seqState.prevOffset[i]); } @@ -1311,47 +1308,22 @@ static size_t ZSTD_decompressSequences( } -#if 0 HINT_INLINE seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) { seq_t seq; - - U32 const llCode = FSE_peekSymbol(&seqState->stateLL); - U32 const mlCode = FSE_peekSymbol(&seqState->stateML); - U32 const ofCode = FSE_peekSymbol(&seqState->stateOffb); /* <= MaxOff, by table construction */ - - U32 const llBits = LL_bits[llCode]; - U32 const mlBits = ML_bits[mlCode]; - U32 const ofBits = ofCode; + U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; + U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits; + U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits; U32 const totalBits = llBits+mlBits+ofBits; - - static const U32 LL_base[MaxLL+1] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 18, 20, 22, 24, 28, 32, 40, - 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, - 0x2000, 0x4000, 0x8000, 0x10000 }; - - static const U32 ML_base[MaxML+1] = { - 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, - 35, 37, 39, 41, 43, 47, 51, 59, - 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, - 0x1003, 0x2003, 0x4003, 0x8003, 0x10003 }; - - static const U32 OF_base[MaxOff+1] = { - 0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, - 0xFD, 0x1FD, 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD, - 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, - 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD }; + U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue; + U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue; + U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue; /* sequence */ { size_t offset; - if (!ofCode) + if (!ofBits) offset = 0; else { ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); @@ -1359,17 +1331,17 @@ seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const long assert(ofBits <= MaxOff); if (MEM_32bits() && longOffsets) { U32 const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN_32-1); - offset = OF_base[ofCode] + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); + offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); if (MEM_32bits() || extraBits) BIT_reloadDStream(&seqState->DStream); if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); } else { - offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ + offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); } } - if (ofCode <= 1) { - offset += (llCode==0); + if (ofBits <= 1) { + offset += (llBase==0); if (offset) { size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ @@ -1387,7 +1359,7 @@ seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const long seq.offset = offset; } - seq.matchLength = ML_base[mlCode] + ((mlCode>31) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ + seq.matchLength = mlBase + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) BIT_reloadDStream(&seqState->DStream); if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) @@ -1395,7 +1367,7 @@ seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const long /* Verify that there is enough bits to read the rest of the data in 64-bit mode. */ ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); - seq.litLength = LL_base[llCode] + ((llCode>15) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ + seq.litLength = llBase + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); @@ -1407,10 +1379,10 @@ seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const long } /* ANS state update */ - FSE_updateState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ - FSE_updateState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ + ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ + ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ - FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ + ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ return seq; } @@ -1534,9 +1506,9 @@ static size_t ZSTD_decompressSequencesLong( seqState.pos = (size_t)(op-prefixStart); seqState.dictEnd = dictEnd; CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); - FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); - FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); - FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); + ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); + ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); + ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); /* prepare in advance */ for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && (seqNbfParams.windowSize > (1<<24)) && MEM_64bits() /* x86 benefits less from long mode than x64 */ ) return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, isLongOffset); -#endif + return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, isLongOffset); } From c72091556bb5735ef51b9cfd5a79a54f69a875bd Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Feb 2018 09:46:08 -0800 Subject: [PATCH 123/255] fixed minor nit as per @terrelln's comments --- lib/compress/zstd_compress.c | 5 +---- lib/compress/zstdmt_compress.c | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 9e768edd3..0994d78f1 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -251,7 +251,7 @@ static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param) case ZSTD_p_compressionStrategy: return 1; - case ZSTD_p_format : + case ZSTD_p_format: case ZSTD_p_windowLog: case ZSTD_p_contentSizeFlag: case ZSTD_p_checksumFlag: @@ -265,10 +265,7 @@ static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param) case ZSTD_p_ldmMinMatch: case ZSTD_p_ldmBucketSizeLog: case ZSTD_p_ldmHashEveryLog: - return 0; - default: - assert(0); return 0; } } diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 4f327c07c..7ae27888c 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -677,8 +677,8 @@ void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, int compressionLe compressionLevel); mtctx->params.compressionLevel = compressionLevel; if (compressionLevel != ZSTD_CLEVEL_CUSTOM) - cParams = ZSTD_getCParams(compressionLevel, mtctx->frameContentSize, 0 /* should be dictSize */ ); - cParams. windowLog = saved_wlog; + cParams = ZSTD_getCParams(compressionLevel, mtctx->frameContentSize, 0 /* dictSize */ ); + cParams.windowLog = saved_wlog; mtctx->params.cParams = cParams; } From 64ee73269434634393362c09dc6885da149f826d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Feb 2018 12:33:28 -0800 Subject: [PATCH 124/255] decide long-offset mode based on offcode statistics threshold vaguely estimated --- lib/decompress/zstd_decompress.c | 61 ++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index afae80d1c..3d1f36404 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1082,7 +1082,7 @@ size_t ZSTD_execSequence(BYTE* op, static size_t ZSTD_decompressSequences( ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, - const void* seqStart, size_t seqSize, + const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset) { const BYTE* ip = (const BYTE*)seqStart; @@ -1095,16 +1095,8 @@ static size_t ZSTD_decompressSequences( const BYTE* const base = (const BYTE*) (dctx->base); const BYTE* const vBase = (const BYTE*) (dctx->vBase); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); - int nbSeq; - DEBUGLOG(5, "ZSTD_decompressSequences"); - /* Build Decoding Tables */ - { size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); - DEBUGLOG(5, "ZSTD_decodeSeqHeaders: size=%u, nbSeq=%i", - (U32)seqHSize, nbSeq); - if (ZSTD_isError(seqHSize)) return seqHSize; - ip += seqHSize; - } + DEBUGLOG(5, "ZSTD_decompressSequences"); /* Regen sequences */ if (nbSeq) { @@ -1328,7 +1320,7 @@ size_t ZSTD_execSequenceLong(BYTE* op, static size_t ZSTD_decompressSequencesLong( ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, - const void* seqStart, size_t seqSize, + const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset) { const BYTE* ip = (const BYTE*)seqStart; @@ -1341,13 +1333,6 @@ static size_t ZSTD_decompressSequencesLong( const BYTE* const prefixStart = (const BYTE*) (dctx->base); const BYTE* const dictStart = (const BYTE*) (dctx->vBase); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); - int nbSeq; - - /* Build Decoding Tables */ - { size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); - if (ZSTD_isError(seqHSize)) return seqHSize; - ip += seqHSize; - } /* Regen sequences */ if (nbSeq) { @@ -1408,6 +1393,24 @@ static size_t ZSTD_decompressSequencesLong( } +static unsigned +ZSTD_shareLongOffsets(const FSE_DTable* offTable) +{ + U32 const tableLog = ((const FSE_DTableHeader*)offTable)[0].tableLog; + const FSE_decode_t* table = (const FSE_decode_t*)(offTable + 1); + U32 const max = 1 << tableLog; + U32 u, total = 0; + + assert(tableLog <= OffFSELog); + for (u=0; u 23) total += 1; + + total <<= (OffFSELog - tableLog); + + return total; +} + + static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const int frame) @@ -1430,11 +1433,23 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, ip += litCSize; srcSize -= litCSize; } - if ( frame /* windowSize exists */ - && (dctx->fParams.windowSize > (1<<24)) - && MEM_64bits() /* x86 benefits less from long mode than x64 */ ) - return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, isLongOffset); - return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, isLongOffset); + + /* Build Decoding Tables */ + { int nbSeq; + size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, srcSize); + if (ZSTD_isError(seqHSize)) return seqHSize; + ip += seqHSize; + srcSize -= seqHSize; + + if (dctx->fParams.windowSize > (1<<24)) { + U32 const shareLongOffsets = ZSTD_shareLongOffsets(dctx->entropy.OFTable); + U32 const minShare = MEM_64bits() ? 12 : 20; + if (shareLongOffsets >= minShare) + return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset); + } + + return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset); + } } From 426944c3e31c13e0eecce63983d543f14b5caeb0 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Feb 2018 13:10:32 -0800 Subject: [PATCH 125/255] fixed strict aliasing issue tuned threshold --- lib/decompress/zstd_decompress.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 3d1f36404..63985933a 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1394,10 +1394,11 @@ static size_t ZSTD_decompressSequencesLong( static unsigned -ZSTD_shareLongOffsets(const FSE_DTable* offTable) +ZSTD_getLongOffsetsShare(const FSE_DTable* offTable) { - U32 const tableLog = ((const FSE_DTableHeader*)offTable)[0].tableLog; - const FSE_decode_t* table = (const FSE_decode_t*)(offTable + 1); + const void* ptr = offTable; + U32 const tableLog = ((const FSE_DTableHeader*)ptr)[0].tableLog; + const FSE_decode_t* table = ((const FSE_decode_t*)ptr) + 1; U32 const max = 1 << tableLog; U32 u, total = 0; @@ -1442,8 +1443,8 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, srcSize -= seqHSize; if (dctx->fParams.windowSize > (1<<24)) { - U32 const shareLongOffsets = ZSTD_shareLongOffsets(dctx->entropy.OFTable); - U32 const minShare = MEM_64bits() ? 12 : 20; + U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->entropy.OFTable); + U32 const minShare = MEM_64bits() ? 5 : 13; if (shareLongOffsets >= minShare) return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset); } From af48f0b62baf343115547c0a24e9dc5b49d2ea7f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Feb 2018 15:15:46 -0800 Subject: [PATCH 126/255] fix : offset table pointer when using default table --- lib/decompress/zstd_decompress.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 63985933a..c72fc71d8 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -748,7 +748,6 @@ static size_t ZSTD_buildSeqTable(FSE_DTable* DTableSpace, const FSE_DTable** DTa case set_repeat: if (!flagRepeatTable) return ERROR(corruption_detected); return 0; - default : /* impossible */ case set_compressed : { U32 tableLog; S16 norm[MaxSeq+1]; @@ -758,7 +757,10 @@ static size_t ZSTD_buildSeqTable(FSE_DTable* DTableSpace, const FSE_DTable** DTa FSE_buildDTable(DTableSpace, norm, max, tableLog); *DTablePtr = DTableSpace; return headerSize; - } } + } + default : /* impossible */ + assert(0); + } } size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, @@ -1443,7 +1445,7 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, srcSize -= seqHSize; if (dctx->fParams.windowSize > (1<<24)) { - U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->entropy.OFTable); + U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr); U32 const minShare = MEM_64bits() ? 5 : 13; if (shareLongOffsets >= minShare) return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset); From 75689838e4e3fd1657315cc1d0afc383bb8e0375 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Feb 2018 15:53:27 -0800 Subject: [PATCH 127/255] specify new command --single-thread --- programs/fileio.c | 2 +- programs/zstd.1.md | 10 ++++++++-- programs/zstdcli.c | 19 ++++++++++--------- tests/playTests.sh | 1 + 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 0cc807a11..d344b8f69 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -461,7 +461,7 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionStrategy, (U32)comprParams->strategy) ); /* multi-threading */ #ifdef ZSTD_MULTITHREAD - DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbWorkers); + DISPLAYLEVEL(5,"set nb workers = %u \n", g_nbWorkers); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbWorkers, g_nbWorkers) ); #endif /* dictionary */ diff --git a/programs/zstd.1.md b/programs/zstd.1.md index 4d9ec2fce..c970c5cbb 100644 --- a/programs/zstd.1.md +++ b/programs/zstd.1.md @@ -116,10 +116,16 @@ the last one takes effect. Note: If `windowLog` is set to larger than 27, `--long=windowLog` or `--memory=windowSize` needs to be passed to the decompressor. * `-T#`, `--threads=#`: - Compress using `#` threads (default: 1). + Compress using `#` working threads (default: 1). If `#` is 0, attempt to detect and use the number of physical CPU cores. - In all cases, the nb of threads is capped to ZSTDMT_NBTHREADS_MAX==256. + In all cases, the nb of threads is capped to ZSTDMT_NBTHREADS_MAX==200. This modifier does nothing if `zstd` is compiled without multithread support. +* `--single-thread`: + Does not spawn a thread for compression, use caller thread instead. + This is the only available mode when multithread support is disabled. + In this mode, compression is serialized with I/O. + (This is different from `-T1`, which spawns 1 compression thread in parallel of I/O). + Single-thread mode also features lower memory usage. * `-D file`: use `file` as Dictionary to compress or decompress FILE(s) * `--nodictID`: diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 015dc5e4c..6d7957c80 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -135,7 +135,7 @@ static int usage_advanced(const char* programName) DISPLAY( "--ultra : enable levels beyond %i, up to %i (requires more memory)\n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel()); DISPLAY( "--long[=#] : enable long distance matching with given window log (default: %u)\n", g_defaultMaxWindowLog); #ifdef ZSTD_MULTITHREAD - DISPLAY( " -T# : use # threads for compression (default: 1) \n"); + DISPLAY( " -T# : spawns # compression threads (default: 1) \n"); DISPLAY( " -B# : select size of each job (default: 0==automatic) \n"); #endif DISPLAY( "--no-dictID : don't write dictID into header (dictionary compression)\n"); @@ -366,21 +366,21 @@ typedef enum { zom_compress, zom_decompress, zom_test, zom_bench, zom_train, zom int main(int argCount, const char* argv[]) { int argNb, - forceStdout=0, followLinks=0, + forceStdout=0, + lastCommand = 0, + ldmFlag = 0, main_pause=0, - nextEntryIsDictionary=0, - operationResult=0, + nbWorkers = 1, nextArgumentIsOutFileName=0, nextArgumentIsMaxDict=0, nextArgumentIsDictID=0, nextArgumentsAreFiles=0, - ultra=0, - lastCommand = 0, - nbWorkers = 1, - setRealTimePrio = 0, + nextEntryIsDictionary=0, + operationResult=0, separateFiles = 0, - ldmFlag = 0; + setRealTimePrio = 0, + ultra=0; unsigned bench_nbSeconds = 3; /* would be better if this value was synchronized from bench */ size_t blockSize = 0; zstd_operation_mode operation = zom_compress; @@ -481,6 +481,7 @@ int main(int argCount, const char* argv[]) if (!strcmp(argument, "--keep")) { FIO_setRemoveSrcFile(0); continue; } if (!strcmp(argument, "--rm")) { FIO_setRemoveSrcFile(1); continue; } if (!strcmp(argument, "--priority=rt")) { setRealTimePrio = 1; continue; } + if (!strcmp(argument, "--single-thread")) { nbWorkers = 0; continue; } #ifdef ZSTD_GZCOMPRESS if (!strcmp(argument, "--format=gzip")) { suffix = GZ_EXTENSION; FIO_setCompressionType(FIO_gzipCompression); continue; } #endif diff --git a/tests/playTests.sh b/tests/playTests.sh index 0e12ec8e7..258c9c75b 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -634,6 +634,7 @@ roundTripTest -g518K "19 --long" fileRoundTripTest -g5M "3 --long" +roundTripTest -g96K "5 --single-thread" if [ -n "$hasMT" ] then $ECHO "\n===> zstdmt round-trip tests " From 04a3f85ce7ea2738e246cacfdb9c8827efcd9d90 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Feb 2018 15:34:59 -0800 Subject: [PATCH 128/255] fixed gcc warning on a switch code path --- lib/decompress/zstd_decompress.c | 1 + programs/bench.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index c72fc71d8..17139d8ce 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -760,6 +760,7 @@ static size_t ZSTD_buildSeqTable(FSE_DTable* DTableSpace, const FSE_DTable** DTa } default : /* impossible */ assert(0); + return ERROR(GENERIC); } } diff --git a/programs/bench.c b/programs/bench.c index 843920c89..939890064 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -461,8 +461,8 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, } /* for (testNb = 1; testNb <= (g_nbSeconds + !g_nbSeconds); testNb++) */ if (g_displayLevel == 1) { /* hidden display mode -q, used by python speed benchmark */ - double cSpeed = (double)srcSize / fastestC; - double dSpeed = (double)srcSize / fastestD; + double const cSpeed = (double)srcSize / fastestC; + double const dSpeed = (double)srcSize / fastestD; if (g_additionalParam) DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s (param=%d)\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName, g_additionalParam); else From 821efa466ee7572fa99c7423150415446fb03db2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 10 Feb 2018 21:05:48 -0800 Subject: [PATCH 129/255] fixed logo path --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3755315ad..a7d3250e4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

    Zstandard

    +

    Zstandard

    __Zstandard__, or `zstd` as short version, is a fast lossless compression algorithm, targeting real-time compression scenarios at zlib-level and better compression ratios. From 71c07966bbedf478b21dfe62f1507b2a4b96f85f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 12 Feb 2018 16:52:15 -0800 Subject: [PATCH 130/255] added SEQSYMBOL_TABLE_SIZE() as suggested by @terrelln's comment --- lib/decompress/zstd_decompress.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index d63526920..d65dd5fd9 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -93,10 +93,12 @@ typedef struct { U32 baseValue; } ZSTD_seqSymbol; +#define SEQSYMBOL_TABLE_SIZE(log) (1 + (1< STREAM_ACCUMULATOR_MIN_32 \ - ? ZSTD_WINDOWLOG_MAX_32 - STREAM_ACCUMULATOR_MIN_32 \ +#define LONG_OFFSETS_MAX_EXTRA_BITS_32 \ + (ZSTD_WINDOWLOG_MAX_32 > STREAM_ACCUMULATOR_MIN_32 \ + ? ZSTD_WINDOWLOG_MAX_32 - STREAM_ACCUMULATOR_MIN_32 \ : 0) static seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) From 2524cbd8475add0c962f4e7ab0f5fd9eb5b30085 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 13 Feb 2018 10:02:25 -0800 Subject: [PATCH 131/255] added code comment on how to generate default tables as suggested by @terrelln --- lib/decompress/zstd_decompress.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index d65dd5fd9..36dfc6376 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -667,6 +667,15 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, } } +/* Default FSE distribution tables. + * These are pre-calculated FSE decoding tables using default distributions as defined in specification : + * https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#default-distributions + * They were generated programmatically with following method : + * - start from default distributions, present in /lib/common/zstd_internal.h + * - generate tables normally, using ZSTD_buildFSETable() + * - printout the content of tables + * - pretify output, report below, test with fuzzer to ensure it's correct */ + /* Default FSE distribution table for Literal Lengths */ static const ZSTD_seqSymbol LL_defaultDTable[(1< Date: Tue, 13 Feb 2018 10:09:01 -0800 Subject: [PATCH 132/255] pretify last unit test output --- tests/fuzzer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index e7c92edc0..aecc06449 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -1209,7 +1209,7 @@ static int basicUnitTests(U32 seed, double compressibility) if (strcmp("No error detected", ZSTD_getErrorName(ZSTD_error_GENERIC)) != 0) goto _output_error; DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(4, "test%3i : testing ZSTD dictionary sizes : ", testNb++); + DISPLAYLEVEL(3, "test%3i : testing ZSTD dictionary sizes : ", testNb++); RDG_genBuffer(CNBuffer, CNBuffSize, compressibility, 0., seed); { size_t const size = MIN(128 KB, CNBuffSize); @@ -1230,6 +1230,7 @@ static int basicUnitTests(U32 seed, double compressibility) ZSTD_freeCDict(lgCDict); ZSTD_freeCCtx(cctx); } + DISPLAYLEVEL(3, "OK \n"); _end: free(CNBuffer); From 5cb1144872acbfdf995996624efd5f1ea0cd01aa Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 13 Feb 2018 14:56:35 -0800 Subject: [PATCH 133/255] fixed --single-thread was incorrectly set to -T0 (use as many cores as possible) previously --- lib/compress/zstdmt_compress.c | 1 - programs/zstdcli.c | 31 ++++++++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 7ae27888c..4d58e2e2f 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -509,7 +509,6 @@ static ZSTDMT_jobDescription* ZSTDMT_createJobsTable(U32* nbJobsPtr, ZSTD_custom size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers) { if (nbWorkers > ZSTDMT_NBWORKERS_MAX) nbWorkers = ZSTDMT_NBWORKERS_MAX; - if (nbWorkers < 1) nbWorkers = 1; params->nbWorkers = nbWorkers; params->overlapSizeLog = ZSTDMT_OVERLAPLOG_DEFAULT; params->jobSize = 0; diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 6d7957c80..05230227f 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -366,20 +366,21 @@ typedef enum { zom_compress, zom_decompress, zom_test, zom_bench, zom_train, zom int main(int argCount, const char* argv[]) { int argNb, - followLinks=0, - forceStdout=0, + followLinks = 0, + forceStdout = 0, lastCommand = 0, ldmFlag = 0, - main_pause=0, - nbWorkers = 1, - nextArgumentIsOutFileName=0, - nextArgumentIsMaxDict=0, - nextArgumentIsDictID=0, - nextArgumentsAreFiles=0, - nextEntryIsDictionary=0, - operationResult=0, + main_pause = 0, + nbWorkers = 0, + nextArgumentIsOutFileName = 0, + nextArgumentIsMaxDict = 0, + nextArgumentIsDictID = 0, + nextArgumentsAreFiles = 0, + nextEntryIsDictionary = 0, + operationResult = 0, separateFiles = 0, setRealTimePrio = 0, + singleThread = 0, ultra=0; unsigned bench_nbSeconds = 3; /* would be better if this value was synchronized from bench */ size_t blockSize = 0; @@ -418,8 +419,10 @@ int main(int argCount, const char* argv[]) if (filenameTable==NULL) { DISPLAY("zstd: %s \n", strerror(errno)); exit(1); } filenameTable[0] = stdinmark; g_displayOut = stderr; - programName = lastNameFromPath(programName); +#ifdef ZSTD_MULTITHREAD + nbWorkers = 1; +#endif /* preset behaviors */ if (exeNameMatch(programName, ZSTD_ZSTDMT)) nbWorkers=0; @@ -481,7 +484,7 @@ int main(int argCount, const char* argv[]) if (!strcmp(argument, "--keep")) { FIO_setRemoveSrcFile(0); continue; } if (!strcmp(argument, "--rm")) { FIO_setRemoveSrcFile(1); continue; } if (!strcmp(argument, "--priority=rt")) { setRealTimePrio = 1; continue; } - if (!strcmp(argument, "--single-thread")) { nbWorkers = 0; continue; } + if (!strcmp(argument, "--single-thread")) { nbWorkers = 0; singleThread = 1; continue; } #ifdef ZSTD_GZCOMPRESS if (!strcmp(argument, "--format=gzip")) { suffix = GZ_EXTENSION; FIO_setCompressionType(FIO_gzipCompression); continue; } #endif @@ -717,11 +720,13 @@ int main(int argCount, const char* argv[]) /* Welcome message (if verbose) */ DISPLAYLEVEL(3, WELCOME_MESSAGE); - if (nbWorkers == 0) { +#ifdef ZSTD_MULTITHREAD + if ((nbWorkers==0) && (!singleThread)) { /* automatically set # workers based on # of reported cpus */ nbWorkers = UTIL_countPhysicalCores(); DISPLAYLEVEL(3, "Note: %d physical core(s) detected \n", nbWorkers); } +#endif g_utilDisplayLevel = g_displayLevel; if (!followLinks) { From 5ec97fff17965663a2295627e5443b3a4ac9f52b Mon Sep 17 00:00:00 2001 From: Leo Arias Date: Wed, 14 Feb 2018 00:37:58 +0000 Subject: [PATCH 134/255] Add the packaging metadata to build the zstd snap --- snap/snapcraft.yaml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 snap/snapcraft.yaml diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml new file mode 100644 index 000000000..0a77946ae --- /dev/null +++ b/snap/snapcraft.yaml @@ -0,0 +1,28 @@ +name: zstd +version: git +summary: Zstandard - Fast real-time compression algorithm +description: | + Zstandard, or zstd as short version, is a fast lossless compression + algorithm, targeting real-time compression scenarios at zlib-level and better + compression ratios. It's backed by a very fast entropy stage, provided by + Huff0 and FSE library + +grade: devel # must be 'stable' to release into candidate/stable channels +confinement: devmode # use 'strict' once you have the right plugs and slots + +apps: + zstd: + command: usr/local/bin/zstd + plugs: [home, removable-media] + zstdgrep: + command: usr/local/bin/zstdgrep + plugs: [home, removable-media] + zstdless: + command: usr/local/bin/zstdless + plugs: [home, removable-media] + +parts: + zstd: + source: . + plugin: make + build-packages: [g++] From 4319132312de33a74dd10d124e41d0725b78f0d2 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 2 Feb 2018 18:03:09 -0800 Subject: [PATCH 135/255] [decompress] Support BMI2 --- lib/BUCK | 13 + lib/common/compiler.h | 18 + lib/common/cpu.h | 216 ++++++++++++ lib/common/huf.h | 8 + lib/decompress/huf_decompress.c | 463 ++++++-------------------- lib/decompress/huf_decompress_impl.h | 365 ++++++++++++++++++++ lib/decompress/zstd_decompress.c | 389 +++------------------- lib/decompress/zstd_decompress_impl.h | 356 ++++++++++++++++++++ 8 files changed, 1135 insertions(+), 693 deletions(-) create mode 100644 lib/common/cpu.h create mode 100644 lib/decompress/huf_decompress_impl.h create mode 100644 lib/decompress/zstd_decompress_impl.h diff --git a/lib/BUCK b/lib/BUCK index 9e01468f4..dbe8885fc 100644 --- a/lib/BUCK +++ b/lib/BUCK @@ -25,6 +25,9 @@ cxx_library( name='decompress', header_namespace='', visibility=['PUBLIC'], + headers=subdir_glob([ + ('decompress', '*_impl.h'), + ]), srcs=glob(['decompress/zstd*.c']), deps=[ ':common', @@ -80,6 +83,15 @@ cxx_library( ]), ) +cxx_library( + name='cpu', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('common', 'cpu.h'), + ]), +) + cxx_library( name='bitstream', header_namespace='', @@ -196,6 +208,7 @@ cxx_library( deps=[ ':bitstream', ':compiler', + ':cpu', ':entropy', ':errors', ':mem', diff --git a/lib/common/compiler.h b/lib/common/compiler.h index 3a7553c38..24f353280 100644 --- a/lib/common/compiler.h +++ b/lib/common/compiler.h @@ -63,6 +63,24 @@ # endif #endif +/* target attribute */ +#if defined(__GNUC__) +# define TARGET_ATTRIBUTE(target) __attribute__((__target__(target))) +#else +# define TARGET_ATTRIBUTE(target) +#endif + +/* Enable runtime BMI2 dispatch based on the CPU. + * Enabled for clang/gcc on x86 when BMI2 isn't enabled by default. + */ +#ifndef DYNAMIC_BMI2 + #if defined(__GNUC__) && (defined(__x86_64__) || defined(_M_X86)) && !defined(__BMI2__) + # define DYNAMIC_BMI2 1 + #else + # define DYNAMIC_BMI2 0 + #endif +#endif + /* prefetch */ #if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */ # include /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ diff --git a/lib/common/cpu.h b/lib/common/cpu.h new file mode 100644 index 000000000..4eb48e39e --- /dev/null +++ b/lib/common/cpu.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2018-present, 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. + */ + +#ifndef ZSTD_COMMON_CPU_H +#define ZSTD_COMMON_CPU_H + +/** + * Implementation taken from folly/CpuId.h + * https://github.com/facebook/folly/blob/master/folly/CpuId.h + */ + +#include + +#include "mem.h" + +#ifdef _MSC_VER +#include +#endif + +typedef struct { + U32 f1c; + U32 f1d; + U32 f7b; + U32 f7c; +} ZSTD_cpuid_t; + +MEM_STATIC ZSTD_cpuid_t ZSTD_cpuid(void) { + U32 f1c = 0; + U32 f1d = 0; + U32 f7b = 0; + U32 f7c = 0; +#ifdef _MSC_VER + int reg[4]; + __cpuid((int*)reg, 0); + { + int const n = reg[0]; + if (n >= 1) { + __cpuid((int*)reg, 1); + f1c = (U32)reg[2]; + f1d = (U32)reg[3]; + } + if (n >= 7) { + __cpuidex((int*)reg, 7, 0); + f7b = (U32)reg[1]; + f7c = (U32)reg[2]; + } + } +#elif defined(__i386__) && defined(__PIC__) && !defined(__clang__) && defined(__GNUC__) + /* The following block like the normal cpuid branch below, but gcc + * reserves ebx for use of its pic register so we must specially + * handle the save and restore to avoid clobbering the register + */ + U32 n; + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=a"(n) + : "a"(0) + : "ecx", "edx"); + if (n >= 1) { + U32 f1a; + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=a"(f1a), "=c"(f1c), "=d"(f1d) + : "a"(1) + :); + } + if (n >= 7) { + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "movl %%ebx, %%eax\n\r" + "popl %%ebx" + : "=a"(f7b), "=c"(f7c) + : "a"(7), "c"(0) + : "edx"); + } +#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386__) + U32 n; + __asm__("cpuid" : "=a"(n) : "a"(0) : "ebx", "ecx", "edx"); + if (n >= 1) { + U32 f1a; + __asm__("cpuid" : "=a"(f1a), "=c"(f1c), "=d"(f1d) : "a"(1) : "ebx"); + } + if (n >= 7) { + U32 f7a; + __asm__("cpuid" + : "=a"(f7a), "=b"(f7b), "=c"(f7c) + : "a"(7), "c"(0) + : "edx"); + } +#endif + { + ZSTD_cpuid_t cpuid; + cpuid.f1c = f1c; + cpuid.f1d = f1d; + cpuid.f7b = f7b; + cpuid.f7c = f7c; + return cpuid; + } +} + +#define X(name, r, bit) \ + MEM_STATIC int ZSTD_cpuid_##name(ZSTD_cpuid_t const cpuid) { \ + return ((cpuid.r) & (1U << bit)) != 0; \ + } + +/* cpuid(1): Processor Info and Feature Bits. */ +#define C(name, bit) X(name, f1c, bit) + C(sse3, 0) + C(pclmuldq, 1) + C(dtes64, 2) + C(monitor, 3) + C(dscpl, 4) + C(vmx, 5) + C(smx, 6) + C(eist, 7) + C(tm2, 8) + C(ssse3, 9) + C(cnxtid, 10) + C(fma, 12) + C(cx16, 13) + C(xtpr, 14) + C(pdcm, 15) + C(pcid, 17) + C(dca, 18) + C(sse41, 19) + C(sse42, 20) + C(x2apic, 21) + C(movbe, 22) + C(popcnt, 23) + C(tscdeadline, 24) + C(aes, 25) + C(xsave, 26) + C(osxsave, 27) + C(avx, 28) + C(f16c, 29) + C(rdrand, 30) +#undef C +#define D(name, bit) X(name, f1d, bit) + D(fpu, 0) + D(vme, 1) + D(de, 2) + D(pse, 3) + D(tsc, 4) + D(msr, 5) + D(pae, 6) + D(mce, 7) + D(cx8, 8) + D(apic, 9) + D(sep, 11) + D(mtrr, 12) + D(pge, 13) + D(mca, 14) + D(cmov, 15) + D(pat, 16) + D(pse36, 17) + D(psn, 18) + D(clfsh, 19) + D(ds, 21) + D(acpi, 22) + D(mmx, 23) + D(fxsr, 24) + D(sse, 25) + D(sse2, 26) + D(ss, 27) + D(htt, 28) + D(tm, 29) + D(pbe, 31) +#undef D + +/* cpuid(7): Extended Features. */ +#define B(name, bit) X(name, f7b, bit) + B(bmi1, 3) + B(hle, 4) + B(avx2, 5) + B(smep, 7) + B(bmi2, 8) + B(erms, 9) + B(invpcid, 10) + B(rtm, 11) + B(mpx, 14) + B(avx512f, 16) + B(avx512dq, 17) + B(rdseed, 18) + B(adx, 19) + B(smap, 20) + B(avx512ifma, 21) + B(pcommit, 22) + B(clflushopt, 23) + B(clwb, 24) + B(avx512pf, 26) + B(avx512er, 27) + B(avx512cd, 28) + B(sha, 29) + B(avx512bw, 30) + B(avx512vl, 31) +#undef B +#define C(name, bit) X(name, f7c, bit) + C(prefetchwt1, 0) + C(avx512vbmi, 1) +#undef C + +#undef X + +#endif /* ZSTD_COMMON_CPU_H */ diff --git a/lib/common/huf.h b/lib/common/huf.h index 1cead357a..3b96ae8b7 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -295,6 +295,14 @@ size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cS size_t HUF_decompress1X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); size_t HUF_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); +/* BMI2 variants. + * If the CPU has BMI2 support pass bmi2=1, otherwise pass bmi2=0. + */ +size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2); +size_t HUF_decompress1X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2); +size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2); +size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2); + #endif /* HUF_STATIC_LINKING_ONLY */ #if defined (__cplusplus) diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 79ded96bf..13f852bc9 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -144,72 +144,54 @@ size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize) workSpace, sizeof(workSpace)); } +typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX4; /* double-symbols decoding */ -static BYTE HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, const U32 dtLog) -{ - size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ - BYTE const c = dt[val].byte; - BIT_skipBits(Dstream, dt[val].nbBits); - return c; -} +#define FUNCTION(fn) fn##_default +#define TARGET +#include "huf_decompress_impl.h" +#undef TARGET +#undef FUNCTION -#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) \ - *ptr++ = HUF_decodeSymbolX2(DStreamPtr, dt, dtLog) +#if DYNAMIC_BMI2 -#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \ - if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ - HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) +#define FUNCTION(fn) fn##_bmi2 +#define TARGET TARGET_ATTRIBUTE("bmi2") +#include "huf_decompress_impl.h" +#undef TARGET +#undef FUNCTION -#define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \ - if (MEM_64bits()) \ - HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) +#endif -HINT_INLINE size_t HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog) -{ - BYTE* const pStart = p; - - /* up to 4 symbols at a time */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd-4)) { - HUF_DECODE_SYMBOLX2_2(p, bitDPtr); - HUF_DECODE_SYMBOLX2_1(p, bitDPtr); - HUF_DECODE_SYMBOLX2_2(p, bitDPtr); - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); +typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize, + const void *cSrc, + size_t cSrcSize, + const HUF_DTable *DTable); +#if DYNAMIC_BMI2 +#define X(fn) \ + static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ + size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + { \ + if (bmi2) { \ + return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \ + } \ + return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ } +#else +#define X(fn) \ + static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ + size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + { \ + (void)bmi2; \ + return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ + } +#endif - /* closer to the end */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd)) - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); +X(HUF_decompress1X2_usingDTable_internal) +X(HUF_decompress4X2_usingDTable_internal) +X(HUF_decompress1X4_usingDTable_internal) +X(HUF_decompress4X4_usingDTable_internal) - /* no more data to retrieve from bitstream, hence no need to reload */ - while (p < pEnd) - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); - - return pEnd-pStart; -} - -static size_t HUF_decompress1X2_usingDTable_internal( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - BYTE* op = (BYTE*)dst; - BYTE* const oend = op + dstSize; - const void* dtPtr = DTable + 1; - const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; - BIT_DStream_t bitD; - DTableDesc const dtd = HUF_getDTableDesc(DTable); - U32 const dtLog = dtd.tableLog; - - { size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); - if (HUF_isError(errorCode)) return errorCode; } - - HUF_decodeStreamX2(op, &bitD, oend, dt, dtLog); - - /* check */ - if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected); - - return dstSize; -} +#undef X size_t HUF_decompress1X2_usingDTable( void* dst, size_t dstSize, @@ -218,7 +200,7 @@ size_t HUF_decompress1X2_usingDTable( { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 0) return ERROR(GENERIC); - return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); + return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, @@ -232,7 +214,7 @@ size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress1X2_usingDTable_internal (dst, dstSize, ip, cSrcSize, DCtx); + return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); } @@ -250,100 +232,6 @@ size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cS return HUF_decompress1X2_DCtx (DTable, dst, dstSize, cSrc, cSrcSize); } - -static size_t HUF_decompress4X2_usingDTable_internal( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - /* Check */ - if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ - - { const BYTE* const istart = (const BYTE*) cSrc; - BYTE* const ostart = (BYTE*) dst; - BYTE* const oend = ostart + dstSize; - const void* const dtPtr = DTable + 1; - const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; - - /* Init */ - BIT_DStream_t bitD1; - BIT_DStream_t bitD2; - BIT_DStream_t bitD3; - BIT_DStream_t bitD4; - size_t const length1 = MEM_readLE16(istart); - size_t const length2 = MEM_readLE16(istart+2); - size_t const length3 = MEM_readLE16(istart+4); - size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); - const BYTE* const istart1 = istart + 6; /* jumpTable */ - const BYTE* const istart2 = istart1 + length1; - const BYTE* const istart3 = istart2 + length2; - const BYTE* const istart4 = istart3 + length3; - const size_t segmentSize = (dstSize+3) / 4; - BYTE* const opStart2 = ostart + segmentSize; - BYTE* const opStart3 = opStart2 + segmentSize; - BYTE* const opStart4 = opStart3 + segmentSize; - BYTE* op1 = ostart; - BYTE* op2 = opStart2; - BYTE* op3 = opStart3; - BYTE* op4 = opStart4; - U32 endSignal; - DTableDesc const dtd = HUF_getDTableDesc(DTable); - U32 const dtLog = dtd.tableLog; - - if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ - { size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); - if (HUF_isError(errorCode)) return errorCode; } - - /* 16-32 symbols per loop (4-8 symbols per stream) */ - endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); - for ( ; (endSignal==BIT_DStream_unfinished) && (op4<(oend-7)) ; ) { - HUF_DECODE_SYMBOLX2_2(op1, &bitD1); - HUF_DECODE_SYMBOLX2_2(op2, &bitD2); - HUF_DECODE_SYMBOLX2_2(op3, &bitD3); - HUF_DECODE_SYMBOLX2_2(op4, &bitD4); - HUF_DECODE_SYMBOLX2_1(op1, &bitD1); - HUF_DECODE_SYMBOLX2_1(op2, &bitD2); - HUF_DECODE_SYMBOLX2_1(op3, &bitD3); - HUF_DECODE_SYMBOLX2_1(op4, &bitD4); - HUF_DECODE_SYMBOLX2_2(op1, &bitD1); - HUF_DECODE_SYMBOLX2_2(op2, &bitD2); - HUF_DECODE_SYMBOLX2_2(op3, &bitD3); - HUF_DECODE_SYMBOLX2_2(op4, &bitD4); - HUF_DECODE_SYMBOLX2_0(op1, &bitD1); - HUF_DECODE_SYMBOLX2_0(op2, &bitD2); - HUF_DECODE_SYMBOLX2_0(op3, &bitD3); - HUF_DECODE_SYMBOLX2_0(op4, &bitD4); - endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); - } - - /* check corruption */ - if (op1 > opStart2) return ERROR(corruption_detected); - if (op2 > opStart3) return ERROR(corruption_detected); - if (op3 > opStart4) return ERROR(corruption_detected); - /* note : op4 supposed already verified within main loop */ - - /* finish bitStreams one by one */ - HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog); - HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog); - HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog); - HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog); - - /* check */ - endSignal = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); - if (!endSignal) return ERROR(corruption_detected); - - /* decoded size */ - return dstSize; - } -} - - size_t HUF_decompress4X2_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, @@ -351,13 +239,12 @@ size_t HUF_decompress4X2_usingDTable( { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 0) return ERROR(GENERIC); - return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); + return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } - -size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, +static size_t HUF_decompress4X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) + void* workSpace, size_t wkspSize, int bmi2) { const BYTE* ip = (const BYTE*) cSrc; @@ -367,7 +254,14 @@ size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress4X2_usingDTable_internal (dst, dstSize, ip, cSrcSize, dctx); + return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); +} + +size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + void* workSpace, size_t wkspSize) +{ + return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, 0); } @@ -387,8 +281,6 @@ size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cS /* *************************/ /* double-symbols decoding */ /* *************************/ -typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX4; /* double-symbols decoding */ - typedef struct { BYTE symbol; BYTE weight; } sortedSymbol_t; /* HUF_fillDTableX4Level2() : @@ -588,95 +480,6 @@ size_t HUF_readDTableX4(HUF_DTable* DTable, const void* src, size_t srcSize) workSpace, sizeof(workSpace)); } -static U32 HUF_decodeSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) -{ - size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ - memcpy(op, dt+val, 2); - BIT_skipBits(DStream, dt[val].nbBits); - return dt[val].length; -} - -static U32 HUF_decodeLastSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) -{ - size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ - memcpy(op, dt+val, 1); - if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits); - else { - if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) { - BIT_skipBits(DStream, dt[val].nbBits); - if (DStream->bitsConsumed > (sizeof(DStream->bitContainer)*8)) - /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */ - DStream->bitsConsumed = (sizeof(DStream->bitContainer)*8); - } } - return 1; -} - - -#define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) \ - ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) - -#define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \ - if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ - ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) - -#define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \ - if (MEM_64bits()) \ - ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) - -HINT_INLINE size_t HUF_decodeStreamX4(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, const HUF_DEltX4* const dt, const U32 dtLog) -{ - BYTE* const pStart = p; - - /* up to 8 symbols at a time */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) { - HUF_DECODE_SYMBOLX4_2(p, bitDPtr); - HUF_DECODE_SYMBOLX4_1(p, bitDPtr); - HUF_DECODE_SYMBOLX4_2(p, bitDPtr); - HUF_DECODE_SYMBOLX4_0(p, bitDPtr); - } - - /* closer to end : up to 2 symbols at a time */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2)) - HUF_DECODE_SYMBOLX4_0(p, bitDPtr); - - while (p <= pEnd-2) - HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ - - if (p < pEnd) - p += HUF_decodeLastSymbolX4(p, bitDPtr, dt, dtLog); - - return p-pStart; -} - - -static size_t HUF_decompress1X4_usingDTable_internal( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - BIT_DStream_t bitD; - - /* Init */ - { size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); - if (HUF_isError(errorCode)) return errorCode; - } - - /* decode */ - { BYTE* const ostart = (BYTE*) dst; - BYTE* const oend = ostart + dstSize; - const void* const dtPtr = DTable+1; /* force compiler to not use strict-aliasing */ - const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr; - DTableDesc const dtd = HUF_getDTableDesc(DTable); - HUF_decodeStreamX4(ostart, &bitD, oend, dt, dtd.tableLog); - } - - /* check */ - if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected); - - /* decoded size */ - return dstSize; -} - size_t HUF_decompress1X4_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, @@ -684,7 +487,7 @@ size_t HUF_decompress1X4_usingDTable( { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 1) return ERROR(GENERIC); - return HUF_decompress1X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); + return HUF_decompress1X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, @@ -699,7 +502,7 @@ size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress1X4_usingDTable_internal (dst, dstSize, ip, cSrcSize, DCtx); + return HUF_decompress1X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); } @@ -717,99 +520,6 @@ size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cS return HUF_decompress1X4_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); } -static size_t HUF_decompress4X4_usingDTable_internal( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ - - { const BYTE* const istart = (const BYTE*) cSrc; - BYTE* const ostart = (BYTE*) dst; - BYTE* const oend = ostart + dstSize; - const void* const dtPtr = DTable+1; - const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr; - - /* Init */ - BIT_DStream_t bitD1; - BIT_DStream_t bitD2; - BIT_DStream_t bitD3; - BIT_DStream_t bitD4; - size_t const length1 = MEM_readLE16(istart); - size_t const length2 = MEM_readLE16(istart+2); - size_t const length3 = MEM_readLE16(istart+4); - size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); - const BYTE* const istart1 = istart + 6; /* jumpTable */ - const BYTE* const istart2 = istart1 + length1; - const BYTE* const istart3 = istart2 + length2; - const BYTE* const istart4 = istart3 + length3; - size_t const segmentSize = (dstSize+3) / 4; - BYTE* const opStart2 = ostart + segmentSize; - BYTE* const opStart3 = opStart2 + segmentSize; - BYTE* const opStart4 = opStart3 + segmentSize; - BYTE* op1 = ostart; - BYTE* op2 = opStart2; - BYTE* op3 = opStart3; - BYTE* op4 = opStart4; - U32 endSignal; - DTableDesc const dtd = HUF_getDTableDesc(DTable); - U32 const dtLog = dtd.tableLog; - - if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ - { size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); - if (HUF_isError(errorCode)) return errorCode; } - - /* 16-32 symbols per loop (4-8 symbols per stream) */ - endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); - for ( ; (endSignal==BIT_DStream_unfinished) & (op4<(oend-(sizeof(bitD4.bitContainer)-1))) ; ) { - HUF_DECODE_SYMBOLX4_2(op1, &bitD1); - HUF_DECODE_SYMBOLX4_2(op2, &bitD2); - HUF_DECODE_SYMBOLX4_2(op3, &bitD3); - HUF_DECODE_SYMBOLX4_2(op4, &bitD4); - HUF_DECODE_SYMBOLX4_1(op1, &bitD1); - HUF_DECODE_SYMBOLX4_1(op2, &bitD2); - HUF_DECODE_SYMBOLX4_1(op3, &bitD3); - HUF_DECODE_SYMBOLX4_1(op4, &bitD4); - HUF_DECODE_SYMBOLX4_2(op1, &bitD1); - HUF_DECODE_SYMBOLX4_2(op2, &bitD2); - HUF_DECODE_SYMBOLX4_2(op3, &bitD3); - HUF_DECODE_SYMBOLX4_2(op4, &bitD4); - HUF_DECODE_SYMBOLX4_0(op1, &bitD1); - HUF_DECODE_SYMBOLX4_0(op2, &bitD2); - HUF_DECODE_SYMBOLX4_0(op3, &bitD3); - HUF_DECODE_SYMBOLX4_0(op4, &bitD4); - - endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); - } - - /* check corruption */ - if (op1 > opStart2) return ERROR(corruption_detected); - if (op2 > opStart3) return ERROR(corruption_detected); - if (op3 > opStart4) return ERROR(corruption_detected); - /* note : op4 already verified within main loop */ - - /* finish bitStreams one by one */ - HUF_decodeStreamX4(op1, &bitD1, opStart2, dt, dtLog); - HUF_decodeStreamX4(op2, &bitD2, opStart3, dt, dtLog); - HUF_decodeStreamX4(op3, &bitD3, opStart4, dt, dtLog); - HUF_decodeStreamX4(op4, &bitD4, oend, dt, dtLog); - - /* check */ - { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); - if (!endCheck) return ERROR(corruption_detected); } - - /* decoded size */ - return dstSize; - } -} - - size_t HUF_decompress4X4_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, @@ -817,13 +527,12 @@ size_t HUF_decompress4X4_usingDTable( { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 1) return ERROR(GENERIC); - return HUF_decompress4X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); + return HUF_decompress4X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } - -size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, +static size_t HUF_decompress4X4_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) + void* workSpace, size_t wkspSize, int bmi2) { const BYTE* ip = (const BYTE*) cSrc; @@ -833,7 +542,14 @@ size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress4X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx); + return HUF_decompress4X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); +} + +size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + void* workSpace, size_t wkspSize) +{ + return HUF_decompress4X4_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, /* bmi2 */ 0); } @@ -861,8 +577,8 @@ size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const HUF_DTable* DTable) { DTableDesc const dtd = HUF_getDTableDesc(DTable); - return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable) : - HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable); + return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : + HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, @@ -870,8 +586,8 @@ size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const HUF_DTable* DTable) { DTableDesc const dtd = HUF_getDTableDesc(DTable); - return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable) : - HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable); + return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : + HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } @@ -994,3 +710,42 @@ size_t HUF_decompress1X_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, return HUF_decompress1X_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, sizeof(workSpace)); } + + +size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2) +{ + DTableDesc const dtd = HUF_getDTableDesc(DTable); + return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : + HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); +} + +size_t HUF_decompress1X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) +{ + const BYTE* ip = (const BYTE*) cSrc; + + size_t const hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize); + if (HUF_isError(hSize)) return hSize; + if (hSize >= cSrcSize) return ERROR(srcSize_wrong); + ip += hSize; cSrcSize -= hSize; + + return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); +} + +size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2) +{ + DTableDesc const dtd = HUF_getDTableDesc(DTable); + return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : + HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); +} + +size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) +{ + /* validation checks */ + if (dstSize == 0) return ERROR(dstSize_tooSmall); + if (cSrcSize == 0) return ERROR(corruption_detected); + + { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); + return algoNb ? HUF_decompress4X4_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2) : + HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); + } +} diff --git a/lib/decompress/huf_decompress_impl.h b/lib/decompress/huf_decompress_impl.h new file mode 100644 index 000000000..25068cb35 --- /dev/null +++ b/lib/decompress/huf_decompress_impl.h @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2018-present, 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. + */ + +#ifndef FUNCTION +# error "FUNCTION(name) must be defined" +#endif + +#ifndef TARGET +# error "TARGET must be defined" +#endif + + +static TARGET BYTE FUNCTION(HUF_decodeSymbolX2)(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, const U32 dtLog) +{ + size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ + BYTE const c = dt[val].byte; + BIT_skipBits(Dstream, dt[val].nbBits); + return c; +} + +#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) \ + *ptr++ = FUNCTION(HUF_decodeSymbolX2)(DStreamPtr, dt, dtLog) + +#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \ + if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ + HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) + +#define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \ + if (MEM_64bits()) \ + HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) + +HINT_INLINE TARGET size_t FUNCTION(HUF_decodeStreamX2)(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog) +{ + BYTE* const pStart = p; + + /* up to 4 symbols at a time */ + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd-4)) { + HUF_DECODE_SYMBOLX2_2(p, bitDPtr); + HUF_DECODE_SYMBOLX2_1(p, bitDPtr); + HUF_DECODE_SYMBOLX2_2(p, bitDPtr); + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + } + + /* closer to the end */ + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd)) + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + + /* no more data to retrieve from bitstream, hence no need to reload */ + while (p < pEnd) + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + + return pEnd-pStart; +} + +static TARGET size_t FUNCTION(HUF_decompress1X2_usingDTable_internal)( + void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + const HUF_DTable* DTable) +{ + BYTE* op = (BYTE*)dst; + BYTE* const oend = op + dstSize; + const void* dtPtr = DTable + 1; + const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; + BIT_DStream_t bitD; + DTableDesc const dtd = HUF_getDTableDesc(DTable); + U32 const dtLog = dtd.tableLog; + + { size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); + if (HUF_isError(errorCode)) return errorCode; } + + FUNCTION(HUF_decodeStreamX2)(op, &bitD, oend, dt, dtLog); + + /* check */ + if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected); + + return dstSize; +} + +static TARGET size_t +FUNCTION(HUF_decompress4X2_usingDTable_internal)( + void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + const HUF_DTable* DTable) +{ + /* Check */ + if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ + + { const BYTE* const istart = (const BYTE*) cSrc; + BYTE* const ostart = (BYTE*) dst; + BYTE* const oend = ostart + dstSize; + const void* const dtPtr = DTable + 1; + const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; + + /* Init */ + BIT_DStream_t bitD1; + BIT_DStream_t bitD2; + BIT_DStream_t bitD3; + BIT_DStream_t bitD4; + size_t const length1 = MEM_readLE16(istart); + size_t const length2 = MEM_readLE16(istart+2); + size_t const length3 = MEM_readLE16(istart+4); + size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); + const BYTE* const istart1 = istart + 6; /* jumpTable */ + const BYTE* const istart2 = istart1 + length1; + const BYTE* const istart3 = istart2 + length2; + const BYTE* const istart4 = istart3 + length3; + const size_t segmentSize = (dstSize+3) / 4; + BYTE* const opStart2 = ostart + segmentSize; + BYTE* const opStart3 = opStart2 + segmentSize; + BYTE* const opStart4 = opStart3 + segmentSize; + BYTE* op1 = ostart; + BYTE* op2 = opStart2; + BYTE* op3 = opStart3; + BYTE* op4 = opStart4; + U32 endSignal; + DTableDesc const dtd = HUF_getDTableDesc(DTable); + U32 const dtLog = dtd.tableLog; + + if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ + { size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); + if (HUF_isError(errorCode)) return errorCode; } + { size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); + if (HUF_isError(errorCode)) return errorCode; } + { size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); + if (HUF_isError(errorCode)) return errorCode; } + { size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); + if (HUF_isError(errorCode)) return errorCode; } + + /* 16-32 symbols per loop (4-8 symbols per stream) */ + endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); + for ( ; (endSignal==BIT_DStream_unfinished) && (op4<(oend-7)) ; ) { + HUF_DECODE_SYMBOLX2_2(op1, &bitD1); + HUF_DECODE_SYMBOLX2_2(op2, &bitD2); + HUF_DECODE_SYMBOLX2_2(op3, &bitD3); + HUF_DECODE_SYMBOLX2_2(op4, &bitD4); + HUF_DECODE_SYMBOLX2_1(op1, &bitD1); + HUF_DECODE_SYMBOLX2_1(op2, &bitD2); + HUF_DECODE_SYMBOLX2_1(op3, &bitD3); + HUF_DECODE_SYMBOLX2_1(op4, &bitD4); + HUF_DECODE_SYMBOLX2_2(op1, &bitD1); + HUF_DECODE_SYMBOLX2_2(op2, &bitD2); + HUF_DECODE_SYMBOLX2_2(op3, &bitD3); + HUF_DECODE_SYMBOLX2_2(op4, &bitD4); + HUF_DECODE_SYMBOLX2_0(op1, &bitD1); + HUF_DECODE_SYMBOLX2_0(op2, &bitD2); + HUF_DECODE_SYMBOLX2_0(op3, &bitD3); + HUF_DECODE_SYMBOLX2_0(op4, &bitD4); + endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); + } + + /* check corruption */ + if (op1 > opStart2) return ERROR(corruption_detected); + if (op2 > opStart3) return ERROR(corruption_detected); + if (op3 > opStart4) return ERROR(corruption_detected); + /* note : op4 supposed already verified within main loop */ + + /* finish bitStreams one by one */ + FUNCTION(HUF_decodeStreamX2)(op1, &bitD1, opStart2, dt, dtLog); + FUNCTION(HUF_decodeStreamX2)(op2, &bitD2, opStart3, dt, dtLog); + FUNCTION(HUF_decodeStreamX2)(op3, &bitD3, opStart4, dt, dtLog); + FUNCTION(HUF_decodeStreamX2)(op4, &bitD4, oend, dt, dtLog); + + /* check */ + endSignal = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); + if (!endSignal) return ERROR(corruption_detected); + + /* decoded size */ + return dstSize; + } +} + +#undef HUF_DECODE_SYMBOLX2_0 +#undef HUF_DECODE_SYMBOLX2_1 +#undef HUF_DECODE_SYMBOLX2_2 + +static TARGET U32 FUNCTION(HUF_decodeSymbolX4)(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) +{ + size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ + memcpy(op, dt+val, 2); + BIT_skipBits(DStream, dt[val].nbBits); + return dt[val].length; +} + +static TARGET U32 FUNCTION(HUF_decodeLastSymbolX4)(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) +{ + size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ + memcpy(op, dt+val, 1); + if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits); + else { + if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) { + BIT_skipBits(DStream, dt[val].nbBits); + if (DStream->bitsConsumed > (sizeof(DStream->bitContainer)*8)) + /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */ + DStream->bitsConsumed = (sizeof(DStream->bitContainer)*8); + } } + return 1; +} + + +#define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) \ + ptr += FUNCTION(HUF_decodeSymbolX4)(ptr, DStreamPtr, dt, dtLog) + +#define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \ + if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ + ptr += FUNCTION(HUF_decodeSymbolX4)(ptr, DStreamPtr, dt, dtLog) + +#define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \ + if (MEM_64bits()) \ + ptr += FUNCTION(HUF_decodeSymbolX4)(ptr, DStreamPtr, dt, dtLog) + +HINT_INLINE TARGET size_t FUNCTION(HUF_decodeStreamX4)(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, const HUF_DEltX4* const dt, const U32 dtLog) +{ + BYTE* const pStart = p; + + /* up to 8 symbols at a time */ + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) { + HUF_DECODE_SYMBOLX4_2(p, bitDPtr); + HUF_DECODE_SYMBOLX4_1(p, bitDPtr); + HUF_DECODE_SYMBOLX4_2(p, bitDPtr); + HUF_DECODE_SYMBOLX4_0(p, bitDPtr); + } + + /* closer to end : up to 2 symbols at a time */ + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2)) + HUF_DECODE_SYMBOLX4_0(p, bitDPtr); + + while (p <= pEnd-2) + HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ + + if (p < pEnd) + p += FUNCTION(HUF_decodeLastSymbolX4)(p, bitDPtr, dt, dtLog); + + return p-pStart; +} + +static TARGET size_t FUNCTION(HUF_decompress1X4_usingDTable_internal)( + void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + const HUF_DTable* DTable) +{ + BIT_DStream_t bitD; + + /* Init */ + { size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); + if (HUF_isError(errorCode)) return errorCode; + } + + /* decode */ + { BYTE* const ostart = (BYTE*) dst; + BYTE* const oend = ostart + dstSize; + const void* const dtPtr = DTable+1; /* force compiler to not use strict-aliasing */ + const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr; + DTableDesc const dtd = HUF_getDTableDesc(DTable); + FUNCTION(HUF_decodeStreamX4)(ostart, &bitD, oend, dt, dtd.tableLog); + } + + /* check */ + if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected); + + /* decoded size */ + return dstSize; +} + +static TARGET size_t FUNCTION(HUF_decompress4X4_usingDTable_internal)( + void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + const HUF_DTable* DTable) +{ + if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ + + { const BYTE* const istart = (const BYTE*) cSrc; + BYTE* const ostart = (BYTE*) dst; + BYTE* const oend = ostart + dstSize; + const void* const dtPtr = DTable+1; + const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr; + + /* Init */ + BIT_DStream_t bitD1; + BIT_DStream_t bitD2; + BIT_DStream_t bitD3; + BIT_DStream_t bitD4; + size_t const length1 = MEM_readLE16(istart); + size_t const length2 = MEM_readLE16(istart+2); + size_t const length3 = MEM_readLE16(istart+4); + size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); + const BYTE* const istart1 = istart + 6; /* jumpTable */ + const BYTE* const istart2 = istart1 + length1; + const BYTE* const istart3 = istart2 + length2; + const BYTE* const istart4 = istart3 + length3; + size_t const segmentSize = (dstSize+3) / 4; + BYTE* const opStart2 = ostart + segmentSize; + BYTE* const opStart3 = opStart2 + segmentSize; + BYTE* const opStart4 = opStart3 + segmentSize; + BYTE* op1 = ostart; + BYTE* op2 = opStart2; + BYTE* op3 = opStart3; + BYTE* op4 = opStart4; + U32 endSignal; + DTableDesc const dtd = HUF_getDTableDesc(DTable); + U32 const dtLog = dtd.tableLog; + + if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ + { size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); + if (HUF_isError(errorCode)) return errorCode; } + { size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); + if (HUF_isError(errorCode)) return errorCode; } + { size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); + if (HUF_isError(errorCode)) return errorCode; } + { size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); + if (HUF_isError(errorCode)) return errorCode; } + + /* 16-32 symbols per loop (4-8 symbols per stream) */ + endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); + for ( ; (endSignal==BIT_DStream_unfinished) & (op4<(oend-(sizeof(bitD4.bitContainer)-1))) ; ) { + HUF_DECODE_SYMBOLX4_2(op1, &bitD1); + HUF_DECODE_SYMBOLX4_2(op2, &bitD2); + HUF_DECODE_SYMBOLX4_2(op3, &bitD3); + HUF_DECODE_SYMBOLX4_2(op4, &bitD4); + HUF_DECODE_SYMBOLX4_1(op1, &bitD1); + HUF_DECODE_SYMBOLX4_1(op2, &bitD2); + HUF_DECODE_SYMBOLX4_1(op3, &bitD3); + HUF_DECODE_SYMBOLX4_1(op4, &bitD4); + HUF_DECODE_SYMBOLX4_2(op1, &bitD1); + HUF_DECODE_SYMBOLX4_2(op2, &bitD2); + HUF_DECODE_SYMBOLX4_2(op3, &bitD3); + HUF_DECODE_SYMBOLX4_2(op4, &bitD4); + HUF_DECODE_SYMBOLX4_0(op1, &bitD1); + HUF_DECODE_SYMBOLX4_0(op2, &bitD2); + HUF_DECODE_SYMBOLX4_0(op3, &bitD3); + HUF_DECODE_SYMBOLX4_0(op4, &bitD4); + + endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); + } + + /* check corruption */ + if (op1 > opStart2) return ERROR(corruption_detected); + if (op2 > opStart3) return ERROR(corruption_detected); + if (op3 > opStart4) return ERROR(corruption_detected); + /* note : op4 already verified within main loop */ + + /* finish bitStreams one by one */ + FUNCTION(HUF_decodeStreamX4)(op1, &bitD1, opStart2, dt, dtLog); + FUNCTION(HUF_decodeStreamX4)(op2, &bitD2, opStart3, dt, dtLog); + FUNCTION(HUF_decodeStreamX4)(op3, &bitD3, opStart4, dt, dtLog); + FUNCTION(HUF_decodeStreamX4)(op4, &bitD4, oend, dt, dtLog); + + /* check */ + { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); + if (!endCheck) return ERROR(corruption_detected); } + + /* decoded size */ + return dstSize; + } +} + +#undef HUF_DECODE_SYMBOLX4_0 +#undef HUF_DECODE_SYMBOLX4_1 +#undef HUF_DECODE_SYMBOLX4_2 diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 36dfc6376..97ef6eb90 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -43,6 +43,7 @@ * Dependencies *********************************************************/ #include /* memcpy, memmove, memset */ +#include "cpu.h" #include "mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY #include "fse.h" @@ -131,6 +132,7 @@ struct ZSTD_DCtx_s size_t litSize; size_t rleSize; size_t staticSize; + int bmi2; /* streaming */ ZSTD_DDict* ddictLocal; @@ -188,6 +190,7 @@ static void ZSTD_initDCtx_internal(ZSTD_DCtx* dctx) dctx->inBuffSize = 0; dctx->outBuffSize = 0; dctx->streamStage = zdss_init; + dctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); } ZSTD_DCtx* ZSTD_initStaticDCtx(void *workspace, size_t workspaceSize) @@ -586,13 +589,13 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, if (HUF_isError((litEncType==set_repeat) ? ( singleStream ? - HUF_decompress1X_usingDTable(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr) : - HUF_decompress4X_usingDTable(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr) ) : + HUF_decompress1X_usingDTable_bmi2(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr, dctx->bmi2) : + HUF_decompress4X_usingDTable_bmi2(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr, dctx->bmi2) ) : ( singleStream ? - HUF_decompress1X2_DCtx_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, - dctx->entropy.workspace, sizeof(dctx->entropy.workspace)) : - HUF_decompress4X_hufOnly_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, - dctx->entropy.workspace, sizeof(dctx->entropy.workspace))))) + HUF_decompress1X2_DCtx_wksp_bmi2(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, + dctx->entropy.workspace, sizeof(dctx->entropy.workspace), dctx->bmi2) : + HUF_decompress4X_hufOnly_wksp_bmi2(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, + dctx->entropy.workspace, sizeof(dctx->entropy.workspace), dctx->bmi2)))) return ERROR(corruption_detected); dctx->litPtr = dctx->litBuffer; @@ -1063,105 +1066,6 @@ size_t ZSTD_execSequenceLast7(BYTE* op, } -static void -ZSTD_updateFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD) -{ - ZSTD_seqSymbol const DInfo = DStatePtr->table[DStatePtr->state]; - U32 const nbBits = DInfo.nbBits; - size_t const lowBits = BIT_readBits(bitD, nbBits); - DStatePtr->state = DInfo.nextState + lowBits; -} - - -typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset_e; - -/* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum - * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1) - * bits before reloading. This value is the maximum number of bytes we read - * after reloading when we are decoding long offets. - */ -#define LONG_OFFSETS_MAX_EXTRA_BITS_32 \ - (ZSTD_WINDOWLOG_MAX_32 > STREAM_ACCUMULATOR_MIN_32 \ - ? ZSTD_WINDOWLOG_MAX_32 - STREAM_ACCUMULATOR_MIN_32 \ - : 0) - -static seq_t ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) -{ - seq_t seq; - U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; - U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits; - U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits; - U32 const totalBits = llBits+mlBits+ofBits; - U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue; - U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue; - U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue; - - /* sequence */ - { size_t offset; - if (!ofBits) - offset = 0; - else { - ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); - ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); - assert(ofBits <= MaxOff); - if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) { - U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed); - offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); - BIT_reloadDStream(&seqState->DStream); - if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); - assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32); /* to avoid another reload */ - } else { - offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); - } - } - - if (ofBits <= 1) { - offset += (llBase==0); - if (offset) { - size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; - temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ - if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; - seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset = temp; - } else { /* offset == 0 */ - offset = seqState->prevOffset[0]; - } - } else { - seqState->prevOffset[2] = seqState->prevOffset[1]; - seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset; - } - seq.offset = offset; - } - - seq.matchLength = mlBase - + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/) : 0); /* <= 16 bits */ - if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) - BIT_reloadDStream(&seqState->DStream); - if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) - BIT_reloadDStream(&seqState->DStream); - /* Ensure there are enough bits to read the rest of data in 64-bit mode. */ - ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); - - seq.litLength = llBase - + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits/*>0*/) : 0); /* <= 16 bits */ - if (MEM_32bits()) - BIT_reloadDStream(&seqState->DStream); - - DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u", - (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset); - - /* ANS state update */ - ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ - ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ - ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ - - return seq; -} - - HINT_INLINE size_t ZSTD_execSequence(BYTE* op, BYTE* const oend, seq_t sequence, @@ -1243,162 +1147,6 @@ size_t ZSTD_execSequence(BYTE* op, } -static void -ZSTD_initFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, const ZSTD_seqSymbol* dt) -{ - const void* ptr = dt; - const ZSTD_seqSymbol_header* const DTableH = (const ZSTD_seqSymbol_header*)ptr; - DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); - DEBUGLOG(6, "ZSTD_initFseState : val=%u using %u bits", - (U32)DStatePtr->state, DTableH->tableLog); - BIT_reloadDStream(bitD); - DStatePtr->table = dt + 1; -} - -static size_t ZSTD_decompressSequences( - ZSTD_DCtx* dctx, - void* dst, size_t maxDstSize, - const void* seqStart, size_t seqSize, - const ZSTD_longOffset_e isLongOffset) -{ - const BYTE* ip = (const BYTE*)seqStart; - const BYTE* const iend = ip + seqSize; - BYTE* const ostart = (BYTE* const)dst; - BYTE* const oend = ostart + maxDstSize; - BYTE* op = ostart; - const BYTE* litPtr = dctx->litPtr; - const BYTE* const litEnd = litPtr + dctx->litSize; - const BYTE* const base = (const BYTE*) (dctx->base); - const BYTE* const vBase = (const BYTE*) (dctx->vBase); - const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); - int nbSeq; - DEBUGLOG(5, "ZSTD_decompressSequences"); - - /* Build Decoding Tables */ - { size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); - DEBUGLOG(5, "ZSTD_decodeSeqHeaders: size=%u, nbSeq=%i", - (U32)seqHSize, nbSeq); - if (ZSTD_isError(seqHSize)) return seqHSize; - ip += seqHSize; - } - - /* Regen sequences */ - if (nbSeq) { - seqState_t seqState; - dctx->fseEntropy = 1; - { U32 i; for (i=0; ientropy.rep[i]; } - CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); - ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); - ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); - ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); - - for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) { - nbSeq--; - { seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset); - size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, base, vBase, dictEnd); - DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize); - if (ZSTD_isError(oneSeqSize)) return oneSeqSize; - op += oneSeqSize; - } } - - /* check if reached exact end */ - DEBUGLOG(5, "ZSTD_decompressSequences: after decode loop, remaining nbSeq : %i", nbSeq); - if (nbSeq) return ERROR(corruption_detected); - /* save reps for next block */ - { U32 i; for (i=0; ientropy.rep[i] = (U32)(seqState.prevOffset[i]); } - } - - /* last literal segment */ - { size_t const lastLLSize = litEnd - litPtr; - if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall); - memcpy(op, litPtr, lastLLSize); - op += lastLLSize; - } - - return op-ostart; -} - - - -HINT_INLINE -seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) -{ - seq_t seq; - U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; - U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits; - U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits; - U32 const totalBits = llBits+mlBits+ofBits; - U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue; - U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue; - U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue; - - /* sequence */ - { size_t offset; - if (!ofBits) - offset = 0; - else { - ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); - ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); - assert(ofBits <= MaxOff); - if (MEM_32bits() && longOffsets) { - U32 const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN_32-1); - offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); - if (MEM_32bits() || extraBits) BIT_reloadDStream(&seqState->DStream); - if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); - } else { - offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); - } - } - - if (ofBits <= 1) { - offset += (llBase==0); - if (offset) { - size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; - temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ - if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; - seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset = temp; - } else { - offset = seqState->prevOffset[0]; - } - } else { - seqState->prevOffset[2] = seqState->prevOffset[1]; - seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset; - } - seq.offset = offset; - } - - seq.matchLength = mlBase + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ - if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) - BIT_reloadDStream(&seqState->DStream); - if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) - BIT_reloadDStream(&seqState->DStream); - /* Verify that there is enough bits to read the rest of the data in 64-bit mode. */ - ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); - - seq.litLength = llBase + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ - if (MEM_32bits()) - BIT_reloadDStream(&seqState->DStream); - - { size_t const pos = seqState->pos + seq.litLength; - const BYTE* const matchBase = (seq.offset > pos) ? seqState->dictEnd : seqState->prefixStart; - seq.match = matchBase + pos - seq.offset; /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted. - * No consequence though : no memory access will occur, overly large offset will be detected in ZSTD_execSequenceLong() */ - seqState->pos = pos + seq.matchLength; - } - - /* ANS state update */ - ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ - ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ - ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ - - return seq; -} - - HINT_INLINE size_t ZSTD_execSequenceLong(BYTE* op, BYTE* const oend, seq_t sequence, @@ -1478,88 +1226,51 @@ size_t ZSTD_execSequenceLong(BYTE* op, return sequenceLength; } -static size_t ZSTD_decompressSequencesLong( - ZSTD_DCtx* dctx, - void* dst, size_t maxDstSize, - const void* seqStart, size_t seqSize, - const ZSTD_longOffset_e isLongOffset) +typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset_e; + +#define FUNCTION(fn) fn##_default +#define TARGET +#include "zstd_decompress_impl.h" +#undef TARGET +#undef FUNCTION + +#if DYNAMIC_BMI2 + +#define FUNCTION(fn) fn##_bmi2 +#define TARGET TARGET_ATTRIBUTE("bmi2") +#include "zstd_decompress_impl.h" +#undef TARGET +#undef FUNCTION + +#endif + +typedef size_t (*ZSTD_decompressSequences_t)( + ZSTD_DCtx *dctx, void *dst, size_t maxDstSize, const void *seqStart, + size_t seqSize, const ZSTD_longOffset_e isLongOffset); + +static size_t ZSTD_decompressSequences(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, + const ZSTD_longOffset_e isLongOffset) { - const BYTE* ip = (const BYTE*)seqStart; - const BYTE* const iend = ip + seqSize; - BYTE* const ostart = (BYTE* const)dst; - BYTE* const oend = ostart + maxDstSize; - BYTE* op = ostart; - const BYTE* litPtr = dctx->litPtr; - const BYTE* const litEnd = litPtr + dctx->litSize; - const BYTE* const prefixStart = (const BYTE*) (dctx->base); - const BYTE* const dictStart = (const BYTE*) (dctx->vBase); - const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); - int nbSeq; - - /* Build Decoding Tables */ - { size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); - if (ZSTD_isError(seqHSize)) return seqHSize; - ip += seqHSize; +#if DYNAMIC_BMI2 + if (dctx->bmi2) { + return ZSTD_decompressSequences_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, isLongOffset); } - - /* Regen sequences */ - if (nbSeq) { -#define STORED_SEQS 4 -#define STOSEQ_MASK (STORED_SEQS-1) -#define ADVANCED_SEQS 4 - seq_t sequences[STORED_SEQS]; - int const seqAdvance = MIN(nbSeq, ADVANCED_SEQS); - seqState_t seqState; - int seqNb; - dctx->fseEntropy = 1; - { U32 i; for (i=0; ientropy.rep[i]; } - seqState.prefixStart = prefixStart; - seqState.pos = (size_t)(op-prefixStart); - seqState.dictEnd = dictEnd; - CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); - ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); - ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); - ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); - - /* prepare in advance */ - for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && (seqNbentropy.rep[i] = (U32)(seqState.prevOffset[i]); } - } - - /* last literal segment */ - { size_t const lastLLSize = litEnd - litPtr; - if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall); - memcpy(op, litPtr, lastLLSize); - op += lastLLSize; - } - - return op-ostart; +#endif + return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, isLongOffset); } +static size_t ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, + const ZSTD_longOffset_e isLongOffset) +{ +#if DYNAMIC_BMI2 + if (dctx->bmi2) { + return ZSTD_decompressSequencesLong_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, isLongOffset); + } +#endif + return ZSTD_decompressSequencesLong_default(dctx, dst, maxDstSize, seqStart, seqSize, isLongOffset); +} static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, diff --git a/lib/decompress/zstd_decompress_impl.h b/lib/decompress/zstd_decompress_impl.h new file mode 100644 index 000000000..6454a7429 --- /dev/null +++ b/lib/decompress/zstd_decompress_impl.h @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2018-present, 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. + */ + +#ifndef FUNCTION +# error "FUNCTION(name) must be defined" +#endif + +#ifndef TARGET +# error "TARGET must be defined" +#endif + +static TARGET void +FUNCTION(ZSTD_updateFseState)(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD) +{ + ZSTD_seqSymbol const DInfo = DStatePtr->table[DStatePtr->state]; + U32 const nbBits = DInfo.nbBits; + size_t const lowBits = BIT_readBits(bitD, nbBits); + DStatePtr->state = DInfo.nextState + lowBits; +} + +/* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum + * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1) + * bits before reloading. This value is the maximum number of bytes we read + * after reloading when we are decoding long offets. + */ +#define LONG_OFFSETS_MAX_EXTRA_BITS_32 \ + (ZSTD_WINDOWLOG_MAX_32 > STREAM_ACCUMULATOR_MIN_32 \ + ? ZSTD_WINDOWLOG_MAX_32 - STREAM_ACCUMULATOR_MIN_32 \ + : 0) + +static TARGET seq_t +FUNCTION(ZSTD_decodeSequence)(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) +{ + seq_t seq; + U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; + U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits; + U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits; + U32 const totalBits = llBits+mlBits+ofBits; + U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue; + U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue; + U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue; + + /* sequence */ + { size_t offset; + if (!ofBits) + offset = 0; + else { + ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); + ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); + assert(ofBits <= MaxOff); + if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) { + U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed); + offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); + BIT_reloadDStream(&seqState->DStream); + if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); + assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32); /* to avoid another reload */ + } else { + offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ + if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); + } + } + + if (ofBits <= 1) { + offset += (llBase==0); + if (offset) { + size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; + temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ + if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; + seqState->prevOffset[1] = seqState->prevOffset[0]; + seqState->prevOffset[0] = offset = temp; + } else { /* offset == 0 */ + offset = seqState->prevOffset[0]; + } + } else { + seqState->prevOffset[2] = seqState->prevOffset[1]; + seqState->prevOffset[1] = seqState->prevOffset[0]; + seqState->prevOffset[0] = offset; + } + seq.offset = offset; + } + + seq.matchLength = mlBase + + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/) : 0); /* <= 16 bits */ + if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) + BIT_reloadDStream(&seqState->DStream); + if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) + BIT_reloadDStream(&seqState->DStream); + /* Ensure there are enough bits to read the rest of data in 64-bit mode. */ + ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); + + seq.litLength = llBase + + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits/*>0*/) : 0); /* <= 16 bits */ + if (MEM_32bits()) + BIT_reloadDStream(&seqState->DStream); + + DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u", + (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset); + + /* ANS state update */ + FUNCTION(ZSTD_updateFseState)(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ + FUNCTION(ZSTD_updateFseState)(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ + if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ + FUNCTION(ZSTD_updateFseState)(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ + + return seq; +} + + + +HINT_INLINE seq_t +FUNCTION(ZSTD_decodeSequenceLong)(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) +{ + seq_t seq; + U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; + U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits; + U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits; + U32 const totalBits = llBits+mlBits+ofBits; + U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue; + U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue; + U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue; + + /* sequence */ + { size_t offset; + if (!ofBits) + offset = 0; + else { + ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); + ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); + assert(ofBits <= MaxOff); + if (MEM_32bits() && longOffsets) { + U32 const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN_32-1); + offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); + if (MEM_32bits() || extraBits) BIT_reloadDStream(&seqState->DStream); + if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); + } else { + offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ + if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); + } + } + + if (ofBits <= 1) { + offset += (llBase==0); + if (offset) { + size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; + temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ + if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; + seqState->prevOffset[1] = seqState->prevOffset[0]; + seqState->prevOffset[0] = offset = temp; + } else { + offset = seqState->prevOffset[0]; + } + } else { + seqState->prevOffset[2] = seqState->prevOffset[1]; + seqState->prevOffset[1] = seqState->prevOffset[0]; + seqState->prevOffset[0] = offset; + } + seq.offset = offset; + } + + seq.matchLength = mlBase + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ + if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) + BIT_reloadDStream(&seqState->DStream); + if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) + BIT_reloadDStream(&seqState->DStream); + /* Verify that there is enough bits to read the rest of the data in 64-bit mode. */ + ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); + + seq.litLength = llBase + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ + if (MEM_32bits()) + BIT_reloadDStream(&seqState->DStream); + + { size_t const pos = seqState->pos + seq.litLength; + const BYTE* const matchBase = (seq.offset > pos) ? seqState->dictEnd : seqState->prefixStart; + seq.match = matchBase + pos - seq.offset; /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted. + * No consequence though : no memory access will occur, overly large offset will be detected in ZSTD_execSequenceLong() */ + seqState->pos = pos + seq.matchLength; + } + + /* ANS state update */ + FUNCTION(ZSTD_updateFseState)(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ + FUNCTION(ZSTD_updateFseState)(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ + if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ + FUNCTION(ZSTD_updateFseState)(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ + + return seq; +} + + +static TARGET void +FUNCTION(ZSTD_initFseState)(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, const ZSTD_seqSymbol* dt) +{ + const void* ptr = dt; + const ZSTD_seqSymbol_header* const DTableH = (const ZSTD_seqSymbol_header*)ptr; + DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); + DEBUGLOG(6, "ZSTD_initFseState : val=%u using %u bits", + (U32)DStatePtr->state, DTableH->tableLog); + BIT_reloadDStream(bitD); + DStatePtr->table = dt + 1; +} + +static TARGET +size_t FUNCTION(ZSTD_decompressSequences)( + ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, + const ZSTD_longOffset_e isLongOffset) +{ + const BYTE* ip = (const BYTE*)seqStart; + const BYTE* const iend = ip + seqSize; + BYTE* const ostart = (BYTE* const)dst; + BYTE* const oend = ostart + maxDstSize; + BYTE* op = ostart; + const BYTE* litPtr = dctx->litPtr; + const BYTE* const litEnd = litPtr + dctx->litSize; + const BYTE* const base = (const BYTE*) (dctx->base); + const BYTE* const vBase = (const BYTE*) (dctx->vBase); + const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); + int nbSeq; + DEBUGLOG(5, "ZSTD_decompressSequences"); + + /* Build Decoding Tables */ + { size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); + DEBUGLOG(5, "ZSTD_decodeSeqHeaders: size=%u, nbSeq=%i", + (U32)seqHSize, nbSeq); + if (ZSTD_isError(seqHSize)) return seqHSize; + ip += seqHSize; + } + + /* Regen sequences */ + if (nbSeq) { + seqState_t seqState; + dctx->fseEntropy = 1; + { U32 i; for (i=0; ientropy.rep[i]; } + CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); + FUNCTION(ZSTD_initFseState)(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); + FUNCTION(ZSTD_initFseState)(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); + FUNCTION(ZSTD_initFseState)(&seqState.stateML, &seqState.DStream, dctx->MLTptr); + + for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) { + nbSeq--; + { seq_t const sequence = FUNCTION(ZSTD_decodeSequence)(&seqState, isLongOffset); + size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, base, vBase, dictEnd); + DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize); + if (ZSTD_isError(oneSeqSize)) return oneSeqSize; + op += oneSeqSize; + } } + + /* check if reached exact end */ + DEBUGLOG(5, "ZSTD_decompressSequences: after decode loop, remaining nbSeq : %i", nbSeq); + if (nbSeq) return ERROR(corruption_detected); + /* save reps for next block */ + { U32 i; for (i=0; ientropy.rep[i] = (U32)(seqState.prevOffset[i]); } + } + + /* last literal segment */ + { size_t const lastLLSize = litEnd - litPtr; + if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall); + memcpy(op, litPtr, lastLLSize); + op += lastLLSize; + } + + return op-ostart; +} + +static TARGET +size_t FUNCTION(ZSTD_decompressSequencesLong)( + ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, + const ZSTD_longOffset_e isLongOffset) +{ + const BYTE* ip = (const BYTE*)seqStart; + const BYTE* const iend = ip + seqSize; + BYTE* const ostart = (BYTE* const)dst; + BYTE* const oend = ostart + maxDstSize; + BYTE* op = ostart; + const BYTE* litPtr = dctx->litPtr; + const BYTE* const litEnd = litPtr + dctx->litSize; + const BYTE* const prefixStart = (const BYTE*) (dctx->base); + const BYTE* const dictStart = (const BYTE*) (dctx->vBase); + const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); + int nbSeq; + + /* Build Decoding Tables */ + { size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); + if (ZSTD_isError(seqHSize)) return seqHSize; + ip += seqHSize; + } + + /* Regen sequences */ + if (nbSeq) { +#define STORED_SEQS 4 +#define STOSEQ_MASK (STORED_SEQS-1) +#define ADVANCED_SEQS 4 + seq_t sequences[STORED_SEQS]; + int const seqAdvance = MIN(nbSeq, ADVANCED_SEQS); + seqState_t seqState; + int seqNb; + dctx->fseEntropy = 1; + { U32 i; for (i=0; ientropy.rep[i]; } + seqState.prefixStart = prefixStart; + seqState.pos = (size_t)(op-prefixStart); + seqState.dictEnd = dictEnd; + CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); + FUNCTION(ZSTD_initFseState)(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); + FUNCTION(ZSTD_initFseState)(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); + FUNCTION(ZSTD_initFseState)(&seqState.stateML, &seqState.DStream, dctx->MLTptr); + + /* prepare in advance */ + for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && (seqNbentropy.rep[i] = (U32)(seqState.prevOffset[i]); } +#undef STORED_SEQS +#undef STOSEQ_MASK +#undef ADVANCED_SEQS + } + + /* last literal segment */ + { size_t const lastLLSize = litEnd - litPtr; + if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall); + memcpy(op, litPtr, lastLLSize); + op += lastLLSize; + } + + return op-ostart; +} From b58f01537ea49d2f87c7b9e6273611736f13925e Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 14 Feb 2018 19:20:32 -0800 Subject: [PATCH 136/255] [compress] Support BMI2 --- lib/common/huf.h | 4 +- lib/compress/huf_compress.c | 162 +++++++++----------------- lib/compress/huf_compress_impl.h | 120 +++++++++++++++++++ lib/compress/zstd_compress.c | 132 +++++++-------------- lib/compress/zstd_compress_impl.h | 106 +++++++++++++++++ lib/compress/zstd_compress_internal.h | 1 + 6 files changed, 331 insertions(+), 194 deletions(-) create mode 100644 lib/compress/huf_compress_impl.h create mode 100644 lib/compress/zstd_compress_impl.h diff --git a/lib/common/huf.h b/lib/common/huf.h index 3b96ae8b7..cadee595d 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -223,7 +223,7 @@ typedef enum { * If it uses hufTable it does not modify hufTable or repeat. * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. * If preferRepeat then the old table will always be used if valid. */ -size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ +size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. @@ -279,7 +279,7 @@ size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, si * If it uses hufTable it does not modify hufTable or repeat. * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. * If preferRepeat then the old table will always be used if valid. */ -size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ +size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */ diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index cfc5a98bb..846fd4c62 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -46,6 +46,7 @@ #include /* memcpy, memset */ #include /* printf (debug) */ #include "bitstream.h" +#include "compiler.h" #define FSE_STATIC_LINKING_ONLY /* FSE_optimalTableLog_internal */ #include "fse.h" /* header compression */ #define HUF_STATIC_LINKING_ONLY @@ -433,117 +434,69 @@ static int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, uns return !bad; } -static void HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable) -{ - BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits); -} - size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); } -#define HUF_FLUSHBITS(s) BIT_flushBits(s) -#define HUF_FLUSHBITS_1(stream) \ - if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*2+7) HUF_FLUSHBITS(stream) +#define FUNCTION(fn) fn##_default +#define TARGET +#include "huf_compress_impl.h" +#undef TARGET +#undef FUNCTION -#define HUF_FLUSHBITS_2(stream) \ - if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*4+7) HUF_FLUSHBITS(stream) +#if DYNAMIC_BMI2 + +#define FUNCTION(fn) fn##_bmi2 +#define TARGET TARGET_ATTRIBUTE("bmi2") +#include "huf_compress_impl.h" +#undef TARGET +#undef FUNCTION + +#endif + +static size_t HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize, + const void* src, size_t srcSize, + const HUF_CElt* CTable, const int bmi2) +{ +#if DYNAMIC_BMI2 + if (bmi2) { + return HUF_compress1X_usingCTable_internal_bmi2(dst, dstSize, src, srcSize, CTable); + } +#endif + (void)bmi2; + return HUF_compress1X_usingCTable_internal_default(dst, dstSize, src, srcSize, CTable); +} + +static size_t HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize, + const void* src, size_t srcSize, + const HUF_CElt* CTable, const int bmi2) +{ +#if DYNAMIC_BMI2 + if (bmi2) { + return HUF_compress4X_usingCTable_internal_bmi2(dst, dstSize, src, srcSize, CTable); + } +#endif + (void)bmi2; + return HUF_compress4X_usingCTable_internal_default(dst, dstSize, src, srcSize, CTable); +} size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) { - const BYTE* ip = (const BYTE*) src; - BYTE* const ostart = (BYTE*)dst; - BYTE* const oend = ostart + dstSize; - BYTE* op = ostart; - size_t n; - BIT_CStream_t bitC; - - /* init */ - if (dstSize < 8) return 0; /* not enough space to compress */ - { size_t const initErr = BIT_initCStream(&bitC, op, oend-op); - if (HUF_isError(initErr)) return 0; } - - n = srcSize & ~3; /* join to mod 4 */ - switch (srcSize & 3) - { - case 3 : HUF_encodeSymbol(&bitC, ip[n+ 2], CTable); - HUF_FLUSHBITS_2(&bitC); - /* fall-through */ - case 2 : HUF_encodeSymbol(&bitC, ip[n+ 1], CTable); - HUF_FLUSHBITS_1(&bitC); - /* fall-through */ - case 1 : HUF_encodeSymbol(&bitC, ip[n+ 0], CTable); - HUF_FLUSHBITS(&bitC); - /* fall-through */ - case 0 : /* fall-through */ - default: break; - } - - for (; n>0; n-=4) { /* note : n&3==0 at this stage */ - HUF_encodeSymbol(&bitC, ip[n- 1], CTable); - HUF_FLUSHBITS_1(&bitC); - HUF_encodeSymbol(&bitC, ip[n- 2], CTable); - HUF_FLUSHBITS_2(&bitC); - HUF_encodeSymbol(&bitC, ip[n- 3], CTable); - HUF_FLUSHBITS_1(&bitC); - HUF_encodeSymbol(&bitC, ip[n- 4], CTable); - HUF_FLUSHBITS(&bitC); - } - - return BIT_closeCStream(&bitC); + return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0); } - size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) { - size_t const segmentSize = (srcSize+3)/4; /* first 3 segments */ - const BYTE* ip = (const BYTE*) src; - const BYTE* const iend = ip + srcSize; - BYTE* const ostart = (BYTE*) dst; - BYTE* const oend = ostart + dstSize; - BYTE* op = ostart; - - if (dstSize < 6 + 1 + 1 + 1 + 8) return 0; /* minimum space to compress successfully */ - if (srcSize < 12) return 0; /* no saving possible : too small input */ - op += 6; /* jumpTable */ - - { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, segmentSize, CTable) ); - if (cSize==0) return 0; - MEM_writeLE16(ostart, (U16)cSize); - op += cSize; - } - - ip += segmentSize; - { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, segmentSize, CTable) ); - if (cSize==0) return 0; - MEM_writeLE16(ostart+2, (U16)cSize); - op += cSize; - } - - ip += segmentSize; - { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, segmentSize, CTable) ); - if (cSize==0) return 0; - MEM_writeLE16(ostart+4, (U16)cSize); - op += cSize; - } - - ip += segmentSize; - { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, iend-ip, CTable) ); - if (cSize==0) return 0; - op += cSize; - } - - return op-ostart; + return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0); } - static size_t HUF_compressCTable_internal( BYTE* const ostart, BYTE* op, BYTE* const oend, const void* src, size_t srcSize, - unsigned singleStream, const HUF_CElt* CTable) + unsigned singleStream, const HUF_CElt* CTable, const int bmi2) { size_t const cSize = singleStream ? - HUF_compress1X_usingCTable(op, oend - op, src, srcSize, CTable) : - HUF_compress4X_usingCTable(op, oend - op, src, srcSize, CTable); + HUF_compress1X_usingCTable_internal(op, oend - op, src, srcSize, CTable, bmi2) : + HUF_compress4X_usingCTable_internal(op, oend - op, src, srcSize, CTable, bmi2); if (HUF_isError(cSize)) { return cSize; } if (cSize==0) { return 0; } /* uncompressible */ op += cSize; @@ -560,7 +513,8 @@ static size_t HUF_compress_internal ( unsigned maxSymbolValue, unsigned huffLog, unsigned singleStream, void* workSpace, size_t wkspSize, - HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat) + HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat, + const int bmi2) { BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstSize; @@ -589,7 +543,7 @@ static size_t HUF_compress_internal ( /* Heuristic : If we don't need to check the validity of the old table use the old table for small inputs */ if (preferRepeat && repeat && *repeat == HUF_repeat_valid) { - return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable); + return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable, bmi2); } /* Scan input and build symbol stats */ @@ -604,7 +558,7 @@ static size_t HUF_compress_internal ( } /* Heuristic : use existing table for small inputs */ if (preferRepeat && repeat && *repeat != HUF_repeat_none) { - return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable); + return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable, bmi2); } /* Build Huffman Tree */ @@ -622,7 +576,7 @@ static size_t HUF_compress_internal ( size_t const oldSize = HUF_estimateCompressedSize(oldHufTable, count, maxSymbolValue); size_t const newSize = HUF_estimateCompressedSize(CTable, count, maxSymbolValue); if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) { - return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable); + return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable, bmi2); } } /* Use the new table */ @@ -631,7 +585,7 @@ static size_t HUF_compress_internal ( if (repeat) { *repeat = HUF_repeat_none; } if (oldHufTable) { memcpy(oldHufTable, CTable, CTableSize); } /* Save the new table */ } - return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, CTable); + return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, CTable, bmi2); } @@ -640,16 +594,16 @@ size_t HUF_compress1X_wksp (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, NULL, NULL, 0); + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, NULL, NULL, 0, /* bmi2 */ 0); } size_t HUF_compress1X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat) + HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, hufTable, repeat, preferRepeat); + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, hufTable, repeat, preferRepeat, bmi2); } size_t HUF_compress1X (void* dst, size_t dstSize, @@ -665,16 +619,16 @@ size_t HUF_compress4X_wksp (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, NULL, NULL, 0); + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, NULL, NULL, 0, /* bmi2 */ 0); } size_t HUF_compress4X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat) + HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, hufTable, repeat, preferRepeat); + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, hufTable, repeat, preferRepeat, bmi2); } size_t HUF_compress2 (void* dst, size_t dstSize, diff --git a/lib/compress/huf_compress_impl.h b/lib/compress/huf_compress_impl.h new file mode 100644 index 000000000..fa045399f --- /dev/null +++ b/lib/compress/huf_compress_impl.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018-present, 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. + */ + +#ifndef FUNCTION +# error "FUNCTION(name) must be defined" +#endif + +#ifndef TARGET +# error "TARGET must be defined" +#endif + + +static void FUNCTION(HUF_encodeSymbol)(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable) +{ + BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits); +} + +#define HUF_FLUSHBITS(s) BIT_flushBits(s) + +#define HUF_FLUSHBITS_1(stream) \ + if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*2+7) HUF_FLUSHBITS(stream) + +#define HUF_FLUSHBITS_2(stream) \ + if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*4+7) HUF_FLUSHBITS(stream) + +static TARGET +size_t FUNCTION(HUF_compress1X_usingCTable_internal)(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) +{ + const BYTE* ip = (const BYTE*) src; + BYTE* const ostart = (BYTE*)dst; + BYTE* const oend = ostart + dstSize; + BYTE* op = ostart; + size_t n; + BIT_CStream_t bitC; + + /* init */ + if (dstSize < 8) return 0; /* not enough space to compress */ + { size_t const initErr = BIT_initCStream(&bitC, op, oend-op); + if (HUF_isError(initErr)) return 0; } + + n = srcSize & ~3; /* join to mod 4 */ + switch (srcSize & 3) + { + case 3 : FUNCTION(HUF_encodeSymbol)(&bitC, ip[n+ 2], CTable); + HUF_FLUSHBITS_2(&bitC); + /* fall-through */ + case 2 : FUNCTION(HUF_encodeSymbol)(&bitC, ip[n+ 1], CTable); + HUF_FLUSHBITS_1(&bitC); + /* fall-through */ + case 1 : FUNCTION(HUF_encodeSymbol)(&bitC, ip[n+ 0], CTable); + HUF_FLUSHBITS(&bitC); + /* fall-through */ + case 0 : /* fall-through */ + default: break; + } + + for (; n>0; n-=4) { /* note : n&3==0 at this stage */ + FUNCTION(HUF_encodeSymbol)(&bitC, ip[n- 1], CTable); + HUF_FLUSHBITS_1(&bitC); + FUNCTION(HUF_encodeSymbol)(&bitC, ip[n- 2], CTable); + HUF_FLUSHBITS_2(&bitC); + FUNCTION(HUF_encodeSymbol)(&bitC, ip[n- 3], CTable); + HUF_FLUSHBITS_1(&bitC); + FUNCTION(HUF_encodeSymbol)(&bitC, ip[n- 4], CTable); + HUF_FLUSHBITS(&bitC); + } + + return BIT_closeCStream(&bitC); +} + + +static TARGET +size_t FUNCTION(HUF_compress4X_usingCTable_internal)(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) +{ + size_t const segmentSize = (srcSize+3)/4; /* first 3 segments */ + const BYTE* ip = (const BYTE*) src; + const BYTE* const iend = ip + srcSize; + BYTE* const ostart = (BYTE*) dst; + BYTE* const oend = ostart + dstSize; + BYTE* op = ostart; + + if (dstSize < 6 + 1 + 1 + 1 + 8) return 0; /* minimum space to compress successfully */ + if (srcSize < 12) return 0; /* no saving possible : too small input */ + op += 6; /* jumpTable */ + + { CHECK_V_F(cSize, FUNCTION(HUF_compress1X_usingCTable_internal)(op, oend-op, ip, segmentSize, CTable) ); + if (cSize==0) return 0; + MEM_writeLE16(ostart, (U16)cSize); + op += cSize; + } + + ip += segmentSize; + { CHECK_V_F(cSize, FUNCTION(HUF_compress1X_usingCTable_internal)(op, oend-op, ip, segmentSize, CTable) ); + if (cSize==0) return 0; + MEM_writeLE16(ostart+2, (U16)cSize); + op += cSize; + } + + ip += segmentSize; + { CHECK_V_F(cSize, FUNCTION(HUF_compress1X_usingCTable_internal)(op, oend-op, ip, segmentSize, CTable) ); + if (cSize==0) return 0; + MEM_writeLE16(ostart+4, (U16)cSize); + op += cSize; + } + + ip += segmentSize; + { CHECK_V_F(cSize, FUNCTION(HUF_compress1X_usingCTable_internal)(op, oend-op, ip, iend-ip, CTable) ); + if (cSize==0) return 0; + op += cSize; + } + + return op-ostart; +} diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 8c6393ed5..80b1d3662 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -21,6 +21,7 @@ * Dependencies ***************************************/ #include /* memset */ +#include "cpu.h" #include "mem.h" #define FSE_STATIC_LINKING_ONLY /* FSE_encodeSymbol */ #include "fse.h" @@ -73,6 +74,7 @@ ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem) cctx->customMem = customMem; cctx->requestedParams.compressionLevel = ZSTD_CLEVEL_DEFAULT; cctx->requestedParams.fParams.contentSizeFlag = 1; + cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); return cctx; } } @@ -96,6 +98,7 @@ ZSTD_CCtx* ZSTD_initStaticCCtx(void *workspace, size_t workspaceSize) void* const ptr = cctx->blockState.nextCBlock + 1; cctx->entropyWorkspace = (U32*)ptr; } + cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); return cctx; } @@ -1401,7 +1404,7 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy, ZSTD_strategy strategy, void* dst, size_t dstCapacity, const void* src, size_t srcSize, - U32* workspace) + U32* workspace, const int bmi2) { size_t const minGain = ZSTD_minGain(srcSize); size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB); @@ -1426,9 +1429,9 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy, int const preferRepeat = strategy < ZSTD_lazy ? srcSize <= 1024 : 0; if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1; cLitSize = singleStream ? HUF_compress1X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, - workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat) + workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat, bmi2) : HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, - workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat); + workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat, bmi2); if (repeat != HUF_repeat_none) { /* reused the existing table */ hType = set_repeat; @@ -1583,99 +1586,52 @@ size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, } } -MEM_STATIC +#define FUNCTION(fn) fn##_default +#define TARGET +#include "zstd_compress_impl.h" +#undef TARGET +#undef FUNCTION + +#if DYNAMIC_BMI2 + +#define FUNCTION(fn) fn##_bmi2 +#define TARGET TARGET_ATTRIBUTE("bmi2") +#include "zstd_compress_impl.h" +#undef TARGET +#undef FUNCTION + +#endif + size_t ZSTD_encodeSequences( void* dst, size_t dstCapacity, FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, - seqDef const* sequences, size_t nbSeq, int longOffsets) + seqDef const* sequences, size_t nbSeq, int longOffsets, int bmi2) { - BIT_CStream_t blockStream; - FSE_CState_t stateMatchLength; - FSE_CState_t stateOffsetBits; - FSE_CState_t stateLitLength; - - CHECK_E(BIT_initCStream(&blockStream, dst, dstCapacity), dstSize_tooSmall); /* not enough space remaining */ - - /* first symbols */ - FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]); - FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]); - FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]); - BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]); - if (MEM_32bits()) BIT_flushBits(&blockStream); - BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]); - if (MEM_32bits()) BIT_flushBits(&blockStream); - if (longOffsets) { - U32 const ofBits = ofCodeTable[nbSeq-1]; - int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); - if (extraBits) { - BIT_addBits(&blockStream, sequences[nbSeq-1].offset, extraBits); - BIT_flushBits(&blockStream); - } - BIT_addBits(&blockStream, sequences[nbSeq-1].offset >> extraBits, - ofBits - extraBits); - } else { - BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]); - } - BIT_flushBits(&blockStream); - - { size_t n; - for (n=nbSeq-2 ; n= 64-7-(LLFSELog+MLFSELog+OffFSELog))) - BIT_flushBits(&blockStream); /* (7)*/ - BIT_addBits(&blockStream, sequences[n].litLength, llBits); - if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream); - BIT_addBits(&blockStream, sequences[n].matchLength, mlBits); - if (MEM_32bits() || (ofBits+mlBits+llBits > 56)) BIT_flushBits(&blockStream); - if (longOffsets) { - int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); - if (extraBits) { - BIT_addBits(&blockStream, sequences[n].offset, extraBits); - BIT_flushBits(&blockStream); /* (7)*/ - } - BIT_addBits(&blockStream, sequences[n].offset >> extraBits, - ofBits - extraBits); /* 31 */ - } else { - BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ - } - BIT_flushBits(&blockStream); /* (7)*/ - } } - - DEBUGLOG(6, "ZSTD_encodeSequences: flushing ML state with %u bits", stateMatchLength.stateLog); - FSE_flushCState(&blockStream, &stateMatchLength); - DEBUGLOG(6, "ZSTD_encodeSequences: flushing Off state with %u bits", stateOffsetBits.stateLog); - FSE_flushCState(&blockStream, &stateOffsetBits); - DEBUGLOG(6, "ZSTD_encodeSequences: flushing LL state with %u bits", stateLitLength.stateLog); - FSE_flushCState(&blockStream, &stateLitLength); - - { size_t const streamSize = BIT_closeCStream(&blockStream); - if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */ - return streamSize; +#if DYNAMIC_BMI2 + if (bmi2) { + return ZSTD_encodeSequences_bmi2(dst, dstCapacity, + CTable_MatchLength, mlCodeTable, + CTable_OffsetBits, ofCodeTable, + CTable_LitLength, llCodeTable, + sequences, nbSeq, longOffsets); } +#endif + (void)bmi2; + return ZSTD_encodeSequences_default(dst, dstCapacity, + CTable_MatchLength, mlCodeTable, + CTable_OffsetBits, ofCodeTable, + CTable_LitLength, llCodeTable, + sequences, nbSeq, longOffsets); } MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, ZSTD_entropyCTables_t const* prevEntropy, ZSTD_entropyCTables_t* nextEntropy, ZSTD_compressionParameters const* cParams, - void* dst, size_t dstCapacity, U32* workspace) + void* dst, size_t dstCapacity, U32* workspace, + const int bmi2) { const int longOffsets = cParams->windowLog > STREAM_ACCUMULATOR_MIN; U32 count[MaxSeq+1]; @@ -1700,7 +1656,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, size_t const litSize = seqStorePtr->lit - literals; size_t const cSize = ZSTD_compressLiterals(prevEntropy, nextEntropy, cParams->strategy, op, dstCapacity, literals, litSize, - workspace); + workspace, bmi2); if (ZSTD_isError(cSize)) return cSize; assert(cSize <= dstCapacity); @@ -1780,7 +1736,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, CTable_OffsetBits, ofCodeTable, CTable_LitLength, llCodeTable, sequences, nbSeq, - longOffsets); + longOffsets, bmi2); if (ZSTD_isError(bitstreamSize)) return bitstreamSize; op += bitstreamSize; } @@ -1793,11 +1749,11 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, ZSTD_entropyCTables_t* nextEntropy, ZSTD_compressionParameters const* cParams, void* dst, size_t dstCapacity, - size_t srcSize, U32* workspace) + size_t srcSize, U32* workspace, int bmi2) { size_t const cSize = ZSTD_compressSequences_internal( seqStorePtr, prevEntropy, nextEntropy, cParams, dst, dstCapacity, - workspace); + workspace, bmi2); /* If the srcSize <= dstCapacity, then there is enough space to write a * raw uncompressed block. Since we ran out of space, the block must not * be compressible, so fall back to a raw uncompressed block. @@ -1898,7 +1854,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, } } /* encode sequences and literals */ - { size_t const cSize = ZSTD_compressSequences(&zc->seqStore, &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize, zc->entropyWorkspace); + { size_t const cSize = ZSTD_compressSequences(&zc->seqStore, &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize, zc->entropyWorkspace, zc->bmi2); if (ZSTD_isError(cSize) || cSize == 0) return cSize; /* confirm repcodes and entropy tables */ { ZSTD_compressedBlockState_t* const tmp = zc->blockState.prevCBlock; diff --git a/lib/compress/zstd_compress_impl.h b/lib/compress/zstd_compress_impl.h new file mode 100644 index 000000000..e6a30b1d0 --- /dev/null +++ b/lib/compress/zstd_compress_impl.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018-present, 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. + */ + +#ifndef FUNCTION +# error "FUNCTION(name) must be defined" +#endif + +#ifndef TARGET +# error "TARGET must be defined" +#endif + + +MEM_STATIC TARGET +size_t FUNCTION(ZSTD_encodeSequences)( + void* dst, size_t dstCapacity, + FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, + FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, + FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, + seqDef const* sequences, size_t nbSeq, int longOffsets) +{ + BIT_CStream_t blockStream; + FSE_CState_t stateMatchLength; + FSE_CState_t stateOffsetBits; + FSE_CState_t stateLitLength; + + CHECK_E(BIT_initCStream(&blockStream, dst, dstCapacity), dstSize_tooSmall); /* not enough space remaining */ + + /* first symbols */ + FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]); + FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]); + FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]); + BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]); + if (MEM_32bits()) BIT_flushBits(&blockStream); + BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]); + if (MEM_32bits()) BIT_flushBits(&blockStream); + if (longOffsets) { + U32 const ofBits = ofCodeTable[nbSeq-1]; + int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); + if (extraBits) { + BIT_addBits(&blockStream, sequences[nbSeq-1].offset, extraBits); + BIT_flushBits(&blockStream); + } + BIT_addBits(&blockStream, sequences[nbSeq-1].offset >> extraBits, + ofBits - extraBits); + } else { + BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]); + } + BIT_flushBits(&blockStream); + + { size_t n; + for (n=nbSeq-2 ; n= 64-7-(LLFSELog+MLFSELog+OffFSELog))) + BIT_flushBits(&blockStream); /* (7)*/ + BIT_addBits(&blockStream, sequences[n].litLength, llBits); + if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream); + BIT_addBits(&blockStream, sequences[n].matchLength, mlBits); + if (MEM_32bits() || (ofBits+mlBits+llBits > 56)) BIT_flushBits(&blockStream); + if (longOffsets) { + int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); + if (extraBits) { + BIT_addBits(&blockStream, sequences[n].offset, extraBits); + BIT_flushBits(&blockStream); /* (7)*/ + } + BIT_addBits(&blockStream, sequences[n].offset >> extraBits, + ofBits - extraBits); /* 31 */ + } else { + BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ + } + BIT_flushBits(&blockStream); /* (7)*/ + } } + + DEBUGLOG(6, "ZSTD_encodeSequences: flushing ML state with %u bits", stateMatchLength.stateLog); + FSE_flushCState(&blockStream, &stateMatchLength); + DEBUGLOG(6, "ZSTD_encodeSequences: flushing Off state with %u bits", stateOffsetBits.stateLog); + FSE_flushCState(&blockStream, &stateOffsetBits); + DEBUGLOG(6, "ZSTD_encodeSequences: flushing LL state with %u bits", stateLitLength.stateLog); + FSE_flushCState(&blockStream, &stateLitLength); + + { size_t const streamSize = BIT_closeCStream(&blockStream); + if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */ + return streamSize; + } +} diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 9da2cbfeb..ba4d79c66 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -170,6 +170,7 @@ struct ZSTD_CCtx_params_s { struct ZSTD_CCtx_s { ZSTD_compressionStage_e stage; int cParamsChanged; /* == 1 if cParams(except wlog) or compression level are changed in requestedParams. Triggers transmission of new params to ZSTDMT (if available) then reset to 0. */ + int bmi2; ZSTD_CCtx_params requestedParams; ZSTD_CCtx_params appliedParams; U32 dictID; From 70163bf0d3b3f1fb9cc56eab316041311622c68b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Feb 2018 12:54:49 -0800 Subject: [PATCH 137/255] added clarification comments in zstd_errors.h answering some points in #1018 --- lib/common/zstd_errors.h | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/common/zstd_errors.h b/lib/common/zstd_errors.h index 4bcb7769f..2af61f3bd 100644 --- a/lib/common/zstd_errors.h +++ b/lib/common/zstd_errors.h @@ -35,12 +35,20 @@ extern "C" { # define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBILITY #endif -/*-**************************************** - * error codes list - * note : this API is still considered unstable - * and shall not be used with a dynamic library. - * only static linking is allowed - ******************************************/ +/*-********************************************* + * Error codes list + *-********************************************* + * Error codes _values_ are pinned down since v1.3.1 only. + * Therefore, don't rely on values if you may link to any version < v1.3.1. + * + * Only values < 100 are considered stable. + * + * note 1 : this API shall be used with static linking only. + * dynamic linking is not yet officially supported. + * note 2 : Prefer relying on the enum than on its value whenever possible + * This is the only supported way to use the error list < v1.3.1 + * note 3 : ZSTD_isError() is always correct, whatever the library version. + **********************************************/ typedef enum { ZSTD_error_no_error = 0, ZSTD_error_GENERIC = 1, @@ -63,7 +71,7 @@ typedef enum { ZSTD_error_memory_allocation = 64, ZSTD_error_dstSize_tooSmall = 70, ZSTD_error_srcSize_wrong = 72, - /* following error codes are not stable and may be removed or changed in a future version */ + /* following error codes are __NOT STABLE__, they can be removed or changed in future versions */ ZSTD_error_frameIndex_tooLarge = 100, ZSTD_error_seekableIO = 102, ZSTD_error_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */ From 6e128d35343896389ec8df153a1d97d05cdca6ee Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 20 Feb 2018 14:12:11 -0800 Subject: [PATCH 138/255] [BMI2] Add comments to the bmi2 variable in the contexts --- lib/compress/zstd_compress_internal.h | 2 +- lib/decompress/zstd_decompress.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index ba4d79c66..67c1da6ef 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -170,7 +170,7 @@ struct ZSTD_CCtx_params_s { struct ZSTD_CCtx_s { ZSTD_compressionStage_e stage; int cParamsChanged; /* == 1 if cParams(except wlog) or compression level are changed in requestedParams. Triggers transmission of new params to ZSTDMT (if available) then reset to 0. */ - int bmi2; + int bmi2; /* == 1 if the CPU supports BMI2 and 0 otherwise. CPU support is determined dynamically once per context lifetime. */ ZSTD_CCtx_params requestedParams; ZSTD_CCtx_params appliedParams; U32 dictID; diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 97ef6eb90..50e34e358 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -132,7 +132,7 @@ struct ZSTD_DCtx_s size_t litSize; size_t rleSize; size_t staticSize; - int bmi2; + int bmi2; /* == 1 if the CPU supports BMI2 and 0 otherwise. CPU support is determined dynamically once per context lifetime. */ /* streaming */ ZSTD_DDict* ddictLocal; From d3364aa39e2243638de9ead10699ccd35e0926eb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Feb 2018 14:48:09 -0800 Subject: [PATCH 139/255] improve benchmark measurement for small inputs by invoking time() once per batch, instead of once per compression / decompression. Batch is dynamically resized so that each round lasts approximately 1 second. Also : increases time accuracy to nanosecond --- programs/bench.c | 71 ++++++++++++++++++++++++++++-------------------- programs/util.h | 12 +++++++- 2 files changed, 53 insertions(+), 30 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index bf3dcb47d..e67c886b3 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -22,7 +22,7 @@ * Compiler Warnings ****************************************/ #ifdef _MSC_VER -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif @@ -34,6 +34,7 @@ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen */ +#include /* assert */ #include "mem.h" #define ZSTD_STATIC_LINKING_ONLY @@ -51,8 +52,9 @@ # define ZSTD_GIT_COMMIT_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_GIT_COMMIT) #endif -#define TIMELOOP_MICROSEC 1*1000000ULL /* 1 second */ -#define ACTIVEPERIOD_MICROSEC 70*1000000ULL /* 70 seconds */ +#define TIMELOOP_MICROSEC (1*1000000ULL) /* 1 second */ +#define TIMELOOP_NANOSEC (1*1000000000ULL) /* 1 second */ +#define ACTIVEPERIOD_MICROSEC (70*TIMELOOP_MICROSEC) /* 70 seconds */ #define COOLPERIOD_SEC 10 #define KB *(1 <<10) @@ -264,7 +266,9 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, { U64 fastestC = (U64)(-1LL), fastestD = (U64)(-1LL); U64 const crcOrig = g_decodeOnly ? 0 : XXH64(srcBuffer, srcSize, 0); UTIL_time_t coolTime; - U64 const maxTime = (g_nbSeconds * TIMELOOP_MICROSEC) + 1; + U64 const maxTime = (g_nbSeconds * TIMELOOP_NANOSEC) + 1; + U32 nbDecodeLoops = (U32)((100 MB) / (srcSize+1)) + 1; /* initial conservative speed estimate */ + U32 nbCompressionLoops = (U32)((2 MB) / (srcSize+1)) + 1; /* initial conservative speed estimate */ U64 totalCTime=0, totalDTime=0; U32 cCompleted=g_decodeOnly, dCompleted=0; # define NB_MARKS 4 @@ -283,18 +287,16 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, } if (!g_decodeOnly) { - UTIL_time_t clockStart; /* Compression */ DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->\r", marks[markNb], displayName, (U32)srcSize); if (!cCompleted) memset(compressedBuffer, 0xE5, maxCompressedSize); /* warm up and erase result buffer */ - UTIL_sleepMilli(1); /* give processor time to other processes */ + UTIL_sleepMilli(5); /* give processor time to other processes */ UTIL_waitForNextTick(); - clockStart = UTIL_getTime(); if (!cCompleted) { /* still some time to do compression tests */ - U64 const clockLoop = g_nbSeconds ? TIMELOOP_MICROSEC : 1; U32 nbLoops = 0; + UTIL_time_t const clockStart = UTIL_getTime(); ZSTD_CCtx_setParameter(ctx, ZSTD_p_nbWorkers, g_nbWorkers); ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionLevel, cLevel); ZSTD_CCtx_setParameter(ctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag); @@ -314,7 +316,9 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, ZSTD_CCtx_setParameter(ctx, ZSTD_p_targetLength, comprParams->targetLength); ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionStrategy, comprParams->strategy); ZSTD_CCtx_loadDictionary(ctx, dictBuffer, dictBufferSize); - do { + + if (!g_nbSeconds) nbCompressionLoops=1; + for (nbLoops=0; nbLoops 0) { + if (loopDuration < fastestC * nbCompressionLoops) + fastestC = loopDuration / nbCompressionLoops; + nbCompressionLoops = (1000000000 / fastestC) + 1; + } else { + assert(nbCompressionLoops < 40000000); /* avoid overflow */ + nbCompressionLoops *= 100; } - nbLoops++; - } while (UTIL_clockSpanMicro(clockStart) < clockLoop); - { U64 const loopDuration = UTIL_clockSpanMicro(clockStart); - if (loopDuration < fastestC*nbLoops) - fastestC = loopDuration / nbLoops; totalCTime += loopDuration; cCompleted = (totalCTime >= maxTime); /* end compression tests */ } } @@ -358,7 +366,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, ratio = (double)srcSize / (double)cSize; markNb = (markNb+1) % NB_MARKS; { int const ratioAccuracy = (ratio < 10.) ? 3 : 2; - double const compressionSpeed = (double)srcSize / fastestC; + double const compressionSpeed = ((double)srcSize / fastestC) * 1000; int const cSpeedAccuracy = (compressionSpeed < 10.) ? 2 : 1; DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.*f),%6.*f MB/s\r", marks[markNb], displayName, (U32)srcSize, (U32)cSize, @@ -376,16 +384,16 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, /* Decompression */ if (!dCompleted) memset(resultBuffer, 0xD6, srcSize); /* warm result buffer */ - UTIL_sleepMilli(1); /* give processor time to other processes */ + UTIL_sleepMilli(5); /* give processor time to other processes */ UTIL_waitForNextTick(); if (!dCompleted) { - U64 clockLoop = g_nbSeconds ? TIMELOOP_MICROSEC : 1; U32 nbLoops = 0; ZSTD_DDict* const ddict = ZSTD_createDDict(dictBuffer, dictBufferSize); UTIL_time_t const clockStart = UTIL_getTime(); if (!ddict) EXM_THROW(2, "ZSTD_createDDict() allocation failure"); - do { + if (!g_nbSeconds) nbDecodeLoops = 1; + for (nbLoops=0; nbLoops < nbDecodeLoops; nbLoops++) { U32 blockNb; for (blockNb=0; blockNb 0) { + if (loopDuration < fastestD * nbDecodeLoops) + fastestD = loopDuration / nbDecodeLoops; + nbDecodeLoops = (1000000000/*1sec*/ / fastestD) + 1; + } else { + assert(nbDecodeLoops < 40000000); /* avoid overflow */ + nbDecodeLoops *= 100; + } totalDTime += loopDuration; dCompleted = (totalDTime >= maxTime); } } markNb = (markNb+1) % NB_MARKS; { int const ratioAccuracy = (ratio < 10.) ? 3 : 2; - double const compressionSpeed = (double)srcSize / fastestC; + double const compressionSpeed = ((double)srcSize / fastestC) * 1000; int const cSpeedAccuracy = (compressionSpeed < 10.) ? 2 : 1; - double const decompressionSpeed = (double)srcSize / fastestD; + double const decompressionSpeed = ((double)srcSize / fastestD) * 1000; DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.*f),%6.*f MB/s ,%6.1f MB/s \r", marks[markNb], displayName, (U32)srcSize, (U32)cSize, ratioAccuracy, ratio, @@ -461,8 +473,8 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, } /* for (testNb = 1; testNb <= (g_nbSeconds + !g_nbSeconds); testNb++) */ if (g_displayLevel == 1) { /* hidden display mode -q, used by python speed benchmark */ - double cSpeed = (double)srcSize / fastestC; - double dSpeed = (double)srcSize / fastestD; + double cSpeed = ((double)srcSize / fastestC) * 1000; + double dSpeed = ((double)srcSize / fastestD) * 1000; if (g_additionalParam) DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s (param=%d)\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName, g_additionalParam); else @@ -634,7 +646,8 @@ static void BMK_benchFileTable(const char* const * const fileNamesTable, unsigne } -static void BMK_syntheticTest(int cLevel, int cLevelLast, double compressibility, const ZSTD_compressionParameters* compressionParams) +static void BMK_syntheticTest(int cLevel, int cLevelLast, double compressibility, + const ZSTD_compressionParameters* compressionParams) { char name[20] = {0}; size_t benchedSize = 10000000; diff --git a/programs/util.h b/programs/util.h index 4f22236a9..3e6974579 100644 --- a/programs/util.h +++ b/programs/util.h @@ -142,7 +142,9 @@ static int g_utilDisplayLevel; } return 1000000000ULL*(clockEnd.QuadPart - clockStart.QuadPart)/ticksPerSecond.QuadPart; } + #elif defined(__APPLE__) && defined(__MACH__) + #include #define UTIL_TIME_INITIALIZER 0 typedef U64 UTIL_time_t; @@ -167,7 +169,9 @@ static int g_utilDisplayLevel; } return ((clockEnd - clockStart) * (U64)rate.numer) / ((U64)rate.denom); } + #elif (PLATFORM_POSIX_VERSION >= 200112L) && (defined __UCLIBC__ || ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) || __GLIBC__ > 2)) + #define UTIL_TIME_INITIALIZER { 0, 0 } typedef struct timespec UTIL_freq_t; typedef struct timespec UTIL_time_t; @@ -217,12 +221,18 @@ static int g_utilDisplayLevel; #define SEC_TO_MICRO 1000000 /* returns time span in microseconds */ -UTIL_STATIC U64 UTIL_clockSpanMicro( UTIL_time_t clockStart ) +UTIL_STATIC U64 UTIL_clockSpanMicro(UTIL_time_t clockStart ) { UTIL_time_t const clockEnd = UTIL_getTime(); return UTIL_getSpanTimeMicro(clockStart, clockEnd); } +/* returns time span in microseconds */ +UTIL_STATIC U64 UTIL_clockSpanNano(UTIL_time_t clockStart ) +{ + UTIL_time_t const clockEnd = UTIL_getTime(); + return UTIL_getSpanTimeNano(clockStart, clockEnd); +} UTIL_STATIC void UTIL_waitForNextTick(void) { From 3538a535bfe1532fb33bfa85bb2b70868506d682 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Feb 2018 15:33:56 -0800 Subject: [PATCH 140/255] use TIMELOOP_NANOSEC as suggested by @terrelln --- programs/bench.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index e67c886b3..5997cfbca 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -352,7 +352,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, if (loopDuration > 0) { if (loopDuration < fastestC * nbCompressionLoops) fastestC = loopDuration / nbCompressionLoops; - nbCompressionLoops = (1000000000 / fastestC) + 1; + nbCompressionLoops = (TIMELOOP_NANOSEC / fastestC) + 1; } else { assert(nbCompressionLoops < 40000000); /* avoid overflow */ nbCompressionLoops *= 100; @@ -411,7 +411,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, if (loopDuration > 0) { if (loopDuration < fastestD * nbDecodeLoops) fastestD = loopDuration / nbDecodeLoops; - nbDecodeLoops = (1000000000/*1sec*/ / fastestD) + 1; + nbDecodeLoops = (TIMELOOP_NANOSEC / fastestD) + 1; } else { assert(nbDecodeLoops < 40000000); /* avoid overflow */ nbDecodeLoops *= 100; From 25d00d10fc479c516206373571f75cc213f5415a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Feb 2018 16:52:28 -0800 Subject: [PATCH 141/255] fixed minor conversion warning --- programs/bench.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 5997cfbca..b4cd619c4 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -352,7 +352,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, if (loopDuration > 0) { if (loopDuration < fastestC * nbCompressionLoops) fastestC = loopDuration / nbCompressionLoops; - nbCompressionLoops = (TIMELOOP_NANOSEC / fastestC) + 1; + nbCompressionLoops = (U32)(TIMELOOP_NANOSEC / fastestC) + 1; } else { assert(nbCompressionLoops < 40000000); /* avoid overflow */ nbCompressionLoops *= 100; @@ -411,7 +411,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, if (loopDuration > 0) { if (loopDuration < fastestD * nbDecodeLoops) fastestD = loopDuration / nbDecodeLoops; - nbDecodeLoops = (TIMELOOP_NANOSEC / fastestD) + 1; + nbDecodeLoops = (U32)(TIMELOOP_NANOSEC / fastestD) + 1; } else { assert(nbDecodeLoops < 40000000); /* avoid overflow */ nbDecodeLoops *= 100; From 364ce19463c42a651438fd63ded8c0d50a7d6f98 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 21 Feb 2018 07:30:26 -0800 Subject: [PATCH 142/255] update fullbench measurement methodology to use less calls to time(), like bench.c. also upgraded accuracy to nanosecond. --- tests/fullbench.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/tests/fullbench.c b/tests/fullbench.c index 199f4966b..6a4084940 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -413,33 +413,48 @@ static size_t benchMem(const void* src, size_t srcSize, U32 benchNb) break; /* test functions */ - /* by convention, test functions can be added > 100 */ + /* convention: test functions have ID > 100 */ default : ; } - { size_t i; for (i=0; i %s !! \n", benchName, ZSTD_getErrorName(benchResult)); exit(1); } - } - { U64 const clockSpanMicro = UTIL_clockSpanMicro(clockStart); - double const averageTime = (double)clockSpanMicro / TIME_SEC_MICROSEC / nbRounds; - if (averageTime < bestTime) bestTime = averageTime; - DISPLAY("%2i- %-30.30s : %7.1f MB/s (%9u)\r", loopNb, benchName, (double)srcSize / (1 MB) / bestTime, (U32)benchResult); + if (ZSTD_isError(benchResult)) { + DISPLAY("ERROR ! %s() => %s !! \n", benchName, ZSTD_getErrorName(benchResult)); + exit(1); + } } + { U64 const clockSpanNano = UTIL_clockSpanNano(clockStart); + double const averageTime = (double)clockSpanNano / TIME_SEC_NANOSEC / nbRounds; + if (clockSpanNano > 0) { + if (averageTime < bestTime) bestTime = averageTime; + assert(bestTime > (1./2000000000)); + nbRounds = (U32)(1. / bestTime); /* aim for 1 sec */ + DISPLAY("%2i- %-30.30s : %7.1f MB/s (%9u)\r", + loopNb, benchName, + (double)srcSize / (1 MB) / bestTime, + (U32)benchResult); + } else { + assert(nbRounds < 40000000); /* avoid overflow */ + nbRounds *= 100; + } } } } DISPLAY("%2u\n", benchNb); @@ -573,7 +588,7 @@ int main(int argc, const char** argv) for(i=1; i Date: Wed, 21 Feb 2018 11:34:49 -0800 Subject: [PATCH 143/255] fixed huf_compress workspace size --- lib/compress/huf_compress.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 846fd4c62..74d8fb9d7 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -610,10 +610,13 @@ size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog) { - unsigned workSpace[1024]; + unsigned workSpace[HUF_WORKSPACE_SIZE_U32]; return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); } +/* HUF_compress4X_repeat(): + * compress input using 4 streams. + * provide workspace to generate compression tables */ size_t HUF_compress4X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, @@ -622,6 +625,9 @@ size_t HUF_compress4X_wksp (void* dst, size_t dstSize, return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, NULL, NULL, 0, /* bmi2 */ 0); } +/* HUF_compress4X_repeat(): + * compress input using 4 streams. + * re-use an existing huffman compression table */ size_t HUF_compress4X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, @@ -635,7 +641,7 @@ size_t HUF_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog) { - unsigned workSpace[1024]; + unsigned workSpace[HUF_WORKSPACE_SIZE_U32]; return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); } From 6e481504eea25774029da26ebd15bb9adb58a429 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 21 Feb 2018 11:42:23 -0800 Subject: [PATCH 144/255] fullbench includes assert.h as it is missing for Windows --- tests/fullbench.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fullbench.c b/tests/fullbench.c index 6a4084940..6abdd4da0 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -15,6 +15,7 @@ #include "util.h" /* Compiler options, UTIL_GetFileSize */ #include /* malloc */ #include /* fprintf, fopen, ftello64 */ +#include /* assert */ #include "mem.h" /* U32 */ #ifndef ZSTD_DLL_IMPORT From 0fd4df6ed320f73b00ad2c3b2e53c8609dabd925 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 21 Feb 2018 23:52:45 -0800 Subject: [PATCH 145/255] Implemented BMI2 functions directly within huf_decompress.c This makes it easier to edit for maintenance and evolutions (I plan to experiment modifications in huffman decompression functions). The methology followed seems broadly applicable to other BMI2 modules. Performance was tracked rigorously at each step, there is no noticeable loss (nor win) of performance compared to `#include` version. Note however that 4X decoder variants tend to be extremely sensitive to code alignment. This source code resulted in pretty good performance for gcc 7.2 and 7.3, but future changes (even in other parts of the code) might trigger the issue again. --- lib/decompress/huf_decompress.c | 405 +++++++++++++++++++++++++-- lib/decompress/huf_decompress_impl.h | 365 ------------------------ 2 files changed, 376 insertions(+), 394 deletions(-) delete mode 100644 lib/decompress/huf_decompress_impl.h diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 13f852bc9..0bd1f42f8 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -49,18 +49,19 @@ ****************************************************************/ #define HUF_isError ERR_isError #define HUF_STATIC_ASSERT(c) { enum { HUF_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ +#define CHECK_F(f) { size_t const err_ = (f); if (HUF_isError(err_)) return err_; } /* ************************************************************** * Byte alignment for workSpace management ****************************************************************/ -#define HUF_ALIGN(x, a) HUF_ALIGN_MASK((x), (a) - 1) +#define HUF_ALIGN(x, a) HUF_ALIGN_MASK((x), (a) - 1) #define HUF_ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) + /*-***************************/ /* generic DTableDesc */ /*-***************************/ - typedef struct { BYTE maxTableLog; BYTE tableType; BYTE tableLog; BYTE reserved; } DTableDesc; static DTableDesc HUF_getDTableDesc(const HUF_DTable* table) @@ -74,7 +75,6 @@ static DTableDesc HUF_getDTableDesc(const HUF_DTable* table) /*-***************************/ /* single-symbol decoding */ /*-***************************/ - typedef struct { BYTE byte; BYTE nbBits; } HUF_DEltX2; /* single-symbol decoding */ size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize) @@ -146,44 +146,390 @@ size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize) typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX4; /* double-symbols decoding */ -#define FUNCTION(fn) fn##_default -#define TARGET -#include "huf_decompress_impl.h" -#undef TARGET -#undef FUNCTION +static BYTE HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, const U32 dtLog) +{ + size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ + BYTE const c = dt[val].byte; + BIT_skipBits(Dstream, dt[val].nbBits); + return c; +} -#if DYNAMIC_BMI2 +#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) \ + *ptr++ = HUF_decodeSymbolX2(DStreamPtr, dt, dtLog) -#define FUNCTION(fn) fn##_bmi2 -#define TARGET TARGET_ATTRIBUTE("bmi2") -#include "huf_decompress_impl.h" -#undef TARGET -#undef FUNCTION +#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \ + if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ + HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) + +#define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \ + if (MEM_64bits()) \ + HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) + +HINT_INLINE size_t +HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog) +{ + BYTE* const pStart = p; + + /* up to 4 symbols at a time */ + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd-4)) { + HUF_DECODE_SYMBOLX2_2(p, bitDPtr); + HUF_DECODE_SYMBOLX2_1(p, bitDPtr); + HUF_DECODE_SYMBOLX2_2(p, bitDPtr); + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + } + + /* closer to the end */ + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd)) + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + + /* no more data to retrieve from bitstream, hence no need to reload */ + while (p < pEnd) + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + + return pEnd-pStart; +} + +FORCE_INLINE_TEMPLATE size_t +HUF_decompress1X2_usingDTable_internal_body( + void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + const HUF_DTable* DTable) +{ + BYTE* op = (BYTE*)dst; + BYTE* const oend = op + dstSize; + const void* dtPtr = DTable + 1; + const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; + BIT_DStream_t bitD; + DTableDesc const dtd = HUF_getDTableDesc(DTable); + U32 const dtLog = dtd.tableLog; + + CHECK_F( BIT_initDStream(&bitD, cSrc, cSrcSize) ); + + HUF_decodeStreamX2(op, &bitD, oend, dt, dtLog); + + if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected); + + return dstSize; +} + +FORCE_INLINE_TEMPLATE size_t +HUF_decompress4X2_usingDTable_internal_body( + void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + const HUF_DTable* DTable) +{ + /* Check */ + if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ + + { const BYTE* const istart = (const BYTE*) cSrc; + BYTE* const ostart = (BYTE*) dst; + BYTE* const oend = ostart + dstSize; + const void* const dtPtr = DTable + 1; + const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; + + /* Init */ + BIT_DStream_t bitD1; + BIT_DStream_t bitD2; + BIT_DStream_t bitD3; + BIT_DStream_t bitD4; + size_t const length1 = MEM_readLE16(istart); + size_t const length2 = MEM_readLE16(istart+2); + size_t const length3 = MEM_readLE16(istart+4); + size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); + const BYTE* const istart1 = istart + 6; /* jumpTable */ + const BYTE* const istart2 = istart1 + length1; + const BYTE* const istart3 = istart2 + length2; + const BYTE* const istart4 = istart3 + length3; + const size_t segmentSize = (dstSize+3) / 4; + BYTE* const opStart2 = ostart + segmentSize; + BYTE* const opStart3 = opStart2 + segmentSize; + BYTE* const opStart4 = opStart3 + segmentSize; + BYTE* op1 = ostart; + BYTE* op2 = opStart2; + BYTE* op3 = opStart3; + BYTE* op4 = opStart4; + U32 endSignal = BIT_DStream_unfinished; + DTableDesc const dtd = HUF_getDTableDesc(DTable); + U32 const dtLog = dtd.tableLog; + + if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ + CHECK_F( BIT_initDStream(&bitD1, istart1, length1) ); + CHECK_F( BIT_initDStream(&bitD2, istart2, length2) ); + CHECK_F( BIT_initDStream(&bitD3, istart3, length3) ); + CHECK_F( BIT_initDStream(&bitD4, istart4, length4) ); + + /* up to 16 symbols per loop (4 symbols per stream) in 64-bit mode */ + endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); + while ( (endSignal==BIT_DStream_unfinished) && (op4<(oend-3)) ) { + HUF_DECODE_SYMBOLX2_2(op1, &bitD1); + HUF_DECODE_SYMBOLX2_2(op2, &bitD2); + HUF_DECODE_SYMBOLX2_2(op3, &bitD3); + HUF_DECODE_SYMBOLX2_2(op4, &bitD4); + HUF_DECODE_SYMBOLX2_1(op1, &bitD1); + HUF_DECODE_SYMBOLX2_1(op2, &bitD2); + HUF_DECODE_SYMBOLX2_1(op3, &bitD3); + HUF_DECODE_SYMBOLX2_1(op4, &bitD4); + HUF_DECODE_SYMBOLX2_2(op1, &bitD1); + HUF_DECODE_SYMBOLX2_2(op2, &bitD2); + HUF_DECODE_SYMBOLX2_2(op3, &bitD3); + HUF_DECODE_SYMBOLX2_2(op4, &bitD4); + HUF_DECODE_SYMBOLX2_0(op1, &bitD1); + HUF_DECODE_SYMBOLX2_0(op2, &bitD2); + HUF_DECODE_SYMBOLX2_0(op3, &bitD3); + HUF_DECODE_SYMBOLX2_0(op4, &bitD4); + BIT_reloadDStream(&bitD1); + BIT_reloadDStream(&bitD2); + BIT_reloadDStream(&bitD3); + BIT_reloadDStream(&bitD4); + } + + /* check corruption */ + /* note : should not be necessary : op# advance in lock step, and we control op4. + * but curiously, binary generated by gcc 7.2 with -mbmi2 runs faster when they are present */ + if (op1 > opStart2) return ERROR(corruption_detected); + if (op2 > opStart3) return ERROR(corruption_detected); + if (op3 > opStart4) return ERROR(corruption_detected); + /* note : op4 supposed already verified within main loop */ + + /* finish bitStreams one by one */ + HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog); + HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog); + HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog); + HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog); + + /* check */ + { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); + if (!endCheck) return ERROR(corruption_detected); } + + /* decoded size */ + return dstSize; + } +} + + +static U32 HUF_decodeSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) +{ + size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ + memcpy(op, dt+val, 2); + BIT_skipBits(DStream, dt[val].nbBits); + return dt[val].length; +} + +FORCE_INLINE_TEMPLATE U32 +HUF_decodeLastSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) +{ + size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ + memcpy(op, dt+val, 1); + if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits); + else { + if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) { + BIT_skipBits(DStream, dt[val].nbBits); + if (DStream->bitsConsumed > (sizeof(DStream->bitContainer)*8)) + /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */ + DStream->bitsConsumed = (sizeof(DStream->bitContainer)*8); + } } + return 1; +} + +#define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) \ + ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) + +#define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \ + if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ + ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) + +#define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \ + if (MEM_64bits()) \ + ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) + +HINT_INLINE size_t +HUF_decodeStreamX4(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, + const HUF_DEltX4* const dt, const U32 dtLog) +{ + BYTE* const pStart = p; + + /* up to 8 symbols at a time */ + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) { + HUF_DECODE_SYMBOLX4_2(p, bitDPtr); + HUF_DECODE_SYMBOLX4_1(p, bitDPtr); + HUF_DECODE_SYMBOLX4_2(p, bitDPtr); + HUF_DECODE_SYMBOLX4_0(p, bitDPtr); + } + + /* closer to end : up to 2 symbols at a time */ + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2)) + HUF_DECODE_SYMBOLX4_0(p, bitDPtr); + + while (p <= pEnd-2) + HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ + + if (p < pEnd) + p += HUF_decodeLastSymbolX4(p, bitDPtr, dt, dtLog); + + return p-pStart; +} + +FORCE_INLINE_TEMPLATE size_t +HUF_decompress1X4_usingDTable_internal_body( + void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + const HUF_DTable* DTable) +{ + BIT_DStream_t bitD; + + /* Init */ + CHECK_F( BIT_initDStream(&bitD, cSrc, cSrcSize) ); + + /* decode */ + { BYTE* const ostart = (BYTE*) dst; + BYTE* const oend = ostart + dstSize; + const void* const dtPtr = DTable+1; /* force compiler to not use strict-aliasing */ + const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr; + DTableDesc const dtd = HUF_getDTableDesc(DTable); + HUF_decodeStreamX4(ostart, &bitD, oend, dt, dtd.tableLog); + } + + /* check */ + if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected); + + /* decoded size */ + return dstSize; +} + + +FORCE_INLINE_TEMPLATE size_t +HUF_decompress4X4_usingDTable_internal_body( + void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + const HUF_DTable* DTable) +{ + if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ + + { const BYTE* const istart = (const BYTE*) cSrc; + BYTE* const ostart = (BYTE*) dst; + BYTE* const oend = ostart + dstSize; + const void* const dtPtr = DTable+1; + const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr; + + /* Init */ + BIT_DStream_t bitD1; + BIT_DStream_t bitD2; + BIT_DStream_t bitD3; + BIT_DStream_t bitD4; + size_t const length1 = MEM_readLE16(istart); + size_t const length2 = MEM_readLE16(istart+2); + size_t const length3 = MEM_readLE16(istart+4); + size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); + const BYTE* const istart1 = istart + 6; /* jumpTable */ + const BYTE* const istart2 = istart1 + length1; + const BYTE* const istart3 = istart2 + length2; + const BYTE* const istart4 = istart3 + length3; + size_t const segmentSize = (dstSize+3) / 4; + BYTE* const opStart2 = ostart + segmentSize; + BYTE* const opStart3 = opStart2 + segmentSize; + BYTE* const opStart4 = opStart3 + segmentSize; + BYTE* op1 = ostart; + BYTE* op2 = opStart2; + BYTE* op3 = opStart3; + BYTE* op4 = opStart4; + U32 endSignal; + DTableDesc const dtd = HUF_getDTableDesc(DTable); + U32 const dtLog = dtd.tableLog; + + if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ + CHECK_F( BIT_initDStream(&bitD1, istart1, length1) ); + CHECK_F( BIT_initDStream(&bitD2, istart2, length2) ); + CHECK_F( BIT_initDStream(&bitD3, istart3, length3) ); + CHECK_F( BIT_initDStream(&bitD4, istart4, length4) ); + + /* 16-32 symbols per loop (4-8 symbols per stream) */ + endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); + for ( ; (endSignal==BIT_DStream_unfinished) & (op4<(oend-(sizeof(bitD4.bitContainer)-1))) ; ) { + HUF_DECODE_SYMBOLX4_2(op1, &bitD1); + HUF_DECODE_SYMBOLX4_2(op2, &bitD2); + HUF_DECODE_SYMBOLX4_2(op3, &bitD3); + HUF_DECODE_SYMBOLX4_2(op4, &bitD4); + HUF_DECODE_SYMBOLX4_1(op1, &bitD1); + HUF_DECODE_SYMBOLX4_1(op2, &bitD2); + HUF_DECODE_SYMBOLX4_1(op3, &bitD3); + HUF_DECODE_SYMBOLX4_1(op4, &bitD4); + HUF_DECODE_SYMBOLX4_2(op1, &bitD1); + HUF_DECODE_SYMBOLX4_2(op2, &bitD2); + HUF_DECODE_SYMBOLX4_2(op3, &bitD3); + HUF_DECODE_SYMBOLX4_2(op4, &bitD4); + HUF_DECODE_SYMBOLX4_0(op1, &bitD1); + HUF_DECODE_SYMBOLX4_0(op2, &bitD2); + HUF_DECODE_SYMBOLX4_0(op3, &bitD3); + HUF_DECODE_SYMBOLX4_0(op4, &bitD4); + + endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); + } + + /* check corruption */ + if (op1 > opStart2) return ERROR(corruption_detected); + if (op2 > opStart3) return ERROR(corruption_detected); + if (op3 > opStart4) return ERROR(corruption_detected); + /* note : op4 already verified within main loop */ + + /* finish bitStreams one by one */ + HUF_decodeStreamX4(op1, &bitD1, opStart2, dt, dtLog); + HUF_decodeStreamX4(op2, &bitD2, opStart3, dt, dtLog); + HUF_decodeStreamX4(op3, &bitD3, opStart4, dt, dtLog); + HUF_decodeStreamX4(op4, &bitD4, oend, dt, dtLog); + + /* check */ + { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); + if (!endCheck) return ERROR(corruption_detected); } + + /* decoded size */ + return dstSize; + } +} -#endif typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); #if DYNAMIC_BMI2 -#define X(fn) \ - static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ - { \ - if (bmi2) { \ - return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \ - } \ - return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ + +#define X(fn) \ + \ + static size_t fn##_default( \ + void* dst, size_t dstSize, \ + const void* cSrc, size_t cSrcSize, \ + const HUF_DTable* DTable) \ + { \ + return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ + } \ + \ + static TARGET_ATTRIBUTE("bmi2") size_t fn##_bmi2( \ + void* dst, size_t dstSize, \ + const void* cSrc, size_t cSrcSize, \ + const HUF_DTable* DTable) \ + { \ + return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ + } \ + \ + static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ + size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + { \ + if (bmi2) { \ + return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \ + } \ + return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ } + #else -#define X(fn) \ - static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ - { \ - (void)bmi2; \ - return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ + +#define X(fn) \ + static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ + size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + { \ + (void)bmi2; \ + return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ } + #endif X(HUF_decompress1X2_usingDTable_internal) @@ -193,6 +539,7 @@ X(HUF_decompress4X4_usingDTable_internal) #undef X + size_t HUF_decompress1X2_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, diff --git a/lib/decompress/huf_decompress_impl.h b/lib/decompress/huf_decompress_impl.h deleted file mode 100644 index 25068cb35..000000000 --- a/lib/decompress/huf_decompress_impl.h +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright (c) 2018-present, 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. - */ - -#ifndef FUNCTION -# error "FUNCTION(name) must be defined" -#endif - -#ifndef TARGET -# error "TARGET must be defined" -#endif - - -static TARGET BYTE FUNCTION(HUF_decodeSymbolX2)(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, const U32 dtLog) -{ - size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ - BYTE const c = dt[val].byte; - BIT_skipBits(Dstream, dt[val].nbBits); - return c; -} - -#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) \ - *ptr++ = FUNCTION(HUF_decodeSymbolX2)(DStreamPtr, dt, dtLog) - -#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \ - if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ - HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) - -#define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \ - if (MEM_64bits()) \ - HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) - -HINT_INLINE TARGET size_t FUNCTION(HUF_decodeStreamX2)(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog) -{ - BYTE* const pStart = p; - - /* up to 4 symbols at a time */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd-4)) { - HUF_DECODE_SYMBOLX2_2(p, bitDPtr); - HUF_DECODE_SYMBOLX2_1(p, bitDPtr); - HUF_DECODE_SYMBOLX2_2(p, bitDPtr); - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); - } - - /* closer to the end */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd)) - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); - - /* no more data to retrieve from bitstream, hence no need to reload */ - while (p < pEnd) - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); - - return pEnd-pStart; -} - -static TARGET size_t FUNCTION(HUF_decompress1X2_usingDTable_internal)( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - BYTE* op = (BYTE*)dst; - BYTE* const oend = op + dstSize; - const void* dtPtr = DTable + 1; - const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; - BIT_DStream_t bitD; - DTableDesc const dtd = HUF_getDTableDesc(DTable); - U32 const dtLog = dtd.tableLog; - - { size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); - if (HUF_isError(errorCode)) return errorCode; } - - FUNCTION(HUF_decodeStreamX2)(op, &bitD, oend, dt, dtLog); - - /* check */ - if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected); - - return dstSize; -} - -static TARGET size_t -FUNCTION(HUF_decompress4X2_usingDTable_internal)( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - /* Check */ - if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ - - { const BYTE* const istart = (const BYTE*) cSrc; - BYTE* const ostart = (BYTE*) dst; - BYTE* const oend = ostart + dstSize; - const void* const dtPtr = DTable + 1; - const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; - - /* Init */ - BIT_DStream_t bitD1; - BIT_DStream_t bitD2; - BIT_DStream_t bitD3; - BIT_DStream_t bitD4; - size_t const length1 = MEM_readLE16(istart); - size_t const length2 = MEM_readLE16(istart+2); - size_t const length3 = MEM_readLE16(istart+4); - size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); - const BYTE* const istart1 = istart + 6; /* jumpTable */ - const BYTE* const istart2 = istart1 + length1; - const BYTE* const istart3 = istart2 + length2; - const BYTE* const istart4 = istart3 + length3; - const size_t segmentSize = (dstSize+3) / 4; - BYTE* const opStart2 = ostart + segmentSize; - BYTE* const opStart3 = opStart2 + segmentSize; - BYTE* const opStart4 = opStart3 + segmentSize; - BYTE* op1 = ostart; - BYTE* op2 = opStart2; - BYTE* op3 = opStart3; - BYTE* op4 = opStart4; - U32 endSignal; - DTableDesc const dtd = HUF_getDTableDesc(DTable); - U32 const dtLog = dtd.tableLog; - - if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ - { size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); - if (HUF_isError(errorCode)) return errorCode; } - - /* 16-32 symbols per loop (4-8 symbols per stream) */ - endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); - for ( ; (endSignal==BIT_DStream_unfinished) && (op4<(oend-7)) ; ) { - HUF_DECODE_SYMBOLX2_2(op1, &bitD1); - HUF_DECODE_SYMBOLX2_2(op2, &bitD2); - HUF_DECODE_SYMBOLX2_2(op3, &bitD3); - HUF_DECODE_SYMBOLX2_2(op4, &bitD4); - HUF_DECODE_SYMBOLX2_1(op1, &bitD1); - HUF_DECODE_SYMBOLX2_1(op2, &bitD2); - HUF_DECODE_SYMBOLX2_1(op3, &bitD3); - HUF_DECODE_SYMBOLX2_1(op4, &bitD4); - HUF_DECODE_SYMBOLX2_2(op1, &bitD1); - HUF_DECODE_SYMBOLX2_2(op2, &bitD2); - HUF_DECODE_SYMBOLX2_2(op3, &bitD3); - HUF_DECODE_SYMBOLX2_2(op4, &bitD4); - HUF_DECODE_SYMBOLX2_0(op1, &bitD1); - HUF_DECODE_SYMBOLX2_0(op2, &bitD2); - HUF_DECODE_SYMBOLX2_0(op3, &bitD3); - HUF_DECODE_SYMBOLX2_0(op4, &bitD4); - endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); - } - - /* check corruption */ - if (op1 > opStart2) return ERROR(corruption_detected); - if (op2 > opStart3) return ERROR(corruption_detected); - if (op3 > opStart4) return ERROR(corruption_detected); - /* note : op4 supposed already verified within main loop */ - - /* finish bitStreams one by one */ - FUNCTION(HUF_decodeStreamX2)(op1, &bitD1, opStart2, dt, dtLog); - FUNCTION(HUF_decodeStreamX2)(op2, &bitD2, opStart3, dt, dtLog); - FUNCTION(HUF_decodeStreamX2)(op3, &bitD3, opStart4, dt, dtLog); - FUNCTION(HUF_decodeStreamX2)(op4, &bitD4, oend, dt, dtLog); - - /* check */ - endSignal = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); - if (!endSignal) return ERROR(corruption_detected); - - /* decoded size */ - return dstSize; - } -} - -#undef HUF_DECODE_SYMBOLX2_0 -#undef HUF_DECODE_SYMBOLX2_1 -#undef HUF_DECODE_SYMBOLX2_2 - -static TARGET U32 FUNCTION(HUF_decodeSymbolX4)(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) -{ - size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ - memcpy(op, dt+val, 2); - BIT_skipBits(DStream, dt[val].nbBits); - return dt[val].length; -} - -static TARGET U32 FUNCTION(HUF_decodeLastSymbolX4)(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) -{ - size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ - memcpy(op, dt+val, 1); - if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits); - else { - if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) { - BIT_skipBits(DStream, dt[val].nbBits); - if (DStream->bitsConsumed > (sizeof(DStream->bitContainer)*8)) - /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */ - DStream->bitsConsumed = (sizeof(DStream->bitContainer)*8); - } } - return 1; -} - - -#define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) \ - ptr += FUNCTION(HUF_decodeSymbolX4)(ptr, DStreamPtr, dt, dtLog) - -#define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \ - if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ - ptr += FUNCTION(HUF_decodeSymbolX4)(ptr, DStreamPtr, dt, dtLog) - -#define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \ - if (MEM_64bits()) \ - ptr += FUNCTION(HUF_decodeSymbolX4)(ptr, DStreamPtr, dt, dtLog) - -HINT_INLINE TARGET size_t FUNCTION(HUF_decodeStreamX4)(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, const HUF_DEltX4* const dt, const U32 dtLog) -{ - BYTE* const pStart = p; - - /* up to 8 symbols at a time */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) { - HUF_DECODE_SYMBOLX4_2(p, bitDPtr); - HUF_DECODE_SYMBOLX4_1(p, bitDPtr); - HUF_DECODE_SYMBOLX4_2(p, bitDPtr); - HUF_DECODE_SYMBOLX4_0(p, bitDPtr); - } - - /* closer to end : up to 2 symbols at a time */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2)) - HUF_DECODE_SYMBOLX4_0(p, bitDPtr); - - while (p <= pEnd-2) - HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ - - if (p < pEnd) - p += FUNCTION(HUF_decodeLastSymbolX4)(p, bitDPtr, dt, dtLog); - - return p-pStart; -} - -static TARGET size_t FUNCTION(HUF_decompress1X4_usingDTable_internal)( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - BIT_DStream_t bitD; - - /* Init */ - { size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); - if (HUF_isError(errorCode)) return errorCode; - } - - /* decode */ - { BYTE* const ostart = (BYTE*) dst; - BYTE* const oend = ostart + dstSize; - const void* const dtPtr = DTable+1; /* force compiler to not use strict-aliasing */ - const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr; - DTableDesc const dtd = HUF_getDTableDesc(DTable); - FUNCTION(HUF_decodeStreamX4)(ostart, &bitD, oend, dt, dtd.tableLog); - } - - /* check */ - if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected); - - /* decoded size */ - return dstSize; -} - -static TARGET size_t FUNCTION(HUF_decompress4X4_usingDTable_internal)( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ - - { const BYTE* const istart = (const BYTE*) cSrc; - BYTE* const ostart = (BYTE*) dst; - BYTE* const oend = ostart + dstSize; - const void* const dtPtr = DTable+1; - const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr; - - /* Init */ - BIT_DStream_t bitD1; - BIT_DStream_t bitD2; - BIT_DStream_t bitD3; - BIT_DStream_t bitD4; - size_t const length1 = MEM_readLE16(istart); - size_t const length2 = MEM_readLE16(istart+2); - size_t const length3 = MEM_readLE16(istart+4); - size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); - const BYTE* const istart1 = istart + 6; /* jumpTable */ - const BYTE* const istart2 = istart1 + length1; - const BYTE* const istart3 = istart2 + length2; - const BYTE* const istart4 = istart3 + length3; - size_t const segmentSize = (dstSize+3) / 4; - BYTE* const opStart2 = ostart + segmentSize; - BYTE* const opStart3 = opStart2 + segmentSize; - BYTE* const opStart4 = opStart3 + segmentSize; - BYTE* op1 = ostart; - BYTE* op2 = opStart2; - BYTE* op3 = opStart3; - BYTE* op4 = opStart4; - U32 endSignal; - DTableDesc const dtd = HUF_getDTableDesc(DTable); - U32 const dtLog = dtd.tableLog; - - if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ - { size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); - if (HUF_isError(errorCode)) return errorCode; } - { size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); - if (HUF_isError(errorCode)) return errorCode; } - - /* 16-32 symbols per loop (4-8 symbols per stream) */ - endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); - for ( ; (endSignal==BIT_DStream_unfinished) & (op4<(oend-(sizeof(bitD4.bitContainer)-1))) ; ) { - HUF_DECODE_SYMBOLX4_2(op1, &bitD1); - HUF_DECODE_SYMBOLX4_2(op2, &bitD2); - HUF_DECODE_SYMBOLX4_2(op3, &bitD3); - HUF_DECODE_SYMBOLX4_2(op4, &bitD4); - HUF_DECODE_SYMBOLX4_1(op1, &bitD1); - HUF_DECODE_SYMBOLX4_1(op2, &bitD2); - HUF_DECODE_SYMBOLX4_1(op3, &bitD3); - HUF_DECODE_SYMBOLX4_1(op4, &bitD4); - HUF_DECODE_SYMBOLX4_2(op1, &bitD1); - HUF_DECODE_SYMBOLX4_2(op2, &bitD2); - HUF_DECODE_SYMBOLX4_2(op3, &bitD3); - HUF_DECODE_SYMBOLX4_2(op4, &bitD4); - HUF_DECODE_SYMBOLX4_0(op1, &bitD1); - HUF_DECODE_SYMBOLX4_0(op2, &bitD2); - HUF_DECODE_SYMBOLX4_0(op3, &bitD3); - HUF_DECODE_SYMBOLX4_0(op4, &bitD4); - - endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); - } - - /* check corruption */ - if (op1 > opStart2) return ERROR(corruption_detected); - if (op2 > opStart3) return ERROR(corruption_detected); - if (op3 > opStart4) return ERROR(corruption_detected); - /* note : op4 already verified within main loop */ - - /* finish bitStreams one by one */ - FUNCTION(HUF_decodeStreamX4)(op1, &bitD1, opStart2, dt, dtLog); - FUNCTION(HUF_decodeStreamX4)(op2, &bitD2, opStart3, dt, dtLog); - FUNCTION(HUF_decodeStreamX4)(op3, &bitD3, opStart4, dt, dtLog); - FUNCTION(HUF_decodeStreamX4)(op4, &bitD4, oend, dt, dtLog); - - /* check */ - { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); - if (!endCheck) return ERROR(corruption_detected); } - - /* decoded size */ - return dstSize; - } -} - -#undef HUF_DECODE_SYMBOLX4_0 -#undef HUF_DECODE_SYMBOLX4_1 -#undef HUF_DECODE_SYMBOLX4_2 From af866b3a5854aa7a22853b8a2e545e2d61dfa6c3 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 20 Feb 2018 19:34:43 -0800 Subject: [PATCH 146/255] Split block compresser out of long range matcher * `ZSTD_ldm_generateSequences()` generates the LDM sequences and stores them in a table. It should work with any chunk size, but is currently only called one block at a time. * `ZSTD_ldm_blockCompress()` emits the pre-defined sequences, and instead of encoding the literals directly, it passes them to a secondary block compressor. The code to handle chunk sizes greater than the block size is currently commented out, since it is unused. The next PR will uncomment exercise this code. * During optimal parsing, ensure LDM `minMatchLength` is at least `targetLength`. Also don't emit repcode matches in the LDM block compressor. Enabling the LDM with the optimal parser now actually improves the compression ratio. * The compression ratio is very similar to before. It is very slightly different, because the repcode handling is slightly different. If I remove immediate repcode checking in both branches the compressed size is exactly the same. * The speed looks to be the same or better than before. Up Next (in a separate PR) -------------------------- Allow sequence generation to happen prior to compression, and produce more than a block worth of sequences. Expose some API for zstdmt to consume. This will test out some currently untested code in `ZSTD_ldm_blockCompress()`. --- lib/common/zstd_internal.h | 6 + lib/compress/zstd_compress.c | 51 ++- lib/compress/zstd_compress_internal.h | 1 + lib/compress/zstd_ldm.c | 537 +++++++++++++------------- lib/compress/zstd_ldm.h | 67 +++- 5 files changed, 342 insertions(+), 320 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 65c08a825..1c6841a32 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -213,6 +213,12 @@ MEM_STATIC void ZSTD_wildcopy_e(void* dst, const void* src, void* dstEnd) /* s /*-******************************************* * Private declarations *********************************************/ +typedef struct rawSeq_s { + U32 offset; + U32 litLength; + U32 matchLength; +} rawSeq; + typedef struct seqDef_s { U32 offset; U16 litLength; diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 80b1d3662..74a51c25e 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -718,12 +718,11 @@ size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params) size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t); size_t const matchStateSize = ZSTD_sizeof_matchState(¶ms->cParams, /* forCCtx */ 1); - size_t const ldmSpace = params->ldmParams.enableLdm ? - ZSTD_ldm_getTableSize(params->ldmParams.hashLog, - params->ldmParams.bucketSizeLog) : 0; + size_t const ldmSpace = ZSTD_ldm_getTableSize(params->ldmParams); + size_t const ldmSeqSpace = ZSTD_ldm_getMaxNbSeq(params->ldmParams, blockSize) * sizeof(rawSeq); size_t const neededSpace = entropySpace + blockStateSpace + tokenSpace + - matchStateSize + ldmSpace; + matchStateSize + ldmSpace + ldmSeqSpace; DEBUGLOG(5, "sizeof(ZSTD_CCtx) : %u", (U32)sizeof(ZSTD_CCtx)); DEBUGLOG(5, "estimate workSpace : %u", (U32)neededSpace); @@ -990,7 +989,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, if (params.ldmParams.enableLdm) { /* Adjust long distance matching parameters */ - ZSTD_ldm_adjustParameters(¶ms.ldmParams, params.cParams.windowLog); + ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams); assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog); assert(params.ldmParams.hashEveryLog < 32); zc->ldmState.hashPower = @@ -1005,17 +1004,19 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, size_t const buffOutSize = (zbuff==ZSTDb_buffered) ? ZSTD_compressBound(blockSize)+1 : 0; size_t const buffInSize = (zbuff==ZSTDb_buffered) ? windowSize + blockSize : 0; size_t const matchStateSize = ZSTD_sizeof_matchState(¶ms.cParams, /* forCCtx */ 1); + size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(params.ldmParams, blockSize); void* ptr; /* Check if workSpace is large enough, alloc a new one if needed */ { size_t const entropySpace = HUF_WORKSPACE_SIZE; size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t); size_t const bufferSpace = buffInSize + buffOutSize; - size_t const ldmSpace = params.ldmParams.enableLdm - ? ZSTD_ldm_getTableSize(params.ldmParams.hashLog, params.ldmParams.bucketSizeLog) - : 0; + size_t const ldmSpace = ZSTD_ldm_getTableSize(params.ldmParams); + size_t const ldmSeqSpace = maxNbLdmSeq * sizeof(rawSeq); + size_t const neededSpace = entropySpace + blockStateSpace + ldmSpace + - matchStateSize + tokenSpace + bufferSpace; + ldmSeqSpace + matchStateSize + tokenSpace + + bufferSpace; DEBUGLOG(4, "Need %uKB workspace, including %uKB for match state, and %uKB for buffers", (U32)(neededSpace>>10), (U32)(matchStateSize>>10), (U32)(bufferSpace>>10)); DEBUGLOG(4, "windowSize: %u - blockSize: %u", (U32)windowSize, (U32)blockSize); @@ -1070,7 +1071,10 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ zc->ldmState.hashTable = (ldmEntry_t*)ptr; ptr = zc->ldmState.hashTable + ldmHSize; + zc->ldmSequences = (rawSeq*)ptr; + ptr = zc->ldmSequences + maxNbLdmSeq; } + assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ ptr = ZSTD_reset_matchState(&zc->blockState.matchState, ptr, ¶ms.cParams, crp, /* forCCtx */ 1); @@ -1820,34 +1824,39 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { + ZSTD_matchState_t* const ms = &zc->blockState.matchState; DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)", - (U32)dstCapacity, zc->blockState.matchState.dictLimit, zc->blockState.matchState.nextToUpdate); + (U32)dstCapacity, ms->dictLimit, ms->nextToUpdate); if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) return 0; /* don't even attempt compression below a certain srcSize */ ZSTD_resetSeqStore(&(zc->seqStore)); /* limited update after a very long match */ - { const BYTE* const base = zc->blockState.matchState.base; + { const BYTE* const base = ms->base; const BYTE* const istart = (const BYTE*)src; const U32 current = (U32)(istart-base); - if (current > zc->blockState.matchState.nextToUpdate + 384) - zc->blockState.matchState.nextToUpdate = current - MIN(192, (U32)(current - zc->blockState.matchState.nextToUpdate - 384)); + if (current > ms->nextToUpdate + 384) + ms->nextToUpdate = current - MIN(192, (U32)(current - ms->nextToUpdate - 384)); } /* select and store sequences */ - { U32 const extDict = zc->blockState.matchState.lowLimit < zc->blockState.matchState.dictLimit; + { U32 const extDict = ms->lowLimit < ms->dictLimit; size_t lastLLSize; { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i]; } if (zc->appliedParams.ldmParams.enableLdm) { - typedef size_t (*ZSTD_ldmBlockCompressor)( - ldmState_t* ldms, ZSTD_matchState_t* ms, seqStore_t* seqStore, - U32 rep[ZSTD_REP_NUM], ZSTD_CCtx_params const* params, - void const* src, size_t srcSize); - ZSTD_ldmBlockCompressor const ldmBlockCompressor = extDict ? ZSTD_compressBlock_ldm_extDict : ZSTD_compressBlock_ldm; - lastLLSize = ldmBlockCompressor(&zc->ldmState, &zc->blockState.matchState, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams, src, srcSize); + size_t const nbSeq = + ZSTD_ldm_generateSequences(&zc->ldmState, zc->ldmSequences, + ms, &zc->appliedParams.ldmParams, + src, srcSize, extDict); + lastLLSize = + ZSTD_ldm_blockCompress(zc->ldmSequences, nbSeq, + ms, &zc->seqStore, + zc->blockState.nextCBlock->rep, + &zc->appliedParams.cParams, + src, srcSize, extDict); } else { /* not long range mode */ ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict); - lastLLSize = blockCompressor(&zc->blockState.matchState, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, src, srcSize); + lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, src, srcSize); } { const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize; ZSTD_storeLastLiterals(&zc->seqStore, lastLiterals, lastLLSize); diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 67c1da6ef..8a02f764e 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -186,6 +186,7 @@ struct ZSTD_CCtx_s { seqStore_t seqStore; /* sequences storage ptrs */ ldmState_t ldmState; /* long distance matching state */ + rawSeq* ldmSequences; /* Storage for the ldm output sequences */ ZSTD_blockState_t blockState; U32* entropyWorkspace; /* entropy workspace of HUF_WORKSPACE_SIZE bytes */ diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index cc71de2e0..c18d68c8a 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -28,8 +28,17 @@ size_t ZSTD_ldm_initializeParameters(ldmParams_t* params, U32 enableLdm) return 0; } -void ZSTD_ldm_adjustParameters(ldmParams_t* params, U32 windowLog) +void ZSTD_ldm_adjustParameters(ldmParams_t* params, + ZSTD_compressionParameters const* cParams) { + U32 const windowLog = cParams->windowLog; + if (cParams->strategy >= ZSTD_btopt) { + /* Get out of the way of the optimal parser */ + U32 const minMatch = MAX(cParams->targetLength, params->minMatchLength); + assert(minMatch >= ZSTD_LDM_MINMATCH_MIN); + assert(minMatch <= ZSTD_LDM_MINMATCH_MAX); + params->minMatchLength = minMatch; + } if (params->hashLog == 0) { params->hashLog = MAX(ZSTD_HASHLOG_MIN, windowLog - LDM_HASH_RLOG); assert(params->hashLog <= ZSTD_HASHLOG_MAX); @@ -41,12 +50,19 @@ void ZSTD_ldm_adjustParameters(ldmParams_t* params, U32 windowLog) params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog); } -size_t ZSTD_ldm_getTableSize(U32 hashLog, U32 bucketSizeLog) { - size_t const ldmHSize = ((size_t)1) << hashLog; - size_t const ldmBucketSizeLog = MIN(bucketSizeLog, hashLog); +size_t ZSTD_ldm_getTableSize(ldmParams_t params) +{ + size_t const ldmHSize = ((size_t)1) << params.hashLog; + size_t const ldmBucketSizeLog = MIN(params.bucketSizeLog, params.hashLog); size_t const ldmBucketSize = - ((size_t)1) << (hashLog - ldmBucketSizeLog); - return ldmBucketSize + (ldmHSize * (sizeof(ldmEntry_t))); + ((size_t)1) << (params.hashLog - ldmBucketSizeLog); + size_t const totalSize = ldmBucketSize + ldmHSize * sizeof(ldmEntry_t); + return params.enableLdm ? totalSize : 0; +} + +size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize) +{ + return params.enableLdm ? (maxChunkSize / params.minMatchLength) : 0; } /** ZSTD_ldm_getSmallHash() : @@ -278,50 +294,54 @@ static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor) } } -size_t ZSTD_compressBlock_ldm( - ldmState_t* ldmState, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_CCtx_params const* params, void const* src, size_t srcSize) - +size_t ZSTD_ldm_generateSequences( + ldmState_t* ldmState, rawSeq* sequences, ZSTD_matchState_t const* ms, + ldmParams_t const* params, void const* src, size_t srcSize, + int const extDict) { - ZSTD_compressionParameters const* cParams = ¶ms->cParams; - const ldmParams_t ldmParams = params->ldmParams; - const U64 hashPower = ldmState->hashPower; - const U32 hBits = ldmParams.hashLog - ldmParams.bucketSizeLog; - const U32 ldmBucketSize = ((U32)1 << ldmParams.bucketSizeLog); - const U32 ldmTagMask = ((U32)1 << ldmParams.hashEveryLog) - 1; - const BYTE* const base = ms->base; - const BYTE* const istart = (const BYTE*)src; - const BYTE* ip = istart; - const BYTE* anchor = istart; - const U32 lowestIndex = ms->dictLimit; - const BYTE* const lowest = base + lowestIndex; - const BYTE* const iend = istart + srcSize; - const BYTE* const ilimit = iend - MAX(ldmParams.minMatchLength, HASH_READ_SIZE); - - const ZSTD_blockCompressor blockCompressor = - ZSTD_selectBlockCompressor(cParams->strategy, 0); + rawSeq const* const sequencesStart = sequences; + /* LDM parameters */ + U32 const minMatchLength = params->minMatchLength; + U64 const hashPower = ldmState->hashPower; + U32 const hBits = params->hashLog - params->bucketSizeLog; + U32 const ldmBucketSize = 1U << params->bucketSizeLog; + U32 const hashEveryLog = params->hashEveryLog; + U32 const ldmTagMask = (1U << params->hashEveryLog) - 1; + /* Prefix and extDict parameters */ + U32 const dictLimit = ms->dictLimit; + U32 const lowestIndex = extDict ? ms->lowLimit : dictLimit; + BYTE const* const base = ms->base; + BYTE const* const dictBase = extDict ? ms->dictBase : NULL; + BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL; + BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL; + BYTE const* const lowPrefixPtr = base + dictLimit; + /* Input bounds */ + BYTE const* const istart = (BYTE const*)src; + BYTE const* const iend = istart + srcSize; + BYTE const* const ilimit = iend - MAX(minMatchLength, HASH_READ_SIZE); + /* Input positions */ + BYTE const* anchor = istart; + BYTE const* ip = istart; + /* Rolling hash */ + BYTE const* lastHashed = NULL; U64 rollingHash = 0; - const BYTE* lastHashed = NULL; - size_t i, lastLiterals; - /* Main Search Loop */ - while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ + while (ip <= ilimit) { size_t mLength; U32 const current = (U32)(ip - base); size_t forwardMatchLength = 0, backwardMatchLength = 0; ldmEntry_t* bestEntry = NULL; if (ip != istart) { rollingHash = ZSTD_ldm_updateHash(rollingHash, lastHashed[0], - lastHashed[ldmParams.minMatchLength], + lastHashed[minMatchLength], hashPower); } else { - rollingHash = ZSTD_ldm_getRollingHash(ip, ldmParams.minMatchLength); + rollingHash = ZSTD_ldm_getRollingHash(ip, minMatchLength); } lastHashed = ip; /* Do not insert and do not look for a match */ - if (ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashEveryLog) != - ldmTagMask) { + if (ZSTD_ldm_getTag(rollingHash, hBits, hashEveryLog) != ldmTagMask) { ip++; continue; } @@ -331,27 +351,49 @@ size_t ZSTD_compressBlock_ldm( ldmEntry_t* const bucket = ZSTD_ldm_getBucket(ldmState, ZSTD_ldm_getSmallHash(rollingHash, hBits), - ldmParams); + *params); ldmEntry_t* cur; size_t bestMatchLength = 0; U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits); for (cur = bucket; cur < bucket + ldmBucketSize; ++cur) { - const BYTE* const pMatch = cur->offset + base; size_t curForwardMatchLength, curBackwardMatchLength, curTotalMatchLength; if (cur->checksum != checksum || cur->offset <= lowestIndex) { continue; } + if (extDict) { + BYTE const* const curMatchBase = + cur->offset < dictLimit ? dictBase : base; + BYTE const* const pMatch = curMatchBase + cur->offset; + BYTE const* const matchEnd = + cur->offset < dictLimit ? dictEnd : iend; + BYTE const* const lowMatchPtr = + cur->offset < dictLimit ? dictStart : lowPrefixPtr; - curForwardMatchLength = ZSTD_count(ip, pMatch, iend); - if (curForwardMatchLength < ldmParams.minMatchLength) { - continue; + curForwardMatchLength = ZSTD_count_2segments( + ip, pMatch, iend, + matchEnd, lowPrefixPtr); + if (curForwardMatchLength < minMatchLength) { + continue; + } + curBackwardMatchLength = + ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch, + lowMatchPtr); + curTotalMatchLength = curForwardMatchLength + + curBackwardMatchLength; + } else { /* !extDict */ + BYTE const* const pMatch = base + cur->offset; + curForwardMatchLength = ZSTD_count(ip, pMatch, iend); + if (curForwardMatchLength < minMatchLength) { + continue; + } + curBackwardMatchLength = + ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch, + lowPrefixPtr); + curTotalMatchLength = curForwardMatchLength + + curBackwardMatchLength; } - curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch( - ip, anchor, pMatch, lowest); - curTotalMatchLength = curForwardMatchLength + - curBackwardMatchLength; if (curTotalMatchLength > bestMatchLength) { bestMatchLength = curTotalMatchLength; @@ -366,7 +408,7 @@ size_t ZSTD_compressBlock_ldm( if (bestEntry == NULL) { ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits, current, - ldmParams); + *params); ip++; continue; } @@ -375,280 +417,217 @@ size_t ZSTD_compressBlock_ldm( mLength = forwardMatchLength + backwardMatchLength; ip -= backwardMatchLength; - /* Call the block compressor on the remaining literals */ { + /* Store the sequence: + * ip = current - backwardMatchLength + * The match is at (bestEntry->offset - backwardMatchLength) + */ U32 const matchIndex = bestEntry->offset; - const BYTE* const match = base + matchIndex - backwardMatchLength; - U32 const offset = (U32)(ip - match); + U32 const offset = current - matchIndex; - /* Fill tables for block compressor */ - ZSTD_ldm_limitTableUpdate(ms, anchor); - ZSTD_ldm_fillFastTables(ms, cParams, anchor); - - /* Call block compressor and get remaining literals */ - lastLiterals = blockCompressor(ms, seqStore, rep, cParams, anchor, ip - anchor); - ms->nextToUpdate = (U32)(ip - base); - - /* Update repToConfirm with the new offset */ - for (i = ZSTD_REP_NUM - 1; i > 0; i--) - rep[i] = rep[i-1]; - rep[0] = offset; - - /* Store the sequence with the leftover literals */ - ZSTD_storeSeq(seqStore, lastLiterals, ip - lastLiterals, - offset + ZSTD_REP_MOVE, mLength - MINMATCH); + sequences->litLength = (U32)(ip - anchor); + sequences->matchLength = (U32)mLength; + sequences->offset = offset; + ++sequences; } /* Insert the current entry into the hash table */ ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits, (U32)(lastHashed - base), - ldmParams); + *params); assert(ip + backwardMatchLength == lastHashed); /* Fill the hash table from lastHashed+1 to ip+mLength*/ /* Heuristic: don't need to fill the entire table at end of block */ - if (ip + mLength < ilimit) { + if (ip + mLength <= ilimit) { rollingHash = ZSTD_ldm_fillLdmHashTable( ldmState, rollingHash, lastHashed, - ip + mLength, base, hBits, ldmParams); + ip + mLength, base, hBits, *params); lastHashed = ip + mLength - 1; } ip += mLength; anchor = ip; - /* Check immediate repcode */ - while ( (ip < ilimit) - && ( (rep[1] > 0) && (rep[1] <= (U32)(ip-lowest)) - && (MEM_read32(ip) == MEM_read32(ip - rep[1])) )) { - - size_t const rLength = ZSTD_count(ip+4, ip+4-rep[1], - iend) + 4; - /* Swap repToConfirm[1] <=> repToConfirm[0] */ - { - U32 const tmpOff = rep[1]; - rep[1] = rep[0]; - rep[0] = tmpOff; - } - - ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); - - /* Fill the hash table from lastHashed+1 to ip+rLength*/ - if (ip + rLength < ilimit) { - rollingHash = ZSTD_ldm_fillLdmHashTable( - ldmState, rollingHash, lastHashed, - ip + rLength, base, hBits, ldmParams); - lastHashed = ip + rLength - 1; - } - ip += rLength; - anchor = ip; - } } - - ZSTD_ldm_limitTableUpdate(ms, anchor); - ZSTD_ldm_fillFastTables(ms, cParams, anchor); - - lastLiterals = blockCompressor(ms, seqStore, rep, cParams, anchor, iend - anchor); - ms->nextToUpdate = (U32)(iend - base); - - /* Return the last literals size */ - return lastLiterals; + /* Return the number of sequences generated */ + return sequences - sequencesStart; } -size_t ZSTD_compressBlock_ldm_extDict( - ldmState_t* ldmState, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_CCtx_params const* params, void const* src, size_t srcSize) +#if 0 +/** + * If the sequence length is longer than remaining then the sequence is split + * between this block and the next. + * + * Returns the current sequence to handle, or if the rest of the block should + * be literals, it returns a sequence with offset == 0. + */ +static rawSeq maybeSplitSequence(rawSeq* sequences, size_t* nbSeq, + size_t const seq, size_t const remaining, + U32 const minMatch) { - const ldmParams_t ldmParams = params->ldmParams; - ZSTD_compressionParameters const* cParams = ¶ms->cParams; - const U64 hashPower = ldmState->hashPower; - const U32 hBits = ldmParams.hashLog - ldmParams.bucketSizeLog; - const U32 ldmBucketSize = ((U32)1 << ldmParams.bucketSizeLog); - const U32 ldmTagMask = ((U32)1 << ldmParams.hashEveryLog) - 1; - const BYTE* const base = ms->base; - const BYTE* const dictBase = ms->dictBase; - const BYTE* const istart = (const BYTE*)src; - const BYTE* ip = istart; - const BYTE* anchor = istart; - const U32 lowestIndex = ms->lowLimit; - const BYTE* const dictStart = dictBase + lowestIndex; - const U32 dictLimit = ms->dictLimit; - const BYTE* const lowPrefixPtr = base + dictLimit; - const BYTE* const dictEnd = dictBase + dictLimit; - const BYTE* const iend = istart + srcSize; - const BYTE* const ilimit = iend - MAX(ldmParams.minMatchLength, HASH_READ_SIZE); + rawSeq sequence = sequences[seq]; + assert(sequence.offset > 0); + /* Handle partial sequences */ + if (remaining <= sequence.litLength) { + /* Split the literals that we have out of the sequence. + * They will become the last literals of this block. + * The next block starts off with the remaining literals. + */ + sequences[seq].litLength -= remaining; + *nbSeq = seq; + sequence.offset = 0; + } else if (remaining < sequence.litLength + sequence.matchLength) { + /* Split the match up into two sequences. One in this block, and one + * in the next with no literals. If either match would be shorter + * than searchLength we omit it. + */ + U32 const matchPrefix = remaining - sequence.litLength; + U32 const matchSuffix = sequence.matchLength - matchPrefix; - const ZSTD_blockCompressor blockCompressor = - ZSTD_selectBlockCompressor(cParams->strategy, 1); - U64 rollingHash = 0; - const BYTE* lastHashed = NULL; - size_t i, lastLiterals; - - /* Search Loop */ - while (ip < ilimit) { /* < instead of <=, because (ip+1) */ - size_t mLength; - const U32 current = (U32)(ip-base); - size_t forwardMatchLength = 0, backwardMatchLength = 0; - ldmEntry_t* bestEntry = NULL; - if (ip != istart) { - rollingHash = ZSTD_ldm_updateHash(rollingHash, lastHashed[0], - lastHashed[ldmParams.minMatchLength], - hashPower); + assert(remaining > sequence.litLength); + assert(matchPrefix < sequence.matchLength); + assert(matchPrefix + matchSuffix == sequence.matchLength); + /* Update the current sequence */ + sequence.matchLength = matchPrefix; + /* Update the next sequence when long enough, otherwise omit it. */ + if (matchSuffix >= minMatch) { + sequences[seq].litLength = 0; + sequences[seq].matchLength = matchSuffix; + *nbSeq = seq; } else { - rollingHash = ZSTD_ldm_getRollingHash(ip, ldmParams.minMatchLength); + sequences[seq + 1].litLength += matchSuffix; + *nbSeq = seq + 1; } - lastHashed = ip; - - if (ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashEveryLog) != - ldmTagMask) { - /* Don't insert and don't look for a match */ - ip++; - continue; + if (sequence.matchLength < minMatch) { + /* Skip the current sequence if it is too short */ + sequence.offset = 0; } + } + return sequence; +} +#endif - /* Get the best entry and compute the match lengths */ +size_t ZSTD_ldm_blockCompress(rawSeq const* sequences, size_t nbSeq, + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize, + int const extDict) +{ + ZSTD_blockCompressor const blockCompressor = + ZSTD_selectBlockCompressor(cParams->strategy, extDict); + int const doImmediateRepCheck = cParams->strategy < ZSTD_btopt; + /* Prefix and extDict parameters */ + U32 const dictLimit = ms->dictLimit; + U32 const lowestIndex = extDict ? ms->lowLimit : dictLimit; + BYTE const* const base = ms->base; + BYTE const* const dictBase = extDict ? ms->dictBase : NULL; + BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL; + BYTE const* const lowPrefixPtr = base + dictLimit; + /* Input bounds */ + BYTE const* const istart = (BYTE const*)src; + BYTE const* const iend = istart + srcSize; + /* Input positions */ + BYTE const* ip = istart; + size_t seq; + /* Loop through each sequence and apply the block compressor to the lits */ + for (seq = 0; seq < nbSeq; ++seq) { + rawSeq const sequence = sequences[seq]; + int i; + + if (sequence.offset == 0) + break; + + assert(ip + sequence.litLength + sequence.matchLength <= iend); + + /* Fill tables for block compressor */ + ZSTD_ldm_limitTableUpdate(ms, ip); + ZSTD_ldm_fillFastTables(ms, cParams, ip); + /* Run the block compressor */ { - ldmEntry_t* const bucket = - ZSTD_ldm_getBucket(ldmState, - ZSTD_ldm_getSmallHash(rollingHash, hBits), - ldmParams); - ldmEntry_t* cur; - size_t bestMatchLength = 0; - U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits); - - for (cur = bucket; cur < bucket + ldmBucketSize; ++cur) { - const BYTE* const curMatchBase = - cur->offset < dictLimit ? dictBase : base; - const BYTE* const pMatch = curMatchBase + cur->offset; - const BYTE* const matchEnd = - cur->offset < dictLimit ? dictEnd : iend; - const BYTE* const lowMatchPtr = - cur->offset < dictLimit ? dictStart : lowPrefixPtr; - size_t curForwardMatchLength, curBackwardMatchLength, - curTotalMatchLength; - - if (cur->checksum != checksum || cur->offset <= lowestIndex) { - continue; - } - - curForwardMatchLength = ZSTD_count_2segments( - ip, pMatch, iend, - matchEnd, lowPrefixPtr); - if (curForwardMatchLength < ldmParams.minMatchLength) { - continue; - } - curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch( - ip, anchor, pMatch, lowMatchPtr); - curTotalMatchLength = curForwardMatchLength + - curBackwardMatchLength; - - if (curTotalMatchLength > bestMatchLength) { - bestMatchLength = curTotalMatchLength; - forwardMatchLength = curForwardMatchLength; - backwardMatchLength = curBackwardMatchLength; - bestEntry = cur; - } - } - } - - /* No match found -- continue searching */ - if (bestEntry == NULL) { - ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits, - (U32)(lastHashed - base), - ldmParams); - ip++; - continue; - } - - /* Match found */ - mLength = forwardMatchLength + backwardMatchLength; - ip -= backwardMatchLength; - - /* Call the block compressor on the remaining literals */ - { - /* ip = current - backwardMatchLength - * The match is at (bestEntry->offset - backwardMatchLength) */ - U32 const matchIndex = bestEntry->offset; - U32 const offset = current - matchIndex; - - /* Fill the hash table for the block compressor */ - ZSTD_ldm_limitTableUpdate(ms, anchor); - ZSTD_ldm_fillFastTables(ms, cParams, anchor); - - /* Call block compressor and get remaining literals */ - lastLiterals = blockCompressor(ms, seqStore, rep, cParams, anchor, ip - anchor); + size_t const newLitLength = + blockCompressor(ms, seqStore, rep, cParams, ip, + sequence.litLength); + ip += sequence.litLength; ms->nextToUpdate = (U32)(ip - base); - - /* Update repToConfirm with the new offset */ + /* Update the repcodes */ for (i = ZSTD_REP_NUM - 1; i > 0; i--) rep[i] = rep[i-1]; - rep[0] = offset; - - /* Store the sequence with the leftover literals */ - ZSTD_storeSeq(seqStore, lastLiterals, ip - lastLiterals, - offset + ZSTD_REP_MOVE, mLength - MINMATCH); + rep[0] = sequence.offset; + /* Store the sequence */ + ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, + sequence.offset + ZSTD_REP_MOVE, + sequence.matchLength - MINMATCH); + ip += sequence.matchLength; } + /* Check immediate repcode */ + if (doImmediateRepCheck) { + rawSeq* nextSeq = (seq + 1 < *nbSeq) ? &sequences[seq + 1] : NULL; + /* Allow repcodes up to the next predefined sequence */ + BYTE const* const repCheckEnd = nextSeq + ? ip + nextSeq->litLength + : iend; + BYTE const* const repCheckLimit = repCheckEnd - HASH_READ_SIZE; - /* Insert the current entry into the hash table */ - ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits, - (U32)(lastHashed - base), - ldmParams); + assert(repCheckEnd <= iend); - /* Fill the hash table from lastHashed+1 to ip+mLength */ - assert(ip + backwardMatchLength == lastHashed); - if (ip + mLength < ilimit) { - rollingHash = ZSTD_ldm_fillLdmHashTable( - ldmState, rollingHash, lastHashed, - ip + mLength, base, hBits, - ldmParams); - lastHashed = ip + mLength - 1; - } - ip += mLength; - anchor = ip; + if (extDict) { + while (ip < repCheckLimit) { + U32 const current = (U32)(ip-base); + U32 const repIndex = current - rep[1]; + const BYTE* repMatch = repIndex < dictLimit ? + dictBase + repIndex : base + repIndex; + if ( (((U32)((dictLimit-1) - repIndex) >= 3) & + (repIndex > lowestIndex)) /* intentional overflow */ + && (MEM_read32(repMatch) == MEM_read32(ip)) ) { + const BYTE* const repEnd = repIndex < dictLimit ? + dictEnd : repCheckEnd; + size_t const rLength = ZSTD_count_2segments( + ip+4, repMatch+4, repCheckEnd, repEnd, + lowPrefixPtr) + 4; + /* Swap rep[1] <=> rep[0] */ + U32 const tmpOffset = rep[1]; + rep[1] = rep[0]; + rep[0] = tmpOffset; - /* check immediate repcode */ - while (ip < ilimit) { - U32 const current2 = (U32)(ip-base); - U32 const repIndex2 = current2 - rep[1]; - const BYTE* repMatch2 = repIndex2 < dictLimit ? - dictBase + repIndex2 : base + repIndex2; - if ( (((U32)((dictLimit-1) - repIndex2) >= 3) & - (repIndex2 > lowestIndex)) /* intentional overflow */ - && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < dictLimit ? - dictEnd : iend; - size_t const repLength2 = - ZSTD_count_2segments(ip+4, repMatch2+4, iend, - repEnd2, lowPrefixPtr) + 4; + ZSTD_storeSeq(seqStore, 0, ip, 0, rLength-MINMATCH); + ip += rLength; + if (nextSeq) { + assert(nextSeq->litLength >= rLength); + nextSeq->litLength -= rLength; + } + continue; + } + break; + } + } else { + while ( (ip < repCheckLimit) + && ( (rep[1] > 0) && (rep[1] <= (U32)(ip - lowPrefixPtr)) + && (MEM_read32(ip) == MEM_read32(ip - rep[1])) )) { - U32 tmpOffset = rep[1]; - rep[1] = rep[0]; - rep[0] = tmpOffset; + size_t const rLength = ZSTD_count(ip+4, ip+4-rep[1], + repCheckEnd) + 4; + /* Swap rep[1] <=> rep[0] */ + { + U32 const tmpOff = rep[1]; + rep[1] = rep[0]; + rep[0] = tmpOff; + } - ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); - - /* Fill the hash table from lastHashed+1 to ip+repLength2*/ - if (ip + repLength2 < ilimit) { - rollingHash = ZSTD_ldm_fillLdmHashTable( - ldmState, rollingHash, lastHashed, - ip + repLength2, base, hBits, - ldmParams); - lastHashed = ip + repLength2 - 1; + ZSTD_storeSeq(seqStore, 0, ip, 0, rLength-MINMATCH); + ip += rLength; + if (nextSeq) { + assert(nextSeq->litLength >= rLength); + nextSeq->litLength -= rLength; + } } - ip += repLength2; - anchor = ip; - continue; } - break; } } - - ZSTD_ldm_limitTableUpdate(ms, anchor); - ZSTD_ldm_fillFastTables(ms, cParams, anchor); - - /* Call the block compressor one last time on the last literals */ - lastLiterals = blockCompressor(ms, seqStore, rep, cParams, anchor, iend - anchor); - ms->nextToUpdate = (U32)(iend - base); - - /* Return the last literals size */ - return lastLiterals; + ZSTD_ldm_limitTableUpdate(ms, ip); + ZSTD_ldm_fillFastTables(ms, cParams, ip); + { + size_t const lastLiterals = blockCompressor(ms, seqStore, rep, cParams, + ip, iend - ip); + ms->nextToUpdate = (U32)(iend - base); + return lastLiterals; + } } diff --git a/lib/compress/zstd_ldm.h b/lib/compress/zstd_ldm.h index 7041c65a0..a836033a6 100644 --- a/lib/compress/zstd_ldm.h +++ b/lib/compress/zstd_ldm.h @@ -24,34 +24,57 @@ extern "C" { #define ZSTD_LDM_DEFAULT_WINDOW_LOG ZSTD_WINDOWLOG_DEFAULTMAX #define ZSTD_LDM_HASHEVERYLOG_NOTSET 9999 -/** ZSTD_compressBlock_ldm_generic() : +/** + * ZSTD_ldm_generateSequences(): * - * This is a block compressor intended for long distance matching. + * Generates the sequences using the long distance match finder. + * The sequences completely parse a prefix of the source, but leave off the last + * literals. Returns the number of sequences generated into `sequences`. * - * The function searches for matches of length at least - * ldmParams.minMatchLength using a hash table in cctx->ldmState. - * Matches can be at a distance of up to cParams.windowLog. + * NOTE: The source may be any size, assuming it doesn't overflow the hash table + * indices, and the output sequences table is large enough.. + */ +size_t ZSTD_ldm_generateSequences( + ldmState_t* ldms, rawSeq* sequences, ZSTD_matchState_t const* ms, + ldmParams_t const* params, void const* src, size_t srcSize, + int const extDict); + +/** + * ZSTD_ldm_blockCompress(): * - * Upon finding a match, the unmatched literals are compressed using a - * ZSTD_blockCompressor (depending on the strategy in the compression - * parameters), which stores the matched sequences. The "long distance" - * match is then stored with the remaining literals from the - * ZSTD_blockCompressor. */ -size_t ZSTD_compressBlock_ldm( - ldmState_t* ldms, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_CCtx_params const* params, void const* src, size_t srcSize); + * Compresses a block using the predefined sequences, along with a secondary + * block compressor. The literals section of every sequence is passed to the + * secondary block compressor, and those sequences are interspersed with the + * predefined sequences. Returns the length of the last literals. + * `nbSeq` is the number of sequences available in `sequences`. + * + * NOTE: The source must be at most the maximum block size, but the predefined + * sequences can be any size, and may be longer than the block. In the case that + * they are longer than the block, the last sequences may need to be split into + * two. We handle that case correctly, and update `sequences` and `nbSeq` + * appropriately. + */ +size_t ZSTD_ldm_blockCompress(rawSeq const* sequences, size_t nbSeq, + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize, + int const extDict); -size_t ZSTD_compressBlock_ldm_extDict( - ldmState_t* ldms, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_CCtx_params const* params, void const* src, size_t srcSize); /** ZSTD_ldm_initializeParameters() : * Initialize the long distance matching parameters to their default values. */ size_t ZSTD_ldm_initializeParameters(ldmParams_t* params, U32 enableLdm); /** ZSTD_ldm_getTableSize() : - * Estimate the space needed for long distance matching tables. */ -size_t ZSTD_ldm_getTableSize(U32 hashLog, U32 bucketSizeLog); + * Estimate the space needed for long distance matching tables or 0 if LDM is + * disabled. + */ +size_t ZSTD_ldm_getTableSize(ldmParams_t params); + +/** ZSTD_ldm_getSeqSpace() : + * Return an upper bound on the number of sequences that can be produced by + * the long distance matcher, or 0 if LDM is disabled. + */ +size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize); /** ZSTD_ldm_getTableSize() : * Return prime8bytes^(minMatchLength-1) */ @@ -62,8 +85,12 @@ U64 ZSTD_ldm_getHashPower(U32 minMatchLength); * windowLog and params->hashLog. * * Ensures that params->bucketSizeLog is <= params->hashLog (setting it to - * params->hashLog if it is not). */ -void ZSTD_ldm_adjustParameters(ldmParams_t* params, U32 windowLog); + * params->hashLog if it is not). + * + * Ensures that the minMatchLength >= targetLength during optimal parsing. + */ +void ZSTD_ldm_adjustParameters(ldmParams_t* params, + ZSTD_compressionParameters const* cParams); #if defined (__cplusplus) } From 7e2bf4ebad3975b25be95551005b84595f91b64b Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 21 Feb 2018 19:30:05 -0800 Subject: [PATCH 147/255] Remove long range matcher immediate repcode check The compression ratio gets about 0.01% worse on the files I tested, but the code is much simpler. --- lib/compress/zstd_ldm.c | 70 ----------------------------------------- 1 file changed, 70 deletions(-) diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index c18d68c8a..2c304b38b 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -514,14 +514,7 @@ size_t ZSTD_ldm_blockCompress(rawSeq const* sequences, size_t nbSeq, { ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(cParams->strategy, extDict); - int const doImmediateRepCheck = cParams->strategy < ZSTD_btopt; - /* Prefix and extDict parameters */ - U32 const dictLimit = ms->dictLimit; - U32 const lowestIndex = extDict ? ms->lowLimit : dictLimit; BYTE const* const base = ms->base; - BYTE const* const dictBase = extDict ? ms->dictBase : NULL; - BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL; - BYTE const* const lowPrefixPtr = base + dictLimit; /* Input bounds */ BYTE const* const istart = (BYTE const*)src; BYTE const* const iend = istart + srcSize; @@ -558,69 +551,6 @@ size_t ZSTD_ldm_blockCompress(rawSeq const* sequences, size_t nbSeq, sequence.matchLength - MINMATCH); ip += sequence.matchLength; } - /* Check immediate repcode */ - if (doImmediateRepCheck) { - rawSeq* nextSeq = (seq + 1 < *nbSeq) ? &sequences[seq + 1] : NULL; - /* Allow repcodes up to the next predefined sequence */ - BYTE const* const repCheckEnd = nextSeq - ? ip + nextSeq->litLength - : iend; - BYTE const* const repCheckLimit = repCheckEnd - HASH_READ_SIZE; - - assert(repCheckEnd <= iend); - - if (extDict) { - while (ip < repCheckLimit) { - U32 const current = (U32)(ip-base); - U32 const repIndex = current - rep[1]; - const BYTE* repMatch = repIndex < dictLimit ? - dictBase + repIndex : base + repIndex; - if ( (((U32)((dictLimit-1) - repIndex) >= 3) & - (repIndex > lowestIndex)) /* intentional overflow */ - && (MEM_read32(repMatch) == MEM_read32(ip)) ) { - const BYTE* const repEnd = repIndex < dictLimit ? - dictEnd : repCheckEnd; - size_t const rLength = ZSTD_count_2segments( - ip+4, repMatch+4, repCheckEnd, repEnd, - lowPrefixPtr) + 4; - /* Swap rep[1] <=> rep[0] */ - U32 const tmpOffset = rep[1]; - rep[1] = rep[0]; - rep[0] = tmpOffset; - - ZSTD_storeSeq(seqStore, 0, ip, 0, rLength-MINMATCH); - ip += rLength; - if (nextSeq) { - assert(nextSeq->litLength >= rLength); - nextSeq->litLength -= rLength; - } - continue; - } - break; - } - } else { - while ( (ip < repCheckLimit) - && ( (rep[1] > 0) && (rep[1] <= (U32)(ip - lowPrefixPtr)) - && (MEM_read32(ip) == MEM_read32(ip - rep[1])) )) { - - size_t const rLength = ZSTD_count(ip+4, ip+4-rep[1], - repCheckEnd) + 4; - /* Swap rep[1] <=> rep[0] */ - { - U32 const tmpOff = rep[1]; - rep[1] = rep[0]; - rep[0] = tmpOff; - } - - ZSTD_storeSeq(seqStore, 0, ip, 0, rLength-MINMATCH); - ip += rLength; - if (nextSeq) { - assert(nextSeq->litLength >= rLength); - nextSeq->litLength -= rLength; - } - } - } - } } ZSTD_ldm_limitTableUpdate(ms, ip); ZSTD_ldm_fillFastTables(ms, cParams, ip); From 653383f74a4bb922aeeff11a922be44d20ecf7d7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 22 Feb 2018 15:44:26 -0800 Subject: [PATCH 148/255] minor nit from Mac XCode --- lib/compress/fse_compress.c | 8 ++++---- lib/compress/huf_compress.c | 5 ++++- lib/decompress/huf_decompress.c | 12 +++--------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index 549c115d4..ee5eb81ab 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -248,7 +248,7 @@ static size_t FSE_writeNCount_generic (void* header, size_t headerBufferSize, bitCount -= (count>=1; + while (remaining>=1; } } if (bitCount>16) { if ((!writeIsSafe) && (out > oend - 2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ @@ -540,7 +540,7 @@ static size_t FSE_normalizeM2(short* norm, U32 tableLog, const unsigned* count, find max, then give all remaining points to max */ U32 maxV = 0, maxC = 0; for (s=0; s<=maxSymbolValue; s++) - if (count[s] > maxC) maxV=s, maxC=count[s]; + if (count[s] > maxC) { maxV=s; maxC=count[s]; } norm[maxV] += (short)ToDistribute; return 0; } @@ -548,7 +548,7 @@ static size_t FSE_normalizeM2(short* norm, U32 tableLog, const unsigned* count, if (total == 0) { /* all of the symbols were low enough for the lowOne or lowThreshold */ for (s=0; ToDistribute > 0; s = (s+1)%(maxSymbolValue+1)) - if (norm[s] > 0) ToDistribute--, norm[s]++; + if (norm[s] > 0) { ToDistribute--; norm[s]++; } return 0; } @@ -604,7 +604,7 @@ size_t FSE_normalizeCount (short* normalizedCounter, unsigned tableLog, U64 restToBeat = vStep * rtbTable[proba]; proba += (count[s]*step) - ((U64)proba< restToBeat; } - if (proba > largestP) largestP=proba, largest=s; + if (proba > largestP) { largestP=proba; largest=s; } normalizedCounter[s] = proba; stillToDistribute -= proba; } } diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 74d8fb9d7..10320f75f 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -323,7 +323,10 @@ static void HUF_sort(nodeElt* huffNode, const U32* count, U32 maxSymbolValue) U32 const c = count[n]; U32 const r = BIT_highbit32(c+1) + 1; U32 pos = rank[r].current++; - while ((pos > rank[r].base) && (c > huffNode[pos-1].count)) huffNode[pos]=huffNode[pos-1], pos--; + while ((pos > rank[r].base) && (c > huffNode[pos-1].count)) { + huffNode[pos] = huffNode[pos-1]; + pos--; + } huffNode[pos].count = c; huffNode[pos].byte = (BYTE)n; } diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 0bd1f42f8..9afec6d50 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -94,10 +94,7 @@ size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize huffWeight = (BYTE *)((U32 *)workSpace + spaceUsed32); spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; - if ((spaceUsed32 << 2) > wkspSize) - return ERROR(tableLog_tooLarge); - workSpace = (U32 *)workSpace + spaceUsed32; - wkspSize -= (spaceUsed32 << 2); + if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge); HUF_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable)); /* memset(huffWeight, 0, sizeof(huffWeight)); */ /* is not necessary, even though some analyzer complain ... */ @@ -285,7 +282,7 @@ HUF_decompress4X2_usingDTable_internal_body( /* check corruption */ /* note : should not be necessary : op# advance in lock step, and we control op4. - * but curiously, binary generated by gcc 7.2 with -mbmi2 runs faster when they are present */ + * but curiously, binary generated by gcc 7.2 & 7.3 with -mbmi2 runs faster when >=2 tests are present */ if (op1 > opStart2) return ERROR(corruption_detected); if (op2 > opStart3) return ERROR(corruption_detected); if (op3 > opStart4) return ERROR(corruption_detected); @@ -747,10 +744,7 @@ size_t HUF_readDTableX4_wksp(HUF_DTable* DTable, const void* src, weightList = (BYTE *)((U32 *)workSpace + spaceUsed32); spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; - if ((spaceUsed32 << 2) > wkspSize) - return ERROR(tableLog_tooLarge); - workSpace = (U32 *)workSpace + spaceUsed32; - wkspSize -= (spaceUsed32 << 2); + if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge); rankStart = rankStart0 + 1; memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1)); From 7e5e226cbf296db0318dd707f7166866cb7ad9ba Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 23 Feb 2018 16:48:18 -0800 Subject: [PATCH 149/255] Split the window state into substructure --- lib/compress/zstd_compress.c | 120 ++++--------------- lib/compress/zstd_compress_internal.h | 165 +++++++++++++++++++++++++- lib/compress/zstd_double_fast.c | 14 +-- lib/compress/zstd_fast.c | 14 +-- lib/compress/zstd_lazy.c | 48 ++++---- lib/compress/zstd_ldm.c | 16 +-- lib/compress/zstd_opt.c | 26 ++-- 7 files changed, 241 insertions(+), 162 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 74a51c25e..467515370 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -883,13 +883,9 @@ static void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs) */ static void ZSTD_invalidateMatchState(ZSTD_matchState_t* ms) { - size_t const endT = (size_t)(ms->nextSrc - ms->base); - U32 const end = (U32)endT; - assert(endT < (3U<<30)); + ZSTD_window_clear(&ms->window); - ms->lowLimit = end; - ms->dictLimit = end; - ms->nextToUpdate = end + 1; + ms->nextToUpdate = ms->window.dictLimit + 1; ms->loadedDictEnd = 0; ms->opt.litLengthSum = 0; /* force reset of btopt stats */ } @@ -931,10 +927,8 @@ static void* ZSTD_reset_matchState(ZSTD_matchState_t* ms, void* ptr, ZSTD_compre assert(((size_t)ptr & 3) == 0); - ms->nextSrc = NULL; - ms->base = NULL; - ms->dictBase = NULL; ms->hashLog3 = hashLog3; + memset(&ms->window, 0, sizeof(ms->window)); ZSTD_invalidateMatchState(ms); /* opt parser space */ @@ -1114,7 +1108,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) { int i; for (i=0; iblockState.prevCBlock->rep[i] = 0; - assert(/* !extDict */ cctx->blockState.matchState.lowLimit == cctx->blockState.matchState.dictLimit); + assert(!ZSTD_window_hasExtDict(cctx->blockState.matchState.window)); } static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, @@ -1156,13 +1150,9 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, { ZSTD_matchState_t const* srcMatchState = &cdict->matchState; ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState; + dstMatchState->window = srcMatchState->window; dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3; - dstMatchState->nextSrc = srcMatchState->nextSrc; - dstMatchState->base = srcMatchState->base; - dstMatchState->dictBase = srcMatchState->dictBase; - dstMatchState->dictLimit = srcMatchState->dictLimit; - dstMatchState->lowLimit = srcMatchState->lowLimit; dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd; } cctx->dictID = cdict->dictID; @@ -1217,13 +1207,9 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, { ZSTD_matchState_t const* srcMatchState = &srcCCtx->blockState.matchState; ZSTD_matchState_t* dstMatchState = &dstCCtx->blockState.matchState; + dstMatchState->window = srcMatchState->window; dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3; - dstMatchState->nextSrc = srcMatchState->nextSrc; - dstMatchState->base = srcMatchState->base; - dstMatchState->dictBase = srcMatchState->dictBase; - dstMatchState->dictLimit = srcMatchState->dictLimit; - dstMatchState->lowLimit = srcMatchState->lowLimit; dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd; } dstCCtx->dictID = srcCCtx->dictID; @@ -1826,13 +1812,13 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, { ZSTD_matchState_t* const ms = &zc->blockState.matchState; DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)", - (U32)dstCapacity, ms->dictLimit, ms->nextToUpdate); + (U32)dstCapacity, ms->window.dictLimit, ms->nextToUpdate); if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) return 0; /* don't even attempt compression below a certain srcSize */ ZSTD_resetSeqStore(&(zc->seqStore)); /* limited update after a very long match */ - { const BYTE* const base = ms->base; + { const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; const U32 current = (U32)(istart-base); if (current > ms->nextToUpdate + 384) @@ -1840,7 +1826,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, } /* select and store sequences */ - { U32 const extDict = ms->lowLimit < ms->dictLimit; + { U32 const extDict = ZSTD_window_hasExtDict(ms->window); size_t lastLLSize; { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i]; } if (zc->appliedParams.ldmParams.enableLdm) { @@ -1907,52 +1893,19 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, return ERROR(dstSize_tooSmall); /* not enough space to store compressed block */ if (remaining < blockSize) blockSize = remaining; - /* preemptive overflow correction: - * 1. correction is large enough: - * lowLimit > (3<<29) ==> current > 3<<29 + 1< (3<<29 + 1< (3<<29 - blockSize) - (1< (3<<29 - blockSize) - (1<<30) (NOTE: chainLog <= 30) - * > 1<<29 - 1<<17 - * - * 2. (ip+blockSize - cctx->base) doesn't overflow: - * In 32 bit mode we limit windowLog to 30 so we don't get - * differences larger than 1<<31-1. - * 3. cctx->lowLimit < 1<<32: - * windowLog <= 31 ==> 3<<29 + 1<lowLimit > (3U<<29)) { - U32 const cycleMask = ((U32)1 << ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy)) - 1; - U32 const current = (U32)(ip - cctx->blockState.matchState.base); - U32 const newCurrent = (current & cycleMask) + ((U32)1 << cctx->appliedParams.cParams.windowLog); - U32 const correction = current - newCurrent; + if (ZSTD_window_needOverflowCorrection(ms->window)) { + U32 const cycleLog = ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy); + U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, maxDist, ip); ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30); ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30); ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31); - assert(current > newCurrent); - assert(correction > 1<<28); /* Loose bound, should be about 1<<29 */ + ZSTD_reduceIndex(cctx, correction); - ms->base += correction; - ms->dictBase += correction; - ms->lowLimit -= correction; - ms->dictLimit -= correction; if (ms->nextToUpdate < correction) ms->nextToUpdate = 0; else ms->nextToUpdate -= correction; - DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x", correction, ms->lowLimit); - } - /* enforce maxDist */ - if ((U32)(ip+blockSize - ms->base) > ms->loadedDictEnd + maxDist) { - U32 const newLowLimit = (U32)(ip+blockSize - ms->base) - maxDist; - if (ms->lowLimit < newLowLimit) ms->lowLimit = newLowLimit; - if (ms->dictLimit < ms->lowLimit) - DEBUGLOG(5, "ZSTD_compress_frameChunk : update dictLimit from %u to %u ", - ms->dictLimit, ms->lowLimit); - if (ms->dictLimit < ms->lowLimit) ms->dictLimit = ms->lowLimit; - if (ms->nextToUpdate < ms->lowLimit) ms->nextToUpdate = ms->lowLimit; } + ZSTD_window_enforceMaxDist(&ms->window, ip + blockSize, ms->loadedDictEnd + maxDist); + if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit; { size_t cSize = ZSTD_compressBlock_internal(cctx, op+ZSTD_blockHeaderSize, dstCapacity-ZSTD_blockHeaderSize, @@ -2044,38 +1997,12 @@ size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity) } -static void ZSTD_manageWindowContinuity(ZSTD_matchState_t* ms, void const* src, size_t srcSize) -{ - const BYTE* const ip = (const BYTE*) src; - - /* Check if blocks follow each other */ - if (src != ms->nextSrc) { - /* not contiguous */ - size_t const distanceFromBase = (size_t)(ms->nextSrc - ms->base); - DEBUGLOG(5, "ZSTD_manageWindowContinuity: non contiguous blocks, new segment starts at %u", ms->dictLimit); - ms->lowLimit = ms->dictLimit; - assert(distanceFromBase == (size_t)(U32)distanceFromBase); /* should never overflow */ - ms->dictLimit = (U32)distanceFromBase; - ms->dictBase = ms->base; - ms->base = ip - distanceFromBase; - ms->nextToUpdate = ms->dictLimit; - if (ms->dictLimit - ms->lowLimit < HASH_READ_SIZE) ms->lowLimit = ms->dictLimit; /* too small extDict */ - } - ms->nextSrc = ip + srcSize; - /* if input and dictionary overlap : reduce dictionary (area presumed modified by input) */ - if ((ip+srcSize > ms->dictBase + ms->lowLimit) & (ip < ms->dictBase + ms->dictLimit)) { - ptrdiff_t const highInputIdx = (ip + srcSize) - ms->dictBase; - U32 const lowLimitMax = (highInputIdx > (ptrdiff_t)ms->dictLimit) ? ms->dictLimit : (U32)highInputIdx; - ms->lowLimit = lowLimitMax; - } -} - - static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 frame, U32 lastFrameChunk) { + ZSTD_matchState_t* ms = &cctx->blockState.matchState; size_t fhSize = 0; DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u, srcSize: %u", @@ -2093,7 +2020,9 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, if (!srcSize) return fhSize; /* do not generate an empty block if no input */ - ZSTD_manageWindowContinuity(&cctx->blockState.matchState, src, srcSize); + if (!ZSTD_window_update(&ms->window, src, srcSize)) { + ms->nextToUpdate = ms->window.dictLimit; + } DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (U32)cctx->blockSize); { size_t const cSize = frame ? @@ -2145,15 +2074,8 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, ZSTD_CCtx_params const BYTE* const iend = ip + srcSize; ZSTD_compressionParameters const* cParams = ¶ms->cParams; - /* input becomes current prefix */ - ms->lowLimit = ms->dictLimit; - ms->dictLimit = (U32)(ms->nextSrc - ms->base); - ms->dictBase = ms->base; - ms->base = ip - ms->dictLimit; - ms->nextToUpdate = ms->dictLimit; - ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->base); + ZSTD_window_update(&ms->window, src, srcSize); - ms->nextSrc = iend; if (srcSize <= HASH_READ_SIZE) return 0; switch(params->cParams.strategy) @@ -2183,7 +2105,7 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, ZSTD_CCtx_params assert(0); /* not possible : not a valid strategy id */ } - ms->nextToUpdate = (U32)(iend - ms->base); + ms->nextToUpdate = (U32)(iend - ms->window.base); return 0; } diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 8a02f764e..98f866cde 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -110,10 +110,14 @@ typedef struct { BYTE const* dictBase; /* extDict indexes relative to this position */ U32 dictLimit; /* below that point, need extDict */ U32 lowLimit; /* below that point, no more data */ - U32 nextToUpdate; /* index from which to continue table update */ - U32 nextToUpdate3; /* index from which to continue table update */ - U32 hashLog3; /* dispatch table : larger == faster, more memory */ - U32 loadedDictEnd; /* index of end of dictionary */ +} ZSTD_window_t; + +typedef struct { + ZSTD_window_t window; /* State for window round buffer management */ + U32 loadedDictEnd; /* index of end of dictionary */ + U32 nextToUpdate; /* index from which to continue table update */ + U32 nextToUpdate3; /* index from which to continue table update */ + U32 hashLog3; /* dispatch table : larger == faster, more memory */ U32* hashTable; U32* hashTable3; U32* chainTable; @@ -441,6 +445,159 @@ MEM_STATIC size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls) } } +/*-************************************* +* Round buffer management +***************************************/ + +#define ZSTD_LOWLIMIT_MAX (3U << 29) /* Max lowLimit allowed */ +/* Maximum chunk size before overflow correction needs to be called again */ +#define ZSTD_CHUNKSIZE_MAX \ + ( ((U32)-1) /* Maximum ending current index */ \ + - (1U << ZSTD_WINDOWLOG_MAX) /* Max distance from lowLimit to current */ \ + - ZSTD_LOWLIMIT_MAX) /* Maximum beginning lowLimit */ + +/** + * ZSTD_window_clear(): + * Clears the window containing the history by simply setting it to empty. + */ +MEM_STATIC void ZSTD_window_clear(ZSTD_window_t* window) +{ + size_t const endT = (size_t)(window->nextSrc - window->base); + U32 const end = (U32)endT; + + window->lowLimit = end; + window->dictLimit = end; +} + +/** + * ZSTD_window_hasExtDict(): + * Returns non-zero if the window has a non-empty extDict. + */ +MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window) +{ + return window.lowLimit < window.dictLimit; +} + +/** + * ZSTD_window_needOverflowCorrection(): + * Returns non-zero if the indices are getting too large and need overflow + * protection. + */ +MEM_STATIC U32 ZSTD_window_needOverflowCorrection(ZSTD_window_t const window) +{ + return window.lowLimit > ZSTD_LOWLIMIT_MAX; +} + +/** + * ZSTD_window_correctOverflow(): + * Reduces the indices to protect from index overflow. + * Returns the correction made to the indices, which must be applied to every + * stored index. + * + * The least significant cycleLog bits of the indices must remain the same, + * which may be 0. Every index up to maxDist in the past must be valid. + * NOTE: (maxDist & cycleMask) must be zero. + */ +MEM_STATIC U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog, + U32 maxDist, void const* src) +{ + /* preemptive overflow correction: + * 1. correction is large enough: + * lowLimit > (3<<29) ==> current > 3<<29 + 1< (3<<29 + 1< (3<<29) - (1< (3<<29) - (1<<30) (NOTE: chainLog <= 30) + * > 1<<29 + * + * 2. (ip+ZSTD_CHUNKSIZE_MAX - cctx->base) doesn't overflow: + * After correction, current is less than (1<base < 1<<32. + * 3. (cctx->lowLimit + 1< 3<<29 + 1<base); + U32 const newCurrent = (current & cycleMask) + maxDist; + U32 const correction = current - newCurrent; + assert((maxDist & cycleMask) == 0); + assert(current > newCurrent); + /* Loose bound, should be around 1<<29 (see above) */ + assert(correction > 1<<28); + + window->base += correction; + window->dictBase += correction; + window->lowLimit -= correction; + window->dictLimit -= correction; + + DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x", correction, + window->lowLimit); + return correction; +} + +/** + * ZSTD_window_enforceMaxDist(): + * Sets lowLimit such that indices earlier than (srcEnd - base) - lowLimit are + * invalid. This allows a simple check index >= lowLimit to see if it is valid. + * Source pointers past srcEnd are not guaranteed to be valid. + */ +MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window, + void const* srcEnd, U32 maxDist) +{ + U32 const current = (U32)((BYTE const*)srcEnd - window->base); + if (current > maxDist) { + U32 const newLowLimit = current - maxDist; + if (window->lowLimit < newLowLimit) window->lowLimit = newLowLimit; + if (window->dictLimit < window->lowLimit) { + DEBUGLOG(5, "Update dictLimit from %u to %u", window->dictLimit, + window->lowLimit); + window->dictLimit = window->lowLimit; + } + } +} + +/** + * ZSTD_window_update(): + * Updates the window by appending [src, src + srcSize) to the window. + * If it is not contiguous, the current prefix becomes the extDict, and we + * forget about the extDict. Handles overlap of the prefix and extDict. + * Returns non-zero if the segment is contiguous. + */ +MEM_STATIC U32 ZSTD_window_update(ZSTD_window_t* window, + void const* src, size_t srcSize) +{ + BYTE const* const ip = (BYTE const*)src; + U32 contiguous = 1; + /* Check if blocks follow each other */ + if (src != window->nextSrc) { + /* not contiguous */ + size_t const distanceFromBase = (size_t)(window->nextSrc - window->base); + DEBUGLOG(5, "Non contiguous blocks, new segment starts at %u", + window->dictLimit); + window->lowLimit = window->dictLimit; + assert(distanceFromBase == (size_t)(U32)distanceFromBase); /* should never overflow */ + window->dictLimit = (U32)distanceFromBase; + window->dictBase = window->base; + window->base = ip - distanceFromBase; + // ms->nextToUpdate = window->dictLimit; + if (window->dictLimit - window->lowLimit < HASH_READ_SIZE) window->lowLimit = window->dictLimit; /* too small extDict */ + contiguous = 0; + } + window->nextSrc = ip + srcSize; + /* if input and dictionary overlap : reduce dictionary (area presumed modified by input) */ + if ( (ip+srcSize > window->dictBase + window->lowLimit) + & (ip < window->dictBase + window->dictLimit)) { + ptrdiff_t const highInputIdx = (ip + srcSize) - window->dictBase; + U32 const lowLimitMax = (highInputIdx > (ptrdiff_t)window->dictLimit) ? window->dictLimit : (U32)highInputIdx; + window->lowLimit = lowLimitMax; + } + return contiguous; +} + #if defined (__cplusplus) } #endif diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 6415480e2..86e6b3962 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -21,7 +21,7 @@ void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, U32 const mls = cParams->searchLength; U32* const hashSmall = ms->chainTable; U32 const hBitsS = cParams->chainLog; - const BYTE* const base = ms->base; + const BYTE* const base = ms->window.base; const BYTE* ip = base + ms->nextToUpdate; const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; const U32 fastHashFillStep = 3; @@ -55,11 +55,11 @@ size_t ZSTD_compressBlock_doubleFast_generic( const U32 hBitsL = cParams->hashLog; U32* const hashSmall = ms->chainTable; const U32 hBitsS = cParams->chainLog; - const BYTE* const base = ms->base; + const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ms->dictLimit; + const U32 lowestIndex = ms->window.dictLimit; const BYTE* const lowest = base + lowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; @@ -187,14 +187,14 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic( U32 const hBitsL = cParams->hashLog; U32* const hashSmall = ms->chainTable; U32 const hBitsS = cParams->chainLog; - const BYTE* const base = ms->base; - const BYTE* const dictBase = ms->dictBase; + const BYTE* const base = ms->window.base; + const BYTE* const dictBase = ms->window.dictBase; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ms->lowLimit; + const U32 lowestIndex = ms->window.lowLimit; const BYTE* const dictStart = dictBase + lowestIndex; - const U32 dictLimit = ms->dictLimit; + const U32 dictLimit = ms->window.dictLimit; const BYTE* const lowPrefixPtr = base + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const iend = istart + srcSize; diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index b60f21580..d2f50488e 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -19,7 +19,7 @@ void ZSTD_fillHashTable(ZSTD_matchState_t* ms, U32* const hashTable = ms->hashTable; U32 const hBits = cParams->hashLog; U32 const mls = cParams->searchLength; - const BYTE* const base = ms->base; + const BYTE* const base = ms->window.base; const BYTE* ip = base + ms->nextToUpdate; const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; const U32 fastHashFillStep = 3; @@ -45,11 +45,11 @@ size_t ZSTD_compressBlock_fast_generic( U32 const hlog, U32 const mls) { U32* const hashTable = ms->hashTable; - const BYTE* const base = ms->base; + const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ms->dictLimit; + const U32 lowestIndex = ms->window.dictLimit; const BYTE* const lowest = base + lowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; @@ -149,14 +149,14 @@ static size_t ZSTD_compressBlock_fast_extDict_generic( U32 const hlog, U32 const mls) { U32* hashTable = ms->hashTable; - const BYTE* const base = ms->base; - const BYTE* const dictBase = ms->dictBase; + const BYTE* const base = ms->window.base; + const BYTE* const dictBase = ms->window.dictBase; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ms->lowLimit; + const U32 lowestIndex = ms->window.lowLimit; const BYTE* const dictStart = dictBase + lowestIndex; - const U32 dictLimit = ms->dictLimit; + const U32 dictLimit = ms->window.dictLimit; const BYTE* const lowPrefixPtr = base + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const iend = istart + srcSize; diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 0dab117a3..9f158123f 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -28,17 +28,17 @@ void ZSTD_updateDUBT( U32 const btLog = cParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; - const BYTE* const base = ms->base; + const BYTE* const base = ms->window.base; U32 const target = (U32)(ip - base); U32 idx = ms->nextToUpdate; if (idx != target) DEBUGLOG(7, "ZSTD_updateDUBT, from %u to %u (dictLimit:%u)", - idx, target, ms->dictLimit); + idx, target, ms->window.dictLimit); assert(ip + 8 <= iend); /* condition for ZSTD_hashPtr */ (void)iend; - assert(idx >= ms->dictLimit); /* condition for valid base+idx */ + assert(idx >= ms->window.dictLimit); /* condition for valid base+idx */ for ( ; idx < target ; idx++) { size_t const h = ZSTD_hashPtr(base + idx, hashLog, mls); /* assumption : ip + 8 <= iend */ U32 const matchIndex = hashTable[h]; @@ -68,9 +68,9 @@ static void ZSTD_insertDUBT1( U32 const btLog = cParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; size_t commonLengthSmaller=0, commonLengthLarger=0; - const BYTE* const base = ms->base; - const BYTE* const dictBase = ms->dictBase; - const U32 dictLimit = ms->dictLimit; + const BYTE* const base = ms->window.base; + const BYTE* const dictBase = ms->window.dictBase; + const U32 dictLimit = ms->window.dictLimit; const BYTE* const ip = (current>=dictLimit) ? base + current : dictBase + current; const BYTE* const iend = (current>=dictLimit) ? inputEnd : dictBase + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; @@ -80,7 +80,7 @@ static void ZSTD_insertDUBT1( U32* largerPtr = smallerPtr + 1; U32 matchIndex = *smallerPtr; U32 dummy32; /* to be nullified at the end */ - U32 const windowLow = ms->lowLimit; + U32 const windowLow = ms->window.lowLimit; DEBUGLOG(8, "ZSTD_insertDUBT1(%u) (dictLimit=%u, lowLimit=%u)", current, dictLimit, windowLow); @@ -150,9 +150,9 @@ static size_t ZSTD_DUBT_findBestMatch ( size_t const h = ZSTD_hashPtr(ip, hashLog, mls); U32 matchIndex = hashTable[h]; - const BYTE* const base = ms->base; + const BYTE* const base = ms->window.base; U32 const current = (U32)(ip-base); - U32 const windowLow = ms->lowLimit; + U32 const windowLow = ms->window.lowLimit; U32* const bt = ms->chainTable; U32 const btLog = cParams->chainLog - 1; @@ -203,8 +203,8 @@ static size_t ZSTD_DUBT_findBestMatch ( /* find longest match */ { size_t commonLengthSmaller=0, commonLengthLarger=0; - const BYTE* const dictBase = ms->dictBase; - const U32 dictLimit = ms->dictLimit; + const BYTE* const dictBase = ms->window.dictBase; + const U32 dictLimit = ms->window.dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; U32* smallerPtr = bt + 2*(current&btMask); @@ -279,7 +279,7 @@ static size_t ZSTD_BtFindBestMatch ( const U32 mls /* template */) { DEBUGLOG(7, "ZSTD_BtFindBestMatch"); - if (ip < ms->base + ms->nextToUpdate) return 0; /* skipped area */ + if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */ ZSTD_updateDUBT(ms, cParams, ip, iLimit, mls); return ZSTD_DUBT_findBestMatch(ms, cParams, ip, iLimit, offsetPtr, mls, 0); } @@ -309,7 +309,7 @@ static size_t ZSTD_BtFindBestMatch_extDict ( const U32 mls) { DEBUGLOG(7, "ZSTD_BtFindBestMatch_extDict"); - if (ip < ms->base + ms->nextToUpdate) return 0; /* skipped area */ + if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */ ZSTD_updateDUBT(ms, cParams, ip, iLimit, mls); return ZSTD_DUBT_findBestMatch(ms, cParams, ip, iLimit, offsetPtr, mls, 1); } @@ -347,7 +347,7 @@ static U32 ZSTD_insertAndFindFirstIndex_internal( const U32 hashLog = cParams->hashLog; U32* const chainTable = ms->chainTable; const U32 chainMask = (1 << cParams->chainLog) - 1; - const BYTE* const base = ms->base; + const BYTE* const base = ms->window.base; const U32 target = (U32)(ip - base); U32 idx = ms->nextToUpdate; @@ -381,12 +381,12 @@ size_t ZSTD_HcFindBestMatch_generic ( U32* const chainTable = ms->chainTable; const U32 chainSize = (1 << cParams->chainLog); const U32 chainMask = chainSize-1; - const BYTE* const base = ms->base; - const BYTE* const dictBase = ms->dictBase; - const U32 dictLimit = ms->dictLimit; + const BYTE* const base = ms->window.base; + const BYTE* const dictBase = ms->window.dictBase; + const U32 dictLimit = ms->window.dictLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; - const U32 lowLimit = ms->lowLimit; + const U32 lowLimit = ms->window.lowLimit; const U32 current = (U32)(ip-base); const U32 minChain = current > chainSize ? current - chainSize : 0; U32 nbAttempts = 1U << cParams->searchLog; @@ -471,7 +471,7 @@ size_t ZSTD_compressBlock_lazy_generic( const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; - const BYTE* const base = ms->base + ms->dictLimit; + const BYTE* const base = ms->window.base + ms->window.dictLimit; typedef size_t (*searchMax_f)( ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, @@ -635,13 +635,13 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; - const BYTE* const base = ms->base; - const U32 dictLimit = ms->dictLimit; - const U32 lowestIndex = ms->lowLimit; + const BYTE* const base = ms->window.base; + const U32 dictLimit = ms->window.dictLimit; + const U32 lowestIndex = ms->window.lowLimit; const BYTE* const prefixStart = base + dictLimit; - const BYTE* const dictBase = ms->dictBase; + const BYTE* const dictBase = ms->window.dictBase; const BYTE* const dictEnd = dictBase + dictLimit; - const BYTE* const dictStart = dictBase + ms->lowLimit; + const BYTE* const dictStart = dictBase + lowestIndex; typedef size_t (*searchMax_f)( ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index 2c304b38b..a043556b0 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -231,12 +231,12 @@ static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms, { case ZSTD_fast: ZSTD_fillHashTable(ms, cParams, iend); - ms->nextToUpdate = (U32)(iend - ms->base); + ms->nextToUpdate = (U32)(iend - ms->window.base); break; case ZSTD_dfast: ZSTD_fillDoubleHashTable(ms, cParams, iend); - ms->nextToUpdate = (U32)(iend - ms->base); + ms->nextToUpdate = (U32)(iend - ms->window.base); break; case ZSTD_greedy: @@ -287,7 +287,7 @@ static U64 ZSTD_ldm_fillLdmHashTable(ldmState_t* state, * (after a long match, only update tables a limited amount). */ static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor) { - U32 const current = (U32)(anchor - ms->base); + U32 const current = (U32)(anchor - ms->window.base); if (current > ms->nextToUpdate + 1024) { ms->nextToUpdate = current - MIN(512, current - ms->nextToUpdate - 1024); @@ -308,10 +308,10 @@ size_t ZSTD_ldm_generateSequences( U32 const hashEveryLog = params->hashEveryLog; U32 const ldmTagMask = (1U << params->hashEveryLog) - 1; /* Prefix and extDict parameters */ - U32 const dictLimit = ms->dictLimit; - U32 const lowestIndex = extDict ? ms->lowLimit : dictLimit; - BYTE const* const base = ms->base; - BYTE const* const dictBase = extDict ? ms->dictBase : NULL; + U32 const dictLimit = ms->window.dictLimit; + U32 const lowestIndex = extDict ? ms->window.lowLimit : dictLimit; + BYTE const* const base = ms->window.base; + BYTE const* const dictBase = extDict ? ms->window.dictBase : NULL; BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL; BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL; BYTE const* const lowPrefixPtr = base + dictLimit; @@ -514,7 +514,7 @@ size_t ZSTD_ldm_blockCompress(rawSeq const* sequences, size_t nbSeq, { ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(cParams->strategy, extDict); - BYTE const* const base = ms->base; + BYTE const* const base = ms->window.base; /* Input bounds */ BYTE const* const istart = (BYTE const*)src; BYTE const* const iend = istart + srcSize; diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 149e63687..f63f0c585 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -247,7 +247,7 @@ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE* { U32* const hashTable3 = ms->hashTable3; U32 const hashLog3 = ms->hashLog3; - const BYTE* const base = ms->base; + const BYTE* const base = ms->window.base; U32 idx = ms->nextToUpdate3; U32 const target = ms->nextToUpdate3 = (U32)(ip - base); size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3); @@ -281,9 +281,9 @@ static U32 ZSTD_insertBt1( U32 const btMask = (1 << btLog) - 1; U32 matchIndex = hashTable[h]; size_t commonLengthSmaller=0, commonLengthLarger=0; - const BYTE* const base = ms->base; - const BYTE* const dictBase = ms->dictBase; - const U32 dictLimit = ms->dictLimit; + const BYTE* const base = ms->window.base; + const BYTE* const dictBase = ms->window.dictBase; + const U32 dictLimit = ms->window.dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* match; @@ -292,7 +292,7 @@ static U32 ZSTD_insertBt1( U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = smallerPtr + 1; U32 dummy32; /* to be nullified at the end */ - U32 const windowLow = ms->lowLimit; + U32 const windowLow = ms->window.lowLimit; U32 matchEndIdx = current+8+1; size_t bestLength = 8; U32 nbCompares = 1U << cParams->searchLog; @@ -383,7 +383,7 @@ void ZSTD_updateTree_internal( const BYTE* const ip, const BYTE* const iend, const U32 mls, const U32 extDict) { - const BYTE* const base = ms->base; + const BYTE* const base = ms->window.base; U32 const target = (U32)(ip - base); U32 idx = ms->nextToUpdate; DEBUGLOG(7, "ZSTD_updateTree_internal, from %u to %u (extDict:%u)", @@ -409,7 +409,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( ZSTD_match_t* matches, const U32 lengthToBeat, U32 const mls /* template */) { U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1); - const BYTE* const base = ms->base; + const BYTE* const base = ms->window.base; U32 const current = (U32)(ip-base); U32 const hashLog = cParams->hashLog; U32 const minMatch = (mls==3) ? 3 : 4; @@ -420,12 +420,12 @@ U32 ZSTD_insertBtAndGetAllMatches ( U32 const btLog = cParams->chainLog - 1; U32 const btMask= (1U << btLog) - 1; size_t commonLengthSmaller=0, commonLengthLarger=0; - const BYTE* const dictBase = ms->dictBase; - U32 const dictLimit = ms->dictLimit; + const BYTE* const dictBase = ms->window.dictBase; + U32 const dictLimit = ms->window.dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit; U32 const btLow = btMask >= current ? 0 : current - btMask; - U32 const windowLow = ms->lowLimit; + U32 const windowLow = ms->window.lowLimit; U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = bt + 2*(current&btMask) + 1; U32 matchEndIdx = current+8+1; /* farthest referenced position of any match => detects repetitive patterns */ @@ -566,7 +566,7 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches ( { U32 const matchLengthSearch = cParams->searchLength; DEBUGLOG(7, "ZSTD_BtGetAllMatches"); - if (ip < ms->base + ms->nextToUpdate) return 0; /* skipped area */ + if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */ ZSTD_updateTree_internal(ms, cParams, ip, iHighLimit, matchLengthSearch, extDict); switch(matchLengthSearch) { @@ -675,8 +675,8 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,seqStore_t* seqStore const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; - const BYTE* const base = ms->base; - const BYTE* const prefixStart = base + ms->dictLimit; + const BYTE* const base = ms->window.base; + const BYTE* const prefixStart = base + ms->window.dictLimit; U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1); U32 const minMatch = (cParams->searchLength == 3) ? 3 : 4; From 6b88d592fd4eb855a80517f2d1c185b5905cc92b Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 26 Feb 2018 13:30:24 -0800 Subject: [PATCH 150/255] Reduce ZSTD_CHAINLOG_MAX to 29 in 32-bit mode --- lib/zstd.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/zstd.h b/lib/zstd.h index 986b81980..deedd3643 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -381,7 +381,9 @@ ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output #define ZSTD_WINDOWLOG_MIN 10 #define ZSTD_HASHLOG_MAX ((ZSTD_WINDOWLOG_MAX < 30) ? ZSTD_WINDOWLOG_MAX : 30) #define ZSTD_HASHLOG_MIN 6 -#define ZSTD_CHAINLOG_MAX ((ZSTD_WINDOWLOG_MAX < 29) ? ZSTD_WINDOWLOG_MAX+1 : 30) +#define ZSTD_CHAINLOG_MAX_32 29 +#define ZSTD_CHAINLOG_MAX_64 30 +#define ZSTD_CHAINLOG_MAX ((unsigned)(sizeof(size_t) == 4 ? ZSTD_CHAINLOG_MAX_32 : ZSTD_CHAINLOG_MAX_64)) #define ZSTD_CHAINLOG_MIN ZSTD_HASHLOG_MIN #define ZSTD_HASHLOG3_MAX 17 #define ZSTD_SEARCHLOG_MAX (ZSTD_WINDOWLOG_MAX-1) From 6cdf690441a8fdcd4d19174b16f54997943b7ce2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 26 Feb 2018 14:52:23 -0800 Subject: [PATCH 151/255] minor cleaning of huff0 Update code documentation, and properly names a few "magic constants". Also, HUF_compress_internal() gets a cleaner way to determine size of tables inside workspace. --- lib/common/huf.h | 19 +++--- lib/compress/huf_compress.c | 112 +++++++++++++++++++------------- lib/decompress/huf_decompress.c | 13 ++-- 3 files changed, 85 insertions(+), 59 deletions(-) diff --git a/lib/common/huf.h b/lib/common/huf.h index cadee595d..4e7eee0cb 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -67,7 +67,6 @@ HUF_compress() : `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB. @return : size of compressed data (<= `dstCapacity`). Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!! - if return == 1, srcData is a single repeated byte symbol (RLE compression). if HUF_isError(return), compression failed (more details using HUF_getErrorName()) */ HUF_PUBLIC_API size_t HUF_compress(void* dst, size_t dstCapacity, @@ -80,7 +79,7 @@ HUF_decompress() : `originalSize` : **must** be the ***exact*** size of original (uncompressed) data. Note : in contrast with FSE, HUF_decompress can regenerate RLE (cSrcSize==1) and uncompressed (cSrcSize==dstSize) data, - because it knows size to regenerate. + because it knows size to regenerate (originalSize). @return : size of regenerated data (== originalSize), or an error code, which can be tested using HUF_isError() */ @@ -101,6 +100,7 @@ HUF_PUBLIC_API const char* HUF_getErrorName(size_t code); /**< provides error c /** HUF_compress2() : * Same as HUF_compress(), but offers direct control over `maxSymbolValue` and `tableLog`. + * `maxSymbolValue` must be <= HUF_SYMBOLVALUE_MAX . * `tableLog` must be `<= HUF_TABLELOG_MAX` . */ HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); @@ -129,7 +129,7 @@ HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const /* ****************************************************************** * WARNING !! * The following section contains advanced and experimental definitions - * which shall never be used in the context of dll + * which shall never be used in the context of a dynamic library, * because they are not guaranteed to remain stable in the future. * Only consider them in association with static linking. *******************************************************************/ @@ -141,11 +141,11 @@ HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const /* *** Constants *** */ -#define HUF_TABLELOG_MAX 12 /* max configured tableLog (for static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */ -#define HUF_TABLELOG_DEFAULT 11 /* tableLog by default, when not specified */ +#define HUF_TABLELOG_MAX 12 /* max runtime value of tableLog (due to static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */ +#define HUF_TABLELOG_DEFAULT 11 /* default tableLog value when none specified */ #define HUF_SYMBOLVALUE_MAX 255 -#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ +#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ #if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX) # error "HUF_TABLELOG_MAX is too large !" #endif @@ -227,8 +227,9 @@ size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. - * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of 1024 unsigned. + * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of HUF_CTABLE_WORKSPACE_SIZE_U32 unsigned. */ + #define HUF_CTABLE_WORKSPACE_SIZE_U32 (2*HUF_SYMBOLVALUE_MAX +1 +1) size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize); /*! HUF_readStats() : @@ -236,8 +237,8 @@ size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValu `huffWeight` is destination buffer. @return : size read from `src` , or an error Code . Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */ -size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats, - U32* nbSymbolsPtr, U32* tableLogPtr, +size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, + U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr, const void* src, size_t srcSize); /** HUF_readCTable() : diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 10320f75f..59c5c6c85 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -335,10 +335,10 @@ static void HUF_sort(nodeElt* huffNode, const U32* count, U32 maxSymbolValue) /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. - * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of 1024 unsigned. + * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of HUF_CTABLE_WORKSPACE_SIZE_U32 unsigned. */ #define STARTNODE (HUF_SYMBOLVALUE_MAX+1) -typedef nodeElt huffNodeTable[2*HUF_SYMBOLVALUE_MAX+1 +1]; +typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32]; size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize) { nodeElt* const huffNode0 = (nodeElt*)workSpace; @@ -349,9 +349,10 @@ size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValu U32 nodeRoot; /* safety checks */ - if (wkspSize < sizeof(huffNodeTable)) return ERROR(GENERIC); /* workSpace is not large enough */ + if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */ + if (wkspSize < sizeof(huffNodeTable)) return ERROR(workSpace_tooSmall); if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT; - if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(GENERIC); + if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge); memset(huffNode0, 0, sizeof(huffNodeTable)); /* sort, decreasing order */ @@ -508,8 +509,14 @@ static size_t HUF_compressCTable_internal( return op-ostart; } +typedef struct { + U32 count[HUF_SYMBOLVALUE_MAX + 1]; + HUF_CElt CTable[HUF_SYMBOLVALUE_MAX + 1]; + huffNodeTable nodeTable; +} HUF_compress_tables_t; -/* `workSpace` must a table of at least 1024 unsigned */ +/* HUF_compress_internal() : + * `workSpace` must a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ static size_t HUF_compress_internal ( void* dst, size_t dstSize, const void* src, size_t srcSize, @@ -519,76 +526,81 @@ static size_t HUF_compress_internal ( HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat, const int bmi2) { + HUF_compress_tables_t* const table = (HUF_compress_tables_t*)workSpace; BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstSize; BYTE* op = ostart; - U32* count; - size_t const countSize = sizeof(U32) * (HUF_SYMBOLVALUE_MAX + 1); - HUF_CElt* CTable; - size_t const CTableSize = sizeof(HUF_CElt) * (HUF_SYMBOLVALUE_MAX + 1); - /* checks & inits */ - if (wkspSize < sizeof(huffNodeTable) + countSize + CTableSize) return ERROR(GENERIC); - if (!srcSize) return 0; /* Uncompressed (note : 1 means rle, so first byte must be correct) */ - if (!dstSize) return 0; /* cannot fit within dst budget */ + if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */ + if (wkspSize < sizeof(table)) return ERROR(workSpace_tooSmall); + if (!srcSize) return 0; /* Uncompressed */ + if (!dstSize) return 0; /* cannot fit anything within dst budget */ if (srcSize > HUF_BLOCKSIZE_MAX) return ERROR(srcSize_wrong); /* current block size limit */ if (huffLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); + if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge); if (!maxSymbolValue) maxSymbolValue = HUF_SYMBOLVALUE_MAX; if (!huffLog) huffLog = HUF_TABLELOG_DEFAULT; - count = (U32*)workSpace; - workSpace = (BYTE*)workSpace + countSize; - wkspSize -= countSize; - CTable = (HUF_CElt*)workSpace; - workSpace = (BYTE*)workSpace + CTableSize; - wkspSize -= CTableSize; - - /* Heuristic : If we don't need to check the validity of the old table use the old table for small inputs */ + /* Heuristic : If old table is valid, use it for small inputs */ if (preferRepeat && repeat && *repeat == HUF_repeat_valid) { - return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable, bmi2); + return HUF_compressCTable_internal(ostart, op, oend, + src, srcSize, + singleStream, oldHufTable, bmi2); } /* Scan input and build symbol stats */ - { CHECK_V_F(largest, FSE_count_wksp (count, &maxSymbolValue, (const BYTE*)src, srcSize, (U32*)workSpace) ); + { CHECK_V_F(largest, FSE_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, table->count) ); if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */ - if (largest <= (srcSize >> 7)+1) return 0; /* Fast heuristic : not compressible enough */ + if (largest <= (srcSize >> 7)+1) return 0; /* heuristic : probably not compressible enough */ } /* Check validity of previous table */ - if (repeat && *repeat == HUF_repeat_check && !HUF_validateCTable(oldHufTable, count, maxSymbolValue)) { + if ( repeat + && *repeat == HUF_repeat_check + && !HUF_validateCTable(oldHufTable, table->count, maxSymbolValue)) { *repeat = HUF_repeat_none; } /* Heuristic : use existing table for small inputs */ if (preferRepeat && repeat && *repeat != HUF_repeat_none) { - return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable, bmi2); + return HUF_compressCTable_internal(ostart, op, oend, + src, srcSize, + singleStream, oldHufTable, bmi2); } /* Build Huffman Tree */ huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); - { CHECK_V_F(maxBits, HUF_buildCTable_wksp (CTable, count, maxSymbolValue, huffLog, workSpace, wkspSize) ); + { CHECK_V_F(maxBits, HUF_buildCTable_wksp(table->CTable, table->count, + maxSymbolValue, huffLog, + table->nodeTable, sizeof(table->nodeTable)) ); huffLog = (U32)maxBits; - /* Zero the unused symbols so we can check it for validity */ - memset(CTable + maxSymbolValue + 1, 0, CTableSize - (maxSymbolValue + 1) * sizeof(HUF_CElt)); + /* Zero unused symbols in CTable, so we can check it for validity */ + memset(table->CTable + (maxSymbolValue + 1), 0, + sizeof(table->CTable) - ((maxSymbolValue + 1) * sizeof(HUF_CElt))); } /* Write table description header */ - { CHECK_V_F(hSize, HUF_writeCTable (op, dstSize, CTable, maxSymbolValue, huffLog) ); - /* Check if using the previous table will be beneficial */ + { CHECK_V_F(hSize, HUF_writeCTable (op, dstSize, table->CTable, maxSymbolValue, huffLog) ); + /* Check if using previous huffman table is beneficial */ if (repeat && *repeat != HUF_repeat_none) { - size_t const oldSize = HUF_estimateCompressedSize(oldHufTable, count, maxSymbolValue); - size_t const newSize = HUF_estimateCompressedSize(CTable, count, maxSymbolValue); + size_t const oldSize = HUF_estimateCompressedSize(oldHufTable, table->count, maxSymbolValue); + size_t const newSize = HUF_estimateCompressedSize(table->CTable, table->count, maxSymbolValue); if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) { - return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable, bmi2); - } - } - /* Use the new table */ + return HUF_compressCTable_internal(ostart, op, oend, + src, srcSize, + singleStream, oldHufTable, bmi2); + } } + + /* Use the new huffman table */ if (hSize + 12ul >= srcSize) { return 0; } op += hSize; if (repeat) { *repeat = HUF_repeat_none; } - if (oldHufTable) { memcpy(oldHufTable, CTable, CTableSize); } /* Save the new table */ + if (oldHufTable) + memcpy(oldHufTable, table->CTable, sizeof(table->CTable)); /* Save new table */ } - return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, CTable, bmi2); + return HUF_compressCTable_internal(ostart, op, oend, + src, srcSize, + singleStream, table->CTable, bmi2); } @@ -597,7 +609,10 @@ size_t HUF_compress1X_wksp (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, NULL, NULL, 0, /* bmi2 */ 0); + return HUF_compress_internal(dst, dstSize, src, srcSize, + maxSymbolValue, huffLog, 1 /*single stream*/, + workSpace, wkspSize, + NULL, NULL, 0, 0 /*bmi2*/); } size_t HUF_compress1X_repeat (void* dst, size_t dstSize, @@ -606,7 +621,10 @@ size_t HUF_compress1X_repeat (void* dst, size_t dstSize, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, hufTable, repeat, preferRepeat, bmi2); + return HUF_compress_internal(dst, dstSize, src, srcSize, + maxSymbolValue, huffLog, 1 /*single stream*/, + workSpace, wkspSize, hufTable, + repeat, preferRepeat, bmi2); } size_t HUF_compress1X (void* dst, size_t dstSize, @@ -625,7 +643,10 @@ size_t HUF_compress4X_wksp (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, NULL, NULL, 0, /* bmi2 */ 0); + return HUF_compress_internal(dst, dstSize, src, srcSize, + maxSymbolValue, huffLog, 0 /*4 streams*/, + workSpace, wkspSize, + NULL, NULL, 0, 0 /*bmi2*/); } /* HUF_compress4X_repeat(): @@ -637,7 +658,10 @@ size_t HUF_compress4X_repeat (void* dst, size_t dstSize, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, hufTable, repeat, preferRepeat, bmi2); + return HUF_compress_internal(dst, dstSize, src, srcSize, + maxSymbolValue, huffLog, 0 /* 4 streams */, + workSpace, wkspSize, + hufTable, repeat, preferRepeat, bmi2); } size_t HUF_compress2 (void* dst, size_t dstSize, @@ -650,5 +674,5 @@ size_t HUF_compress2 (void* dst, size_t dstSize, size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize) { - return HUF_compress2(dst, maxDstSize, src, (U32)srcSize, 255, HUF_TABLELOG_DEFAULT); + return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT); } diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 9afec6d50..d2e1bb4ac 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -168,18 +168,19 @@ HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, cons BYTE* const pStart = p; /* up to 4 symbols at a time */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd-4)) { + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-3)) { HUF_DECODE_SYMBOLX2_2(p, bitDPtr); HUF_DECODE_SYMBOLX2_1(p, bitDPtr); HUF_DECODE_SYMBOLX2_2(p, bitDPtr); HUF_DECODE_SYMBOLX2_0(p, bitDPtr); } - /* closer to the end */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd)) - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + /* [0-3] symbols remaining */ + if (MEM_32bits()) + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd)) + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); - /* no more data to retrieve from bitstream, hence no need to reload */ + /* no more data to retrieve from bitstream, no need to reload */ while (p < pEnd) HUF_DECODE_SYMBOLX2_0(p, bitDPtr); @@ -282,7 +283,7 @@ HUF_decompress4X2_usingDTable_internal_body( /* check corruption */ /* note : should not be necessary : op# advance in lock step, and we control op4. - * but curiously, binary generated by gcc 7.2 & 7.3 with -mbmi2 runs faster when >=2 tests are present */ + * but curiously, binary generated by gcc 7.2 & 7.3 with -mbmi2 runs faster when >=1 test is present */ if (op1 > opStart2) return ERROR(corruption_detected); if (op2 > opStart3) return ERROR(corruption_detected); if (op3 > opStart4) return ERROR(corruption_detected); From 89741653abc390a1fa879b460720b79e5385f9bc Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 26 Feb 2018 15:11:50 -0800 Subject: [PATCH 152/255] added error code workSpace_tooSmall --- lib/common/error_private.c | 1 + lib/common/zstd_errors.h | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/common/error_private.c b/lib/common/error_private.c index 11f7cdab1..d004ee636 100644 --- a/lib/common/error_private.c +++ b/lib/common/error_private.c @@ -29,6 +29,7 @@ const char* ERR_getErrorString(ERR_enum code) case PREFIX(parameter_outOfBound): return "Parameter is out of bound"; case PREFIX(init_missing): return "Context should be init first"; case PREFIX(memory_allocation): return "Allocation error : not enough memory"; + case PREFIX(workSpace_tooSmall): return "workSpace buffer is not large enough"; case PREFIX(stage_wrong): return "Operation not authorized at current processing stage"; case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported"; case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large"; diff --git a/lib/common/zstd_errors.h b/lib/common/zstd_errors.h index 2af61f3bd..57533f286 100644 --- a/lib/common/zstd_errors.h +++ b/lib/common/zstd_errors.h @@ -69,6 +69,7 @@ typedef enum { ZSTD_error_stage_wrong = 60, ZSTD_error_init_missing = 62, ZSTD_error_memory_allocation = 64, + ZSTD_error_workSpace_tooSmall= 66, ZSTD_error_dstSize_tooSmall = 70, ZSTD_error_srcSize_wrong = 72, /* following error codes are __NOT STABLE__, they can be removed or changed in future versions */ From 33a3f188488e68e67fe15b73ecbf7e56f981088c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 26 Feb 2018 18:27:51 -0800 Subject: [PATCH 153/255] fixed wrong size test --- lib/compress/huf_compress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 59c5c6c85..69007cd92 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -533,7 +533,7 @@ static size_t HUF_compress_internal ( /* checks & inits */ if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */ - if (wkspSize < sizeof(table)) return ERROR(workSpace_tooSmall); + if (wkspSize < sizeof(*table)) return ERROR(workSpace_tooSmall); if (!srcSize) return 0; /* Uncompressed */ if (!dstSize) return 0; /* cannot fit anything within dst budget */ if (srcSize > HUF_BLOCKSIZE_MAX) return ERROR(srcSize_wrong); /* current block size limit */ From 0a0e64c641f632f4b552f10d5cb158799a8a7638 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 23 Feb 2018 18:17:44 -0800 Subject: [PATCH 154/255] LDM manages its own window round buffer --- lib/compress/zstd_compress.c | 25 +++------- lib/compress/zstd_compress_internal.h | 2 + lib/compress/zstd_ldm.c | 68 ++++++++++++++++++++++++--- lib/compress/zstd_ldm.h | 6 ++- 4 files changed, 74 insertions(+), 27 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 467515370..66fb4827a 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -983,6 +983,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, if (params.ldmParams.enableLdm) { /* Adjust long distance matching parameters */ + params.ldmParams.windowLog = params.cParams.windowLog; ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams); assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog); assert(params.ldmParams.hashEveryLog < 32); @@ -1067,6 +1068,8 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ptr = zc->ldmState.hashTable + ldmHSize; zc->ldmSequences = (rawSeq*)ptr; ptr = zc->ldmSequences + maxNbLdmSeq; + + memset(&zc->ldmState.window, 0, sizeof(zc->ldmState.window)); } assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ @@ -1277,19 +1280,6 @@ static void ZSTD_reduceTable_btlazy2(U32* const table, U32 const size, U32 const ZSTD_reduceTable_internal(table, size, reducerValue, 1); } - -/*! ZSTD_ldm_reduceTable() : - * reduce table indexes by `reducerValue` */ -static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size, - U32 const reducerValue) -{ - U32 u; - for (u = 0; u < size; u++) { - if (table[u].offset < reducerValue) table[u].offset = 0; - else table[u].offset -= reducerValue; - } -} - /*! ZSTD_reduceIndex() : * rescale all indexes to avoid future overflow (indexes are U32) */ static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue) @@ -1311,11 +1301,6 @@ static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue) U32 const h3Size = (U32)1 << ms->hashLog3; ZSTD_reduceTable(ms->hashTable3, h3Size, reducerValue); } - - if (zc->appliedParams.ldmParams.enableLdm) { - U32 const ldmHSize = (U32)1 << zc->appliedParams.ldmParams.hashLog; - ZSTD_ldm_reduceTable(zc->ldmState.hashTable, ldmHSize, reducerValue); - } } @@ -1832,7 +1817,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, if (zc->appliedParams.ldmParams.enableLdm) { size_t const nbSeq = ZSTD_ldm_generateSequences(&zc->ldmState, zc->ldmSequences, - ms, &zc->appliedParams.ldmParams, + &zc->appliedParams.ldmParams, src, srcSize, extDict); lastLLSize = ZSTD_ldm_blockCompress(zc->ldmSequences, nbSeq, @@ -2023,6 +2008,8 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, if (!ZSTD_window_update(&ms->window, src, srcSize)) { ms->nextToUpdate = ms->window.dictLimit; } + if (cctx->appliedParams.ldmParams.enableLdm) + ZSTD_window_update(&cctx->ldmState.window, src, srcSize); DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (U32)cctx->blockSize); { size_t const cSize = frame ? diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 98f866cde..92bd0c707 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -136,6 +136,7 @@ typedef struct { } ldmEntry_t; typedef struct { + ZSTD_window_t window; /* State for the window round buffer management */ ldmEntry_t* hashTable; BYTE* bucketOffsets; /* Next position in bucket to insert entry */ U64 hashPower; /* Used to compute the rolling hash. @@ -148,6 +149,7 @@ typedef struct { U32 bucketSizeLog; /* Log bucket size for collision resolution, at most 8 */ U32 minMatchLength; /* Minimum match length */ U32 hashEveryLog; /* Log number of entries to skip */ + U32 windowLog; /* Window log for the LDM */ } ldmParams_t; struct ZSTD_CCtx_params_s { diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index a043556b0..22f9b4a88 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -294,8 +294,8 @@ static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor) } } -size_t ZSTD_ldm_generateSequences( - ldmState_t* ldmState, rawSeq* sequences, ZSTD_matchState_t const* ms, +static size_t ZSTD_ldm_generateSequences_internal( + ldmState_t* ldmState, rawSeq* sequences, ldmParams_t const* params, void const* src, size_t srcSize, int const extDict) { @@ -308,10 +308,10 @@ size_t ZSTD_ldm_generateSequences( U32 const hashEveryLog = params->hashEveryLog; U32 const ldmTagMask = (1U << params->hashEveryLog) - 1; /* Prefix and extDict parameters */ - U32 const dictLimit = ms->window.dictLimit; - U32 const lowestIndex = extDict ? ms->window.lowLimit : dictLimit; - BYTE const* const base = ms->window.base; - BYTE const* const dictBase = extDict ? ms->window.dictBase : NULL; + U32 const dictLimit = ldmState->window.dictLimit; + U32 const lowestIndex = extDict ? ldmState->window.lowLimit : dictLimit; + BYTE const* const base = ldmState->window.base; + BYTE const* const dictBase = extDict ? ldmState->window.dictBase : NULL; BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL; BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL; BYTE const* const lowPrefixPtr = base + dictLimit; @@ -453,6 +453,62 @@ size_t ZSTD_ldm_generateSequences( return sequences - sequencesStart; } +/*! ZSTD_ldm_reduceTable() : + * reduce table indexes by `reducerValue` */ +static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size, + U32 const reducerValue) +{ + U32 u; + for (u = 0; u < size; u++) { + if (table[u].offset < reducerValue) table[u].offset = 0; + else table[u].offset -= reducerValue; + } +} + +size_t ZSTD_ldm_generateSequences( + ldmState_t* ldmState, rawSeq* sequences, + ldmParams_t const* params, void const* src, size_t srcSize, + int const extDict) +{ + U32 const maxDist = 1U << params->windowLog; + BYTE const* const istart = (BYTE const*)src; + size_t const kMaxChunkSize = 1 << 20; + size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0); + size_t nbSeq = 0; + size_t chunk; + + assert(ZSTD_CHUNKSIZE_MAX >= kMaxChunkSize); + /* Check that ZSTD_window_update() has been called for this chunk prior + * to passing it to this function. + */ + assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize); + for (chunk = 0; chunk < nbChunks; ++chunk) { + size_t const chunkStart = chunk * kMaxChunkSize; + size_t const chunkEnd = MIN(chunkStart + kMaxChunkSize, srcSize); + size_t const chunkSize = chunkEnd - chunkStart; + + assert(chunkStart < srcSize); + if (ZSTD_window_needOverflowCorrection(ldmState->window)) { + U32 const ldmHSize = 1U << params->hashLog; + U32 const correction = ZSTD_window_correctOverflow( + &ldmState->window, /* cycleLog */ 0, maxDist, src); + ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction); + } + /* kMaxChunkSize should be small enough that we don't lose too much of + * the window through early invalidation. + * TODO: * Test the chunk size. + * * Try invalidation after the sequence generation and test the + * the offset against maxDist directly. + */ + ZSTD_window_enforceMaxDist(&ldmState->window, istart + chunkEnd, + maxDist); + nbSeq += ZSTD_ldm_generateSequences_internal( + ldmState, sequences + nbSeq, params, istart + chunkStart, chunkSize, + extDict); + } + return nbSeq; +} + #if 0 /** * If the sequence length is longer than remaining then the sequence is split diff --git a/lib/compress/zstd_ldm.h b/lib/compress/zstd_ldm.h index a836033a6..d9219a18f 100644 --- a/lib/compress/zstd_ldm.h +++ b/lib/compress/zstd_ldm.h @@ -29,13 +29,15 @@ extern "C" { * * Generates the sequences using the long distance match finder. * The sequences completely parse a prefix of the source, but leave off the last - * literals. Returns the number of sequences generated into `sequences`. + * literals. Returns the number of sequences generated into `sequences`. The + * user must have called ZSTD_window_update() for all of the input they have, + * even if they pass it to ZSTD_ldm_generateSequences() in chunks. * * NOTE: The source may be any size, assuming it doesn't overflow the hash table * indices, and the output sequences table is large enough.. */ size_t ZSTD_ldm_generateSequences( - ldmState_t* ldms, rawSeq* sequences, ZSTD_matchState_t const* ms, + ldmState_t* ldms, rawSeq* sequences, ldmParams_t const* params, void const* src, size_t srcSize, int const extDict); From 564cb1b640c753160b6655d95bae62d1f03f7781 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 27 Feb 2018 17:37:05 -0800 Subject: [PATCH 155/255] update doc/README.md on provided tools to test 3rd party implementations --- doc/README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/doc/README.md b/doc/README.md index 47cfe3617..1f01fa47d 100644 --- a/doc/README.md +++ b/doc/README.md @@ -2,19 +2,24 @@ Zstandard Documentation ======================= This directory contains material defining the Zstandard format, -as well as for help using the `zstd` library. +as well as detailed instructions to use `zstd` library. + +__`zstd_manual.html`__ : Documentation of `zstd.h` API, in html format. +Click on this link: [http://zstd.net/zstd_manual.html](http://zstd.net/zstd_manual.html) +to display documentation of latest release in readable format within a browser. __`zstd_compression_format.md`__ : This document defines the Zstandard compression format. Compliant decoders must adhere to this document, and compliant encoders must generate data that follows it. +Should you look for ressources to develop your own port of Zstandard algorithm, +you may find the following ressources useful : + __`educational_decoder`__ : This directory contains an implementation of a Zstandard decoder, compliant with the Zstandard compression format. It can be used, for example, to better understand the format, -or as the basis for a separate implementation a Zstandard decoder/encoder. - -__`zstd_manual.html`__ : Documentation on the functions found in `zstd.h`. -See [http://zstd.net/zstd_manual.html](http://zstd.net/zstd_manual.html) for -the manual released with the latest official `zstd` release. - +or as the basis for a separate implementation of Zstandard decoder. +[__`decode_corpus`__](https://github.com/facebook/zstd/tree/dev/tests#decodecorpus---tool-to-generate-zstandard-frames-for-decoder-testing) : +This tool, stored in `/tests` directory, is able to generate random valid frames, +which is useful if you wish to test your decoder and verify it fully supports the specification. From 606374269cf3485972c90b993fbb84dc20da032f Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Wed, 28 Feb 2018 14:16:30 -0800 Subject: [PATCH 156/255] FIO_addFInfo: Fully initialize output 'total' struct Silence a Coverity warning about 'windowSize' being uninitialized. (Yes, nothing that calls this routine actually uses the windowSize value. Still, appeasing Coverity is pretty harmless in this case.) --- programs/fileio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/programs/fileio.c b/programs/fileio.c index d344b8f69..94dcb916d 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -1963,6 +1963,7 @@ static void displayInfo(const char* inFileName, const fileInfo_t* info, int disp static fileInfo_t FIO_addFInfo(fileInfo_t fi1, fileInfo_t fi2) { fileInfo_t total; + memset(&total, 0, sizeof(total)); total.numActualFrames = fi1.numActualFrames + fi2.numActualFrames; total.numSkippableFrames = fi1.numSkippableFrames + fi2.numSkippableFrames; total.compressedSize = fi1.compressedSize + fi2.compressedSize; From b01552a07ae20b5ed28fdb2084dbe9dadd1a6d9a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 1 Mar 2018 11:28:42 -0800 Subject: [PATCH 157/255] force inlining of HUF_decodeSymbol*() functions which was not done properly by gcc 4.8 resulting in major performance difference. ex : zstd -b1 silesia.tar before : dec 680 MB/s after : dec 710 MB/s (without bmi2) after : dec 770 MB/s (with DYNAMIC_BMI2) --- lib/decompress/huf_decompress.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index d2e1bb4ac..8b292e792 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -143,7 +143,8 @@ size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize) typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX4; /* double-symbols decoding */ -static BYTE HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, const U32 dtLog) +FORCE_INLINE_TEMPLATE BYTE +HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, const U32 dtLog) { size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ BYTE const c = dt[val].byte; @@ -305,7 +306,8 @@ HUF_decompress4X2_usingDTable_internal_body( } -static U32 HUF_decodeSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) +FORCE_INLINE_TEMPLATE U32 +HUF_decodeSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) { size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ memcpy(op, dt+val, 2); From 45b09e7625ae3d121aa8586b65db906a8da3ec93 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 1 Mar 2018 15:02:18 -0800 Subject: [PATCH 158/255] limit DYNAMIC_BMI2 to gcc >= 4.8 attribute bmi2 not supported by gcc 4.4 --- lib/common/compiler.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/common/compiler.h b/lib/common/compiler.h index 24f353280..63d32258a 100644 --- a/lib/common/compiler.h +++ b/lib/common/compiler.h @@ -71,10 +71,11 @@ #endif /* Enable runtime BMI2 dispatch based on the CPU. - * Enabled for clang/gcc on x86 when BMI2 isn't enabled by default. + * Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default. */ #ifndef DYNAMIC_BMI2 - #if defined(__GNUC__) && (defined(__x86_64__) || defined(_M_X86)) && !defined(__BMI2__) + #if defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) \ + && (defined(__x86_64__) || defined(_M_X86)) && !defined(__BMI2__) # define DYNAMIC_BMI2 1 #else # define DYNAMIC_BMI2 0 From d02b44cf55105c23048c5cc6290b73c79bb519a2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 4 Mar 2018 16:05:59 -0800 Subject: [PATCH 159/255] DYNAMIC_BMI2 enabled for clang clang only claims compatibility with gcc 4.2. Consequently, recent patch which reserved DYNAMIC_BMI2 for gcc >= 4.8 also disabled it for clang. fix : __clang__ is now enough to enable DYNAMIC_BMI2 (associated with other existing conditions : x64/x64, !bmi2) --- lib/common/compiler.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/common/compiler.h b/lib/common/compiler.h index 63d32258a..bb8730126 100644 --- a/lib/common/compiler.h +++ b/lib/common/compiler.h @@ -74,8 +74,11 @@ * Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default. */ #ifndef DYNAMIC_BMI2 - #if defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) \ - && (defined(__x86_64__) || defined(_M_X86)) && !defined(__BMI2__) + #if defined(__clang__) \ + || (defined(__GNUC__) \ + && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) \ + && (defined(__x86_64__) || defined(_M_X86)) \ + && !defined(__BMI2__) # define DYNAMIC_BMI2 1 #else # define DYNAMIC_BMI2 0 From cb789d2df8ac2c10d7cb46d7a4dea09a831b5c46 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 5 Mar 2018 13:08:59 -0800 Subject: [PATCH 160/255] re-inserted offset evaluation --- lib/decompress/zstd_decompress.c | 52 ++++++++++++++++----------- lib/decompress/zstd_decompress_impl.h | 20 ++--------- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 5fa4e3056..470aac286 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1248,47 +1248,49 @@ typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset #endif typedef size_t (*ZSTD_decompressSequences_t)( - ZSTD_DCtx *dctx, void *dst, size_t maxDstSize, const void *seqStart, - size_t seqSize, const ZSTD_longOffset_e isLongOffset); + ZSTD_DCtx *dctx, void *dst, size_t maxDstSize, + const void *seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset); static size_t ZSTD_decompressSequences(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, - const void* seqStart, size_t seqSize, + const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset) { #if DYNAMIC_BMI2 if (dctx->bmi2) { - return ZSTD_decompressSequences_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, isLongOffset); + return ZSTD_decompressSequences_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); } #endif - return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, isLongOffset); + return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); } -static size_t ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, - const void* seqStart, size_t seqSize, - const ZSTD_longOffset_e isLongOffset) +static size_t ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset) { #if DYNAMIC_BMI2 if (dctx->bmi2) { - return ZSTD_decompressSequencesLong_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, isLongOffset); + return ZSTD_decompressSequencesLong_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); } #endif - return ZSTD_decompressSequencesLong_default(dctx, dst, maxDstSize, seqStart, seqSize, isLongOffset); + return ZSTD_decompressSequencesLong_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); } static unsigned -ZSTD_getLongOffsetsShare(const FSE_DTable* offTable) +ZSTD_getLongOffsetsShare(const ZSTD_seqSymbol* offTable) { const void* ptr = offTable; U32 const tableLog = ((const FSE_DTableHeader*)ptr)[0].tableLog; - const FSE_decode_t* table = ((const FSE_decode_t*)ptr) + 1; + const ZSTD_seqSymbol* table = offTable + 1; U32 const max = 1 << tableLog; U32 u, total = 0; - assert(tableLog <= OffFSELog); for (u=0; u 23) total += 1; + if (table[u].nbAdditionalBits > 23) total += 1; - total <<= (OffFSELog - tableLog); + assert(tableLog <= OffFSELog); + total <<= (OffFSELog - tableLog); /* scale to OffFSELog */ return total; } @@ -1317,12 +1319,22 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, srcSize -= litCSize; } - if ( frame /* windowSize exists */ - && (dctx->fParams.windowSize > (1<<24)) - && MEM_64bits() /* x86 benefits less from long mode than x64 */ ) - return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, isLongOffset); + /* Build Decoding Tables */ + { int nbSeq; + size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, srcSize); + if (ZSTD_isError(seqHSize)) return seqHSize; + ip += seqHSize; + srcSize -= seqHSize; - return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, isLongOffset); + if (dctx->fParams.windowSize > (1<<24)) { + U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr); + U32 const minShare = MEM_64bits() ? 5 : 13; + if (shareLongOffsets >= minShare) + return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset); + } + + return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset); + } } diff --git a/lib/decompress/zstd_decompress_impl.h b/lib/decompress/zstd_decompress_impl.h index 6454a7429..18b3a296e 100644 --- a/lib/decompress/zstd_decompress_impl.h +++ b/lib/decompress/zstd_decompress_impl.h @@ -209,7 +209,7 @@ static TARGET size_t FUNCTION(ZSTD_decompressSequences)( ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, - const void* seqStart, size_t seqSize, + const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset) { const BYTE* ip = (const BYTE*)seqStart; @@ -222,17 +222,8 @@ size_t FUNCTION(ZSTD_decompressSequences)( const BYTE* const base = (const BYTE*) (dctx->base); const BYTE* const vBase = (const BYTE*) (dctx->vBase); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); - int nbSeq; DEBUGLOG(5, "ZSTD_decompressSequences"); - /* Build Decoding Tables */ - { size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); - DEBUGLOG(5, "ZSTD_decodeSeqHeaders: size=%u, nbSeq=%i", - (U32)seqHSize, nbSeq); - if (ZSTD_isError(seqHSize)) return seqHSize; - ip += seqHSize; - } - /* Regen sequences */ if (nbSeq) { seqState_t seqState; @@ -273,7 +264,7 @@ static TARGET size_t FUNCTION(ZSTD_decompressSequencesLong)( ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, - const void* seqStart, size_t seqSize, + const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset) { const BYTE* ip = (const BYTE*)seqStart; @@ -286,13 +277,6 @@ size_t FUNCTION(ZSTD_decompressSequencesLong)( const BYTE* const prefixStart = (const BYTE*) (dctx->base); const BYTE* const dictStart = (const BYTE*) (dctx->vBase); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); - int nbSeq; - - /* Build Decoding Tables */ - { size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); - if (ZSTD_isError(seqHSize)) return seqHSize; - ip += seqHSize; - } /* Regen sequences */ if (nbSeq) { From 03e7e1419249bf7f0ce52244e945224497c216f7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 5 Mar 2018 13:50:07 -0800 Subject: [PATCH 161/255] fix benchmark issue when measuring only decoding speed zstd bench module can focus on decompression speed _only_. This is useful when trying to measure performance on large input data compressed using a high level as compression time becomes problematic (too long). This mode is triggered by command : zstd -b -d Problem was : in such a mode, measured decoding speed was > 10% slower than in nominal mode (compression + decompression), making decompression benchmark mode much less useful. This patch fixes the issue. It's not completely clear why, but moving the `memcpy()` operation sooner in the pipeline fixed it. I can still measure some difference, but it is in the < 2% range, so it's much more tolerable. also : it doesn't matter anymore in which order are selected commands `-b` and `-d`. The combination always triggers bench_decodeOnly mode. --- programs/bench.c | 10 ++++++---- programs/zstdcli.c | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index b4cd619c4..39ac0dc5e 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -260,7 +260,11 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, } } } /* warmimg up memory */ - RDG_genBuffer(compressedBuffer, maxCompressedSize, 0.10, 0.50, 1); + if (g_decodeOnly) { + memcpy(compressedBuffer, srcBuffer, loadedCompressedSize); + } else { + RDG_genBuffer(compressedBuffer, maxCompressedSize, 0.10, 0.50, 1); + } /* Bench */ { U64 fastestC = (U64)(-1LL), fastestD = (U64)(-1LL); @@ -373,9 +377,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, ratioAccuracy, ratio, cSpeedAccuracy, compressionSpeed ); } - } else { /* g_decodeOnly */ - memcpy(compressedBuffer, srcBuffer, loadedCompressedSize); - } + } /* if (!g_decodeOnly) */ #if 0 /* disable decompression test */ dCompleted=1; diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 05230227f..3fd3282b1 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -573,7 +573,8 @@ int main(int argCount, const char* argv[]) /* Decoding */ case 'd': #ifndef ZSTD_NOBENCH - if (operation==zom_bench) { BMK_setDecodeOnlyMode(1); argument++; break; } /* benchmark decode (hidden option) */ + BMK_setDecodeOnlyMode(1); + if (operation==zom_bench) { argument++; break; } /* benchmark decode (hidden option) */ #endif operation=zom_decompress; argument++; break; From c0393a538f8a73160f5818afd276f4775804c642 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 5 Mar 2018 15:12:10 -0800 Subject: [PATCH 162/255] fixed counting long distance weights --- lib/decompress/zstd_decompress.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 470aac286..b28bcae63 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -94,7 +94,7 @@ typedef struct { U32 baseValue; } ZSTD_seqSymbol; -#define SEQSYMBOL_TABLE_SIZE(log) (1 + (1<bmi2) { return ZSTD_decompressSequences_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); @@ -1269,6 +1270,7 @@ static size_t ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx, const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset) { + DEBUGLOG(5, "ZSTD_decompressSequencesLong"); #if DYNAMIC_BMI2 if (dctx->bmi2) { return ZSTD_decompressSequencesLong_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); @@ -1281,13 +1283,16 @@ static unsigned ZSTD_getLongOffsetsShare(const ZSTD_seqSymbol* offTable) { const void* ptr = offTable; - U32 const tableLog = ((const FSE_DTableHeader*)ptr)[0].tableLog; + U32 const tableLog = ((const ZSTD_seqSymbol_header*)ptr)[0].tableLog; const ZSTD_seqSymbol* table = offTable + 1; U32 const max = 1 << tableLog; U32 u, total = 0; + DEBUGLOG(5, "ZSTD_getLongOffsetsShare: (tableLog=%u)", tableLog); - for (u=0; u 23) total += 1; + assert(max <= (1 << OffFSELog)); /* max not too large */ + for (u=0; u 22) total += 1; + } assert(tableLog <= OffFSELog); total <<= (OffFSELog - tableLog); /* scale to OffFSELog */ From 7bd7a3ad432b2102dbb33b88c12a9e05d693df0e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 5 Mar 2018 16:16:49 -0800 Subject: [PATCH 163/255] long offset mode : new default threshold for 64-bits mode --- lib/decompress/zstd_decompress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index b28bcae63..ef6e16d3f 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1333,7 +1333,7 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, if (dctx->fParams.windowSize > (1<<24)) { U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr); - U32 const minShare = MEM_64bits() ? 5 : 13; + U32 const minShare = MEM_64bits() ? 7 : 13; if (shareLongOffsets >= minShare) return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset); } From 9a91afe6efc8ac24a8b3f7f3b98df42d44abe07f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 5 Mar 2018 16:41:08 -0800 Subject: [PATCH 164/255] long offset mode : new default threshold for 32-bit --- lib/decompress/zstd_decompress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index ef6e16d3f..12c46e572 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1333,7 +1333,7 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, if (dctx->fParams.windowSize > (1<<24)) { U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr); - U32 const minShare = MEM_64bits() ? 7 : 13; + U32 const minShare = MEM_64bits() ? 7 : 20; if (shareLongOffsets >= minShare) return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset); } From 06ca9c7d7ce2c9883551ab1e4c535f6a456b5e5d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 6 Mar 2018 01:50:19 -0800 Subject: [PATCH 165/255] fixed 0-seq blocks in block-decompression mode --- lib/decompress/zstd_decompress.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 12c46e572..53f555337 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1331,7 +1331,8 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, ip += seqHSize; srcSize -= seqHSize; - if (dctx->fParams.windowSize > (1<<24)) { + if ( (dctx->fParams.windowSize > (1<<24) || !frame) + && (nbSeq>0) ) { /* could probably use a larger nbSeq limit */ U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr); U32 const minShare = MEM_64bits() ? 7 : 20; if (shareLongOffsets >= minShare) From db147ea620151f30b6825ad7c2fb29161cade94d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 6 Mar 2018 18:15:26 -0800 Subject: [PATCH 166/255] improved comments following @terrelln suggestions --- lib/decompress/zstd_decompress.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 53f555337..8aa21db7e 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1279,6 +1279,10 @@ static size_t ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx, return ZSTD_decompressSequencesLong_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); } +/* ZSTD_getLongOffsetsShare() : + * condition : offTable must be valid + * @return : "share" of long offsets (arbitrarily defined as > (1<<23)) + * compared to maximum possible of (1<fParams.windowSize > (1<<24) || !frame) + if ( (!frame || dctx->fParams.windowSize > (1<<24)) && (nbSeq>0) ) { /* could probably use a larger nbSeq limit */ U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr); - U32 const minShare = MEM_64bits() ? 7 : 20; + U32 const minShare = MEM_64bits() ? 7 : 20; /* heuristic values, correspond to 2.73% and 7.81% */ if (shareLongOffsets >= minShare) return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset); } From 7e103cdaf51cb8bf46c4bd0582c4e9759b483239 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 28 Feb 2018 20:57:30 -0800 Subject: [PATCH 167/255] [threading] Cast unused arguments to void --- lib/common/threading.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/common/threading.h b/lib/common/threading.h index 197770db2..d806c89d0 100644 --- a/lib/common/threading.h +++ b/lib/common/threading.h @@ -45,15 +45,15 @@ extern "C" { /* mutex */ #define ZSTD_pthread_mutex_t CRITICAL_SECTION -#define ZSTD_pthread_mutex_init(a, b) (InitializeCriticalSection((a)), 0) +#define ZSTD_pthread_mutex_init(a, b) ((void)(b), InitializeCriticalSection((a)), 0) #define ZSTD_pthread_mutex_destroy(a) DeleteCriticalSection((a)) #define ZSTD_pthread_mutex_lock(a) EnterCriticalSection((a)) #define ZSTD_pthread_mutex_unlock(a) LeaveCriticalSection((a)) /* condition variable */ #define ZSTD_pthread_cond_t CONDITION_VARIABLE -#define ZSTD_pthread_cond_init(a, b) (InitializeConditionVariable((a)), 0) -#define ZSTD_pthread_cond_destroy(a) /* No delete */ +#define ZSTD_pthread_cond_init(a, b) ((void)(b), InitializeConditionVariable((a)), 0) +#define ZSTD_pthread_cond_destroy(a) ((void)(a)) #define ZSTD_pthread_cond_wait(a, b) SleepConditionVariableCS((a), (b), INFINITE) #define ZSTD_pthread_cond_signal(a) WakeConditionVariable((a)) #define ZSTD_pthread_cond_broadcast(a) WakeAllConditionVariable((a)) @@ -100,17 +100,17 @@ int ZSTD_pthread_join(ZSTD_pthread_t thread, void** value_ptr); /* No multithreading support */ typedef int ZSTD_pthread_mutex_t; -#define ZSTD_pthread_mutex_init(a, b) ((void)a, 0) -#define ZSTD_pthread_mutex_destroy(a) -#define ZSTD_pthread_mutex_lock(a) -#define ZSTD_pthread_mutex_unlock(a) +#define ZSTD_pthread_mutex_init(a, b) ((void)(a), (void)(b), 0) +#define ZSTD_pthread_mutex_destroy(a) ((void)(a)) +#define ZSTD_pthread_mutex_lock(a) ((void)(a)) +#define ZSTD_pthread_mutex_unlock(a) ((void)(a)) typedef int ZSTD_pthread_cond_t; -#define ZSTD_pthread_cond_init(a, b) ((void)a, 0) -#define ZSTD_pthread_cond_destroy(a) -#define ZSTD_pthread_cond_wait(a, b) -#define ZSTD_pthread_cond_signal(a) -#define ZSTD_pthread_cond_broadcast(a) +#define ZSTD_pthread_cond_init(a, b) ((void)(a), (void)(b), 0) +#define ZSTD_pthread_cond_destroy(a) ((void)(a)) +#define ZSTD_pthread_cond_wait(a, b) ((void)(a), (void)(b)) +#define ZSTD_pthread_cond_signal(a) ((void)(a)) +#define ZSTD_pthread_cond_broadcast(a) ((void)(a)) /* do not use ZSTD_pthread_t */ From e916b9090e49101fbf9ed1bab33e17a045b03974 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Mar 2018 11:52:14 -0800 Subject: [PATCH 168/255] gen_html: changed CFLAGS for CXXFLAGS since it's associated with $(CXX) --- contrib/gen_html/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/gen_html/Makefile b/contrib/gen_html/Makefile index d9b32e351..63598b8d7 100644 --- a/contrib/gen_html/Makefile +++ b/contrib/gen_html/Makefile @@ -7,10 +7,10 @@ # in the COPYING file in the root directory of this source tree). # ################################################################ -CFLAGS ?= -O3 -CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 -Wswitch-enum -Wno-comment -CFLAGS += $(MOREFLAGS) -FLAGS = $(CPPFLAGS) $(CFLAGS) $(CXXFLAGS) $(LDFLAGS) +CXXFLAGS ?= -O3 +CXXFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 -Wswitch-enum -Wno-comment +CXXFLAGS += $(MOREFLAGS) +FLAGS = $(CPPFLAGS) $(CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) ZSTDAPI = ../../lib/zstd.h ZSTDMANUAL = ../../doc/zstd_manual.html From 8c6dbf490b876da14a6b1b1fd2aa5fe64ff7c5ea Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Mar 2018 16:22:34 -0800 Subject: [PATCH 169/255] updated NEWS in preparation for v1.3.4 --- NEWS | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index a45960b96..f23203bb2 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,16 @@ +v1.3.4 +perf: faster speed for recent cpus (haswell+) +perf: faster decoding speed +perf: faster zstd_btlazy2 compression strategy (levels 13-15) +cli : asynchronous mode by default, for improved performance (use --single-thread for former behavior) +cli : fix crash when not overwriting existing files, by Pádraig Brady (@pixelb) +api : `nbThreads` becomes `nbWorkers` : 1 triggers asynchronous mode +api : ZSTD_getFrameProgression() : get precise progress status of ZSTDMT anytime +api : ZSTDMT can accept new compression parameters during compression +build: improved meson recipe, by Shawn Landden (@shawnl) + v1.3.3 -perf: faster zstd_opt strategy (levels 17-19) +perf: faster zstd_opt strategy (levels 16-19) fix : bug #944 : multithreading with shared ditionary and large data, reported by @gsliepen cli : fix : content size written in header by default cli : fix : improved LZ4 format support, by @felixhandte From 17626ba56ed526cdb657956dd4d83fee9aea2aaa Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Mar 2018 18:03:25 -0800 Subject: [PATCH 170/255] restored ZSTD_decodeSequence() into zstd_decompress.c --- lib/decompress/huf_decompress.c | 62 +++++++-------- lib/decompress/zstd_decompress.c | 109 ++++++++++++++++++++++++++ lib/decompress/zstd_decompress_impl.h | 102 +----------------------- 3 files changed, 143 insertions(+), 130 deletions(-) diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 8b292e792..71c1d20bc 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -493,41 +493,41 @@ typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize, const HUF_DTable *DTable); #if DYNAMIC_BMI2 -#define X(fn) \ - \ - static size_t fn##_default( \ - void* dst, size_t dstSize, \ - const void* cSrc, size_t cSrcSize, \ - const HUF_DTable* DTable) \ - { \ - return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ - } \ - \ - static TARGET_ATTRIBUTE("bmi2") size_t fn##_bmi2( \ - void* dst, size_t dstSize, \ - const void* cSrc, size_t cSrcSize, \ - const HUF_DTable* DTable) \ - { \ - return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ - } \ - \ - static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ - { \ - if (bmi2) { \ - return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \ - } \ - return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ +#define X(fn) \ + \ + static size_t fn##_default( \ + void* dst, size_t dstSize, \ + const void* cSrc, size_t cSrcSize, \ + const HUF_DTable* DTable) \ + { \ + return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ + } \ + \ + static TARGET_ATTRIBUTE("bmi2") size_t fn##_bmi2( \ + void* dst, size_t dstSize, \ + const void* cSrc, size_t cSrcSize, \ + const HUF_DTable* DTable) \ + { \ + return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ + } \ + \ + static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ + size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + { \ + if (bmi2) { \ + return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \ + } \ + return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ } #else -#define X(fn) \ - static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ - { \ - (void)bmi2; \ - return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ +#define X(fn) \ + static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ + size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + { \ + (void)bmi2; \ + return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ } #endif diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 8aa21db7e..ae030b25e 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1229,8 +1229,111 @@ size_t ZSTD_execSequenceLong(BYTE* op, return sequenceLength; } +FORCE_INLINE_TEMPLATE void +ZSTD_updateFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD) +{ + ZSTD_seqSymbol const DInfo = DStatePtr->table[DStatePtr->state]; + U32 const nbBits = DInfo.nbBits; + size_t const lowBits = BIT_readBits(bitD, nbBits); + DStatePtr->state = DInfo.nextState + lowBits; +} + +/* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum + * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1) + * bits before reloading. This value is the maximum number of bytes we read + * after reloading when we are decoding long offets. + */ +#define LONG_OFFSETS_MAX_EXTRA_BITS_32 \ + (ZSTD_WINDOWLOG_MAX_32 > STREAM_ACCUMULATOR_MIN_32 \ + ? ZSTD_WINDOWLOG_MAX_32 - STREAM_ACCUMULATOR_MIN_32 \ + : 0) + typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset_e; +FORCE_INLINE_TEMPLATE seq_t +ZSTD_decodeSequence_body(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) +{ + seq_t seq; + U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; + U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits; + U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits; + U32 const totalBits = llBits+mlBits+ofBits; + U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue; + U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue; + U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue; + + /* sequence */ + { size_t offset; + if (!ofBits) + offset = 0; + else { + ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); + ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); + assert(ofBits <= MaxOff); + if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) { + U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed); + offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); + BIT_reloadDStream(&seqState->DStream); + if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); + assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32); /* to avoid another reload */ + } else { + offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ + if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); + } + } + + if (ofBits <= 1) { + offset += (llBase==0); + if (offset) { + size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; + temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ + if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; + seqState->prevOffset[1] = seqState->prevOffset[0]; + seqState->prevOffset[0] = offset = temp; + } else { /* offset == 0 */ + offset = seqState->prevOffset[0]; + } + } else { + seqState->prevOffset[2] = seqState->prevOffset[1]; + seqState->prevOffset[1] = seqState->prevOffset[0]; + seqState->prevOffset[0] = offset; + } + seq.offset = offset; + } + + seq.matchLength = mlBase + + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/) : 0); /* <= 16 bits */ + if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) + BIT_reloadDStream(&seqState->DStream); + if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) + BIT_reloadDStream(&seqState->DStream); + /* Ensure there are enough bits to read the rest of data in 64-bit mode. */ + ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); + + seq.litLength = llBase + + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits/*>0*/) : 0); /* <= 16 bits */ + if (MEM_32bits()) + BIT_reloadDStream(&seqState->DStream); + + DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u", + (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset); + + /* ANS state update */ + ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ + ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ + if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ + ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ + + return seq; +} + +static seq_t +ZSTD_decodeSequence_default(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) +{ + return ZSTD_decodeSequence_body(seqState, longOffsets); +} + + #define FUNCTION(fn) fn##_default #define TARGET #include "zstd_decompress_impl.h" @@ -1239,6 +1342,12 @@ typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset #if DYNAMIC_BMI2 +static TARGET_ATTRIBUTE("bmi2") seq_t +ZSTD_decodeSequence_bmi2(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) +{ + return ZSTD_decodeSequence_body(seqState, longOffsets); +} + #define FUNCTION(fn) fn##_bmi2 #define TARGET TARGET_ATTRIBUTE("bmi2") #include "zstd_decompress_impl.h" diff --git a/lib/decompress/zstd_decompress_impl.h b/lib/decompress/zstd_decompress_impl.h index 18b3a296e..72d7c2644 100644 --- a/lib/decompress/zstd_decompress_impl.h +++ b/lib/decompress/zstd_decompress_impl.h @@ -16,102 +16,6 @@ # error "TARGET must be defined" #endif -static TARGET void -FUNCTION(ZSTD_updateFseState)(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD) -{ - ZSTD_seqSymbol const DInfo = DStatePtr->table[DStatePtr->state]; - U32 const nbBits = DInfo.nbBits; - size_t const lowBits = BIT_readBits(bitD, nbBits); - DStatePtr->state = DInfo.nextState + lowBits; -} - -/* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum - * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1) - * bits before reloading. This value is the maximum number of bytes we read - * after reloading when we are decoding long offets. - */ -#define LONG_OFFSETS_MAX_EXTRA_BITS_32 \ - (ZSTD_WINDOWLOG_MAX_32 > STREAM_ACCUMULATOR_MIN_32 \ - ? ZSTD_WINDOWLOG_MAX_32 - STREAM_ACCUMULATOR_MIN_32 \ - : 0) - -static TARGET seq_t -FUNCTION(ZSTD_decodeSequence)(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) -{ - seq_t seq; - U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; - U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits; - U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits; - U32 const totalBits = llBits+mlBits+ofBits; - U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue; - U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue; - U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue; - - /* sequence */ - { size_t offset; - if (!ofBits) - offset = 0; - else { - ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); - ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); - assert(ofBits <= MaxOff); - if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) { - U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed); - offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); - BIT_reloadDStream(&seqState->DStream); - if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); - assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32); /* to avoid another reload */ - } else { - offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); - } - } - - if (ofBits <= 1) { - offset += (llBase==0); - if (offset) { - size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; - temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ - if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; - seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset = temp; - } else { /* offset == 0 */ - offset = seqState->prevOffset[0]; - } - } else { - seqState->prevOffset[2] = seqState->prevOffset[1]; - seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset; - } - seq.offset = offset; - } - - seq.matchLength = mlBase - + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/) : 0); /* <= 16 bits */ - if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) - BIT_reloadDStream(&seqState->DStream); - if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) - BIT_reloadDStream(&seqState->DStream); - /* Ensure there are enough bits to read the rest of data in 64-bit mode. */ - ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); - - seq.litLength = llBase - + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits/*>0*/) : 0); /* <= 16 bits */ - if (MEM_32bits()) - BIT_reloadDStream(&seqState->DStream); - - DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u", - (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset); - - /* ANS state update */ - FUNCTION(ZSTD_updateFseState)(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ - FUNCTION(ZSTD_updateFseState)(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ - FUNCTION(ZSTD_updateFseState)(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ - - return seq; -} - HINT_INLINE seq_t @@ -184,10 +88,10 @@ FUNCTION(ZSTD_decodeSequenceLong)(seqState_t* seqState, ZSTD_longOffset_e const } /* ANS state update */ - FUNCTION(ZSTD_updateFseState)(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ - FUNCTION(ZSTD_updateFseState)(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ + ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ + ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ - FUNCTION(ZSTD_updateFseState)(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ + ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ return seq; } From a166eae1bad6b9ac15e80bb7964e659f5bb261f1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Mar 2018 18:11:14 -0800 Subject: [PATCH 171/255] incorporate ZSTD_decodeSequenceLong() within zstd_decompress.c --- lib/decompress/zstd_decompress.c | 90 +++++++++++++++++++++++++++ lib/decompress/zstd_decompress_impl.h | 79 ----------------------- 2 files changed, 90 insertions(+), 79 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index ae030b25e..aa10ae49f 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1333,6 +1333,90 @@ ZSTD_decodeSequence_default(seqState_t* seqState, const ZSTD_longOffset_e longOf return ZSTD_decodeSequence_body(seqState, longOffsets); } +FORCE_INLINE_TEMPLATE seq_t +ZSTD_decodeSequenceLong_body(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) +{ + seq_t seq; + U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; + U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits; + U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits; + U32 const totalBits = llBits+mlBits+ofBits; + U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue; + U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue; + U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue; + + /* sequence */ + { size_t offset; + if (!ofBits) + offset = 0; + else { + ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); + ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); + assert(ofBits <= MaxOff); + if (MEM_32bits() && longOffsets) { + U32 const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN_32-1); + offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); + if (MEM_32bits() || extraBits) BIT_reloadDStream(&seqState->DStream); + if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); + } else { + offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ + if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); + } + } + + if (ofBits <= 1) { + offset += (llBase==0); + if (offset) { + size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; + temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ + if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; + seqState->prevOffset[1] = seqState->prevOffset[0]; + seqState->prevOffset[0] = offset = temp; + } else { + offset = seqState->prevOffset[0]; + } + } else { + seqState->prevOffset[2] = seqState->prevOffset[1]; + seqState->prevOffset[1] = seqState->prevOffset[0]; + seqState->prevOffset[0] = offset; + } + seq.offset = offset; + } + + seq.matchLength = mlBase + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ + if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) + BIT_reloadDStream(&seqState->DStream); + if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) + BIT_reloadDStream(&seqState->DStream); + /* Verify that there is enough bits to read the rest of the data in 64-bit mode. */ + ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); + + seq.litLength = llBase + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ + if (MEM_32bits()) + BIT_reloadDStream(&seqState->DStream); + + { size_t const pos = seqState->pos + seq.litLength; + const BYTE* const matchBase = (seq.offset > pos) ? seqState->dictEnd : seqState->prefixStart; + seq.match = matchBase + pos - seq.offset; /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted. + * No consequence though : no memory access will occur, overly large offset will be detected in ZSTD_execSequenceLong() */ + seqState->pos = pos + seq.matchLength; + } + + /* ANS state update */ + ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ + ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ + if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ + ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ + + return seq; +} + +static seq_t +ZSTD_decodeSequenceLong_default(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) +{ + return ZSTD_decodeSequenceLong_body(seqState, longOffsets); +} + #define FUNCTION(fn) fn##_default #define TARGET @@ -1348,6 +1432,12 @@ ZSTD_decodeSequence_bmi2(seqState_t* seqState, const ZSTD_longOffset_e longOffse return ZSTD_decodeSequence_body(seqState, longOffsets); } +static TARGET_ATTRIBUTE("bmi2") seq_t +ZSTD_decodeSequenceLong_bmi2(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) +{ + return ZSTD_decodeSequenceLong_body(seqState, longOffsets); +} + #define FUNCTION(fn) fn##_bmi2 #define TARGET TARGET_ATTRIBUTE("bmi2") #include "zstd_decompress_impl.h" diff --git a/lib/decompress/zstd_decompress_impl.h b/lib/decompress/zstd_decompress_impl.h index 72d7c2644..73550bd27 100644 --- a/lib/decompress/zstd_decompress_impl.h +++ b/lib/decompress/zstd_decompress_impl.h @@ -18,85 +18,6 @@ -HINT_INLINE seq_t -FUNCTION(ZSTD_decodeSequenceLong)(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) -{ - seq_t seq; - U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; - U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits; - U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits; - U32 const totalBits = llBits+mlBits+ofBits; - U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue; - U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue; - U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue; - - /* sequence */ - { size_t offset; - if (!ofBits) - offset = 0; - else { - ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); - ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); - assert(ofBits <= MaxOff); - if (MEM_32bits() && longOffsets) { - U32 const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN_32-1); - offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); - if (MEM_32bits() || extraBits) BIT_reloadDStream(&seqState->DStream); - if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); - } else { - offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); - } - } - - if (ofBits <= 1) { - offset += (llBase==0); - if (offset) { - size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; - temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ - if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; - seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset = temp; - } else { - offset = seqState->prevOffset[0]; - } - } else { - seqState->prevOffset[2] = seqState->prevOffset[1]; - seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset; - } - seq.offset = offset; - } - - seq.matchLength = mlBase + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ - if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) - BIT_reloadDStream(&seqState->DStream); - if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) - BIT_reloadDStream(&seqState->DStream); - /* Verify that there is enough bits to read the rest of the data in 64-bit mode. */ - ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); - - seq.litLength = llBase + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ - if (MEM_32bits()) - BIT_reloadDStream(&seqState->DStream); - - { size_t const pos = seqState->pos + seq.litLength; - const BYTE* const matchBase = (seq.offset > pos) ? seqState->dictEnd : seqState->prefixStart; - seq.match = matchBase + pos - seq.offset; /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted. - * No consequence though : no memory access will occur, overly large offset will be detected in ZSTD_execSequenceLong() */ - seqState->pos = pos + seq.matchLength; - } - - /* ANS state update */ - ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ - ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ - ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ - - return seq; -} - - static TARGET void FUNCTION(ZSTD_initFseState)(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, const ZSTD_seqSymbol* dt) { From cdb1f1433e23e2223d9490f2174f6a6ba8890d17 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Mar 2018 18:16:10 -0800 Subject: [PATCH 172/255] incorporated ZSTD_initFseState() inside zstd_decompress.c --- lib/decompress/zstd_decompress.c | 12 ++++++++++++ lib/decompress/zstd_decompress_impl.h | 24 ++++++------------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index aa10ae49f..2553621fc 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1229,6 +1229,18 @@ size_t ZSTD_execSequenceLong(BYTE* op, return sequenceLength; } +static void +ZSTD_initFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, const ZSTD_seqSymbol* dt) +{ + const void* ptr = dt; + const ZSTD_seqSymbol_header* const DTableH = (const ZSTD_seqSymbol_header*)ptr; + DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); + DEBUGLOG(6, "ZSTD_initFseState : val=%u using %u bits", + (U32)DStatePtr->state, DTableH->tableLog); + BIT_reloadDStream(bitD); + DStatePtr->table = dt + 1; +} + FORCE_INLINE_TEMPLATE void ZSTD_updateFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD) { diff --git a/lib/decompress/zstd_decompress_impl.h b/lib/decompress/zstd_decompress_impl.h index 73550bd27..5303b0d6f 100644 --- a/lib/decompress/zstd_decompress_impl.h +++ b/lib/decompress/zstd_decompress_impl.h @@ -18,18 +18,6 @@ -static TARGET void -FUNCTION(ZSTD_initFseState)(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, const ZSTD_seqSymbol* dt) -{ - const void* ptr = dt; - const ZSTD_seqSymbol_header* const DTableH = (const ZSTD_seqSymbol_header*)ptr; - DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); - DEBUGLOG(6, "ZSTD_initFseState : val=%u using %u bits", - (U32)DStatePtr->state, DTableH->tableLog); - BIT_reloadDStream(bitD); - DStatePtr->table = dt + 1; -} - static TARGET size_t FUNCTION(ZSTD_decompressSequences)( ZSTD_DCtx* dctx, @@ -55,9 +43,9 @@ size_t FUNCTION(ZSTD_decompressSequences)( dctx->fseEntropy = 1; { U32 i; for (i=0; ientropy.rep[i]; } CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); - FUNCTION(ZSTD_initFseState)(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); - FUNCTION(ZSTD_initFseState)(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); - FUNCTION(ZSTD_initFseState)(&seqState.stateML, &seqState.DStream, dctx->MLTptr); + ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); + ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); + ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) { nbSeq--; @@ -118,9 +106,9 @@ size_t FUNCTION(ZSTD_decompressSequencesLong)( seqState.pos = (size_t)(op-prefixStart); seqState.dictEnd = dictEnd; CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); - FUNCTION(ZSTD_initFseState)(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); - FUNCTION(ZSTD_initFseState)(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); - FUNCTION(ZSTD_initFseState)(&seqState.stateML, &seqState.DStream, dctx->MLTptr); + ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); + ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); + ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); /* prepare in advance */ for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && (seqNb Date: Fri, 9 Mar 2018 19:35:57 -0800 Subject: [PATCH 173/255] incorporated ZSTD_decompressSequences() into zstd_decompress() --- lib/decompress/zstd_decompress.c | 80 ++++++++++++++++++++++++--- lib/decompress/zstd_decompress_impl.h | 55 ------------------ 2 files changed, 71 insertions(+), 64 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 2553621fc..a4608717c 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1339,12 +1339,71 @@ ZSTD_decodeSequence_body(seqState_t* seqState, const ZSTD_longOffset_e longOffse return seq; } -static seq_t -ZSTD_decodeSequence_default(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) +FORCE_INLINE_TEMPLATE size_t +ZSTD_decompressSequences_body( ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset) { - return ZSTD_decodeSequence_body(seqState, longOffsets); + const BYTE* ip = (const BYTE*)seqStart; + const BYTE* const iend = ip + seqSize; + BYTE* const ostart = (BYTE* const)dst; + BYTE* const oend = ostart + maxDstSize; + BYTE* op = ostart; + const BYTE* litPtr = dctx->litPtr; + const BYTE* const litEnd = litPtr + dctx->litSize; + const BYTE* const base = (const BYTE*) (dctx->base); + const BYTE* const vBase = (const BYTE*) (dctx->vBase); + const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); + DEBUGLOG(5, "ZSTD_decompressSequences"); + + /* Regen sequences */ + if (nbSeq) { + seqState_t seqState; + dctx->fseEntropy = 1; + { U32 i; for (i=0; ientropy.rep[i]; } + CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); + ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); + ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); + ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); + + for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) { + nbSeq--; + { seq_t const sequence = ZSTD_decodeSequence_body(&seqState, isLongOffset); + size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, base, vBase, dictEnd); + DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize); + if (ZSTD_isError(oneSeqSize)) return oneSeqSize; + op += oneSeqSize; + } } + + /* check if reached exact end */ + DEBUGLOG(5, "ZSTD_decompressSequences: after decode loop, remaining nbSeq : %i", nbSeq); + if (nbSeq) return ERROR(corruption_detected); + /* save reps for next block */ + { U32 i; for (i=0; ientropy.rep[i] = (U32)(seqState.prevOffset[i]); } + } + + /* last literal segment */ + { size_t const lastLLSize = litEnd - litPtr; + if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall); + memcpy(op, litPtr, lastLLSize); + op += lastLLSize; + } + + return op-ostart; } +static size_t +ZSTD_decompressSequences_default(ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset) +{ + return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); +} + + + FORCE_INLINE_TEMPLATE seq_t ZSTD_decodeSequenceLong_body(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) { @@ -1438,18 +1497,21 @@ ZSTD_decodeSequenceLong_default(seqState_t* seqState, ZSTD_longOffset_e const lo #if DYNAMIC_BMI2 -static TARGET_ATTRIBUTE("bmi2") seq_t -ZSTD_decodeSequence_bmi2(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) -{ - return ZSTD_decodeSequence_body(seqState, longOffsets); -} - static TARGET_ATTRIBUTE("bmi2") seq_t ZSTD_decodeSequenceLong_bmi2(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) { return ZSTD_decodeSequenceLong_body(seqState, longOffsets); } +static TARGET_ATTRIBUTE("bmi2") size_t +ZSTD_decompressSequences_bmi2(ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset) +{ + return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); +} + #define FUNCTION(fn) fn##_bmi2 #define TARGET TARGET_ATTRIBUTE("bmi2") #include "zstd_decompress_impl.h" diff --git a/lib/decompress/zstd_decompress_impl.h b/lib/decompress/zstd_decompress_impl.h index 5303b0d6f..123d5da5c 100644 --- a/lib/decompress/zstd_decompress_impl.h +++ b/lib/decompress/zstd_decompress_impl.h @@ -18,61 +18,6 @@ -static TARGET -size_t FUNCTION(ZSTD_decompressSequences)( - ZSTD_DCtx* dctx, - void* dst, size_t maxDstSize, - const void* seqStart, size_t seqSize, int nbSeq, - const ZSTD_longOffset_e isLongOffset) -{ - const BYTE* ip = (const BYTE*)seqStart; - const BYTE* const iend = ip + seqSize; - BYTE* const ostart = (BYTE* const)dst; - BYTE* const oend = ostart + maxDstSize; - BYTE* op = ostart; - const BYTE* litPtr = dctx->litPtr; - const BYTE* const litEnd = litPtr + dctx->litSize; - const BYTE* const base = (const BYTE*) (dctx->base); - const BYTE* const vBase = (const BYTE*) (dctx->vBase); - const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); - DEBUGLOG(5, "ZSTD_decompressSequences"); - - /* Regen sequences */ - if (nbSeq) { - seqState_t seqState; - dctx->fseEntropy = 1; - { U32 i; for (i=0; ientropy.rep[i]; } - CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); - ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); - ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); - ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); - - for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) { - nbSeq--; - { seq_t const sequence = FUNCTION(ZSTD_decodeSequence)(&seqState, isLongOffset); - size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, base, vBase, dictEnd); - DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize); - if (ZSTD_isError(oneSeqSize)) return oneSeqSize; - op += oneSeqSize; - } } - - /* check if reached exact end */ - DEBUGLOG(5, "ZSTD_decompressSequences: after decode loop, remaining nbSeq : %i", nbSeq); - if (nbSeq) return ERROR(corruption_detected); - /* save reps for next block */ - { U32 i; for (i=0; ientropy.rep[i] = (U32)(seqState.prevOffset[i]); } - } - - /* last literal segment */ - { size_t const lastLLSize = litEnd - litPtr; - if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall); - memcpy(op, litPtr, lastLLSize); - op += lastLLSize; - } - - return op-ostart; -} - static TARGET size_t FUNCTION(ZSTD_decompressSequencesLong)( ZSTD_DCtx* dctx, From fe321f9e2a374611d4d9fdd12e09150727873b35 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 9 Mar 2018 19:48:06 -0800 Subject: [PATCH 174/255] re-integrate ZSTD_decompressSequencesLong() into zstd_decompress.c removed zstd_decompress_impl.h --- lib/decompress/zstd_decompress.c | 118 +++++++++++++++++++++----- lib/decompress/zstd_decompress_impl.h | 98 --------------------- 2 files changed, 96 insertions(+), 120 deletions(-) delete mode 100644 lib/decompress/zstd_decompress_impl.h diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index a4608717c..4cd1f3da6 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1263,7 +1263,7 @@ ZSTD_updateFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD) typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset_e; FORCE_INLINE_TEMPLATE seq_t -ZSTD_decodeSequence_body(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) +ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) { seq_t seq; U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; @@ -1369,7 +1369,7 @@ ZSTD_decompressSequences_body( ZSTD_DCtx* dctx, for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) { nbSeq--; - { seq_t const sequence = ZSTD_decodeSequence_body(&seqState, isLongOffset); + { seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset); size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, base, vBase, dictEnd); DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize); if (ZSTD_isError(oneSeqSize)) return oneSeqSize; @@ -1405,7 +1405,7 @@ ZSTD_decompressSequences_default(ZSTD_DCtx* dctx, FORCE_INLINE_TEMPLATE seq_t -ZSTD_decodeSequenceLong_body(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) +ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) { seq_t seq; U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits; @@ -1482,27 +1482,98 @@ ZSTD_decodeSequenceLong_body(seqState_t* seqState, ZSTD_longOffset_e const longO return seq; } -static seq_t -ZSTD_decodeSequenceLong_default(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) +FORCE_INLINE_TEMPLATE size_t +ZSTD_decompressSequencesLong_body( + ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset) { - return ZSTD_decodeSequenceLong_body(seqState, longOffsets); + const BYTE* ip = (const BYTE*)seqStart; + const BYTE* const iend = ip + seqSize; + BYTE* const ostart = (BYTE* const)dst; + BYTE* const oend = ostart + maxDstSize; + BYTE* op = ostart; + const BYTE* litPtr = dctx->litPtr; + const BYTE* const litEnd = litPtr + dctx->litSize; + const BYTE* const prefixStart = (const BYTE*) (dctx->base); + const BYTE* const dictStart = (const BYTE*) (dctx->vBase); + const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); + + /* Regen sequences */ + if (nbSeq) { +#define STORED_SEQS 4 +#define STOSEQ_MASK (STORED_SEQS-1) +#define ADVANCED_SEQS 4 + seq_t sequences[STORED_SEQS]; + int const seqAdvance = MIN(nbSeq, ADVANCED_SEQS); + seqState_t seqState; + int seqNb; + dctx->fseEntropy = 1; + { U32 i; for (i=0; ientropy.rep[i]; } + seqState.prefixStart = prefixStart; + seqState.pos = (size_t)(op-prefixStart); + seqState.dictEnd = dictEnd; + CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); + ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); + ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); + ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); + + /* prepare in advance */ + for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && (seqNbentropy.rep[i] = (U32)(seqState.prevOffset[i]); } +#undef STORED_SEQS +#undef STOSEQ_MASK +#undef ADVANCED_SEQS + } + + /* last literal segment */ + { size_t const lastLLSize = litEnd - litPtr; + if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall); + memcpy(op, litPtr, lastLLSize); + op += lastLLSize; + } + + return op-ostart; +} + +static size_t +ZSTD_decompressSequencesLong_default(ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset) +{ + return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); } -#define FUNCTION(fn) fn##_default -#define TARGET -#include "zstd_decompress_impl.h" -#undef TARGET -#undef FUNCTION #if DYNAMIC_BMI2 -static TARGET_ATTRIBUTE("bmi2") seq_t -ZSTD_decodeSequenceLong_bmi2(seqState_t* seqState, ZSTD_longOffset_e const longOffsets) -{ - return ZSTD_decodeSequenceLong_body(seqState, longOffsets); -} - static TARGET_ATTRIBUTE("bmi2") size_t ZSTD_decompressSequences_bmi2(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, @@ -1512,11 +1583,14 @@ ZSTD_decompressSequences_bmi2(ZSTD_DCtx* dctx, return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); } -#define FUNCTION(fn) fn##_bmi2 -#define TARGET TARGET_ATTRIBUTE("bmi2") -#include "zstd_decompress_impl.h" -#undef TARGET -#undef FUNCTION +static TARGET_ATTRIBUTE("bmi2") size_t +ZSTD_decompressSequencesLong_bmi2(ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset) +{ + return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset); +} #endif diff --git a/lib/decompress/zstd_decompress_impl.h b/lib/decompress/zstd_decompress_impl.h deleted file mode 100644 index 123d5da5c..000000000 --- a/lib/decompress/zstd_decompress_impl.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2018-present, 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. - */ - -#ifndef FUNCTION -# error "FUNCTION(name) must be defined" -#endif - -#ifndef TARGET -# error "TARGET must be defined" -#endif - - - -static TARGET -size_t FUNCTION(ZSTD_decompressSequencesLong)( - ZSTD_DCtx* dctx, - void* dst, size_t maxDstSize, - const void* seqStart, size_t seqSize, int nbSeq, - const ZSTD_longOffset_e isLongOffset) -{ - const BYTE* ip = (const BYTE*)seqStart; - const BYTE* const iend = ip + seqSize; - BYTE* const ostart = (BYTE* const)dst; - BYTE* const oend = ostart + maxDstSize; - BYTE* op = ostart; - const BYTE* litPtr = dctx->litPtr; - const BYTE* const litEnd = litPtr + dctx->litSize; - const BYTE* const prefixStart = (const BYTE*) (dctx->base); - const BYTE* const dictStart = (const BYTE*) (dctx->vBase); - const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); - - /* Regen sequences */ - if (nbSeq) { -#define STORED_SEQS 4 -#define STOSEQ_MASK (STORED_SEQS-1) -#define ADVANCED_SEQS 4 - seq_t sequences[STORED_SEQS]; - int const seqAdvance = MIN(nbSeq, ADVANCED_SEQS); - seqState_t seqState; - int seqNb; - dctx->fseEntropy = 1; - { U32 i; for (i=0; ientropy.rep[i]; } - seqState.prefixStart = prefixStart; - seqState.pos = (size_t)(op-prefixStart); - seqState.dictEnd = dictEnd; - CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); - ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); - ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); - ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); - - /* prepare in advance */ - for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && (seqNbentropy.rep[i] = (U32)(seqState.prevOffset[i]); } -#undef STORED_SEQS -#undef STOSEQ_MASK -#undef ADVANCED_SEQS - } - - /* last literal segment */ - { size_t const lastLLSize = litEnd - litPtr; - if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall); - memcpy(op, litPtr, lastLLSize); - op += lastLLSize; - } - - return op-ostart; -} From c6351021e4e8be917bfda988eba4abe9d2bb6228 Mon Sep 17 00:00:00 2001 From: Haydn Trigg Date: Sun, 11 Mar 2018 00:15:31 +1030 Subject: [PATCH 175/255] Visual Studio 2017 build scripts --- README.md | 4 ++-- build/VS_scripts/build.VS2017Community.cmd | 7 +++++++ build/VS_scripts/build.VS2017Enterprise.cmd | 7 +++++++ build/VS_scripts/build.VS2017Professional.cmd | 7 +++++++ build/VS_scripts/build.generic.cmd | 4 +++- 5 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 build/VS_scripts/build.VS2017Community.cmd create mode 100644 build/VS_scripts/build.VS2017Enterprise.cmd create mode 100644 build/VS_scripts/build.VS2017Professional.cmd diff --git a/README.md b/README.md index a7d3250e4..808451d12 100644 --- a/README.md +++ b/README.md @@ -128,8 +128,8 @@ A Meson project is provided within `contrib/meson`. Going into `build` directory, you will find additional possibilities: - Projects for Visual Studio 2005, 2008 and 2010. - + VS2010 project is compatible with VS2012, VS2013 and VS2015. -- Automated build scripts for Visual compiler by @KrzysFR , in `build/VS_scripts`, + + VS2010 project is compatible with VS2012, VS2013, VS2015 and VS2017. +- Automated build scripts for Visual compiler by [@KrzysFR](https://github.com/KrzysFR) and [@HaydnTrigg](https://github.com/HaydnTrigg) , in `build/VS_scripts`, which will build `zstd` cli and `libzstd` library without any need to open Visual Studio solution. diff --git a/build/VS_scripts/build.VS2017Community.cmd b/build/VS_scripts/build.VS2017Community.cmd new file mode 100644 index 000000000..133e1b456 --- /dev/null +++ b/build/VS_scripts/build.VS2017Community.cmd @@ -0,0 +1,7 @@ +@echo off + +rem build 32-bit +call "%~p0%build.generic.cmd" VS2017Community Win32 Release v141 + +rem build 64-bit +call "%~p0%build.generic.cmd" VS2017Community x64 Release v141 \ No newline at end of file diff --git a/build/VS_scripts/build.VS2017Enterprise.cmd b/build/VS_scripts/build.VS2017Enterprise.cmd new file mode 100644 index 000000000..6a7093205 --- /dev/null +++ b/build/VS_scripts/build.VS2017Enterprise.cmd @@ -0,0 +1,7 @@ +@echo off + +rem build 32-bit +call "%~p0%build.generic.cmd" VS2017Enterprise Win32 Release v141 + +rem build 64-bit +call "%~p0%build.generic.cmd" VS2017Enterprise x64 Release v141 \ No newline at end of file diff --git a/build/VS_scripts/build.VS2017Professional.cmd b/build/VS_scripts/build.VS2017Professional.cmd new file mode 100644 index 000000000..d183519df --- /dev/null +++ b/build/VS_scripts/build.VS2017Professional.cmd @@ -0,0 +1,7 @@ +@echo off + +rem build 32-bit +call "%~p0%build.generic.cmd" VS2017Professional Win32 Release v141 + +rem build 64-bit +call "%~p0%build.generic.cmd" VS2017Professional x64 Release v141 \ No newline at end of file diff --git a/build/VS_scripts/build.generic.cmd b/build/VS_scripts/build.generic.cmd index 362952340..aa24053d0 100644 --- a/build/VS_scripts/build.generic.cmd +++ b/build/VS_scripts/build.generic.cmd @@ -31,7 +31,9 @@ EXIT /B 1 SET msbuild="%windir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" IF %msbuild_version% == VS2013 SET msbuild="%programfiles(x86)%\MSBuild\12.0\Bin\MSBuild.exe" IF %msbuild_version% == VS2015 SET msbuild="%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" -rem TODO: Visual Studio "15" (vNext) will use MSBuild 15.0 ? +IF %msbuild_version% == VS2017Community SET msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" +IF %msbuild_version% == VS2017Professional SET msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\MSBuild.exe" +IF %msbuild_version% == VS2017Enterprise SET msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MSBuild.exe" SET project="%~p0\..\VS2010\zstd.sln" From facc09aa0327267c06708acf2cb2e29cd77002c8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 11 Mar 2018 03:06:52 -0700 Subject: [PATCH 176/255] minor compression level adaptation level 12 compresses slightly more and faster due to better btlazy2 mode --- lib/compress/zstd_compress.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 66fb4827a..706845eb7 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3216,8 +3216,8 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 18, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ { 18, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ { 18, 18, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 11.*/ - { 18, 18, 17, 7, 4, 8, ZSTD_lazy2 }, /* level 12.*/ - { 18, 19, 17, 6, 4, 8, ZSTD_btlazy2 }, /* level 13 */ + { 18, 18, 17, 5, 4, 8, ZSTD_btlazy2 }, /* level 12.*/ + { 18, 19, 17, 7, 4, 8, ZSTD_btlazy2 }, /* level 13 */ { 18, 18, 18, 4, 4, 16, ZSTD_btopt }, /* level 14.*/ { 18, 18, 18, 4, 3, 16, ZSTD_btopt }, /* level 15.*/ { 18, 19, 18, 6, 3, 32, ZSTD_btopt }, /* level 16.*/ From a146ee04ae5866b948be0c1911418e0436d80cb4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 11 Mar 2018 05:21:53 -0700 Subject: [PATCH 177/255] added negative compression levels negative compression level trade compression ratio for more compression speed. They turn off huffman compression of literals, and use row 0 as baseline with a stepSize = -cLevel. added associated test in fuzzer also added : new advanced parameter ZSTD_p_literalCompression --- lib/compress/zstd_compress.c | 122 +++++++++++++++----------- lib/compress/zstd_compress_internal.h | 21 +++-- lib/compress/zstd_fast.c | 45 +++++----- lib/zstd.h | 48 +++++----- tests/fuzzer.c | 7 +- 5 files changed, 133 insertions(+), 110 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 706845eb7..fb310d369 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -252,6 +252,7 @@ static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param) case ZSTD_p_minMatch: case ZSTD_p_targetLength: case ZSTD_p_compressionStrategy: + case ZSTD_p_literalCompression: return 1; case ZSTD_p_format: @@ -305,6 +306,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v } return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); + case ZSTD_p_literalCompression: case ZSTD_p_contentSizeFlag: case ZSTD_p_checksumFlag: case ZSTD_p_dictIDFlag: @@ -417,6 +419,10 @@ size_t ZSTD_CCtxParam_setParameter( } return (size_t)CCtxParams->cParams.strategy; + case ZSTD_p_literalCompression: + CCtxParams->disableLiteralCompression = !value; + return !!value; + case ZSTD_p_contentSizeFlag : /* Content size written in frame header _when known_ (default:1) */ DEBUGLOG(4, "set content size flag = %u", (value>0)); @@ -1376,7 +1382,7 @@ static size_t ZSTD_minGain(size_t srcSize) { return (srcSize >> 6) + 2; } static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy, ZSTD_entropyCTables_t* nextEntropy, - ZSTD_strategy strategy, + ZSTD_strategy strategy, int disableLiteralCompression, void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32* workspace, const int bmi2) @@ -1393,10 +1399,14 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy, memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, sizeof(prevEntropy->hufCTable)); + if (disableLiteralCompression) + return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); + /* small ? don't even attempt compression (speed opt) */ # define LITERAL_NOENTROPY 63 - { size_t const minLitSize = prevEntropy->hufCTable_repeatMode == HUF_repeat_valid ? 6 : LITERAL_NOENTROPY; - if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); + { size_t const minLitSize = (prevEntropy->hufCTable_repeatMode == HUF_repeat_valid) ? 6 : LITERAL_NOENTROPY; + if (srcSize <= minLitSize) + return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } if (dstCapacity < lhSize+1) return ERROR(dstSize_tooSmall); /* not enough space for compression */ @@ -1604,11 +1614,11 @@ size_t ZSTD_encodeSequences( MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, ZSTD_entropyCTables_t const* prevEntropy, ZSTD_entropyCTables_t* nextEntropy, - ZSTD_compressionParameters const* cParams, + ZSTD_CCtx_params const* cctxParams, void* dst, size_t dstCapacity, U32* workspace, const int bmi2) { - const int longOffsets = cParams->windowLog > STREAM_ACCUMULATOR_MIN; + const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; U32 count[MaxSeq+1]; FSE_CTable* CTable_LitLength = nextEntropy->litlengthCTable; FSE_CTable* CTable_OffsetBits = nextEntropy->offcodeCTable; @@ -1629,9 +1639,12 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, /* Compress literals */ { const BYTE* const literals = seqStorePtr->litStart; size_t const litSize = seqStorePtr->lit - literals; - size_t const cSize = ZSTD_compressLiterals(prevEntropy, nextEntropy, - cParams->strategy, op, dstCapacity, literals, litSize, - workspace, bmi2); + size_t const cSize = ZSTD_compressLiterals( + prevEntropy, nextEntropy, + cctxParams->cParams.strategy, cctxParams->disableLiteralCompression, + op, dstCapacity, + literals, litSize, + workspace, bmi2); if (ZSTD_isError(cSize)) return cSize; assert(cSize <= dstCapacity); @@ -1722,28 +1735,24 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, ZSTD_entropyCTables_t const* prevEntropy, ZSTD_entropyCTables_t* nextEntropy, - ZSTD_compressionParameters const* cParams, + ZSTD_CCtx_params const* cctxParams, void* dst, size_t dstCapacity, size_t srcSize, U32* workspace, int bmi2) { size_t const cSize = ZSTD_compressSequences_internal( - seqStorePtr, prevEntropy, nextEntropy, cParams, dst, dstCapacity, + seqStorePtr, prevEntropy, nextEntropy, cctxParams, dst, dstCapacity, workspace, bmi2); - /* If the srcSize <= dstCapacity, then there is enough space to write a - * raw uncompressed block. Since we ran out of space, the block must not - * be compressible, so fall back to a raw uncompressed block. + /* When srcSize <= dstCapacity, there is enough space to write a raw uncompressed block. + * Since we ran out of space, block must be not compressible, so fall back to raw uncompressed block. */ - int const uncompressibleError = (cSize == ERROR(dstSize_tooSmall)) && (srcSize <= dstCapacity); - if (ZSTD_isError(cSize) && !uncompressibleError) - return cSize; + if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity)) + return 0; /* block not compressed */ + if (ZSTD_isError(cSize)) return cSize; /* Check compressibility */ - { size_t const minGain = ZSTD_minGain(srcSize); /* note : fixed formula, maybe should depend on compression level, or strategy */ - size_t const maxCSize = srcSize - minGain; - if (cSize >= maxCSize || uncompressibleError) { - return 0; /* block not compressed */ - } } - assert(!ZSTD_isError(cSize)); + { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize); /* note : fixed formula, maybe should depend on compression level, or strategy */ + if (cSize >= maxCSize) return 0; /* block not compressed */ + } /* We check that dictionaries have offset codes available for the first * block. After the first block, the offcode table might not have large @@ -1813,7 +1822,10 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, /* select and store sequences */ { U32 const extDict = ZSTD_window_hasExtDict(ms->window); size_t lastLLSize; - { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i]; } + { int i; + for (i = 0; i < ZSTD_REP_NUM; ++i) + zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i]; + } if (zc->appliedParams.ldmParams.enableLdm) { size_t const nbSeq = ZSTD_ldm_generateSequences(&zc->ldmState, zc->ldmSequences, @@ -1834,7 +1846,11 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, } } /* encode sequences and literals */ - { size_t const cSize = ZSTD_compressSequences(&zc->seqStore, &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize, zc->entropyWorkspace, zc->bmi2); + { size_t const cSize = ZSTD_compressSequences(&zc->seqStore, + &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, + &zc->appliedParams, + dst, dstCapacity, + srcSize, zc->entropyWorkspace, zc->bmi2); if (ZSTD_isError(cSize) || cSize == 0) return cSize; /* confirm repcodes and entropy tables */ { ZSTD_compressedBlockState_t* const tmp = zc->blockState.prevCBlock; @@ -2422,20 +2438,20 @@ size_t ZSTD_compress_advanced_internal( return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); } -size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, +size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict, size_t dictSize, int compressionLevel) { - ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize ? srcSize : 1, dict ? dictSize : 0); - params.fParams.contentSizeFlag = 1; - DEBUGLOG(4, "ZSTD_compress_usingDict (level=%i, srcSize=%u, dictSize=%u)", - compressionLevel, (U32)srcSize, (U32)dictSize); - return ZSTD_compress_internal(ctx, dst, dstCapacity, src, srcSize, dict, dictSize, params); + ZSTD_parameters const params = ZSTD_getParams(compressionLevel, srcSize ? srcSize : 1, dict ? dictSize : 0); + ZSTD_CCtx_params cctxParams = ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params); + assert(params.fParams.contentSizeFlag == 1); + ZSTD_CCtxParam_setParameter(&cctxParams, ZSTD_p_literalCompression, compressionLevel>=0); + return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, dict, dictSize, cctxParams); } -size_t ZSTD_compressCCtx (ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) +size_t ZSTD_compressCCtx (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) { DEBUGLOG(4, "ZSTD_compressCCtx (srcSize=%u)", (U32)srcSize); - return ZSTD_compress_usingDict(ctx, dst, dstCapacity, src, srcSize, NULL, 0, compressionLevel); + return ZSTD_compress_usingDict(cctx, dst, dstCapacity, src, srcSize, NULL, 0, compressionLevel); } size_t ZSTD_compress(void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) @@ -3178,11 +3194,11 @@ int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; } static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = { { /* "default" - guarantees a monotonically increasing memory budget */ /* W, C, H, S, L, TL, strat */ - { 18, 12, 12, 1, 7, 16, ZSTD_fast }, /* level 0 - never used */ - { 19, 13, 14, 1, 7, 16, ZSTD_fast }, /* level 1 */ - { 19, 15, 16, 1, 6, 16, ZSTD_fast }, /* level 2 */ - { 20, 16, 17, 1, 5, 16, ZSTD_dfast }, /* level 3 */ - { 20, 17, 18, 1, 5, 16, ZSTD_dfast }, /* level 4 */ + { 19, 12, 13, 1, 6, 1, ZSTD_fast }, /* base for negative levels */ + { 19, 13, 14, 1, 7, 1, ZSTD_fast }, /* level 1 */ + { 19, 15, 16, 1, 6, 1, ZSTD_fast }, /* level 2 */ + { 20, 16, 17, 1, 5, 8, ZSTD_dfast }, /* level 3 */ + { 20, 17, 18, 1, 5, 8, ZSTD_dfast }, /* level 4 */ { 20, 17, 18, 2, 5, 16, ZSTD_greedy }, /* level 5 */ { 21, 17, 19, 2, 5, 16, ZSTD_lazy }, /* level 6 */ { 21, 18, 19, 3, 5, 16, ZSTD_lazy }, /* level 7 */ @@ -3191,9 +3207,9 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 21, 19, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */ { 22, 20, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */ { 22, 20, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */ - { 22, 21, 22, 4, 5, 16, ZSTD_btlazy2 }, /* level 13 */ - { 22, 21, 22, 5, 5, 16, ZSTD_btlazy2 }, /* level 14 */ - { 22, 22, 22, 6, 5, 16, ZSTD_btlazy2 }, /* level 15 */ + { 22, 21, 22, 4, 5, 32, ZSTD_btlazy2 }, /* level 13 */ + { 22, 21, 22, 5, 5, 32, ZSTD_btlazy2 }, /* level 14 */ + { 22, 22, 22, 6, 5, 32, ZSTD_btlazy2 }, /* level 15 */ { 22, 21, 22, 4, 5, 48, ZSTD_btopt }, /* level 16 */ { 23, 22, 22, 4, 4, 48, ZSTD_btopt }, /* level 17 */ { 23, 22, 22, 5, 3, 64, ZSTD_btopt }, /* level 18 */ @@ -3204,8 +3220,8 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV }, { /* for srcSize <= 256 KB */ /* W, C, H, S, L, T, strat */ - { 0, 0, 0, 0, 0, 0, ZSTD_fast }, /* level 0 - not used */ - { 18, 13, 14, 1, 6, 8, ZSTD_fast }, /* level 1 */ + { 18, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ + { 18, 13, 14, 1, 6, 1, ZSTD_fast }, /* level 1 */ { 18, 14, 13, 1, 5, 8, ZSTD_dfast }, /* level 2 */ { 18, 16, 15, 1, 5, 8, ZSTD_dfast }, /* level 3 */ { 18, 15, 17, 1, 5, 8, ZSTD_greedy }, /* level 4.*/ @@ -3230,9 +3246,9 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV }, { /* for srcSize <= 128 KB */ /* W, C, H, S, L, T, strat */ - { 17, 12, 12, 1, 7, 8, ZSTD_fast }, /* level 0 - not used */ - { 17, 12, 13, 1, 6, 8, ZSTD_fast }, /* level 1 */ - { 17, 13, 16, 1, 5, 8, ZSTD_fast }, /* level 2 */ + { 17, 12, 12, 1, 5, 1, ZSTD_fast }, /* level 0 - not used */ + { 17, 12, 13, 1, 6, 1, ZSTD_fast }, /* level 1 */ + { 17, 13, 16, 1, 5, 1, ZSTD_fast }, /* level 2 */ { 17, 16, 16, 2, 5, 8, ZSTD_dfast }, /* level 3 */ { 17, 13, 15, 3, 4, 8, ZSTD_greedy }, /* level 4 */ { 17, 15, 17, 4, 4, 8, ZSTD_greedy }, /* level 5 */ @@ -3256,9 +3272,9 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV }, { /* for srcSize <= 16 KB */ /* W, C, H, S, L, T, strat */ - { 14, 12, 12, 1, 7, 6, ZSTD_fast }, /* level 0 - not used */ - { 14, 14, 14, 1, 6, 6, ZSTD_fast }, /* level 1 */ - { 14, 14, 14, 1, 4, 6, ZSTD_fast }, /* level 2 */ + { 14, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ + { 14, 14, 14, 1, 6, 1, ZSTD_fast }, /* level 1 */ + { 14, 14, 14, 1, 4, 1, ZSTD_fast }, /* level 2 */ { 14, 14, 14, 1, 4, 6, ZSTD_dfast }, /* level 3.*/ { 14, 14, 14, 4, 4, 6, ZSTD_greedy }, /* level 4.*/ { 14, 14, 14, 3, 4, 6, ZSTD_lazy }, /* level 5.*/ @@ -3290,12 +3306,12 @@ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long l size_t const addedSize = srcSizeHint ? 0 : 500; U64 const rSize = srcSizeHint+dictSize ? srcSizeHint+dictSize+addedSize : (U64)-1; U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB); /* intentional underflow for srcSizeHint == 0 */ - DEBUGLOG(4, "ZSTD_getCParams: cLevel=%i, srcSize=%u, dictSize=%u => table %u", - compressionLevel, (U32)srcSizeHint, (U32)dictSize, tableID); - if (compressionLevel <= 0) compressionLevel = ZSTD_CLEVEL_DEFAULT; /* 0 == default; no negative compressionLevel yet */ + if (compressionLevel == 0) compressionLevel = ZSTD_CLEVEL_DEFAULT; /* 0 == default */ + if (compressionLevel < 0) compressionLevel = 0; /* entry 0 is baseline for fast mode */ if (compressionLevel > ZSTD_MAX_CLEVEL) compressionLevel = ZSTD_MAX_CLEVEL; - { ZSTD_compressionParameters const cp = ZSTD_defaultCParameters[tableID][compressionLevel]; - return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize); } + { ZSTD_compressionParameters cp = ZSTD_defaultCParameters[tableID][compressionLevel]; + if (compressionLevel < 0) cp.targetLength = (unsigned)(-compressionLevel); /* acceleration factor */ + return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize); } } diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 92bd0c707..65e99cd90 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -158,6 +158,7 @@ struct ZSTD_CCtx_params_s { ZSTD_frameParameters fParams; int compressionLevel; + int disableLiteralCompression; int forceWindow; /* force back-references to respect limit of * 1<> (32-h) ; } MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h); } /* only in zstd_opt.h */ @@ -453,9 +456,9 @@ MEM_STATIC size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls) #define ZSTD_LOWLIMIT_MAX (3U << 29) /* Max lowLimit allowed */ /* Maximum chunk size before overflow correction needs to be called again */ -#define ZSTD_CHUNKSIZE_MAX \ - ( ((U32)-1) /* Maximum ending current index */ \ - - (1U << ZSTD_WINDOWLOG_MAX) /* Max distance from lowLimit to current */ \ +#define ZSTD_CHUNKSIZE_MAX \ + ( ((U32)-1) /* Maximum ending current index */ \ + - (1U << ZSTD_WINDOWLOG_MAX) /* Max distance from lowLimit to current */ \ - ZSTD_LOWLIMIT_MAX) /* Maximum beginning lowLimit */ /** diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index d2f50488e..df4d28b34 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -42,7 +42,7 @@ FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_fast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, - U32 const hlog, U32 const mls) + U32 const hlog, U32 const stepSize, U32 const mls) { U32* const hashTable = ms->hashTable; const BYTE* const base = ms->window.base; @@ -77,19 +77,19 @@ size_t ZSTD_compressBlock_fast_generic( ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else { - U32 offset; - if ( (matchIndex <= lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { - ip += ((ip-anchor) >> kSearchStrength) + 1; + if ( (matchIndex <= lowestIndex) + || (MEM_read32(match) != MEM_read32(ip)) ) { + assert(stepSize >= 1); + ip += ((ip-anchor) >> kSearchStrength) + stepSize; continue; } mLength = ZSTD_count(ip+4, match+4, iend) + 4; - offset = (U32)(ip-match); - while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - offset_2 = offset_1; - offset_1 = offset; - - ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); - } + { U32 const offset = (U32)(ip-match); + while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + offset_2 = offset_1; + offset_1 = offset; + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + } } /* match found */ ip += mLength; @@ -128,17 +128,18 @@ size_t ZSTD_compressBlock_fast( { U32 const hlog = cParams->hashLog; U32 const mls = cParams->searchLength; + U32 const stepSize = cParams->targetLength; switch(mls) { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, 4); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4); case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, 5); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5); case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, 6); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6); case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, 7); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7); } } @@ -146,7 +147,7 @@ size_t ZSTD_compressBlock_fast( static size_t ZSTD_compressBlock_fast_extDict_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, - U32 const hlog, U32 const mls) + U32 const hlog, U32 const stepSize, U32 const mls) { U32* hashTable = ms->hashTable; const BYTE* const base = ms->window.base; @@ -185,7 +186,8 @@ static size_t ZSTD_compressBlock_fast_extDict_generic( } else { if ( (matchIndex < lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { - ip += ((ip-anchor) >> kSearchStrength) + 1; + assert(stepSize >= 1); + ip += ((ip-anchor) >> kSearchStrength) + stepSize; continue; } { const BYTE* matchEnd = matchIndex < dictLimit ? dictEnd : iend; @@ -241,16 +243,17 @@ size_t ZSTD_compressBlock_fast_extDict( { U32 const hlog = cParams->hashLog; U32 const mls = cParams->searchLength; + U32 const stepSize = cParams->targetLength; switch(mls) { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, 4); + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4); case 5 : - return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, 5); + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5); case 6 : - return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, 6); + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6); case 7 : - return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, 7); + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7); } } diff --git a/lib/zstd.h b/lib/zstd.h index deedd3643..e075115ff 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -390,7 +390,7 @@ ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output #define ZSTD_SEARCHLOG_MIN 1 #define ZSTD_SEARCHLENGTH_MAX 7 /* only for ZSTD_fast, other strategies are limited to 6 */ #define ZSTD_SEARCHLENGTH_MIN 3 /* only for ZSTD_btopt, other strategies are limited to 4 */ -#define ZSTD_TARGETLENGTH_MIN 4 /* only useful for btopt */ +#define ZSTD_TARGETLENGTH_MIN 1 /* only useful for btopt */ #define ZSTD_TARGETLENGTH_MAX 999 /* only useful for btopt */ #define ZSTD_LDM_MINMATCH_MIN 4 #define ZSTD_LDM_MINMATCH_MAX 4096 @@ -1001,15 +1001,17 @@ typedef enum { * (note : a strong exception to this rule is when first invocation sets ZSTD_e_end : it becomes a blocking call). * More workers improve speed, but also increase memory usage. * Default value is `0`, aka "single-threaded mode" : no worker is spawned, compression is performed inside Caller's thread, all invocations are blocking */ - ZSTD_p_jobSize, /* Size of a compression job. This value is only enforced in streaming (non-blocking) mode. - * Each compression job is completed in parallel, so indirectly controls the nb of active threads. + ZSTD_p_jobSize, /* Size of a compression job. This value is enforced only in non-blocking mode. + * Each compression job is completed in parallel, so this value indirectly controls the nb of active threads. * 0 means default, which is dynamically determined based on compression parameters. - * Job size must be a minimum of overlapSize, or 1 KB, whichever is largest + * Job size must be a minimum of overlapSize, or 1 MB, whichever is largest. * The minimum size is automatically and transparently enforced */ ZSTD_p_overlapSizeLog, /* Size of previous input reloaded at the beginning of each job. * 0 => no overlap, 6(default) => use 1/8th of windowSize, >=9 => use full windowSize */ /* advanced parameters - may not remain available after API update */ + ZSTD_p_literalCompression=1000, /* control huffman compression of literals (enabled) by default. + * disabling it improves speed and decreases compression ratio by a large amount. */ ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize, * even when referencing into Dictionary content (default:0) */ ZSTD_p_enableLongDistanceMatching=1200, /* Enable long distance matching. @@ -1070,23 +1072,21 @@ ZSTDLIB_API size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize); /*! ZSTD_CCtx_loadDictionary() : - * Create an internal CDict from dict buffer. - * Decompression will have to use same buffer. + * Create an internal CDict from `dict` buffer. + * Decompression will have to use same dictionary. * @result : 0, or an error code (which can be tested with ZSTD_isError()). - * Special : Adding a NULL (or 0-size) dictionary invalidates any previous dictionary, - * meaning "return to no-dictionary mode". - * Note 1 : `dict` content will be copied internally. Use - * ZSTD_CCtx_loadDictionary_byReference() to reference dictionary - * content instead. The dictionary buffer must then outlive its - * users. + * Special: Adding a NULL (or 0-size) dictionary invalidates previous dictionary, + * meaning "return to no-dictionary mode". + * Note 1 : Dictionary will be used for all future compression jobs. + * To return to "no-dictionary" situation, load a NULL dictionary * Note 2 : Loading a dictionary involves building tables, which are dependent on compression parameters. * For this reason, compression parameters cannot be changed anymore after loading a dictionary. - * It's also a CPU-heavy operation, with non-negligible impact on latency. - * Note 3 : Dictionary will be used for all future compression jobs. - * To return to "no-dictionary" situation, load a NULL dictionary - * Note 5 : Use ZSTD_CCtx_loadDictionary_advanced() to select how dictionary - * content will be interpreted. - */ + * It's also a CPU consuming operation, with non-negligible impact on latency. + * Note 3 :`dict` content will be copied internally. + * Use ZSTD_CCtx_loadDictionary_byReference() to reference dictionary content instead. + * In such a case, dictionary buffer must outlive its users. + * Note 4 : Use ZSTD_CCtx_loadDictionary_advanced() + * to precisely select how dictionary content must be interpreted. */ ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode); @@ -1101,8 +1101,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void * Special : adding a NULL CDict means "return to no-dictionary mode". * Note 1 : Currently, only one dictionary can be managed. * Adding a new dictionary effectively "discards" any previous one. - * Note 2 : CDict is just referenced, its lifetime must outlive CCtx. - */ + * Note 2 : CDict is just referenced, its lifetime must outlive CCtx. */ ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /*! ZSTD_CCtx_refPrefix() : @@ -1112,13 +1111,12 @@ ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); * Subsequent compression jobs will be done without prefix (if none is explicitly referenced). * If there is a need to use same prefix multiple times, consider embedding it into a ZSTD_CDict instead. * @result : 0, or an error code (which can be tested with ZSTD_isError()). - * Special : Adding any prefix (including NULL) invalidates any previous prefix or dictionary + * Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary * Note 1 : Prefix buffer is referenced. It must outlive compression job. * Note 2 : Referencing a prefix involves building tables, which are dependent on compression parameters. - * It's a CPU-heavy operation, with non-negligible impact on latency. - * Note 3 : By default, the prefix is treated as raw content - * (ZSTD_dm_rawContent). Use ZSTD_CCtx_refPrefix_advanced() to alter - * dictMode. */ + * It's a CPU consuming operation, with non-negligible impact on latency. + * Note 3 : By default, the prefix is treated as raw content (ZSTD_dm_rawContent). + * Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode. */ ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize); ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictMode_e dictMode); diff --git a/tests/fuzzer.c b/tests/fuzzer.c index aecc06449..8d8e547c2 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -34,7 +34,7 @@ #include "zdict.h" /* ZDICT_trainFromBuffer */ #include "datagen.h" /* RDG_genBuffer */ #include "mem.h" -#define XXH_STATIC_LINKING_ONLY +#define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ #include "xxhash.h" /* XXH64 */ #include "util.h" @@ -1382,10 +1382,13 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD crcOrig = XXH64(sampleBuffer, sampleSize, 0); /* compression tests */ - { int const cLevel = + { int const cLevelPositive = ( FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (FUZ_highbit32((U32)sampleSize) / cLevelLimiter)) ) + 1; + int const cLevel = ((FUZ_rand(&lseed) & 15) == 3) ? + - (int)((FUZ_rand(&lseed) & 7) + 1) : /* test negative cLevel */ + cLevelPositive; DISPLAYLEVEL(5, "fuzzer t%u: Simple compression test (level %i) \n", testNb, cLevel); cSize = ZSTD_compressCCtx(ctx, cBuffer, cBufferSize, sampleBuffer, sampleSize, cLevel); CHECK(ZSTD_isError(cSize), "ZSTD_compressCCtx failed : %s", ZSTD_getErrorName(cSize)); From 6a9b41b7310db8365c4d899861da58fb86e51f64 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 11 Mar 2018 19:56:48 -0700 Subject: [PATCH 178/255] create command --fast[=#] access negative compression levels from command line for both compression and benchmark modes. also : ensure proper propagation of parameters through ZSTD_compress_generic() interface. added relevant cli tests. --- lib/compress/zstd_compress.c | 65 +++++++++++++++++++++------------- lib/compress/zstdmt_compress.c | 14 ++++---- lib/zstd.h | 12 +++++-- programs/bench.c | 8 ++--- programs/fileio.c | 4 ++- programs/zstd.1.md | 8 +++++ programs/zstdcli.c | 44 +++++++++++++++++------ tests/playTests.sh | 8 +++-- 8 files changed, 111 insertions(+), 52 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index fb310d369..5347cf7ec 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -146,7 +146,7 @@ const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStor static ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams( ZSTD_CCtx_params CCtxParams, U64 srcSizeHint, size_t dictSize) { - DEBUGLOG(4, "ZSTD_getCParamsFromCCtxParams: srcSize = %u, dictSize = %u", + DEBUGLOG(2, "ZSTD_getCParamsFromCCtxParams: srcSize = %u, dictSize = %u", (U32)srcSizeHint, (U32)dictSize); return (CCtxParams.compressionLevel == ZSTD_CLEVEL_CUSTOM) ? CCtxParams.cParams : @@ -356,11 +356,17 @@ size_t ZSTD_CCtxParam_setParameter( CCtxParams->format = (ZSTD_format_e)value; return (size_t)CCtxParams->format; - case ZSTD_p_compressionLevel : - if ((int)value > ZSTD_maxCLevel()) value = ZSTD_maxCLevel(); - if (value) /* 0 : does not change current level */ - CCtxParams->compressionLevel = value; - return CCtxParams->compressionLevel; + case ZSTD_p_compressionLevel : { + int cLevel = (int)value; /* cast expected to restore negative sign */ + if (cLevel > ZSTD_maxCLevel()) cLevel = ZSTD_maxCLevel(); + DEBUGLOG(2, "ZSTD_CCtxParam_setParameter: set cLevel=%i", cLevel); + if (cLevel) { /* 0 : does not change current level */ + CCtxParams->disableLiteralCompression = (cLevel<0); /* negative levels disable huffman */ + CCtxParams->compressionLevel = cLevel; + } + if (CCtxParams->compressionLevel >= 0) return CCtxParams->compressionLevel; + return 0; /* return type (size_t) cannot represent negative values */ + } case ZSTD_p_windowLog : DEBUGLOG(4, "ZSTD_CCtxParam_setParameter: set windowLog=%u", value); @@ -1394,6 +1400,9 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy, symbolEncodingType_e hType = set_compressed; size_t cLitSize; + DEBUGLOG(2,"ZSTD_compressLiterals (disableLiteralCompression=%i)", + disableLiteralCompression); + /* Prepare nextEntropy assuming reusing the existing table */ nextEntropy->hufCTable_repeatMode = prevEntropy->hufCTable_repeatMode; memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, @@ -2759,29 +2768,30 @@ size_t ZSTD_CStreamOutSize(void) return ZSTD_compressBound(ZSTD_BLOCKSIZE_MAX) + ZSTD_blockHeaderSize + 4 /* 32-bits hash */ ; } -static size_t ZSTD_resetCStream_internal(ZSTD_CStream* zcs, +static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx, const void* const dict, size_t const dictSize, ZSTD_dictMode_e const dictMode, const ZSTD_CDict* const cdict, ZSTD_CCtx_params const params, unsigned long long const pledgedSrcSize) { - DEBUGLOG(4, "ZSTD_resetCStream_internal"); + DEBUGLOG(2, "ZSTD_resetCStream_internal (disableLiteralCompression=%i)", + params.disableLiteralCompression); /* params are supposed to be fully validated at this point */ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ - CHECK_F( ZSTD_compressBegin_internal(zcs, + CHECK_F( ZSTD_compressBegin_internal(cctx, dict, dictSize, dictMode, cdict, params, pledgedSrcSize, ZSTDb_buffered) ); - zcs->inToCompress = 0; - zcs->inBuffPos = 0; - zcs->inBuffTarget = zcs->blockSize - + (zcs->blockSize == pledgedSrcSize); /* for small input: avoid automatic flush on reaching end of block, since it would require to add a 3-bytes null block to end frame */ - zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0; - zcs->streamStage = zcss_load; - zcs->frameEnded = 0; + cctx->inToCompress = 0; + cctx->inBuffPos = 0; + cctx->inBuffTarget = cctx->blockSize + + (cctx->blockSize == pledgedSrcSize); /* for small input: avoid automatic flush on reaching end of block, since it would require to add a 3-bytes null block to end frame */ + cctx->outBuffContentSize = cctx->outBuffFlushedSize = 0; + cctx->streamStage = zcss_load; + cctx->frameEnded = 0; return 0; /* ready to go */ } @@ -3084,7 +3094,9 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, ZSTD_prefixDict const prefixDict = cctx->prefixDict; memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict)); /* single usage */ assert(prefixDict.dict==NULL || cctx->cdict==NULL); /* only one can be set */ - DEBUGLOG(4, "ZSTD_compress_generic : transparent init stage"); + DEBUGLOG(2, "ZSTD_compress_generic : transparent init stage"); + DEBUGLOG(2, "ZSTD_compress_generic: disableLiteralCompression=%i", + params.disableLiteralCompression); if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = input->size + 1; /* auto-fix pledgedSrcSize */ params.cParams = ZSTD_getCParamsFromCCtxParams( cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/); @@ -3115,10 +3127,10 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, cctx->appliedParams.nbWorkers = params.nbWorkers; } else #endif - { CHECK_F( ZSTD_resetCStream_internal( - cctx, prefixDict.dict, prefixDict.dictSize, - prefixDict.dictMode, cctx->cdict, params, - cctx->pledgedSrcSizePlusOne-1) ); + { CHECK_F( ZSTD_resetCStream_internal(cctx, + prefixDict.dict, prefixDict.dictSize, prefixDict.dictMode, + cctx->cdict, + params, cctx->pledgedSrcSizePlusOne-1) ); assert(cctx->streamStage == zcss_load); assert(cctx->appliedParams.nbWorkers == 0); } } @@ -3306,10 +3318,12 @@ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long l size_t const addedSize = srcSizeHint ? 0 : 500; U64 const rSize = srcSizeHint+dictSize ? srcSizeHint+dictSize+addedSize : (U64)-1; U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB); /* intentional underflow for srcSizeHint == 0 */ - if (compressionLevel == 0) compressionLevel = ZSTD_CLEVEL_DEFAULT; /* 0 == default */ - if (compressionLevel < 0) compressionLevel = 0; /* entry 0 is baseline for fast mode */ - if (compressionLevel > ZSTD_MAX_CLEVEL) compressionLevel = ZSTD_MAX_CLEVEL; - { ZSTD_compressionParameters cp = ZSTD_defaultCParameters[tableID][compressionLevel]; + int row = compressionLevel; + DEBUGLOG(2, "ZSTD_getCParams (cLevel=%i)", compressionLevel); + if (compressionLevel == 0) row = ZSTD_CLEVEL_DEFAULT; /* 0 == default */ + if (compressionLevel < 0) row = 0; /* entry 0 is baseline for fast mode */ + if (compressionLevel > ZSTD_MAX_CLEVEL) row = ZSTD_MAX_CLEVEL; + { ZSTD_compressionParameters cp = ZSTD_defaultCParameters[tableID][row]; if (compressionLevel < 0) cp.targetLength = (unsigned)(-compressionLevel); /* acceleration factor */ return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize); } @@ -3321,6 +3335,7 @@ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long l ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) { ZSTD_parameters params; ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, srcSizeHint, dictSize); + DEBUGLOG(2, "ZSTD_getParams (cLevel=%i)", compressionLevel); memset(¶ms, 0, sizeof(params)); params.cParams = cParams; params.fParams.contentSizeFlag = 1; diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 4d58e2e2f..bc2df2667 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -660,6 +660,7 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) jobParams.cParams = params.cParams; jobParams.fParams = params.fParams; jobParams.compressionLevel = params.compressionLevel; + jobParams.disableLiteralCompression = params.disableLiteralCompression; jobParams.ldmParams = params.ldmParams; return jobParams; @@ -762,11 +763,12 @@ static size_t ZSTDMT_compress_advanced_internal( assert(jobParams.nbWorkers == 0); assert(mtctx->cctxPool->totalCCtx == params.nbWorkers); - DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbJobs=%2u (rawSize=%u bytes; fixedSize=%u) ", + DEBUGLOG(2, "ZSTDMT_compress_advanced_internal: nbJobs=%2u (rawSize=%u bytes; fixedSize=%u) ", nbJobs, (U32)proposedJobSize, (U32)avgJobSize); if ((nbJobs==1) | (params.nbWorkers<=1)) { /* fallback to single-thread mode : this is a blocking invocation anyway */ ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0]; + DEBUGLOG(2, "ZSTDMT_compress_advanced_internal: fallback to single-thread mode"); if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams); return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, NULL, 0, jobParams); } @@ -909,8 +911,8 @@ size_t ZSTDMT_initCStream_internal( const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize) { - DEBUGLOG(4, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u, nbWorkers=%u, cctxPool=%u)", - (U32)pledgedSrcSize, params.nbWorkers, mtctx->cctxPool->totalCCtx); + DEBUGLOG(2, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u, nbWorkers=%u, cctxPool=%u, disableLiteralCompression=%i)", + (U32)pledgedSrcSize, params.nbWorkers, mtctx->cctxPool->totalCCtx, params.disableLiteralCompression); /* params are supposed to be fully validated at this point */ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ @@ -928,14 +930,14 @@ size_t ZSTDMT_initCStream_internal( mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */ if (mtctx->singleBlockingThread) { ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(params); - DEBUGLOG(4, "ZSTDMT_initCStream_internal: switch to single blocking thread mode"); + DEBUGLOG(2, "ZSTDMT_initCStream_internal: switch to single blocking thread mode"); assert(singleThreadParams.nbWorkers == 0); return ZSTD_initCStream_internal(mtctx->cctxPool->cctx[0], dict, dictSize, cdict, singleThreadParams, pledgedSrcSize); } - DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u workers", params.nbWorkers); + DEBUGLOG(2, "ZSTDMT_initCStream_internal: %u workers", params.nbWorkers); if (mtctx->allJobsCompleted == 0) { /* previous compression not correctly finished */ ZSTDMT_waitForAllJobsCompleted(mtctx); @@ -1242,7 +1244,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, { size_t const newJobThreshold = mtctx->inBuff.prefixSize + mtctx->targetSectionSize; unsigned forwardInputProgress = 0; - DEBUGLOG(5, "ZSTDMT_compressStream_generic (endOp=%u, srcSize=%u)", + DEBUGLOG(2, "ZSTDMT_compressStream_generic (endOp=%u, srcSize=%u)", (U32)endOp, (U32)(input->size - input->pos)); assert(output->pos <= output->size); assert(input->pos <= input->size); diff --git a/lib/zstd.h b/lib/zstd.h index e075115ff..6cb7da7ab 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -947,7 +947,9 @@ typedef enum { /* compression parameters */ ZSTD_p_compressionLevel=100, /* Update all compression parameters according to pre-defined cLevel table * Default level is ZSTD_CLEVEL_DEFAULT==3. - * Special: value 0 means "do not change cLevel". */ + * Special: value 0 means "do not change cLevel". + * Note 1 : it's possible to pass a negative compression level by casting it to unsigned type. + * Note 2 : setting compressionLevel automatically updates ZSTD_p_literalCompression. */ ZSTD_p_windowLog, /* Maximum allowed back-reference distance, expressed as power of 2. * Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX. * Special: value 0 means "do not change windowLog". @@ -1010,10 +1012,16 @@ typedef enum { * 0 => no overlap, 6(default) => use 1/8th of windowSize, >=9 => use full windowSize */ /* advanced parameters - may not remain available after API update */ + ZSTD_p_literalCompression=1000, /* control huffman compression of literals (enabled) by default. - * disabling it improves speed and decreases compression ratio by a large amount. */ + * disabling it improves speed and decreases compression ratio by a large amount. + * note : this setting is updated when changing compression level. + * positive compression levels set literalCompression to 1. + * negative compression levels set literalCompression to 0. */ + ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize, * even when referencing into Dictionary content (default:0) */ + ZSTD_p_enableLongDistanceMatching=1200, /* Enable long distance matching. * This parameter is designed to improve the compression * ratio for large inputs with long distance matches. diff --git a/programs/bench.c b/programs/bench.c index 66f7d69a5..e53f6bc10 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -533,9 +533,8 @@ static void BMK_benchCLevel(const void* srcBuffer, size_t benchedSize, if (g_displayLevel == 1 && !g_additionalParam) DISPLAY("bench %s %s: input %u bytes, %u seconds, %u KB blocks\n", ZSTD_VERSION_STRING, ZSTD_GIT_COMMIT_STRING, (U32)benchedSize, g_nbSeconds, (U32)(g_blockSize>>10)); - if (cLevelLast < cLevel) cLevelLast = cLevel; - for (l=cLevel; l <= cLevelLast; l++) { + DISPLAYLEVEL(6, "BMK_benchCLevel: level %i", l); BMK_benchMem(srcBuffer, benchedSize, displayName, l, fileSizes, nbFiles, @@ -545,8 +544,8 @@ static void BMK_benchCLevel(const void* srcBuffer, size_t benchedSize, /*! BMK_loadFiles() : - Loads `buffer` with content of files listed within `fileNamesTable`. - At most, fills `buffer` entirely */ + * Loads `buffer` with content of files listed within `fileNamesTable`. + * At most, fills `buffer` entirely. */ static void BMK_loadFiles(void* buffer, size_t bufferSize, size_t* fileSizes, const char* const * const fileNamesTable, unsigned nbFiles) @@ -677,7 +676,6 @@ int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, { double const compressibility = (double)g_compressibilityDefault / 100; - if (cLevel < 1) cLevel = 1; /* minimum compression level */ if (cLevel > ZSTD_maxCLevel()) cLevel = ZSTD_maxCLevel(); if (cLevelLast > ZSTD_maxCLevel()) cLevelLast = ZSTD_maxCLevel(); if (cLevelLast < cLevel) cLevelLast = cLevel; diff --git a/programs/fileio.c b/programs/fileio.c index 94dcb916d..d3d8b7dc5 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -420,6 +420,7 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, cRess_t ress; memset(&ress, 0, sizeof(ress)); + DISPLAYLEVEL(6, "FIO_createCResources \n"); ress.cctx = ZSTD_createCCtx(); if (ress.cctx == NULL) EXM_THROW(30, "allocation error : can't create ZSTD_CCtx"); @@ -440,7 +441,7 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_dictIDFlag, g_dictIDFlag) ); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_checksumFlag, g_checksumFlag) ); /* compression level */ - CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionLevel, cLevel) ); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionLevel, (unsigned)cLevel) ); /* long distance matching */ CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag) ); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_ldmHashLog, g_ldmHashLog) ); @@ -908,6 +909,7 @@ static int FIO_compressFilename_dstFile(cRess_t ress, stat_t statbuf; int stat_result = 0; + DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: opening dst: %s", dstFileName); ress.dstFile = FIO_openDstFile(dstFileName); if (ress.dstFile==NULL) return 1; /* could not open dstFileName */ /* Must ony be added after FIO_openDstFile() succeeds. diff --git a/programs/zstd.1.md b/programs/zstd.1.md index c970c5cbb..447ac07fe 100644 --- a/programs/zstd.1.md +++ b/programs/zstd.1.md @@ -115,6 +115,14 @@ the last one takes effect. Note: If `windowLog` is set to larger than 27, `--long=windowLog` or `--memory=windowSize` needs to be passed to the decompressor. +* `--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. + * `-T#`, `--threads=#`: Compress using `#` working threads (default: 1). If `#` is 0, attempt to detect and use the number of physical CPU cores. diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 3fd3282b1..aa811a29d 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -133,7 +133,8 @@ static int usage_advanced(const char* programName) DISPLAY( " -l : print information about zstd compressed files \n"); #ifndef ZSTD_NOCOMPRESS DISPLAY( "--ultra : enable levels beyond %i, up to %i (requires more memory)\n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel()); - DISPLAY( "--long[=#] : enable long distance matching with given window log (default: %u)\n", g_defaultMaxWindowLog); + DISPLAY( "--long[=#]: enable long distance matching with given window log (default: %u)\n", g_defaultMaxWindowLog); + DISPLAY( "--fast[=#]: switch to ultra fast compression level (default: %u)\n", 1); #ifdef ZSTD_MULTITHREAD DISPLAY( " -T# : spawns # compression threads (default: 1) \n"); DISPLAY( " -B# : select size of each job (default: 0==automatic) \n"); @@ -219,10 +220,10 @@ static int exeNameMatch(const char* exeName, const char* test) } /*! readU32FromChar() : - @return : unsigned integer value read from input in `char` format - allows and interprets K, KB, KiB, M, MB and MiB suffix. - Will also modify `*stringPtr`, advancing it to position where it stopped reading. - Note : function result can overflow if digit string > MAX_UINT */ + * @return : unsigned integer value read from input in `char` format. + * allows and interprets K, KB, KiB, M, MB and MiB suffix. + * Will also modify `*stringPtr`, advancing it to position where it stopped reading. + * Note : function result can overflow if digit string > MAX_UINT */ static unsigned readU32FromChar(const char** stringPtr) { unsigned result = 0; @@ -241,7 +242,7 @@ static unsigned readU32FromChar(const char** stringPtr) /** longCommandWArg() : * check if *stringPtr is the same as longCommand. * If yes, @return 1 and advances *stringPtr to the position which immediately follows longCommand. - * @return 0 and doesn't modify *stringPtr otherwise. + * @return 0 and doesn't modify *stringPtr otherwise. */ static unsigned longCommandWArg(const char** stringPtr, const char* longCommand) { @@ -387,7 +388,7 @@ int main(int argCount, const char* argv[]) zstd_operation_mode operation = zom_compress; ZSTD_compressionParameters compressionParams; int cLevel = ZSTDCLI_CLEVEL_DEFAULT; - int cLevelLast = 1; + int cLevelLast = -10000; unsigned recursive = 0; unsigned memLimit = 0; const char** filenameTable = (const char**)malloc(argCount * sizeof(const char*)); /* argCount >= 1 */ @@ -543,6 +544,21 @@ int main(int argCount, const char* argv[]) compressionParams.windowLog = ldmWindowLog; continue; } + if (longCommandWArg(&argument, "--fast")) { + /* Parse optional window log */ + if (*argument == '=') { + U32 fastLevel; + ++argument; + fastLevel = readU32FromChar(&argument); + if (fastLevel) cLevel = - (int)fastLevel; + } else if (*argument != 0) { + /* Invalid character following --fast */ + CLEAN_RETURN(badusage(programName)); + } else { + cLevel = -1; /* default for --fast */ + } + continue; + } /* fall-through, will trigger bad_usage() later on */ } @@ -813,16 +829,22 @@ int main(int argCount, const char* argv[]) } #ifndef ZSTD_NODECOMPRESS - if (operation==zom_test) { outFileName=nulmark; FIO_setRemoveSrcFile(0); } /* test mode */ + if (operation==zom_test) { outFileName=nulmark; FIO_setRemoveSrcFile(0); } /* test mode */ #endif /* No input filename ==> use stdin and stdout */ filenameIdx += !filenameIdx; /* filenameTable[0] is stdin by default */ - if (!strcmp(filenameTable[0], stdinmark) && !outFileName) outFileName = stdoutmark; /* when input is stdin, default output is stdout */ + if (!strcmp(filenameTable[0], stdinmark) && !outFileName) + outFileName = stdoutmark; /* when input is stdin, default output is stdout */ /* Check if input/output defined as console; trigger an error in this case */ - if (!strcmp(filenameTable[0], stdinmark) && IS_CONSOLE(stdin) ) CLEAN_RETURN(badusage(programName)); - if (outFileName && !strcmp(outFileName, stdoutmark) && IS_CONSOLE(stdout) && !strcmp(filenameTable[0], stdinmark) && !forceStdout && operation!=zom_decompress) + if (!strcmp(filenameTable[0], stdinmark) && IS_CONSOLE(stdin) ) + CLEAN_RETURN(badusage(programName)); + if ( outFileName && !strcmp(outFileName, stdoutmark) + && IS_CONSOLE(stdout) + && !strcmp(filenameTable[0], stdinmark) + && !forceStdout + && operation!=zom_decompress ) CLEAN_RETURN(badusage(programName)); #ifndef ZSTD_NOCOMPRESS diff --git a/tests/playTests.sh b/tests/playTests.sh index 258c9c75b..c170ba9f4 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -101,8 +101,11 @@ $ECHO "test : basic compression " $ZSTD -f tmp # trivial compression case, creates tmp.zst $ECHO "test : basic decompression" $ZSTD -df tmp.zst # trivial decompression case (overwrites tmp) -$ECHO "test : too large compression level (must fail)" +$ECHO "test : too large compression level => auto-fix" $ZSTD -99 -f tmp # too large compression level, automatic sized down +$ECHO "test : --fast aka negative compression levels" +$ZSTD --fast -f tmp # == -1 +$ZSTD --fast=3 -f tmp # == -3 $ECHO "test : compress to stdout" $ZSTD tmp -c > tmpCompressed $ZSTD tmp --stdout > tmpCompressed # long command format @@ -201,7 +204,6 @@ fi rm tmp* - $ECHO "\n===> Advanced compression parameters " $ECHO "Hello world!" | $ZSTD --zstd=windowLog=21, - -o tmp.zst && die "wrong parameters not detected!" $ECHO "Hello world!" | $ZSTD --zstd=windowLo=21 - -o tmp.zst && die "wrong parameters not detected!" @@ -474,6 +476,8 @@ $ECHO "bench one file" $ZSTD -bi0 tmp1 $ECHO "bench multiple levels" $ZSTD -i0b0e3 tmp1 +$ECHO "bench negative level" +$ZSTD -bi0 --fast tmp1 $ECHO "with recursive and quiet modes" $ZSTD -rqi1b1e2 tmp1 From f24566b597a6040e3be5f34bc582a61cac17d680 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 12 Mar 2018 04:02:57 -0700 Subject: [PATCH 179/255] minor bench improvements - do not test level 0, as it is converted into level 3, which feels strange when compressing multiple levels - Use direct synchronous mode when a single worker is requested. --- programs/bench.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programs/bench.c b/programs/bench.c index e53f6bc10..014a4fd41 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -214,6 +214,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, /* init */ if (strlen(displayName)>17) displayName += strlen(displayName)-17; /* display last 17 characters */ + if (g_nbWorkers==1) g_nbWorkers=0; /* prefer synchronous mode */ if (g_decodeOnly) { /* benchmark only decompression : source must be already compressed */ const char* srcPtr = (const char*)srcBuffer; @@ -534,7 +535,7 @@ static void BMK_benchCLevel(const void* srcBuffer, size_t benchedSize, DISPLAY("bench %s %s: input %u bytes, %u seconds, %u KB blocks\n", ZSTD_VERSION_STRING, ZSTD_GIT_COMMIT_STRING, (U32)benchedSize, g_nbSeconds, (U32)(g_blockSize>>10)); for (l=cLevel; l <= cLevelLast; l++) { - DISPLAYLEVEL(6, "BMK_benchCLevel: level %i", l); + if (l==0) continue; /* skip level 0 */ BMK_benchMem(srcBuffer, benchedSize, displayName, l, fileSizes, nbFiles, From a57d43d4d414152bcc5199aec2834d7bd90a4349 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 12 Mar 2018 11:34:52 -0700 Subject: [PATCH 180/255] updated documentation of targetLength --- doc/zstd_manual.html | 66 +++++++++++++++++++++++++------------------- lib/zstd.h | 10 +++++-- programs/zstd.1.md | 19 +++++++++---- 3 files changed, 58 insertions(+), 37 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 9b743c34b..2c3148a52 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -753,7 +753,9 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long /* compression parameters */ ZSTD_p_compressionLevel=100, /* Update all compression parameters according to pre-defined cLevel table * Default level is ZSTD_CLEVEL_DEFAULT==3. - * Special: value 0 means "do not change cLevel". */ + * Special: value 0 means "do not change cLevel". + * Note 1 : it's possible to pass a negative compression level by casting it to unsigned type. + * Note 2 : setting compressionLevel automatically updates ZSTD_p_literalCompression. */ ZSTD_p_windowLog, /* Maximum allowed back-reference distance, expressed as power of 2. * Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX. * Special: value 0 means "do not change windowLog". @@ -780,9 +782,13 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long * Note that currently, for all strategies < btopt, effective minimum is 4. * Note that currently, for all strategies > fast, effective maximum is 6. * Special: value 0 means "do not change minMatchLength". */ - ZSTD_p_targetLength, /* Only useful for strategies >= btopt. - * Length of Match considered "good enough" to stop search. - * Larger values make compression stronger and slower. + ZSTD_p_targetLength, /* Impact of this field depends on strategy. + * For strategies btopt & btultra: + * Length of Match considered "good enough" to stop search. + * Larger values make compression stronger, and slower. + * For strategy fast: + * Distance between match sampling. + * Larger values make compression faster, and weaker. * Special: value 0 means "do not change targetLength". */ ZSTD_p_compressionStrategy, /* See ZSTD_strategy enum definition. * Cast selected strategy as unsigned for ZSTD_CCtx_setParameter() compatibility. @@ -807,17 +813,25 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long * (note : a strong exception to this rule is when first invocation sets ZSTD_e_end : it becomes a blocking call). * More workers improve speed, but also increase memory usage. * Default value is `0`, aka "single-threaded mode" : no worker is spawned, compression is performed inside Caller's thread, all invocations are blocking */ - ZSTD_p_jobSize, /* Size of a compression job. This value is only enforced in streaming (non-blocking) mode. - * Each compression job is completed in parallel, so indirectly controls the nb of active threads. + ZSTD_p_jobSize, /* Size of a compression job. This value is enforced only in non-blocking mode. + * Each compression job is completed in parallel, so this value indirectly controls the nb of active threads. * 0 means default, which is dynamically determined based on compression parameters. - * Job size must be a minimum of overlapSize, or 1 KB, whichever is largest + * Job size must be a minimum of overlapSize, or 1 MB, whichever is largest. * The minimum size is automatically and transparently enforced */ ZSTD_p_overlapSizeLog, /* Size of previous input reloaded at the beginning of each job. * 0 => no overlap, 6(default) => use 1/8th of windowSize, >=9 => use full windowSize */ /* advanced parameters - may not remain available after API update */ + + ZSTD_p_literalCompression=1000, /* control huffman compression of literals (enabled) by default. + * disabling it improves speed and decreases compression ratio by a large amount. + * note : this setting is updated when changing compression level. + * positive compression levels set literalCompression to 1. + * negative compression levels set literalCompression to 0. */ + ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize, * even when referencing into Dictionary content (default:0) */ + ZSTD_p_enableLongDistanceMatching=1200, /* Enable long distance matching. * This parameter is designed to improve the compression * ratio for large inputs with long distance matches. @@ -877,23 +891,21 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
    size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
     size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
     size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode);
    -

    Create an internal CDict from dict buffer. - Decompression will have to use same buffer. +

    Create an internal CDict from `dict` buffer. + Decompression will have to use same dictionary. @result : 0, or an error code (which can be tested with ZSTD_isError()). - Special : Adding a NULL (or 0-size) dictionary invalidates any previous dictionary, - meaning "return to no-dictionary mode". - Note 1 : `dict` content will be copied internally. Use - ZSTD_CCtx_loadDictionary_byReference() to reference dictionary - content instead. The dictionary buffer must then outlive its - users. + Special: Adding a NULL (or 0-size) dictionary invalidates previous dictionary, + meaning "return to no-dictionary mode". + Note 1 : Dictionary will be used for all future compression jobs. + To return to "no-dictionary" situation, load a NULL dictionary Note 2 : Loading a dictionary involves building tables, which are dependent on compression parameters. For this reason, compression parameters cannot be changed anymore after loading a dictionary. - It's also a CPU-heavy operation, with non-negligible impact on latency. - Note 3 : Dictionary will be used for all future compression jobs. - To return to "no-dictionary" situation, load a NULL dictionary - Note 5 : Use ZSTD_CCtx_loadDictionary_advanced() to select how dictionary - content will be interpreted. - + It's also a CPU consuming operation, with non-negligible impact on latency. + Note 3 :`dict` content will be copied internally. + Use ZSTD_CCtx_loadDictionary_byReference() to reference dictionary content instead. + In such a case, dictionary buffer must outlive its users. + Note 4 : Use ZSTD_CCtx_loadDictionary_advanced() + to precisely select how dictionary content must be interpreted.


    size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
    @@ -905,8 +917,7 @@ size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size
       Special : adding a NULL CDict means "return to no-dictionary mode".
       Note 1 : Currently, only one dictionary can be managed.
                Adding a new dictionary effectively "discards" any previous one.
    -  Note 2 : CDict is just referenced, its lifetime must outlive CCtx.
    - 
    +  Note 2 : CDict is just referenced, its lifetime must outlive CCtx. 
     


    size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize);
    @@ -917,13 +928,12 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t
       Subsequent compression jobs will be done without prefix (if none is explicitly referenced).
       If there is a need to use same prefix multiple times, consider embedding it into a ZSTD_CDict instead.
      @result : 0, or an error code (which can be tested with ZSTD_isError()).
    -  Special : Adding any prefix (including NULL) invalidates any previous prefix or dictionary
    +  Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary
       Note 1 : Prefix buffer is referenced. It must outlive compression job.
       Note 2 : Referencing a prefix involves building tables, which are dependent on compression parameters.
    -           It's a CPU-heavy operation, with non-negligible impact on latency.
    -  Note 3 : By default, the prefix is treated as raw content
    -           (ZSTD_dm_rawContent). Use ZSTD_CCtx_refPrefix_advanced() to alter
    -           dictMode. 
    +           It's a CPU consuming operation, with non-negligible impact on latency.
    +  Note 3 : By default, the prefix is treated as raw content (ZSTD_dm_rawContent).
    +           Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode. 
     


    typedef enum {
    diff --git a/lib/zstd.h b/lib/zstd.h
    index 6cb7da7ab..5a1ad8aaf 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -976,9 +976,13 @@ typedef enum {
                                   * Note that currently, for all strategies < btopt, effective minimum is 4.
                                   * Note that currently, for all strategies > fast, effective maximum is 6.
                                   * Special: value 0 means "do not change minMatchLength". */
    -    ZSTD_p_targetLength,     /* Only useful for strategies >= btopt.
    -                              * Length of Match considered "good enough" to stop search.
    -                              * Larger values make compression stronger and slower.
    +    ZSTD_p_targetLength,     /* Impact of this field depends on strategy.
    +                              * For strategies btopt & btultra:
    +                              *     Length of Match considered "good enough" to stop search.
    +                              *     Larger values make compression stronger, and slower.
    +                              * For strategy fast:
    +                              *     Distance between match sampling.
    +                              *     Larger values make compression faster, and weaker.
                                   * Special: value 0 means "do not change targetLength". */
         ZSTD_p_compressionStrategy, /* See ZSTD_strategy enum definition.
                                   * Cast selected strategy as unsigned for ZSTD_CCtx_setParameter() compatibility.
    diff --git a/programs/zstd.1.md b/programs/zstd.1.md
    index 447ac07fe..2e2dc54f8 100644
    --- a/programs/zstd.1.md
    +++ b/programs/zstd.1.md
    @@ -347,14 +347,21 @@ The list of available _options_:
         The minimum _slen_ is 3 and the maximum is 7.
     
     - `targetLen`=_tlen_, `tlen`=_tlen_:
    -    Specify the minimum match length that causes a match finder to stop
    -    searching for better matches.
    +    The impact of this field vary depending on selected strategy.
     
    -    A larger minimum match length usually improves compression ratio but
    -    decreases compression speed.
    -    This option is only used with strategies ZSTD_btopt and ZSTD_btultra.
    +    For ZSTD\_btopt and ZSTD\_btultra, it specifies the minimum match length
    +    that causes match finder to stop searching for better matches.
    +    A larger `targetLen` usually improves compression ratio
    +    but decreases compression speed.
     
    -    The minimum _tlen_ is 4 and the maximum is 999.
    +    For ZSTD\_fast, it specifies
    +    the amount of data skipped between match sampling.
    +    Impact is reversed : a larger `targetLen` increases compression speed
    +    but decreases compression ratio.
    +
    +    For all other strategies, this field has no impact.
    +
    +    The minimum _tlen_ is 1 and the maximum is 999.
     
     - `overlapLog`=_ovlog_,  `ovlog`=_ovlog_:
         Determine `overlapSize`, amount of data reloaded from previous job.
    
    From 2291b85a1e6c9f20f8e1a3f4ebb93addd99ba418 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Mon, 12 Mar 2018 11:44:10 -0700
    Subject: [PATCH 181/255] changed ZSTD_p_literalCompression into
     ZSTD_p_compressLiterals
    
    prefer verb+object construction
    ---
     doc/zstd_manual.html         | 4 ++--
     lib/compress/zstd_compress.c | 8 ++++----
     lib/zstd.h                   | 4 ++--
     3 files changed, 8 insertions(+), 8 deletions(-)
    
    diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
    index 2c3148a52..7c55363f0 100644
    --- a/doc/zstd_manual.html
    +++ b/doc/zstd_manual.html
    @@ -755,7 +755,7 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
                                   * Default level is ZSTD_CLEVEL_DEFAULT==3.
                                   * Special: value 0 means "do not change cLevel".
                                   * Note 1 : it's possible to pass a negative compression level by casting it to unsigned type.
    -                              * Note 2 : setting compressionLevel automatically updates ZSTD_p_literalCompression. */
    +                              * Note 2 : setting compressionLevel automatically updates ZSTD_p_compressLiterals. */
         ZSTD_p_windowLog,        /* Maximum allowed back-reference distance, expressed as power of 2.
                                   * Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX.
                                   * Special: value 0 means "do not change windowLog".
    @@ -823,7 +823,7 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
     
         /* advanced parameters - may not remain available after API update */
     
    -    ZSTD_p_literalCompression=1000, /* control huffman compression of literals (enabled) by default.
    +    ZSTD_p_compressLiterals=1000, /* control huffman compression of literals (enabled) by default.
                                   * disabling it improves speed and decreases compression ratio by a large amount.
                                   * note : this setting is updated when changing compression level.
                                   *        positive compression levels set literalCompression to 1.
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index 5347cf7ec..97280d43f 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -252,7 +252,7 @@ static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param)
         case ZSTD_p_minMatch:
         case ZSTD_p_targetLength:
         case ZSTD_p_compressionStrategy:
    -    case ZSTD_p_literalCompression:
    +    case ZSTD_p_compressLiterals:
             return 1;
     
         case ZSTD_p_format:
    @@ -306,7 +306,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v
             }
             return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
     
    -    case ZSTD_p_literalCompression:
    +    case ZSTD_p_compressLiterals:
         case ZSTD_p_contentSizeFlag:
         case ZSTD_p_checksumFlag:
         case ZSTD_p_dictIDFlag:
    @@ -425,7 +425,7 @@ size_t ZSTD_CCtxParam_setParameter(
             }
             return (size_t)CCtxParams->cParams.strategy;
     
    -    case ZSTD_p_literalCompression:
    +    case ZSTD_p_compressLiterals:
             CCtxParams->disableLiteralCompression = !value;
             return !!value;
     
    @@ -2453,7 +2453,7 @@ size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, c
         ZSTD_parameters const params = ZSTD_getParams(compressionLevel, srcSize ? srcSize : 1, dict ? dictSize : 0);
         ZSTD_CCtx_params cctxParams = ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);
         assert(params.fParams.contentSizeFlag == 1);
    -    ZSTD_CCtxParam_setParameter(&cctxParams, ZSTD_p_literalCompression, compressionLevel>=0);
    +    ZSTD_CCtxParam_setParameter(&cctxParams, ZSTD_p_compressLiterals, compressionLevel>=0);
         return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, dict, dictSize, cctxParams);
     }
     
    diff --git a/lib/zstd.h b/lib/zstd.h
    index 5a1ad8aaf..826bade6f 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -949,7 +949,7 @@ typedef enum {
                                   * Default level is ZSTD_CLEVEL_DEFAULT==3.
                                   * Special: value 0 means "do not change cLevel".
                                   * Note 1 : it's possible to pass a negative compression level by casting it to unsigned type.
    -                              * Note 2 : setting compressionLevel automatically updates ZSTD_p_literalCompression. */
    +                              * Note 2 : setting compressionLevel automatically updates ZSTD_p_compressLiterals. */
         ZSTD_p_windowLog,        /* Maximum allowed back-reference distance, expressed as power of 2.
                                   * Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX.
                                   * Special: value 0 means "do not change windowLog".
    @@ -1017,7 +1017,7 @@ typedef enum {
     
         /* advanced parameters - may not remain available after API update */
     
    -    ZSTD_p_literalCompression=1000, /* control huffman compression of literals (enabled) by default.
    +    ZSTD_p_compressLiterals=1000, /* control huffman compression of literals (enabled) by default.
                                   * disabling it improves speed and decreases compression ratio by a large amount.
                                   * note : this setting is updated when changing compression level.
                                   *        positive compression levels set literalCompression to 1.
    
    From 2f70fbf2a3014bbfd37095548aa0aa5715ecb741 Mon Sep 17 00:00:00 2001
    From: Jesse Talavera-Greenberg 
    Date: Mon, 12 Mar 2018 20:43:32 -0400
    Subject: [PATCH 182/255] Made -H's printout specify the semantics of -T0
    
    ---
     programs/zstdcli.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/programs/zstdcli.c b/programs/zstdcli.c
    index 3fd3282b1..9f8d4dea7 100644
    --- a/programs/zstdcli.c
    +++ b/programs/zstdcli.c
    @@ -135,7 +135,7 @@ static int usage_advanced(const char* programName)
         DISPLAY( "--ultra : enable levels beyond %i, up to %i (requires more memory)\n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel());
         DISPLAY( "--long[=#]  : enable long distance matching with given window log (default: %u)\n", g_defaultMaxWindowLog);
     #ifdef ZSTD_MULTITHREAD
    -    DISPLAY( " -T#    : spawns # compression threads (default: 1) \n");
    +    DISPLAY( " -T#    : spawns # compression threads (default: 1, 0==# cores) \n");
         DISPLAY( " -B#    : select size of each job (default: 0==automatic) \n");
     #endif
         DISPLAY( "--no-dictID : don't write dictID into header (dictionary compression)\n");
    
    From 430a2fec19e5acc9447217b58709dedd1f8251d9 Mon Sep 17 00:00:00 2001
    From: Baldur Karlsson 
    Date: Tue, 13 Mar 2018 20:02:21 +0000
    Subject: [PATCH 183/255] Remove non-ASCII characters in header file comments
    
    * Replaced a non-breaking space and an en dash with a plain space and
      a hyphen.
    * This means the files are simple ASCII and less likely to run into
      codepage issues.
    ---
     lib/common/bitstream.h | 2 +-
     programs/platform.h    | 2 +-
     2 files changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/lib/common/bitstream.h b/lib/common/bitstream.h
    index c6f001248..19192dd91 100644
    --- a/lib/common/bitstream.h
    +++ b/lib/common/bitstream.h
    @@ -417,7 +417,7 @@ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits)
      *  Refill `bitD` from buffer previously set in BIT_initDStream() .
      *  This function is safe, it guarantees it will not read beyond src buffer.
      * @return : status of `BIT_DStream_t` internal register.
    - *           when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */
    + *           when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */
     MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD)
     {
         if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8))  /* overflow detected, like end of stream */
    diff --git a/programs/platform.h b/programs/platform.h
    index e9629a0fa..e843bfb90 100644
    --- a/programs/platform.h
    +++ b/programs/platform.h
    @@ -70,7 +70,7 @@ extern "C" {
     ***************************************************************/
     #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) /* UNIX-like OS */ \
        || defined(__midipix__) || defined(__VMS))
    -#  if (defined(__APPLE__) && defined(__MACH__)) || defined(__SVR4) || defined(_AIX) || defined(__hpux) /* POSIX.1–2001 (SUSv3) conformant */ \
    +#  if (defined(__APPLE__) && defined(__MACH__)) || defined(__SVR4) || defined(_AIX) || defined(__hpux) /* POSIX.1-2001 (SUSv3) conformant */ \
          || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)  /* BSD distros */
     #    define PLATFORM_POSIX_VERSION 200112L
     #  else
    
    From a95a88af57a9e20adc872483d71e667ad5c64762 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Tue, 13 Mar 2018 13:44:10 -0700
    Subject: [PATCH 184/255] removed huf_compress_impl.h
    
    re-imported all functions inside huf_compress.c
    for easier source editing.
    
    Also updated a bunch of code comments
    for clarification.
    ---
     lib/common/fse.h                 |   2 +-
     lib/common/huf.h                 | 184 +++++++++++++++++--------------
     lib/compress/fse_compress.c      |  22 ++--
     lib/compress/huf_compress.c      | 172 +++++++++++++++++++++++------
     lib/compress/huf_compress_impl.h | 120 --------------------
     lib/compress/zstd_compress.c     |   4 +-
     lib/decompress/huf_decompress.c  |  25 +++--
     7 files changed, 272 insertions(+), 257 deletions(-)
     delete mode 100644 lib/compress/huf_compress_impl.h
    
    diff --git a/lib/common/fse.h b/lib/common/fse.h
    index afd780196..6a1d272be 100644
    --- a/lib/common/fse.h
    +++ b/lib/common/fse.h
    @@ -345,7 +345,7 @@ size_t FSE_countFast(unsigned* count, unsigned* maxSymbolValuePtr, const void* s
      */
     size_t FSE_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned* workSpace);
     
    -/*! FSE_count_simple
    +/*! FSE_count_simple() :
      * Same as FSE_countFast(), but does not use any additional memory (not even on stack).
      * This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr` (presuming it's also the size of `count`).
     */
    diff --git a/lib/common/huf.h b/lib/common/huf.h
    index 4e7eee0cb..b4645b4e5 100644
    --- a/lib/common/huf.h
    +++ b/lib/common/huf.h
    @@ -58,31 +58,32 @@ extern "C" {
     #endif
     
     
    -/* *** simple functions *** */
    -/**
    -HUF_compress() :
    -    Compress content from buffer 'src', of size 'srcSize', into buffer 'dst'.
    -    'dst' buffer must be already allocated.
    -    Compression runs faster if `dstCapacity` >= HUF_compressBound(srcSize).
    -    `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB.
    -    @return : size of compressed data (<= `dstCapacity`).
    -    Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!!
    -                     if HUF_isError(return), compression failed (more details using HUF_getErrorName())
    -*/
    +/* ========================== */
    +/* ***  simple functions  *** */
    +/* ========================== */
    +
    +/** HUF_compress() :
    + *  Compress content from buffer 'src', of size 'srcSize', into buffer 'dst'.
    + * 'dst' buffer must be already allocated.
    + *  Compression runs faster if `dstCapacity` >= HUF_compressBound(srcSize).
    + * `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB.
    + * @return : size of compressed data (<= `dstCapacity`).
    + *  Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!!
    + *                   if HUF_isError(return), compression failed (more details using HUF_getErrorName())
    + */
     HUF_PUBLIC_API size_t HUF_compress(void* dst, size_t dstCapacity,
                                  const void* src, size_t srcSize);
     
    -/**
    -HUF_decompress() :
    -    Decompress HUF data from buffer 'cSrc', of size 'cSrcSize',
    -    into already allocated buffer 'dst', of minimum size 'dstSize'.
    -    `originalSize` : **must** be the ***exact*** size of original (uncompressed) data.
    -    Note : in contrast with FSE, HUF_decompress can regenerate
    -           RLE (cSrcSize==1) and uncompressed (cSrcSize==dstSize) data,
    -           because it knows size to regenerate (originalSize).
    -    @return : size of regenerated data (== originalSize),
    -              or an error code, which can be tested using HUF_isError()
    -*/
    +/** HUF_decompress() :
    + *  Decompress HUF data from buffer 'cSrc', of size 'cSrcSize',
    + *  into already allocated buffer 'dst', of minimum size 'dstSize'.
    + * `originalSize` : **must** be the ***exact*** size of original (uncompressed) data.
    + *  Note : in contrast with FSE, HUF_decompress can regenerate
    + *         RLE (cSrcSize==1) and uncompressed (cSrcSize==dstSize) data,
    + *         because it knows size to regenerate (originalSize).
    + * @return : size of regenerated data (== originalSize),
    + *           or an error code, which can be tested using HUF_isError()
    + */
     HUF_PUBLIC_API size_t HUF_decompress(void* dst,  size_t originalSize,
                                    const void* cSrc, size_t cSrcSize);
     
    @@ -99,30 +100,22 @@ HUF_PUBLIC_API const char* HUF_getErrorName(size_t code);  /**< provides error c
     /* ***   Advanced function   *** */
     
     /** HUF_compress2() :
    - *  Same as HUF_compress(), but offers direct control over `maxSymbolValue` and `tableLog`.
    - *  `maxSymbolValue` must be <= HUF_SYMBOLVALUE_MAX .
    - *  `tableLog` must be `<= HUF_TABLELOG_MAX` . */
    -HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog);
    + *  Same as HUF_compress(), but offers control over `maxSymbolValue` and `tableLog`.
    + * `maxSymbolValue` must be <= HUF_SYMBOLVALUE_MAX .
    + * `tableLog` must be `<= HUF_TABLELOG_MAX` . */
    +HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity,
    +                               const void* src, size_t srcSize,
    +                               unsigned maxSymbolValue, unsigned tableLog);
     
     /** HUF_compress4X_wksp() :
      *  Same as HUF_compress2(), but uses externally allocated `workSpace`.
    - *  `workspace` must have minimum alignment of 4, and be at least as large as following macro */
    + * `workspace` must have minimum alignment of 4, and be at least as large as HUF_WORKSPACE_SIZE */
     #define HUF_WORKSPACE_SIZE (6 << 10)
     #define HUF_WORKSPACE_SIZE_U32 (HUF_WORKSPACE_SIZE / sizeof(U32))
    -HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
    -
    -/**
    - *  The minimum workspace size for the `workSpace` used in
    - *  HUF_readDTableX2_wksp() and HUF_readDTableX4_wksp().
    - *
    - *  The space used depends on HUF_TABLELOG_MAX, ranging from ~1500 bytes when
    - *  HUF_TABLE_LOG_MAX=12 to ~1850 bytes when HUF_TABLE_LOG_MAX=15.
    - *  Buffer overflow errors may potentially occur if code modifications result in
    - *  a required workspace size greater than that specified in the following
    - *  macro.
    - */
    -#define HUF_DECOMPRESS_WORKSPACE_SIZE (2 << 10)
    -#define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32))
    +HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity,
    +                                     const void* src, size_t srcSize,
    +                                     unsigned maxSymbolValue, unsigned tableLog,
    +                                     void* workSpace, size_t wkspSize);
     
     #endif   /* HUF_H_298734234 */
     
    @@ -132,7 +125,7 @@ HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const
      *  which shall never be used in the context of a dynamic library,
      *  because they are not guaranteed to remain stable in the future.
      *  Only consider them in association with static linking.
    - *******************************************************************/
    + * *****************************************************************/
     #if defined(HUF_STATIC_LINKING_ONLY) && !defined(HUF_H_HUF_STATIC_LINKING_ONLY)
     #define HUF_H_HUF_STATIC_LINKING_ONLY
     
    @@ -192,24 +185,23 @@ size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
     
     
     /* ****************************************
    -*  HUF detailed API
    -******************************************/
    -/*!
    -HUF_compress() does the following:
    -1. count symbol occurrence from source[] into table count[] using FSE_count()
    -2. (optional) refine tableLog using HUF_optimalTableLog()
    -3. build Huffman table from count using HUF_buildCTable()
    -4. save Huffman table to memory buffer using HUF_writeCTable()
    -5. encode the data stream using HUF_compress4X_usingCTable()
    + *  HUF detailed API
    + * ****************************************/
     
    -The following API allows targeting specific sub-functions for advanced tasks.
    -For example, it's possible to compress several blocks using the same 'CTable',
    -or to save and regenerate 'CTable' using external methods.
    -*/
    -/* FSE_count() : exposed within "fse.h" */
    +/*! HUF_compress() does the following:
    + *  1. count symbol occurrence from source[] into table count[] using FSE_count() (exposed within "fse.h")
    + *  2. (optional) refine tableLog using HUF_optimalTableLog()
    + *  3. build Huffman table from count using HUF_buildCTable()
    + *  4. save Huffman table to memory buffer using HUF_writeCTable()
    + *  5. encode the data stream using HUF_compress4X_usingCTable()
    + *
    + *  The following API allows targeting specific sub-functions for advanced tasks.
    + *  For example, it's possible to compress several blocks using the same 'CTable',
    + *  or to save and regenerate 'CTable' using external methods.
    + */
     unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue);
     typedef struct HUF_CElt_s HUF_CElt;   /* incomplete type */
    -size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits);   /* @return : maxNbBits; CTable and count can overlap, in which case, CTable will overwrite count content */
    +size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits);   /* @return : maxNbBits; CTable and count can overlap. In which case, CTable will overwrite count content */
     size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog);
     size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable);
     
    @@ -219,47 +211,65 @@ typedef enum {
        HUF_repeat_valid  /**< Can use the previous table and it is asumed to be valid */
      } HUF_repeat;
     /** HUF_compress4X_repeat() :
    -*   Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
    -*   If it uses hufTable it does not modify hufTable or repeat.
    -*   If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
    -*   If preferRepeat then the old table will always be used if valid. */
    -size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2);  /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
    + *  Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
    + *  If it uses hufTable it does not modify hufTable or repeat.
    + *  If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
    + *  If preferRepeat then the old table will always be used if valid. */
    +size_t HUF_compress4X_repeat(void* dst, size_t dstSize,
    +                       const void* src, size_t srcSize,
    +                       unsigned maxSymbolValue, unsigned tableLog,
    +                       void* workSpace, size_t wkspSize,    /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */
    +                       HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2);
     
     /** HUF_buildCTable_wksp() :
      *  Same as HUF_buildCTable(), but using externally allocated scratch buffer.
    - * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of HUF_CTABLE_WORKSPACE_SIZE_U32 unsigned.
    + * `workSpace` must be aligned on 4-bytes boundaries, and its size must be >= HUF_CTABLE_WORKSPACE_SIZE.
      */
    - #define HUF_CTABLE_WORKSPACE_SIZE_U32 (2*HUF_SYMBOLVALUE_MAX +1 +1)
    +#define HUF_CTABLE_WORKSPACE_SIZE_U32 (2*HUF_SYMBOLVALUE_MAX +1 +1)
    +#define HUF_CTABLE_WORKSPACE_SIZE (HUF_CTABLE_WORKSPACE_SIZE_U32 * sizeof(unsigned))
     size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize);
     
     /*! HUF_readStats() :
    -    Read compact Huffman tree, saved by HUF_writeCTable().
    -    `huffWeight` is destination buffer.
    -    @return : size read from `src` , or an error Code .
    -    Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */
    + *  Read compact Huffman tree, saved by HUF_writeCTable().
    + * `huffWeight` is destination buffer.
    + * @return : size read from `src` , or an error Code .
    + *  Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */
     size_t HUF_readStats(BYTE* huffWeight, size_t hwSize,
                          U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr,
                          const void* src, size_t srcSize);
     
     /** HUF_readCTable() :
    -*   Loading a CTable saved with HUF_writeCTable() */
    + *  Loading a CTable saved with HUF_writeCTable() */
     size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize);
     
     
     /*
    -HUF_decompress() does the following:
    -1. select the decompression algorithm (X2, X4) based on pre-computed heuristics
    -2. build Huffman table from save, using HUF_readDTableXn()
    -3. decode 1 or 4 segments in parallel using HUF_decompressSXn_usingDTable
    -*/
    + * HUF_decompress() does the following:
    + * 1. select the decompression algorithm (X2, X4) based on pre-computed heuristics
    + * 2. build Huffman table from save, using HUF_readDTableX?()
    + * 3. decode 1 or 4 segments in parallel using HUF_decompress?X?_usingDTable()
    + */
     
     /** HUF_selectDecoder() :
    -*   Tells which decoder is likely to decode faster,
    -*   based on a set of pre-determined metrics.
    -*   @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 .
    -*   Assumption : 0 < cSrcSize < dstSize <= 128 KB */
    + *  Tells which decoder is likely to decode faster,
    + *  based on a set of pre-computed metrics.
    + * @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 .
    + *  Assumption : 0 < dstSize <= 128 KB */
     U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize);
     
    +/**
    + *  The minimum workspace size for the `workSpace` used in
    + *  HUF_readDTableX2_wksp() and HUF_readDTableX4_wksp().
    + *
    + *  The space used depends on HUF_TABLELOG_MAX, ranging from ~1500 bytes when
    + *  HUF_TABLE_LOG_MAX=12 to ~1850 bytes when HUF_TABLE_LOG_MAX=15.
    + *  Buffer overflow errors may potentially occur if code modifications result in
    + *  a required workspace size greater than that specified in the following
    + *  macro.
    + */
    +#define HUF_DECOMPRESS_WORKSPACE_SIZE (2 << 10)
    +#define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32))
    +
     size_t HUF_readDTableX2 (HUF_DTable* DTable, const void* src, size_t srcSize);
     size_t HUF_readDTableX2_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize);
     size_t HUF_readDTableX4 (HUF_DTable* DTable, const void* src, size_t srcSize);
    @@ -270,17 +280,23 @@ size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* c
     size_t HUF_decompress4X4_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
     
     
    +/* ====================== */
     /* single stream variants */
    +/* ====================== */
     
     size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog);
     size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);  /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
     size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable);
     /** HUF_compress1X_repeat() :
    -*   Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
    -*   If it uses hufTable it does not modify hufTable or repeat.
    -*   If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
    -*   If preferRepeat then the old table will always be used if valid. */
    -size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2);  /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
    + *  Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
    + *  If it uses hufTable it does not modify hufTable or repeat.
    + *  If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
    + *  If preferRepeat then the old table will always be used if valid. */
    +size_t HUF_compress1X_repeat(void* dst, size_t dstSize,
    +                       const void* src, size_t srcSize,
    +                       unsigned maxSymbolValue, unsigned tableLog,
    +                       void* workSpace, size_t wkspSize,   /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */
    +                       HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2);
     
     size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);   /* single-symbol decoder */
     size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);   /* double-symbol decoder */
    @@ -297,7 +313,7 @@ size_t HUF_decompress1X2_usingDTable(void* dst, size_t maxDstSize, const void* c
     size_t HUF_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
     
     /* BMI2 variants.
    - * If the CPU has BMI2 support pass bmi2=1, otherwise pass bmi2=0.
    + * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0.
      */
     size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2);
     size_t HUF_decompress1X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2);
    diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c
    index ee5eb81ab..cb8f1fa32 100644
    --- a/lib/compress/fse_compress.c
    +++ b/lib/compress/fse_compress.c
    @@ -292,7 +292,7 @@ size_t FSE_writeNCount (void* buffer, size_t bufferSize, const short* normalized
         It doesn't use any additional memory.
         But this function is unsafe : it doesn't check that all values within `src` can fit into `count`.
         For this reason, prefer using a table `count` with 256 elements.
    -    @return : count of most numerous element
    +    @return : count of most numerous element.
     */
     size_t FSE_count_simple(unsigned* count, unsigned* maxSymbolValuePtr,
                             const void* src, size_t srcSize)
    @@ -305,7 +305,10 @@ size_t FSE_count_simple(unsigned* count, unsigned* maxSymbolValuePtr,
         memset(count, 0, (maxSymbolValue+1)*sizeof(*count));
         if (srcSize==0) { *maxSymbolValuePtr = 0; return 0; }
     
    -    while (ip 255) maxSymbolValue = 255;
    +        for (s=0; s<=maxSymbolValue; s++) {
                 count[s] = Counting1[s] + Counting2[s] + Counting3[s] + Counting4[s];
                 if (count[s] > max) max = count[s];
         }   }
    @@ -393,9 +399,11 @@ static size_t FSE_count_parallel_wksp(
      * Same as FSE_countFast(), but using an externally provided scratch buffer.
      * `workSpace` size must be table of >= `1024` unsigned */
     size_t FSE_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr,
    -                     const void* source, size_t sourceSize, unsigned* workSpace)
    +                          const void* source, size_t sourceSize,
    +                          unsigned* workSpace)
     {
    -    if (sourceSize < 1500) return FSE_count_simple(count, maxSymbolValuePtr, source, sourceSize);
    +    if (sourceSize < 1500) /* heuristic threshold */
    +        return FSE_count_simple(count, maxSymbolValuePtr, source, sourceSize);
         return FSE_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 0, workSpace);
     }
     
    diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c
    index 69007cd92..83230b415 100644
    --- a/lib/compress/huf_compress.c
    +++ b/lib/compress/huf_compress.c
    @@ -440,59 +440,169 @@ static int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, uns
     
     size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); }
     
    -
    -#define FUNCTION(fn) fn##_default
    -#define TARGET
    -#include "huf_compress_impl.h"
    -#undef TARGET
    -#undef FUNCTION
    -
    -#if DYNAMIC_BMI2
    -
    -#define FUNCTION(fn) fn##_bmi2
    -#define TARGET TARGET_ATTRIBUTE("bmi2")
    -#include "huf_compress_impl.h"
    -#undef TARGET
    -#undef FUNCTION
    -
    -#endif
    -
    -static size_t HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize,
    -                                                  const void* src, size_t srcSize,
    -                                                  const HUF_CElt* CTable, const int bmi2)
    +FORCE_INLINE_TEMPLATE void
    +HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable)
     {
    +    BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits);
    +}
    +
    +#define HUF_FLUSHBITS(s)  BIT_flushBits(s)
    +
    +#define HUF_FLUSHBITS_1(stream) \
    +    if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*2+7) HUF_FLUSHBITS(stream)
    +
    +#define HUF_FLUSHBITS_2(stream) \
    +    if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*4+7) HUF_FLUSHBITS(stream)
    +
    +FORCE_INLINE_TEMPLATE size_t
    +HUF_compress1X_usingCTable_internal_body(void* dst, size_t dstSize,
    +                                   const void* src, size_t srcSize,
    +                                   const HUF_CElt* CTable)
    +{
    +    const BYTE* ip = (const BYTE*) src;
    +    BYTE* const ostart = (BYTE*)dst;
    +    BYTE* const oend = ostart + dstSize;
    +    BYTE* op = ostart;
    +    size_t n;
    +    BIT_CStream_t bitC;
    +
    +    /* init */
    +    if (dstSize < 8) return 0;   /* not enough space to compress */
    +    { size_t const initErr = BIT_initCStream(&bitC, op, oend-op);
    +      if (HUF_isError(initErr)) return 0; }
    +
    +    n = srcSize & ~3;  /* join to mod 4 */
    +    switch (srcSize & 3)
    +    {
    +        case 3 : HUF_encodeSymbol(&bitC, ip[n+ 2], CTable);
    +                 HUF_FLUSHBITS_2(&bitC);
    +		 /* fall-through */
    +        case 2 : HUF_encodeSymbol(&bitC, ip[n+ 1], CTable);
    +                 HUF_FLUSHBITS_1(&bitC);
    +		 /* fall-through */
    +        case 1 : HUF_encodeSymbol(&bitC, ip[n+ 0], CTable);
    +                 HUF_FLUSHBITS(&bitC);
    +		 /* fall-through */
    +        case 0 : /* fall-through */
    +        default: break;
    +    }
    +
    +    for (; n>0; n-=4) {  /* note : n&3==0 at this stage */
    +        HUF_encodeSymbol(&bitC, ip[n- 1], CTable);
    +        HUF_FLUSHBITS_1(&bitC);
    +        HUF_encodeSymbol(&bitC, ip[n- 2], CTable);
    +        HUF_FLUSHBITS_2(&bitC);
    +        HUF_encodeSymbol(&bitC, ip[n- 3], CTable);
    +        HUF_FLUSHBITS_1(&bitC);
    +        HUF_encodeSymbol(&bitC, ip[n- 4], CTable);
    +        HUF_FLUSHBITS(&bitC);
    +    }
    +
    +    return BIT_closeCStream(&bitC);
    +}
    +
     #if DYNAMIC_BMI2
    +
    +static TARGET_ATTRIBUTE("bmi2") size_t
    +HUF_compress1X_usingCTable_internal_bmi2(void* dst, size_t dstSize,
    +                                   const void* src, size_t srcSize,
    +                                   const HUF_CElt* CTable)
    +{
    +    return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
    +}
    +
    +static size_t
    +HUF_compress1X_usingCTable_internal_default(void* dst, size_t dstSize,
    +                                      const void* src, size_t srcSize,
    +                                      const HUF_CElt* CTable)
    +{
    +    return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
    +}
    +
    +static size_t
    +HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize,
    +                              const void* src, size_t srcSize,
    +                              const HUF_CElt* CTable, const int bmi2)
    +{
         if (bmi2) {
             return HUF_compress1X_usingCTable_internal_bmi2(dst, dstSize, src, srcSize, CTable);
         }
    -#endif
    -    (void)bmi2;
         return HUF_compress1X_usingCTable_internal_default(dst, dstSize, src, srcSize, CTable);
     }
     
    -static size_t HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize,
    -                                                  const void* src, size_t srcSize,
    -                                                  const HUF_CElt* CTable, const int bmi2)
    +#else
    +
    +static size_t
    +HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize,
    +                              const void* src, size_t srcSize,
    +                              const HUF_CElt* CTable, const int bmi2)
     {
    -#if DYNAMIC_BMI2
    -    if (bmi2) {
    -        return HUF_compress4X_usingCTable_internal_bmi2(dst, dstSize, src, srcSize, CTable);
    -    }
    -#endif
         (void)bmi2;
    -    return HUF_compress4X_usingCTable_internal_default(dst, dstSize, src, srcSize, CTable);
    +    return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
     }
     
    +#endif
    +
     size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
     {
         return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0);
     }
     
    +
    +static size_t
    +HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize,
    +                              const void* src, size_t srcSize,
    +                              const HUF_CElt* CTable, int bmi2)
    +{
    +    size_t const segmentSize = (srcSize+3)/4;   /* first 3 segments */
    +    const BYTE* ip = (const BYTE*) src;
    +    const BYTE* const iend = ip + srcSize;
    +    BYTE* const ostart = (BYTE*) dst;
    +    BYTE* const oend = ostart + dstSize;
    +    BYTE* op = ostart;
    +
    +    if (dstSize < 6 + 1 + 1 + 1 + 8) return 0;   /* minimum space to compress successfully */
    +    if (srcSize < 12) return 0;   /* no saving possible : too small input */
    +    op += 6;   /* jumpTable */
    +
    +    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
    +        if (cSize==0) return 0;
    +        assert(cSize <= 65535);
    +        MEM_writeLE16(ostart, (U16)cSize);
    +        op += cSize;
    +    }
    +
    +    ip += segmentSize;
    +    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
    +        if (cSize==0) return 0;
    +        assert(cSize <= 65535);
    +        MEM_writeLE16(ostart+2, (U16)cSize);
    +        op += cSize;
    +    }
    +
    +    ip += segmentSize;
    +    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
    +        if (cSize==0) return 0;
    +        assert(cSize <= 65535);
    +        MEM_writeLE16(ostart+4, (U16)cSize);
    +        op += cSize;
    +    }
    +
    +    ip += segmentSize;
    +    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, iend-ip, CTable, bmi2) );
    +        if (cSize==0) return 0;
    +        op += cSize;
    +    }
    +
    +    return op-ostart;
    +}
    +
     size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
     {
         return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0);
     }
     
    +
     static size_t HUF_compressCTable_internal(
                     BYTE* const ostart, BYTE* op, BYTE* const oend,
                     const void* src, size_t srcSize,
    diff --git a/lib/compress/huf_compress_impl.h b/lib/compress/huf_compress_impl.h
    deleted file mode 100644
    index fa045399f..000000000
    --- a/lib/compress/huf_compress_impl.h
    +++ /dev/null
    @@ -1,120 +0,0 @@
    -/*
    - * Copyright (c) 2018-present, 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.
    - */
    -
    -#ifndef FUNCTION
    -#  error "FUNCTION(name) must be defined"
    -#endif
    -
    -#ifndef TARGET
    -#  error "TARGET must be defined"
    -#endif
    -
    -
    -static void FUNCTION(HUF_encodeSymbol)(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable)
    -{
    -    BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits);
    -}
    -
    -#define HUF_FLUSHBITS(s)  BIT_flushBits(s)
    -
    -#define HUF_FLUSHBITS_1(stream) \
    -    if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*2+7) HUF_FLUSHBITS(stream)
    -
    -#define HUF_FLUSHBITS_2(stream) \
    -    if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*4+7) HUF_FLUSHBITS(stream)
    -
    -static TARGET
    -size_t FUNCTION(HUF_compress1X_usingCTable_internal)(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
    -{
    -    const BYTE* ip = (const BYTE*) src;
    -    BYTE* const ostart = (BYTE*)dst;
    -    BYTE* const oend = ostart + dstSize;
    -    BYTE* op = ostart;
    -    size_t n;
    -    BIT_CStream_t bitC;
    -
    -    /* init */
    -    if (dstSize < 8) return 0;   /* not enough space to compress */
    -    { size_t const initErr = BIT_initCStream(&bitC, op, oend-op);
    -      if (HUF_isError(initErr)) return 0; }
    -
    -    n = srcSize & ~3;  /* join to mod 4 */
    -    switch (srcSize & 3)
    -    {
    -        case 3 : FUNCTION(HUF_encodeSymbol)(&bitC, ip[n+ 2], CTable);
    -                 HUF_FLUSHBITS_2(&bitC);
    -		 /* fall-through */
    -        case 2 : FUNCTION(HUF_encodeSymbol)(&bitC, ip[n+ 1], CTable);
    -                 HUF_FLUSHBITS_1(&bitC);
    -		 /* fall-through */
    -        case 1 : FUNCTION(HUF_encodeSymbol)(&bitC, ip[n+ 0], CTable);
    -                 HUF_FLUSHBITS(&bitC);
    -		 /* fall-through */
    -        case 0 : /* fall-through */
    -        default: break;
    -    }
    -
    -    for (; n>0; n-=4) {  /* note : n&3==0 at this stage */
    -        FUNCTION(HUF_encodeSymbol)(&bitC, ip[n- 1], CTable);
    -        HUF_FLUSHBITS_1(&bitC);
    -        FUNCTION(HUF_encodeSymbol)(&bitC, ip[n- 2], CTable);
    -        HUF_FLUSHBITS_2(&bitC);
    -        FUNCTION(HUF_encodeSymbol)(&bitC, ip[n- 3], CTable);
    -        HUF_FLUSHBITS_1(&bitC);
    -        FUNCTION(HUF_encodeSymbol)(&bitC, ip[n- 4], CTable);
    -        HUF_FLUSHBITS(&bitC);
    -    }
    -
    -    return BIT_closeCStream(&bitC);
    -}
    -
    -
    -static TARGET
    -size_t FUNCTION(HUF_compress4X_usingCTable_internal)(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
    -{
    -    size_t const segmentSize = (srcSize+3)/4;   /* first 3 segments */
    -    const BYTE* ip = (const BYTE*) src;
    -    const BYTE* const iend = ip + srcSize;
    -    BYTE* const ostart = (BYTE*) dst;
    -    BYTE* const oend = ostart + dstSize;
    -    BYTE* op = ostart;
    -
    -    if (dstSize < 6 + 1 + 1 + 1 + 8) return 0;   /* minimum space to compress successfully */
    -    if (srcSize < 12) return 0;   /* no saving possible : too small input */
    -    op += 6;   /* jumpTable */
    -
    -    {   CHECK_V_F(cSize, FUNCTION(HUF_compress1X_usingCTable_internal)(op, oend-op, ip, segmentSize, CTable) );
    -        if (cSize==0) return 0;
    -        MEM_writeLE16(ostart, (U16)cSize);
    -        op += cSize;
    -    }
    -
    -    ip += segmentSize;
    -    {   CHECK_V_F(cSize, FUNCTION(HUF_compress1X_usingCTable_internal)(op, oend-op, ip, segmentSize, CTable) );
    -        if (cSize==0) return 0;
    -        MEM_writeLE16(ostart+2, (U16)cSize);
    -        op += cSize;
    -    }
    -
    -    ip += segmentSize;
    -    {   CHECK_V_F(cSize, FUNCTION(HUF_compress1X_usingCTable_internal)(op, oend-op, ip, segmentSize, CTable) );
    -        if (cSize==0) return 0;
    -        MEM_writeLE16(ostart+4, (U16)cSize);
    -        op += cSize;
    -    }
    -
    -    ip += segmentSize;
    -    {   CHECK_V_F(cSize, FUNCTION(HUF_compress1X_usingCTable_internal)(op, oend-op, ip, iend-ip, CTable) );
    -        if (cSize==0) return 0;
    -        op += cSize;
    -    }
    -
    -    return op-ostart;
    -}
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index 706845eb7..b7f4f7811 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -1394,8 +1394,8 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy,
                sizeof(prevEntropy->hufCTable));
     
         /* small ? don't even attempt compression (speed opt) */
    -#   define LITERAL_NOENTROPY 63
    -    {   size_t const minLitSize = prevEntropy->hufCTable_repeatMode == HUF_repeat_valid ? 6 : LITERAL_NOENTROPY;
    +#   define COMPRESS_LITERALS_SIZE_MIN 63
    +    {   size_t const minLitSize = (prevEntropy->hufCTable_repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN;
             if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
         }
     
    diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c
    index 8b292e792..e06b44533 100644
    --- a/lib/decompress/huf_decompress.c
    +++ b/lib/decompress/huf_decompress.c
    @@ -958,21 +958,22 @@ static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, qu
     };
     
     /** HUF_selectDecoder() :
    -*   Tells which decoder is likely to decode faster,
    -*   based on a set of pre-determined metrics.
    -*   @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 .
    -*   Assumption : 0 < cSrcSize, dstSize <= 128 KB */
    + *  Tells which decoder is likely to decode faster,
    + *  based on a set of pre-computed metrics.
    + * @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 .
    + *  Assumption : 0 < dstSize <= 128 KB */
     U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize)
     {
    +    assert(dstSize > 0);
    +    assert(dstSize <= 128 KB);
         /* decoder timing evaluation */
    -    U32 const Q = cSrcSize >= dstSize ? 15 : (U32)(cSrcSize * 16 / dstSize);   /* Q < 16 */
    -    U32 const D256 = (U32)(dstSize >> 8);
    -    U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256);
    -    U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256);
    -    DTime1 += DTime1 >> 3;  /* advantage to algorithm using less memory, for cache eviction */
    -
    -    return DTime1 < DTime0;
    -}
    +    {   U32 const Q = (cSrcSize >= dstSize) ? 15 : (U32)(cSrcSize * 16 / dstSize);   /* Q < 16 */
    +        U32 const D256 = (U32)(dstSize >> 8);
    +        U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256);
    +        U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256);
    +        DTime1 += DTime1 >> 3;  /* advantage to algorithm using less memory, to reduce cache eviction */
    +        return DTime1 < DTime0;
    +}   }
     
     
     typedef size_t (*decompressionAlgo)(void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);
    
    From 50f763ec44773b7669e14086b6360e4c486cdfe1 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Tue, 13 Mar 2018 14:23:14 -0700
    Subject: [PATCH 185/255] fixed several comments are underlined by @terrelln
    
    ---
     lib/compress/zstd_compress.c   | 15 ++++++---------
     lib/compress/zstdmt_compress.c | 12 ++++++------
     lib/zstd.h                     | 10 +++++-----
     3 files changed, 17 insertions(+), 20 deletions(-)
    
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index 97280d43f..c461fa443 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -146,7 +146,7 @@ const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStor
     static ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(
             ZSTD_CCtx_params CCtxParams, U64 srcSizeHint, size_t dictSize)
     {
    -    DEBUGLOG(2, "ZSTD_getCParamsFromCCtxParams: srcSize = %u, dictSize = %u",
    +    DEBUGLOG(5, "ZSTD_getCParamsFromCCtxParams: srcSize = %u, dictSize = %u",
                     (U32)srcSizeHint, (U32)dictSize);
         return (CCtxParams.compressionLevel == ZSTD_CLEVEL_CUSTOM) ?
                     CCtxParams.cParams :
    @@ -359,7 +359,6 @@ size_t ZSTD_CCtxParam_setParameter(
         case ZSTD_p_compressionLevel : {
             int cLevel = (int)value;  /* cast expected to restore negative sign */
             if (cLevel > ZSTD_maxCLevel()) cLevel = ZSTD_maxCLevel();
    -        DEBUGLOG(2, "ZSTD_CCtxParam_setParameter: set cLevel=%i", cLevel);
             if (cLevel) {  /* 0 : does not change current level */
                 CCtxParams->disableLiteralCompression = (cLevel<0);  /* negative levels disable huffman */
                 CCtxParams->compressionLevel = cLevel;
    @@ -1400,7 +1399,7 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy,
         symbolEncodingType_e hType = set_compressed;
         size_t cLitSize;
     
    -    DEBUGLOG(2,"ZSTD_compressLiterals (disableLiteralCompression=%i)",
    +    DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i)",
                     disableLiteralCompression);
     
         /* Prepare nextEntropy assuming reusing the existing table */
    @@ -2773,7 +2772,7 @@ static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx,
                         const ZSTD_CDict* const cdict,
                         ZSTD_CCtx_params const params, unsigned long long const pledgedSrcSize)
     {
    -    DEBUGLOG(2, "ZSTD_resetCStream_internal (disableLiteralCompression=%i)",
    +    DEBUGLOG(4, "ZSTD_resetCStream_internal (disableLiteralCompression=%i)",
                     params.disableLiteralCompression);
         /* params are supposed to be fully validated at this point */
         assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
    @@ -3094,9 +3093,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx,
             ZSTD_prefixDict const prefixDict = cctx->prefixDict;
             memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict));  /* single usage */
             assert(prefixDict.dict==NULL || cctx->cdict==NULL);   /* only one can be set */
    -        DEBUGLOG(2, "ZSTD_compress_generic : transparent init stage");
    -        DEBUGLOG(2, "ZSTD_compress_generic: disableLiteralCompression=%i",
    -                    params.disableLiteralCompression);
    +        DEBUGLOG(4, "ZSTD_compress_generic : transparent init stage");
             if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = input->size + 1;  /* auto-fix pledgedSrcSize */
             params.cParams = ZSTD_getCParamsFromCCtxParams(
                     cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/);
    @@ -3319,7 +3316,7 @@ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long l
         U64 const rSize = srcSizeHint+dictSize ? srcSizeHint+dictSize+addedSize : (U64)-1;
         U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB);   /* intentional underflow for srcSizeHint == 0 */
         int row = compressionLevel;
    -    DEBUGLOG(2, "ZSTD_getCParams (cLevel=%i)", compressionLevel);
    +    DEBUGLOG(5, "ZSTD_getCParams (cLevel=%i)", compressionLevel);
         if (compressionLevel == 0) row = ZSTD_CLEVEL_DEFAULT;   /* 0 == default */
         if (compressionLevel < 0) row = 0;   /* entry 0 is baseline for fast mode */
         if (compressionLevel > ZSTD_MAX_CLEVEL) row = ZSTD_MAX_CLEVEL;
    @@ -3335,7 +3332,7 @@ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long l
     ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) {
         ZSTD_parameters params;
         ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, srcSizeHint, dictSize);
    -    DEBUGLOG(2, "ZSTD_getParams (cLevel=%i)", compressionLevel);
    +    DEBUGLOG(5, "ZSTD_getParams (cLevel=%i)", compressionLevel);
         memset(¶ms, 0, sizeof(params));
         params.cParams = cParams;
         params.fParams.contentSizeFlag = 1;
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index bc2df2667..a5b6e0143 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -763,12 +763,12 @@ static size_t ZSTDMT_compress_advanced_internal(
         assert(jobParams.nbWorkers == 0);
         assert(mtctx->cctxPool->totalCCtx == params.nbWorkers);
     
    -    DEBUGLOG(2, "ZSTDMT_compress_advanced_internal: nbJobs=%2u (rawSize=%u bytes; fixedSize=%u) ",
    +    DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbJobs=%2u (rawSize=%u bytes; fixedSize=%u) ",
                     nbJobs, (U32)proposedJobSize, (U32)avgJobSize);
     
         if ((nbJobs==1) | (params.nbWorkers<=1)) {   /* fallback to single-thread mode : this is a blocking invocation anyway */
             ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0];
    -        DEBUGLOG(2, "ZSTDMT_compress_advanced_internal: fallback to single-thread mode");
    +        DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: fallback to single-thread mode");
             if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams);
             return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, NULL, 0, jobParams);
         }
    @@ -911,7 +911,7 @@ size_t ZSTDMT_initCStream_internal(
             const ZSTD_CDict* cdict, ZSTD_CCtx_params params,
             unsigned long long pledgedSrcSize)
     {
    -    DEBUGLOG(2, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u, nbWorkers=%u, cctxPool=%u, disableLiteralCompression=%i)",
    +    DEBUGLOG(4, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u, nbWorkers=%u, cctxPool=%u, disableLiteralCompression=%i)",
                     (U32)pledgedSrcSize, params.nbWorkers, mtctx->cctxPool->totalCCtx, params.disableLiteralCompression);
         /* params are supposed to be fully validated at this point */
         assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
    @@ -930,14 +930,14 @@ size_t ZSTDMT_initCStream_internal(
         mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN);  /* do not trigger multi-threading when srcSize is too small */
         if (mtctx->singleBlockingThread) {
             ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(params);
    -        DEBUGLOG(2, "ZSTDMT_initCStream_internal: switch to single blocking thread mode");
    +        DEBUGLOG(5, "ZSTDMT_initCStream_internal: switch to single blocking thread mode");
             assert(singleThreadParams.nbWorkers == 0);
             return ZSTD_initCStream_internal(mtctx->cctxPool->cctx[0],
                                              dict, dictSize, cdict,
                                              singleThreadParams, pledgedSrcSize);
         }
     
    -    DEBUGLOG(2, "ZSTDMT_initCStream_internal: %u workers", params.nbWorkers);
    +    DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u workers", params.nbWorkers);
     
         if (mtctx->allJobsCompleted == 0) {   /* previous compression not correctly finished */
             ZSTDMT_waitForAllJobsCompleted(mtctx);
    @@ -1244,7 +1244,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
     {
         size_t const newJobThreshold = mtctx->inBuff.prefixSize + mtctx->targetSectionSize;
         unsigned forwardInputProgress = 0;
    -    DEBUGLOG(2, "ZSTDMT_compressStream_generic (endOp=%u, srcSize=%u)",
    +    DEBUGLOG(5, "ZSTDMT_compressStream_generic (endOp=%u, srcSize=%u)",
                     (U32)endOp, (U32)(input->size - input->pos));
         assert(output->pos <= output->size);
         assert(input->pos  <= input->size);
    diff --git a/lib/zstd.h b/lib/zstd.h
    index 826bade6f..3ea049eb6 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -390,8 +390,8 @@ ZSTDLIB_API size_t ZSTD_DStreamOutSize(void);   /*!< recommended size for output
     #define ZSTD_SEARCHLOG_MIN       1
     #define ZSTD_SEARCHLENGTH_MAX    7   /* only for ZSTD_fast, other strategies are limited to 6 */
     #define ZSTD_SEARCHLENGTH_MIN    3   /* only for ZSTD_btopt, other strategies are limited to 4 */
    -#define ZSTD_TARGETLENGTH_MIN    1   /* only useful for btopt */
    -#define ZSTD_TARGETLENGTH_MAX  999   /* only useful for btopt */
    +#define ZSTD_TARGETLENGTH_MIN    1   /* only used by btopt, btultra and btfast */
    +#define ZSTD_TARGETLENGTH_MAX  999   /* only used by btopt, btultra and btfast */
     #define ZSTD_LDM_MINMATCH_MIN    4
     #define ZSTD_LDM_MINMATCH_MAX 4096
     #define ZSTD_LDM_BUCKETSIZELOG_MAX 8
    @@ -1019,9 +1019,9 @@ typedef enum {
     
         ZSTD_p_compressLiterals=1000, /* control huffman compression of literals (enabled) by default.
                                   * disabling it improves speed and decreases compression ratio by a large amount.
    -                              * note : this setting is updated when changing compression level.
    -                              *        positive compression levels set literalCompression to 1.
    -                              *        negative compression levels set literalCompression to 0. */
    +                              * note : this setting is automatically updated when changing compression level.
    +                              *        positive compression levels set ZSTD_p_compressLiterals to 1.
    +                              *        negative compression levels set ZSTD_p_compressLiterals to 0. */
     
         ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize,
                                   * even when referencing into Dictionary content (default:0) */
    
    From 33fb966e565831d1efaf3590d114597e5c0c2211 Mon Sep 17 00:00:00 2001
    From: Nick Terrell 
    Date: Tue, 6 Mar 2018 13:07:28 -0800
    Subject: [PATCH 186/255] Fix overflow protection with wlog=31
    
    The overflow protection is broken when the window log is `> (3U << 29)`, so 31.
    It doesn't work when `current` isn't around `1U << windowLog` ahead of `lowLimit`,
    and the the assertion `current > newCurrent` fails. This happens when the same
    context is used many times over, but with a large window log, like in zstdmt.
    
    Fix it by triggering correction based on `nextSrc - base` instead of `lowLimit`.
    
    The added test fails before the patch, and passes after.
    ---
     lib/compress/zstd_compress.c          |  2 +-
     lib/compress/zstd_compress_internal.h | 17 +++++++++--------
     lib/compress/zstd_ldm.c               | 16 +++++++++-------
     tests/fuzzer.c                        | 15 +++++++++++++++
     4 files changed, 34 insertions(+), 16 deletions(-)
    
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index c461fa443..de37f7154 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -1902,7 +1902,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx,
                 return ERROR(dstSize_tooSmall);   /* not enough space to store compressed block */
             if (remaining < blockSize) blockSize = remaining;
     
    -        if (ZSTD_window_needOverflowCorrection(ms->window)) {
    +        if (ZSTD_window_needOverflowCorrection(ms->window, ip + blockSize)) {
                 U32 const cycleLog = ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy);
                 U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, maxDist, ip);
                 ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30);
    diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
    index 65e99cd90..3196aade4 100644
    --- a/lib/compress/zstd_compress_internal.h
    +++ b/lib/compress/zstd_compress_internal.h
    @@ -453,13 +453,12 @@ MEM_STATIC size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls)
     /*-*************************************
     *  Round buffer management
     ***************************************/
    -
    -#define ZSTD_LOWLIMIT_MAX (3U << 29) /* Max lowLimit allowed */
    +/* Max current allowed */
    +#define ZSTD_CURRENT_MAX ((3U << 29) + (1U << ZSTD_WINDOWLOG_MAX))
     /* Maximum chunk size before overflow correction needs to be called again */
    -#define ZSTD_CHUNKSIZE_MAX                                                   \
    -    ( ((U32)-1)                  /* Maximum ending current index */          \
    -    - (1U << ZSTD_WINDOWLOG_MAX) /* Max distance from lowLimit to current */ \
    -    - ZSTD_LOWLIMIT_MAX)         /* Maximum beginning lowLimit */
    +#define ZSTD_CHUNKSIZE_MAX                                                     \
    +    ( ((U32)-1)                  /* Maximum ending current index */            \
    +    - ZSTD_CURRENT_MAX)          /* Maximum beginning lowLimit */
     
     /**
      * ZSTD_window_clear():
    @@ -488,9 +487,11 @@ MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window)
      * Returns non-zero if the indices are getting too large and need overflow
      * protection.
      */
    -MEM_STATIC U32 ZSTD_window_needOverflowCorrection(ZSTD_window_t const window)
    +MEM_STATIC U32 ZSTD_window_needOverflowCorrection(ZSTD_window_t const window,
    +                                                  void const* srcEnd)
     {
    -    return window.lowLimit > ZSTD_LOWLIMIT_MAX;
    +    U32 const current = (U32)((BYTE const*)srcEnd - window.base);
    +    return current > ZSTD_CURRENT_MAX;
     }
     
     /**
    diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c
    index 22f9b4a88..1565687f9 100644
    --- a/lib/compress/zstd_ldm.c
    +++ b/lib/compress/zstd_ldm.c
    @@ -472,6 +472,7 @@ size_t ZSTD_ldm_generateSequences(
     {
         U32 const maxDist = 1U << params->windowLog;
         BYTE const* const istart = (BYTE const*)src;
    +    BYTE const* const iend = istart + srcSize;
         size_t const kMaxChunkSize = 1 << 20;
         size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0);
         size_t nbSeq = 0;
    @@ -483,12 +484,14 @@ size_t ZSTD_ldm_generateSequences(
          */
         assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize);
         for (chunk = 0; chunk < nbChunks; ++chunk) {
    -        size_t const chunkStart = chunk * kMaxChunkSize;
    -        size_t const chunkEnd = MIN(chunkStart + kMaxChunkSize, srcSize);
    +        BYTE const* const chunkStart = istart + chunk * kMaxChunkSize;
    +        size_t const remaining = (size_t)(iend - chunkStart);
    +        BYTE const *const chunkEnd =
    +            (remaining < kMaxChunkSize) ? iend : chunkStart + kMaxChunkSize;
             size_t const chunkSize = chunkEnd - chunkStart;
     
    -        assert(chunkStart < srcSize);
    -        if (ZSTD_window_needOverflowCorrection(ldmState->window)) {
    +        assert(chunkStart < iend);
    +        if (ZSTD_window_needOverflowCorrection(ldmState->window, chunkEnd)) {
                 U32 const ldmHSize = 1U << params->hashLog;
                 U32 const correction = ZSTD_window_correctOverflow(
                     &ldmState->window, /* cycleLog */ 0, maxDist, src);
    @@ -500,10 +503,9 @@ size_t ZSTD_ldm_generateSequences(
              *       * Try invalidation after the sequence generation and test the
              *         the offset against maxDist directly.
              */
    -        ZSTD_window_enforceMaxDist(&ldmState->window, istart + chunkEnd,
    -                                   maxDist);
    +        ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist);
             nbSeq += ZSTD_ldm_generateSequences_internal(
    -            ldmState, sequences + nbSeq, params, istart + chunkStart, chunkSize,
    +            ldmState, sequences + nbSeq, params, chunkStart, chunkSize,
                 extDict);
         }
         return nbSeq;
    diff --git a/tests/fuzzer.c b/tests/fuzzer.c
    index 8d8e547c2..cae509707 100644
    --- a/tests/fuzzer.c
    +++ b/tests/fuzzer.c
    @@ -394,6 +394,21 @@ static int basicUnitTests(U32 seed, double compressibility)
         }
         DISPLAYLEVEL(3, "OK \n");
     
    +    DISPLAYLEVEL(3, "test%3d : large window log smaller data : ", testNb++);
    +    {   ZSTD_CCtx* const cctx = ZSTD_createCCtx();
    +        ZSTD_parameters params = ZSTD_getParams(1, ZSTD_CONTENTSIZE_UNKNOWN, 0);
    +        size_t const nbCompressions = (1U << 31) / CNBuffSize + 1;
    +        size_t i;
    +        params.fParams.contentSizeFlag = 0;
    +        params.cParams.windowLog = ZSTD_WINDOWLOG_MAX;
    +        for (i = 0; i < nbCompressions; ++i) {
    +            CHECK_Z( ZSTD_compressBegin_advanced(cctx, NULL, 0, params, ZSTD_CONTENTSIZE_UNKNOWN) );  /* re-use same parameters */
    +            CHECK_Z( ZSTD_compressEnd(cctx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize) );
    +        }
    +        ZSTD_freeCCtx(cctx);
    +    }
    +    DISPLAYLEVEL(3, "OK \n");
    +
         /* Static CCtx tests */
     #define STATIC_CCTX_LEVEL 3
         DISPLAYLEVEL(3, "test%3i : create static CCtx for level %u :", testNb++, STATIC_CCTX_LEVEL);
    
    From a9a6dcba63805ba3d514bfd314571c2374de9ec2 Mon Sep 17 00:00:00 2001
    From: Nick Terrell 
    Date: Tue, 6 Mar 2018 19:50:50 -0800
    Subject: [PATCH 187/255] Expose reference external sequence API
    
    * Expose the reference external sequences API for zstdmt.
      Allows external sequences of any length, which get split when necessary.
    * Reset the LDM window when the context is reset.
    * Store the maximum number of LDM sequences.
    * Sequence generation now returns the number of last literals.
    * Fix sequence generation to not throw out the last literals when blocks of
      more than 1 MB are encountered.
    ---
     lib/common/zstd_internal.h            |   6 --
     lib/compress/zstd_compress.c          |  45 ++++++++--
     lib/compress/zstd_compress_internal.h |  34 +++++++-
     lib/compress/zstd_ldm.c               | 120 +++++++++++++++++---------
     lib/compress/zstd_ldm.h               |  30 ++++---
     5 files changed, 166 insertions(+), 69 deletions(-)
    
    diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h
    index 1c6841a32..65c08a825 100644
    --- a/lib/common/zstd_internal.h
    +++ b/lib/common/zstd_internal.h
    @@ -213,12 +213,6 @@ MEM_STATIC void ZSTD_wildcopy_e(void* dst, const void* src, void* dstEnd)   /* s
     /*-*******************************************
     *  Private declarations
     *********************************************/
    -typedef struct rawSeq_s {
    -    U32 offset;
    -    U32 litLength;
    -    U32 matchLength;
    -} rawSeq;
    -
     typedef struct seqDef_s {
         U32 offset;
         U16 litLength;
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index c461fa443..fdf6eceb6 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -920,6 +920,8 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pl
             (U32)pledgedSrcSize, cctx->appliedParams.fParams.contentSizeFlag);
         cctx->stage = ZSTDcs_init;
         cctx->dictID = 0;
    +    if (params.ldmParams.enableLdm)
    +        ZSTD_window_clear(&cctx->ldmState.window);
         ZSTD_invalidateMatchState(&cctx->blockState.matchState);
         ZSTD_reset_compressedBlockState(cctx->blockState.prevCBlock);
         XXH64_reset(&cctx->xxhState, 0);
    @@ -1079,6 +1081,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
                 ptr = zc->ldmState.hashTable + ldmHSize;
                 zc->ldmSequences = (rawSeq*)ptr;
                 ptr = zc->ldmSequences + maxNbLdmSeq;
    +            zc->maxNbLdmSequences = maxNbLdmSeq;
     
                 memset(&zc->ldmState.window, 0, sizeof(zc->ldmState.window));
             }
    @@ -1103,6 +1106,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
                 memset(ptr, 0, ldmBucketSize);
                 zc->ldmState.bucketOffsets = (BYTE*)ptr;
                 ptr = zc->ldmState.bucketOffsets + ldmBucketSize;
    +            ZSTD_window_clear(&zc->ldmState.window);
             }
     
             /* buffers */
    @@ -1834,17 +1838,33 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
                 for (i = 0; i < ZSTD_REP_NUM; ++i)
                     zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i];
             }
    -        if (zc->appliedParams.ldmParams.enableLdm) {
    -            size_t const nbSeq =
    -                ZSTD_ldm_generateSequences(&zc->ldmState, zc->ldmSequences,
    -                                           &zc->appliedParams.ldmParams,
    -                                           src, srcSize, extDict);
    +        if (zc->externSeqStore.pos < zc->externSeqStore.size) {
    +            assert(!zc->appliedParams.ldmParams.enableLdm);
    +            /* Updates ldmSeqStore.pos */
                 lastLLSize =
    -                ZSTD_ldm_blockCompress(zc->ldmSequences, nbSeq,
    +                ZSTD_ldm_blockCompress(&zc->externSeqStore,
                                            ms, &zc->seqStore,
                                            zc->blockState.nextCBlock->rep,
                                            &zc->appliedParams.cParams,
                                            src, srcSize, extDict);
    +            assert(zc->externSeqStore.pos <= zc->externSeqStore.size);
    +        } else if (zc->appliedParams.ldmParams.enableLdm) {
    +            rawSeqStore_t ldmSeqStore = {NULL, 0, 0, 0};
    +
    +            ldmSeqStore.seq = zc->ldmSequences;
    +            ldmSeqStore.capacity = zc->maxNbLdmSequences;
    +            /* Updates ldmSeqStore.size */
    +            CHECK_F(ZSTD_ldm_generateSequences(&zc->ldmState, &ldmSeqStore,
    +                                               &zc->appliedParams.ldmParams,
    +                                               src, srcSize));
    +            /* Updates ldmSeqStore.pos */
    +            lastLLSize =
    +                ZSTD_ldm_blockCompress(&ldmSeqStore,
    +                                       ms, &zc->seqStore,
    +                                       zc->blockState.nextCBlock->rep,
    +                                       &zc->appliedParams.cParams,
    +                                       src, srcSize, extDict);
    +            assert(ldmSeqStore.pos == ldmSeqStore.size);
             } else {   /* not long range mode */
                 ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict);
                 lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, src, srcSize);
    @@ -2005,6 +2025,19 @@ size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity)
         }
     }
     
    +size_t ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq)
    +{
    +    if (cctx->stage != ZSTDcs_init)
    +        return ERROR(stage_wrong);
    +    if (cctx->appliedParams.ldmParams.enableLdm)
    +        return ERROR(parameter_unsupported);
    +    cctx->externSeqStore.seq = seq;
    +    cctx->externSeqStore.size = nbSeq;
    +    cctx->externSeqStore.capacity = nbSeq;
    +    cctx->externSeqStore.pos = 0;
    +    return 0;
    +}
    +
     
     static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,
                                   void* dst, size_t dstCapacity,
    diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
    index 65e99cd90..af168c7c4 100644
    --- a/lib/compress/zstd_compress_internal.h
    +++ b/lib/compress/zstd_compress_internal.h
    @@ -152,6 +152,19 @@ typedef struct {
         U32 windowLog;          /* Window log for the LDM */
     } ldmParams_t;
     
    +typedef struct {
    +    U32 offset;
    +    U32 litLength;
    +    U32 matchLength;
    +} rawSeq;
    +
    +typedef struct {
    +  rawSeq* seq;     /* The start of the sequences */
    +  size_t pos;      /* The position where reading stopped. <= size. */
    +  size_t size;     /* The number of sequences. <= capacity. */
    +  size_t capacity; /* The capacity of the `seq` pointer */
    +} rawSeqStore_t;
    +
     struct ZSTD_CCtx_params_s {
         ZSTD_format_e format;
         ZSTD_compressionParameters cParams;
    @@ -191,9 +204,11 @@ struct ZSTD_CCtx_s {
         ZSTD_customMem customMem;
         size_t staticSize;
     
    -    seqStore_t seqStore;    /* sequences storage ptrs */
    -    ldmState_t ldmState;    /* long distance matching state */
    -    rawSeq* ldmSequences;   /* Storage for the ldm output sequences */
    +    seqStore_t seqStore;      /* sequences storage ptrs */
    +    ldmState_t ldmState;      /* long distance matching state */
    +    rawSeq* ldmSequences;     /* Storage for the ldm output sequences */
    +    size_t maxNbLdmSequences;
    +    rawSeqStore_t externSeqStore; /* Mutable reference to external sequences */
         ZSTD_blockState_t blockState;
         U32* entropyWorkspace;  /* entropy workspace of HUF_WORKSPACE_SIZE bytes */
     
    @@ -660,4 +675,17 @@ size_t ZSTD_compress_advanced_internal(ZSTD_CCtx* cctx,
     size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity);
     
     
    +/* ZSTD_referenceExternalSequences() :
    + * Must be called before starting a compression operation.
    + * seqs must parse a prefix of the source.
    + * This cannot be used when long range matching is enabled.
    + * Zstd will use these sequences, and pass the literals to a secondary block
    + * compressor.
    + * @return : An error code on failure.
    + * NOTE: seqs are not verified! Invalid sequences can cause out-of-bounds memory
    + * access and data corruption.
    + */
    +size_t ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq);
    +
    +
     #endif /* ZSTD_COMPRESS_H */
    diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c
    index 22f9b4a88..d75cdf5ae 100644
    --- a/lib/compress/zstd_ldm.c
    +++ b/lib/compress/zstd_ldm.c
    @@ -295,12 +295,11 @@ static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor)
     }
     
     static size_t ZSTD_ldm_generateSequences_internal(
    -        ldmState_t* ldmState, rawSeq* sequences,
    -        ldmParams_t const* params, void const* src, size_t srcSize,
    -        int const extDict)
    +        ldmState_t* ldmState, rawSeqStore_t* rawSeqStore,
    +        ldmParams_t const* params, void const* src, size_t srcSize)
     {
    -    rawSeq const* const sequencesStart = sequences;
         /* LDM parameters */
    +    int const extDict = ZSTD_window_hasExtDict(ldmState->window);
         U32 const minMatchLength = params->minMatchLength;
         U64 const hashPower = ldmState->hashPower;
         U32 const hBits = params->hashLog - params->bucketSizeLog;
    @@ -424,11 +423,15 @@ static size_t ZSTD_ldm_generateSequences_internal(
                  */
                 U32 const matchIndex = bestEntry->offset;
                 U32 const offset = current - matchIndex;
    +            rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size;
     
    -            sequences->litLength = (U32)(ip - anchor);
    -            sequences->matchLength = (U32)mLength;
    -            sequences->offset = offset;
    -            ++sequences;
    +            /* Out of sequence storage */
    +            if (rawSeqStore->size == rawSeqStore->capacity)
    +                return ERROR(dstSize_tooSmall);
    +            seq->litLength = (U32)(ip - anchor);
    +            seq->matchLength = (U32)mLength;
    +            seq->offset = offset;
    +            rawSeqStore->size++;
             }
     
             /* Insert the current entry into the hash table */
    @@ -449,8 +452,7 @@ static size_t ZSTD_ldm_generateSequences_internal(
             ip += mLength;
             anchor = ip;
         }
    -    /* Return the number of sequences generated */
    -    return sequences - sequencesStart;
    +    return iend - anchor;
     }
     
     /*! ZSTD_ldm_reduceTable() :
    @@ -466,35 +468,44 @@ static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size,
     }
     
     size_t ZSTD_ldm_generateSequences(
    -        ldmState_t* ldmState, rawSeq* sequences,
    -        ldmParams_t const* params, void const* src, size_t srcSize,
    -        int const extDict)
    +        ldmState_t* ldmState, rawSeqStore_t* sequences,
    +        ldmParams_t const* params, void const* src, size_t srcSize)
     {
         U32 const maxDist = 1U << params->windowLog;
         BYTE const* const istart = (BYTE const*)src;
         size_t const kMaxChunkSize = 1 << 20;
         size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0);
    -    size_t nbSeq = 0;
         size_t chunk;
    +    size_t leftoverSize = 0;
     
         assert(ZSTD_CHUNKSIZE_MAX >= kMaxChunkSize);
         /* Check that ZSTD_window_update() has been called for this chunk prior
          * to passing it to this function.
          */
         assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize);
    -    for (chunk = 0; chunk < nbChunks; ++chunk) {
    +    /* The input could be very large (in zstdmt), so it must be broken up into
    +     * chunks to enforce the maximmum distance and handle overflow correction.
    +     */
    +    assert(sequences->pos <= sequences->size);
    +    assert(sequences->size <= sequences->capacity);
    +    for (chunk = 0; chunk < nbChunks && sequences->size < sequences->capacity; ++chunk) {
             size_t const chunkStart = chunk * kMaxChunkSize;
             size_t const chunkEnd = MIN(chunkStart + kMaxChunkSize, srcSize);
             size_t const chunkSize = chunkEnd - chunkStart;
    +        size_t newLeftoverSize;
    +        size_t const prevSize = sequences->size;
     
             assert(chunkStart < srcSize);
    +        /* 1. Perform overflow correction if necessary. */
             if (ZSTD_window_needOverflowCorrection(ldmState->window)) {
                 U32 const ldmHSize = 1U << params->hashLog;
                 U32 const correction = ZSTD_window_correctOverflow(
                     &ldmState->window, /* cycleLog */ 0, maxDist, src);
                 ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction);
             }
    -        /* kMaxChunkSize should be small enough that we don't lose too much of
    +        /* 2. We enforce the maximum offset allowed.
    +         *
    +         * kMaxChunkSize should be small enough that we don't lose too much of
              * the window through early invalidation.
              * TODO: * Test the chunk size.
              *       * Try invalidation after the sequence generation and test the
    @@ -502,14 +513,28 @@ size_t ZSTD_ldm_generateSequences(
              */
             ZSTD_window_enforceMaxDist(&ldmState->window, istart + chunkEnd,
                                        maxDist);
    -        nbSeq += ZSTD_ldm_generateSequences_internal(
    -            ldmState, sequences + nbSeq, params, istart + chunkStart, chunkSize,
    -            extDict);
    +        /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */
    +        newLeftoverSize = ZSTD_ldm_generateSequences_internal(
    +            ldmState, sequences, params, istart + chunkStart,
    +            chunkSize);
    +        if (ZSTD_isError(newLeftoverSize))
    +            return newLeftoverSize;
    +        /* 4. We add the leftover literals from previous iterations to the first
    +         *    newly generated sequence, or add the `newLeftoverSize` if none are
    +         *    generated.
    +         */
    +        /* Prepend the leftover literals from the last call */
    +        if (prevSize < sequences->size) {
    +            sequences->seq[prevSize].litLength += (U32)leftoverSize;
    +            leftoverSize = newLeftoverSize;
    +        } else {
    +            assert(newLeftoverSize == chunkSize);
    +            leftoverSize += chunkSize;
    +        }
         }
    -    return nbSeq;
    +    return 0;
     }
     
    -#if 0
     /**
      * If the sequence length is longer than remaining then the sequence is split
      * between this block and the next.
    @@ -517,11 +542,11 @@ size_t ZSTD_ldm_generateSequences(
      * Returns the current sequence to handle, or if the rest of the block should
      * be literals, it returns a sequence with offset == 0.
      */
    -static rawSeq maybeSplitSequence(rawSeq* sequences, size_t* nbSeq,
    -                                 size_t const seq, size_t const remaining,
    -                                 U32 const minMatch)
    +static rawSeq maybeSplitSequence(rawSeqStore_t* rawSeqStore,
    +                                 U32 const remaining, U32 const minMatch)
     {
    -    rawSeq sequence = sequences[seq];
    +    size_t const pos = rawSeqStore->pos;
    +    rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos];
         assert(sequence.offset > 0);
         /* Handle partial sequences */
         if (remaining <= sequence.litLength) {
    @@ -529,8 +554,7 @@ static rawSeq maybeSplitSequence(rawSeq* sequences, size_t* nbSeq,
              * They will become the last literals of this block.
              * The next block starts off with the remaining literals.
              */
    -        sequences[seq].litLength -= remaining;
    -        *nbSeq = seq;
    +        rawSeqStore->seq[pos].litLength -= remaining;
             sequence.offset = 0;
         } else if (remaining < sequence.litLength + sequence.matchLength) {
             /* Split the match up into two sequences. One in this block, and one
    @@ -543,31 +567,38 @@ static rawSeq maybeSplitSequence(rawSeq* sequences, size_t* nbSeq,
             assert(remaining > sequence.litLength);
             assert(matchPrefix < sequence.matchLength);
             assert(matchPrefix + matchSuffix == sequence.matchLength);
    -        /* Update the current sequence */
    +        /* Update the first sequence */
             sequence.matchLength = matchPrefix;
    -        /* Update the next sequence when long enough, otherwise omit it. */
    +        /* Update the second sequence */
             if (matchSuffix >= minMatch) {
    -            sequences[seq].litLength = 0;
    -            sequences[seq].matchLength = matchSuffix;
    -            *nbSeq = seq;
    +            /* Update the second sequence, since the suffix is long enough */
    +            rawSeqStore->seq[pos].litLength = 0;
    +            rawSeqStore->seq[pos].matchLength = matchSuffix;
             } else {
    -            sequences[seq + 1].litLength += matchSuffix;
    -            *nbSeq = seq + 1;
    +            /* Omit the second sequence since the match suffix is too short.
    +             * Add to the next sequences literals (if any).
    +             */
    +            if (pos + 1 < rawSeqStore->size)
    +                rawSeqStore->seq[pos + 1].litLength += matchSuffix;
    +            rawSeqStore->pos++; /* Consume the sequence */
             }
             if (sequence.matchLength < minMatch) {
                 /* Skip the current sequence if it is too short */
                 sequence.offset = 0;
             }
    +    } else {
    +      /* No partial sequence */
    +      rawSeqStore->pos++; /* Consume the sequence */
         }
         return sequence;
     }
    -#endif
     
    -size_t ZSTD_ldm_blockCompress(rawSeq const* sequences, size_t nbSeq,
    +size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize,
         int const extDict)
     {
    +    unsigned const minMatch = cParams->searchLength;
         ZSTD_blockCompressor const blockCompressor =
             ZSTD_selectBlockCompressor(cParams->strategy, extDict);
         BYTE const* const base = ms->window.base;
    @@ -576,15 +607,20 @@ size_t ZSTD_ldm_blockCompress(rawSeq const* sequences, size_t nbSeq,
         BYTE const* const iend = istart + srcSize;
         /* Input positions */
         BYTE const* ip = istart;
    -    size_t seq;
    -    /* Loop through each sequence and apply the block compressor to the lits */
    -    for (seq = 0; seq < nbSeq; ++seq) {
    -        rawSeq const sequence = sequences[seq];
    -        int i;
     
    +    assert(rawSeqStore->pos <= rawSeqStore->size);
    +    assert(rawSeqStore->size <= rawSeqStore->capacity);
    +    /* Loop through each sequence and apply the block compressor to the lits */
    +    while (rawSeqStore->pos < rawSeqStore->size && ip < iend) {
    +        /* maybeSplitSequence updates rawSeqStore->pos */
    +        rawSeq const sequence = maybeSplitSequence(rawSeqStore,
    +                                                   (U32)(iend - ip), minMatch);
    +        int i;
    +        /* End signal */
             if (sequence.offset == 0)
                 break;
     
    +        assert(sequence.offset <= (1U << cParams->windowLog));
             assert(ip + sequence.litLength + sequence.matchLength <= iend);
     
             /* Fill tables for block compressor */
    @@ -608,8 +644,10 @@ size_t ZSTD_ldm_blockCompress(rawSeq const* sequences, size_t nbSeq,
                 ip += sequence.matchLength;
             }
         }
    +    /* Fill the tables for the block compressor */
         ZSTD_ldm_limitTableUpdate(ms, ip);
         ZSTD_ldm_fillFastTables(ms, cParams, ip);
    +    /* Compress the last literals */
         {
             size_t const lastLiterals = blockCompressor(ms, seqStore, rep, cParams,
                                                         ip, iend - ip);
    diff --git a/lib/compress/zstd_ldm.h b/lib/compress/zstd_ldm.h
    index d9219a18f..9d2f7c391 100644
    --- a/lib/compress/zstd_ldm.h
    +++ b/lib/compress/zstd_ldm.h
    @@ -28,18 +28,19 @@ extern "C" {
      * ZSTD_ldm_generateSequences():
      *
      * Generates the sequences using the long distance match finder.
    - * The sequences completely parse a prefix of the source, but leave off the last
    - * literals. Returns the number of sequences generated into `sequences`. The
    - * user must have called ZSTD_window_update() for all of the input they have,
    - * even if they pass it to ZSTD_ldm_generateSequences() in chunks.
    + * Generates long range matching sequences in `sequences`, which parse a prefix
    + * of the source. `sequences` must be large enough to store every sequence,
    + * which can be checked with `ZSTD_ldm_getMaxNbSeq()`.
    + * @returns 0 or an error code.
      *
    - * NOTE: The source may be any size, assuming it doesn't overflow the hash table
    - * indices, and the output sequences table is large enough..
    + * NOTE: The user must have called ZSTD_window_update() for all of the input
    + * they have, even if they pass it to ZSTD_ldm_generateSequences() in chunks.
    + * NOTE: This function returns an error if it runs out of space to store
    + *       sequences.
      */
     size_t ZSTD_ldm_generateSequences(
    -        ldmState_t* ldms, rawSeq* sequences,
    -        ldmParams_t const* params, void const* src, size_t srcSize,
    -        int const extDict);
    +        ldmState_t* ldms, rawSeqStore_t* sequences,
    +        ldmParams_t const* params, void const* src, size_t srcSize);
     
     /**
      * ZSTD_ldm_blockCompress():
    @@ -48,15 +49,18 @@ size_t ZSTD_ldm_generateSequences(
      * block compressor. The literals section of every sequence is passed to the
      * secondary block compressor, and those sequences are interspersed with the
      * predefined sequences. Returns the length of the last literals.
    - * `nbSeq` is the number of sequences available in `sequences`.
    + * Updates `rawSeqStore.pos` to indicate how many sequences have been consumed.
    + * `rawSeqStore.seq` may also be updated to split the last sequence between two
    + * blocks.
    + * @return The length of the last literals.
      *
      * NOTE: The source must be at most the maximum block size, but the predefined
      * sequences can be any size, and may be longer than the block. In the case that
      * they are longer than the block, the last sequences may need to be split into
    - * two. We handle that case correctly, and update `sequences` and `nbSeq`
    - * appropriately.
    + * two. We handle that case correctly, and update `rawSeqStore` appropriately.
    + * NOTE: This function does not return any errors.
      */
    -size_t ZSTD_ldm_blockCompress(rawSeq const* sequences, size_t nbSeq,
    +size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize,
         int const extDict);
    
    From f15a17e19ff517d77ee279635b6615eb7c8cf125 Mon Sep 17 00:00:00 2001
    From: Nick Terrell 
    Date: Tue, 27 Feb 2018 20:09:18 -0800
    Subject: [PATCH 188/255] Use a single buffer in zstdmt
    
    Summary:
    Allocate a single input buffer large enough to house each job, as well as
    enough space for the IO thread to write 2 extra buffers. One goes in the
    `POOL` queue, and one to fill, and then block on a full `POOL` queue.
    Since we can't overlap with the prefix, we allocate space for 3 extra
    input buffers.
    
    Test Plan:
    * CI
    * With and without ASAN/UBSAN run zstdmt with different number of threads
      on two large binaries, and verify that their checksums match.
    * Test on the tip of the zstdmt ldm integration.
    
    Reviewers: cyan
    
    Differential Revision: https://phabricator.intern.facebook.com/D7284007
    
    Tasks: T25664120
    ---
     lib/compress/zstdmt_compress.c | 321 +++++++++++++++++++++++----------
     tests/playTests.sh             |  42 ++---
     2 files changed, 246 insertions(+), 117 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index a5b6e0143..4b2369007 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -309,6 +309,13 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx)
     /* =====          Worker thread         ===== */
     /* ------------------------------------------ */
     
    +typedef struct {
    +    void const* start;
    +    size_t size;
    +} range_t;
    +
    +static const range_t kNullRange = { NULL, 0 };
    +
     typedef struct {
         size_t   consumed;                   /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx */
         size_t   cSize;                      /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx, then set0 by mtctx */
    @@ -317,10 +324,8 @@ typedef struct {
         ZSTDMT_CCtxPool* cctxPool;           /* Thread-safe - used by mtctx and (all) workers */
         ZSTDMT_bufferPool* bufPool;          /* Thread-safe - used by mtctx and (all) workers */
         buffer_t dstBuff;                    /* set by worker (or mtctx), then read by worker & mtctx, then modified by mtctx => no barrier */
    -    buffer_t srcBuff;                    /* set by mtctx, then released by worker => no barrier */
    -    const void* prefixStart;             /* set by mtctx, then read and set0 by worker => no barrier */
    -    size_t   prefixSize;                 /* set by mtctx, then read by worker => no barrier */
    -    size_t   srcSize;                    /* set by mtctx, then read by worker & mtctx => no barrier */
    +    range_t prefix;                      /* set by mtctx, then read by worker & mtctx => no barrier */
    +    range_t src;                         /* set by mtctx, then read by worker & mtctx => no barrier */
         unsigned firstJob;                   /* set by mtctx, then read by worker => no barrier */
         unsigned lastJob;                    /* set by mtctx, then read by worker => no barrier */
         ZSTD_CCtx_params params;             /* set by mtctx, then read by worker => no barrier */
    @@ -335,7 +340,6 @@ void ZSTDMT_compressionJob(void* jobDescription)
     {
         ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription;
         ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool);
    -    const void* const src = (const char*)job->prefixStart + job->prefixSize;
         buffer_t dstBuff = job->dstBuff;
     
         /* ressources */
    @@ -358,7 +362,7 @@ void ZSTDMT_compressionJob(void* jobDescription)
             assert(job->firstJob);  /* only allowed for first job */
             if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; }
         } else {  /* srcStart points at reloaded section */
    -        U64 const pledgedSrcSize = job->firstJob ? job->fullFrameSize : job->srcSize;
    +        U64 const pledgedSrcSize = job->firstJob ? job->fullFrameSize : job->src.size;
             ZSTD_CCtx_params jobParams = job->params;   /* do not modify job->params ! copy it, modify the copy */
             {   size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstJob);
                 if (ZSTD_isError(forceWindowError)) {
    @@ -366,7 +370,7 @@ void ZSTDMT_compressionJob(void* jobDescription)
                     goto _endJob;
             }   }
             {   size_t const initError = ZSTD_compressBegin_advanced_internal(cctx,
    -                                        job->prefixStart, job->prefixSize, ZSTD_dm_rawContent, /* load dictionary in "content-only" mode (no header analysis) */
    +                                        job->prefix.start, job->prefix.size, ZSTD_dm_rawContent, /* load dictionary in "content-only" mode (no header analysis) */
                                             NULL, /*cdict*/
                                             jobParams, pledgedSrcSize);
                 if (ZSTD_isError(initError)) {
    @@ -374,7 +378,7 @@ void ZSTDMT_compressionJob(void* jobDescription)
                     goto _endJob;
         }   }   }
         if (!job->firstJob) {  /* flush and overwrite frame header when it's not first job */
    -        size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, src, 0);
    +        size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0);
             if (ZSTD_isError(hSize)) { job->cSize = hSize; /* save error code */ goto _endJob; }
             DEBUGLOG(5, "ZSTDMT_compressionJob: flush and overwrite %u bytes of frame header (not first job)", (U32)hSize);
             ZSTD_invalidateRepCodes(cctx);
    @@ -382,14 +386,14 @@ void ZSTDMT_compressionJob(void* jobDescription)
     
         /* compress */
         {   size_t const chunkSize = 4*ZSTD_BLOCKSIZE_MAX;
    -        int const nbChunks = (int)((job->srcSize + (chunkSize-1)) / chunkSize);
    -        const BYTE* ip = (const BYTE*) src;
    +        int const nbChunks = (int)((job->src.size + (chunkSize-1)) / chunkSize);
    +        const BYTE* ip = (const BYTE*) job->src.start;
             BYTE* const ostart = (BYTE*)dstBuff.start;
             BYTE* op = ostart;
             BYTE* oend = op + dstBuff.capacity;
             int chunkNb;
    -        if (sizeof(size_t) > sizeof(int)) assert(job->srcSize < ((size_t)INT_MAX) * chunkSize);   /* check overflow */
    -        DEBUGLOG(5, "ZSTDMT_compressionJob: compress %u bytes in %i blocks", (U32)job->srcSize, nbChunks);
    +        if (sizeof(size_t) > sizeof(int)) assert(job->src.size < ((size_t)INT_MAX) * chunkSize);   /* check overflow */
    +        DEBUGLOG(5, "ZSTDMT_compressionJob: compress %u bytes in %i blocks", (U32)job->src.size, nbChunks);
             assert(job->cSize == 0);
             for (chunkNb = 1; chunkNb < nbChunks; chunkNb++) {
                 size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, chunkSize);
    @@ -408,8 +412,8 @@ void ZSTDMT_compressionJob(void* jobDescription)
             /* last block */
             assert(chunkSize > 0); assert((chunkSize & (chunkSize - 1)) == 0);  /* chunkSize must be power of 2 for mask==(chunkSize-1) to work */
             if ((nbChunks > 0) | job->lastJob /*must output a "last block" flag*/ ) {
    -            size_t const lastBlockSize1 = job->srcSize & (chunkSize-1);
    -            size_t const lastBlockSize = ((lastBlockSize1==0) & (job->srcSize>=chunkSize)) ? chunkSize : lastBlockSize1;
    +            size_t const lastBlockSize1 = job->src.size & (chunkSize-1);
    +            size_t const lastBlockSize = ((lastBlockSize1==0) & (job->src.size>=chunkSize)) ? chunkSize : lastBlockSize1;
                 size_t const cSize = (job->lastJob) ?
                      ZSTD_compressEnd     (cctx, op, oend-op, ip, lastBlockSize) :
                      ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize);
    @@ -421,13 +425,14 @@ void ZSTDMT_compressionJob(void* jobDescription)
         }   }
     
     _endJob:
    +    if (job->prefix.size > 0)
    +        DEBUGLOG(5, "Finished with prefix: %zx", (size_t)job->prefix.start);
    +    DEBUGLOG(5, "Finished with source: %zx", (size_t)job->src.start);
         /* release resources */
         ZSTDMT_releaseCCtx(job->cctxPool, cctx);
    -    ZSTDMT_releaseBuffer(job->bufPool, job->srcBuff);
    -    job->srcBuff = g_nullBuffer; job->prefixStart = NULL;
         /* report */
         ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex);
    -    job->consumed = job->srcSize;
    +    job->consumed = job->src.size;
         ZSTD_pthread_cond_signal(&job->job_cond);
         ZSTD_pthread_mutex_unlock(&job->job_mutex);
     }
    @@ -438,12 +443,27 @@ _endJob:
     /* ------------------------------------------ */
     
     typedef struct {
    +    range_t prefix;         /* read-only non-owned prefix buffer */
         buffer_t buffer;
    -    size_t targetCapacity;  /* note : buffers provided by the pool may be larger than target capacity */
    -    size_t prefixSize;
         size_t filled;
     } inBuff_t;
     
    +typedef struct {
    +  BYTE* buffer;     /* The round input buffer. All jobs get references
    +                     * to pieces of the buffer. ZSTDMT_tryGetInputRange()
    +                     * handles handing out job input buffers, and makes
    +                     * sure it doesn't overlap with any pieces still in use.
    +                     */
    +  size_t capacity;  /* The capacity of buffer. */
    +  size_t pos;       /* The position of the current inBuff in the round
    +                     * buffer. Updated past the end if the inBuff once
    +                     * the inBuff is sent to the worker thread.
    +                     * pos <= capacity.
    +                     */
    +} roundBuff_t;
    +
    +static const roundBuff_t kNullRoundBuff = {NULL, 0, 0};
    +
     struct ZSTDMT_CCtx_s {
         POOL_ctx* factory;
         ZSTDMT_jobDescription* jobs;
    @@ -452,6 +472,7 @@ struct ZSTDMT_CCtx_s {
         ZSTD_CCtx_params params;
         size_t targetSectionSize;
         size_t targetPrefixSize;
    +    roundBuff_t roundBuff;
         inBuff_t inBuff;
         int jobReady;        /* 1 => one job is already prepared, but pool has shortage of workers. Don't create another one. */
         XXH64_state_t xxhState;
    @@ -538,6 +559,7 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem)
         mtctx->jobIDMask = nbJobs - 1;
         mtctx->bufPool = ZSTDMT_createBufferPool(nbWorkers, cMem);
         mtctx->cctxPool = ZSTDMT_createCCtxPool(nbWorkers, cMem);
    +    mtctx->roundBuff = kNullRoundBuff;
         if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool) {
             ZSTDMT_freeCCtx(mtctx);
             return NULL;
    @@ -563,14 +585,10 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx)
             ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);
             mtctx->jobs[jobID].dstBuff = g_nullBuffer;
             mtctx->jobs[jobID].cSize = 0;
    -        DEBUGLOG(4, "job%02u: release src address %08X", jobID, (U32)(size_t)mtctx->jobs[jobID].srcBuff.start);
    -        ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].srcBuff);
    -        mtctx->jobs[jobID].srcBuff = g_nullBuffer;
         }
         memset(mtctx->jobs, 0, (mtctx->jobIDMask+1)*sizeof(ZSTDMT_jobDescription));
    -    DEBUGLOG(4, "input: release address %08X", (U32)(size_t)mtctx->inBuff.buffer.start);
    -    ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->inBuff.buffer);
         mtctx->inBuff.buffer = g_nullBuffer;
    +    mtctx->inBuff.filled = 0;
         mtctx->allJobsCompleted = 1;
     }
     
    @@ -580,7 +598,7 @@ static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* mtctx)
         while (mtctx->doneJobID < mtctx->nextJobID) {
             unsigned const jobID = mtctx->doneJobID & mtctx->jobIDMask;
             ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[jobID].job_mutex);
    -        while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].srcSize) {
    +        while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].src.size) {
                 DEBUGLOG(5, "waiting for jobCompleted signal from job %u", mtctx->doneJobID);   /* we want to block when waiting for data to flush */
                 ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex);
             }
    @@ -598,6 +616,8 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx)
         ZSTDMT_freeBufferPool(mtctx->bufPool);
         ZSTDMT_freeCCtxPool(mtctx->cctxPool);
         ZSTD_freeCDict(mtctx->cdictLocal);
    +    if (mtctx->roundBuff.buffer)
    +        ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem);
         ZSTD_free(mtctx, mtctx->cMem);
         return 0;
     }
    @@ -610,7 +630,8 @@ size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx)
                 + ZSTDMT_sizeof_bufferPool(mtctx->bufPool)
                 + (mtctx->jobIDMask+1) * sizeof(ZSTDMT_jobDescription)
                 + ZSTDMT_sizeof_CCtxPool(mtctx->cctxPool)
    -            + ZSTD_sizeof_CDict(mtctx->cdictLocal);
    +            + ZSTD_sizeof_CDict(mtctx->cdictLocal)
    +            + mtctx->roundBuff.capacity;
     }
     
     /* Internal only */
    @@ -701,8 +722,7 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
         DEBUGLOG(6, "ZSTDMT_getFrameProgression");
         fps.consumed = mtctx->consumed;
         fps.produced = mtctx->produced;
    -    assert(mtctx->inBuff.filled >= mtctx->inBuff.prefixSize);
    -    fps.ingested = mtctx->consumed + (mtctx->inBuff.filled - mtctx->inBuff.prefixSize);
    +    fps.ingested = mtctx->consumed + mtctx->inBuff.filled;
         {   unsigned jobNb;
             unsigned lastJobNb = mtctx->nextJobID + mtctx->jobReady; assert(mtctx->jobReady <= 1);
             DEBUGLOG(6, "ZSTDMT_getFrameProgression: jobs: from %u to <%u (jobReady:%u)",
    @@ -713,7 +733,7 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
                 {   size_t const cResult = mtctx->jobs[wJobID].cSize;
                     size_t const produced = ZSTD_isError(cResult) ? 0 : cResult;
                     fps.consumed += mtctx->jobs[wJobID].consumed;
    -                fps.ingested += mtctx->jobs[wJobID].srcSize;
    +                fps.ingested += mtctx->jobs[wJobID].src.size;
                     fps.produced += produced;
                 }
                 ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
    @@ -795,10 +815,10 @@ static size_t ZSTDMT_compress_advanced_internal(
                 buffer_t const dstBuffer = u < compressWithinDst ? dstAsBuffer : g_nullBuffer;
                 size_t dictSize = u ? overlapSize : 0;
     
    -            mtctx->jobs[u].srcBuff = g_nullBuffer;
    -            mtctx->jobs[u].prefixStart = srcStart + frameStartPos - dictSize;
    -            mtctx->jobs[u].prefixSize = dictSize;
    -            mtctx->jobs[u].srcSize = jobSize; assert(jobSize > 0);  /* avoid job.srcSize == 0 */
    +            mtctx->jobs[u].prefix.start = srcStart + frameStartPos - dictSize;
    +            mtctx->jobs[u].prefix.size = dictSize;
    +            mtctx->jobs[u].src.start = srcStart + frameStartPos;
    +            mtctx->jobs[u].src.size = jobSize; assert(jobSize > 0);  /* avoid job.src.size == 0 */
                 mtctx->jobs[u].consumed = 0;
                 mtctx->jobs[u].cSize = 0;
                 mtctx->jobs[u].cdict = (u==0) ? cdict : NULL;
    @@ -817,7 +837,7 @@ static size_t ZSTDMT_compress_advanced_internal(
                 }
     
                 DEBUGLOG(5, "ZSTDMT_compress_advanced_internal: posting job %u  (%u bytes)", u, (U32)jobSize);
    -            DEBUG_PRINTHEX(6, mtctx->jobs[u].prefixStart, 12);
    +            DEBUG_PRINTHEX(6, mtctx->jobs[u].prefix.start, 12);
                 POOL_add(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[u]);
     
                 frameStartPos += jobSize;
    @@ -831,14 +851,13 @@ static size_t ZSTDMT_compress_advanced_internal(
             for (jobID=0; jobIDjobs[jobID].job_mutex);
    -            while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].srcSize) {
    +            while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].src.size) {
                     DEBUGLOG(5, "waiting for jobCompleted signal from job %u", jobID);
                     ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex);
                 }
                 ZSTD_pthread_mutex_unlock(&mtctx->jobs[jobID].job_mutex);
                 DEBUGLOG(5, "ready to write job %u ", jobID);
     
    -            mtctx->jobs[jobID].prefixStart = NULL;
                 {   size_t const cSize = mtctx->jobs[jobID].cSize;
                     if (ZSTD_isError(cSize)) error = cSize;
                     if ((!error) && (dstPos + cSize > dstCapacity)) error = ERROR(dstSize_tooSmall);
    @@ -967,11 +986,30 @@ size_t ZSTDMT_initCStream_internal(
         if (mtctx->targetSectionSize < ZSTDMT_JOBSIZE_MIN) mtctx->targetSectionSize = ZSTDMT_JOBSIZE_MIN;
         if (mtctx->targetSectionSize < mtctx->targetPrefixSize) mtctx->targetSectionSize = mtctx->targetPrefixSize;  /* job size must be >= overlap size */
         DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(mtctx->targetSectionSize>>10), params.jobSize);
    -    mtctx->inBuff.targetCapacity = mtctx->targetPrefixSize + mtctx->targetSectionSize;
    -    DEBUGLOG(4, "inBuff Size : %u KB", (U32)(mtctx->inBuff.targetCapacity>>10));
    -    ZSTDMT_setBufferSize(mtctx->bufPool, MAX(mtctx->inBuff.targetCapacity, ZSTD_compressBound(mtctx->targetSectionSize)) );
    +    DEBUGLOG(4, "inBuff Size : %u KB", (U32)(mtctx->targetSectionSize>>10));
    +    ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(mtctx->targetSectionSize));
    +    {
    +        /* Two buffers of slack, plus extra space for the overlap */
    +        size_t const nbWorkers = MAX(mtctx->params.nbWorkers, 1);
    +        size_t const nbSlackBuffers = MIN(nbWorkers, 2) + (mtctx->targetPrefixSize > 0);
    +        size_t const nbSections = nbWorkers + nbSlackBuffers;
    +        size_t const capacity = mtctx->targetSectionSize * nbSections;
    +        if (mtctx->roundBuff.capacity < capacity) {
    +            if (mtctx->roundBuff.buffer)
    +                ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem);
    +            mtctx->roundBuff.buffer = (BYTE*)ZSTD_malloc(capacity, mtctx->cMem);
    +            if (mtctx->roundBuff.buffer == NULL) {
    +                mtctx->roundBuff.capacity = 0;
    +                return ERROR(memory_allocation);
    +            }
    +            mtctx->roundBuff.capacity = capacity;
    +        }
    +    }
    +    DEBUGLOG(4, "roundBuff capacity : %u KB", (U32)(mtctx->roundBuff.capacity>>10));
    +    mtctx->roundBuff.pos = 0;
         mtctx->inBuff.buffer = g_nullBuffer;
    -    mtctx->inBuff.prefixSize = 0;
    +    mtctx->inBuff.filled = 0;
    +    mtctx->inBuff.prefix = kNullRange;
         mtctx->doneJobID = 0;
         mtctx->nextJobID = 0;
         mtctx->frameEnded = 0;
    @@ -1038,17 +1076,16 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel) {
     static void ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job)
     {
         assert(job->lastJob == 1);
    -    assert(job->srcSize == 0);    /* last job is empty -> will be simplified into a last empty block */
    +    assert(job->src.size == 0);   /* last job is empty -> will be simplified into a last empty block */
         assert(job->firstJob == 0);   /* cannot be first job, as it also needs to create frame header */
    -    /* A job created by streaming variant starts with a src buffer, but no dst buffer.
    -     * It summons a dstBuffer itself, compresses into it, then releases srcBuffer, and gives result to mtctx.
    -     * When done, srcBuffer is empty, while dstBuffer is filled, and will be released by mtctx.
    -     * This shortcut will simply switch srcBuffer for dstBuffer, providing same outcome as a normal job */
         assert(job->dstBuff.start == NULL);   /* invoked from streaming variant only (otherwise, dstBuff might be user's output) */
    -    assert(job->srcBuff.start != NULL);   /* invoked from streaming variant only (otherwise, srcBuff might be user's input) */
    -    assert(job->srcBuff.capacity >= ZSTD_blockHeaderSize);   /* no buffer should ever be that small */
    -    job->dstBuff = job->srcBuff;
    -    job->srcBuff = g_nullBuffer;
    +    job->dstBuff = ZSTDMT_getBuffer(job->bufPool);
    +    if (job->dstBuff.start == NULL) {
    +      job->cSize = ERROR(memory_allocation);
    +      return;
    +    }
    +    assert(job->dstBuff.capacity >= ZSTD_blockHeaderSize);   /* no buffer should ever be that small */
    +    job->src = kNullRange;
         job->cSize = ZSTD_writeLastEmptyBlock(job->dstBuff.start, job->dstBuff.capacity);
         assert(!ZSTD_isError(job->cSize));
         assert(job->consumed == 0);
    @@ -1066,14 +1103,13 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
         }
     
         if (!mtctx->jobReady) {
    +        BYTE const* src = (BYTE const*)mtctx->inBuff.buffer.start;
             DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
    -                    mtctx->nextJobID, (U32)srcSize, (U32)mtctx->inBuff.prefixSize);
    -        assert(mtctx->jobs[jobID].srcBuff.start == NULL);   /* no buffer left : supposed already released */
    -        mtctx->jobs[jobID].srcBuff = mtctx->inBuff.buffer;
    -        mtctx->jobs[jobID].prefixStart = mtctx->inBuff.buffer.start;
    -        mtctx->jobs[jobID].prefixSize = mtctx->inBuff.prefixSize;
    -        mtctx->jobs[jobID].srcSize = srcSize;
    -        assert(mtctx->inBuff.filled >= srcSize + mtctx->inBuff.prefixSize);
    +                    mtctx->nextJobID, (U32)srcSize, (U32)mtctx->inBuff.prefix.size);
    +        mtctx->jobs[jobID].src.start = src;
    +        mtctx->jobs[jobID].src.size = srcSize;
    +        assert(mtctx->inBuff.filled >= srcSize);
    +        mtctx->jobs[jobID].prefix = mtctx->inBuff.prefix;
             mtctx->jobs[jobID].consumed = 0;
             mtctx->jobs[jobID].cSize = 0;
             mtctx->jobs[jobID].params = mtctx->params;
    @@ -1089,29 +1125,20 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
             mtctx->jobs[jobID].frameChecksumNeeded = endFrame && (mtctx->nextJobID>0) && mtctx->params.fParams.checksumFlag;
             mtctx->jobs[jobID].dstFlushed = 0;
     
    -        if (mtctx->params.fParams.checksumFlag)
    -            XXH64_update(&mtctx->xxhState, (const char*)mtctx->inBuff.buffer.start + mtctx->inBuff.prefixSize, srcSize);
    +        if (mtctx->params.fParams.checksumFlag && srcSize > 0)
    +            XXH64_update(&mtctx->xxhState, src, srcSize);
     
    -        /* get a new buffer for next input */
    +        /* Update the round buffer pos and clear the input buffer to be reset */
    +        mtctx->roundBuff.pos += srcSize;
    +        mtctx->inBuff.buffer = g_nullBuffer;
    +        mtctx->inBuff.filled = 0;
    +        /* Set the prefix */
             if (!endFrame) {
    -            size_t const newPrefixSize = MIN(mtctx->inBuff.filled, mtctx->targetPrefixSize);
    -            mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool);
    -            if (mtctx->inBuff.buffer.start == NULL) {    /* not enough memory to allocate a new input buffer */
    -                mtctx->jobs[jobID].srcSize = mtctx->jobs[jobID].consumed = 0;
    -                mtctx->nextJobID++;
    -                ZSTDMT_waitForAllJobsCompleted(mtctx);
    -                ZSTDMT_releaseAllJobResources(mtctx);
    -                return ERROR(memory_allocation);
    -            }
    -            mtctx->inBuff.filled -= (mtctx->inBuff.prefixSize + srcSize) - newPrefixSize;
    -            memmove(mtctx->inBuff.buffer.start,   /* copy end of current job into next job, as "prefix" */
    -                (const char*)mtctx->jobs[jobID].prefixStart + mtctx->inBuff.prefixSize + srcSize - newPrefixSize,
    -                mtctx->inBuff.filled);
    -            mtctx->inBuff.prefixSize = newPrefixSize;
    +            size_t const newPrefixSize = MIN(srcSize, mtctx->targetPrefixSize);
    +            mtctx->inBuff.prefix.start = src + srcSize - newPrefixSize;
    +            mtctx->inBuff.prefix.size = newPrefixSize;
             } else {   /* endFrame==1 => no need for another input buffer */
    -            mtctx->inBuff.buffer = g_nullBuffer;
    -            mtctx->inBuff.filled = 0;
    -            mtctx->inBuff.prefixSize = 0;
    +            mtctx->inBuff.prefix = kNullRange;
                 mtctx->frameEnded = endFrame;
                 if (mtctx->nextJobID == 0) {
                     /* single job exception : checksum is already calculated directly within worker thread */
    @@ -1130,7 +1157,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
     
         DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes  (end:%u, jobNb == %u (mod:%u))",
                     mtctx->nextJobID,
    -                (U32)mtctx->jobs[jobID].srcSize,
    +                (U32)mtctx->jobs[jobID].src.size,
                     mtctx->jobs[jobID].lastJob,
                     mtctx->nextJobID,
                     jobID);
    @@ -1161,9 +1188,9 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
           && (mtctx->doneJobID < mtctx->nextJobID) ) {
             assert(mtctx->jobs[wJobID].dstFlushed <= mtctx->jobs[wJobID].cSize);
             while (mtctx->jobs[wJobID].dstFlushed == mtctx->jobs[wJobID].cSize) {  /* nothing to flush */
    -            if (mtctx->jobs[wJobID].consumed == mtctx->jobs[wJobID].srcSize) {
    +            if (mtctx->jobs[wJobID].consumed == mtctx->jobs[wJobID].src.size) {
                     DEBUGLOG(5, "job %u is completely consumed (%u == %u) => don't wait for cond, there will be none",
    -                            mtctx->doneJobID, (U32)mtctx->jobs[wJobID].consumed, (U32)mtctx->jobs[wJobID].srcSize);
    +                            mtctx->doneJobID, (U32)mtctx->jobs[wJobID].consumed, (U32)mtctx->jobs[wJobID].src.size);
                     break;
                 }
                 DEBUGLOG(5, "waiting for something to flush from job %u (currently flushed: %u bytes)",
    @@ -1174,7 +1201,7 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
         /* try to flush something */
         {   size_t cSize = mtctx->jobs[wJobID].cSize;                  /* shared */
             size_t const srcConsumed = mtctx->jobs[wJobID].consumed;   /* shared */
    -        size_t const srcSize = mtctx->jobs[wJobID].srcSize;        /* read-only, could be done after mutex lock, but no-declaration-after-statement */
    +        size_t const srcSize = mtctx->jobs[wJobID].src.size;        /* read-only, could be done after mutex lock, but no-declaration-after-statement */
             ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
             if (ZSTD_isError(cSize)) {
                 DEBUGLOG(5, "ZSTDMT_flushProduced: job %u : compression error detected : %s",
    @@ -1211,7 +1238,6 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
                   && (mtctx->jobs[wJobID].dstFlushed == cSize) ) {   /* output buffer fully flushed => free this job position */
                     DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one",
                             mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed);
    -                assert(mtctx->jobs[wJobID].srcBuff.start == NULL);  /* srcBuff supposed already released */
                     ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[wJobID].dstBuff);
                     mtctx->jobs[wJobID].dstBuff = g_nullBuffer;
                     mtctx->jobs[wJobID].cSize = 0;   /* ensure this job slot is considered "not started" in future check */
    @@ -1232,6 +1258,109 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
         return 0;   /* internal buffers fully flushed */
     }
     
    +/**
    + * Returns the range of data used by the earliest job that is not yet complete.
    + * If the data of the first job is broken up into two segments, we cover both
    + * sections.
    + */
    +static range_t ZSTDMT_getInputDataInUse(ZSTDMT_CCtx* mtctx)
    +{
    +    unsigned const firstJobID = mtctx->doneJobID;
    +    unsigned const lastJobID = mtctx->nextJobID;
    +    unsigned jobID;
    +
    +    for (jobID = firstJobID; jobID < lastJobID; ++jobID) {
    +        unsigned const wJobID = jobID & mtctx->jobIDMask;
    +        size_t consumed;
    +
    +        ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[wJobID].job_mutex);
    +        consumed = mtctx->jobs[wJobID].consumed;
    +        ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
    +
    +        if (consumed < mtctx->jobs[wJobID].src.size) {
    +            range_t range = mtctx->jobs[wJobID].prefix;
    +            if (range.size == 0) {
    +                /* Empty prefix */
    +                range = mtctx->jobs[wJobID].src;
    +            }
    +            /* Job source in multiple segments not supported yet */
    +            assert(range.start <= mtctx->jobs[wJobID].src.start);
    +            return range;
    +        }
    +    }
    +    return kNullRange;
    +}
    +
    +/**
    + * Returns non-zero iff buffer and range overlap.
    + */
    +static int ZSTDMT_isOverlapped(buffer_t buffer, range_t range)
    +{
    +    BYTE const* const bufferStart = (BYTE const*)buffer.start;
    +    BYTE const* const bufferEnd = bufferStart + buffer.capacity;
    +    BYTE const* const rangeStart = (BYTE const*)range.start;
    +    BYTE const* const rangeEnd = rangeStart + range.size;
    +
    +    if (rangeStart == NULL || bufferStart == NULL)
    +        return 0;
    +
    +    return bufferStart < rangeEnd && rangeStart < bufferEnd;
    +}
    +
    +/**
    + * Attempts to set the inBuff to the next section to fill.
    + * If any part of the new section is still in use we give up.
    + * Returns non-zero if the buffer is filled.
    + */
    +static int ZSTDMT_tryGetInputRange(ZSTDMT_CCtx* mtctx)
    +{
    +    range_t const inUse = ZSTDMT_getInputDataInUse(mtctx);
    +    size_t const spaceLeft = mtctx->roundBuff.capacity - mtctx->roundBuff.pos;
    +    size_t const target = mtctx->targetSectionSize;
    +    buffer_t buffer;
    +
    +    assert(mtctx->inBuff.buffer.start == NULL);
    +    assert(mtctx->roundBuff.capacity >= target);
    +
    +    if (spaceLeft < target) {
    +        /* ZSTD_invalidateRepCodes() doesn't work for extDict variants.
    +         * Simply copy the prefix to the beginning in that case.
    +         */
    +        BYTE* const start = (BYTE*)mtctx->roundBuff.buffer;
    +        size_t const prefixSize = mtctx->inBuff.prefix.size;
    +
    +        buffer.start = start;
    +        buffer.capacity = prefixSize;
    +        if (ZSTDMT_isOverlapped(buffer, inUse)) {
    +            DEBUGLOG(6, "Waiting for buffer...");
    +            return 0;
    +        }
    +        memmove(start, mtctx->inBuff.prefix.start, prefixSize);
    +        mtctx->inBuff.prefix.start = start;
    +        mtctx->roundBuff.pos = prefixSize;
    +    }
    +    buffer.start = mtctx->roundBuff.buffer + mtctx->roundBuff.pos;
    +    buffer.capacity = target;
    +
    +    if (ZSTDMT_isOverlapped(buffer, inUse)) {
    +        DEBUGLOG(6, "Waiting for buffer...");
    +        return 0;
    +    }
    +    assert(!ZSTDMT_isOverlapped(buffer, mtctx->inBuff.prefix));
    +    DEBUGLOG(5, "Using prefix range [%zx, %zx)",
    +                (size_t)mtctx->inBuff.prefix.start,
    +                (size_t)mtctx->inBuff.prefix.start + mtctx->inBuff.prefix.size);
    +    DEBUGLOG(5, "Using source range [%zx, %zx)",
    +                (size_t)buffer.start,
    +                (size_t)buffer.start + buffer.capacity);
    +
    +
    +    mtctx->inBuff.buffer = buffer;
    +    mtctx->inBuff.filled = 0;
    +    assert(mtctx->roundBuff.pos + buffer.capacity <= mtctx->roundBuff.capacity);
    +    return 1;
    +}
    +
     
     /** ZSTDMT_compressStream_generic() :
      *  internal use only - exposed to be invoked from zstd_compress.c
    @@ -1242,7 +1371,6 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
                                          ZSTD_inBuffer* input,
                                          ZSTD_EndDirective endOp)
     {
    -    size_t const newJobThreshold = mtctx->inBuff.prefixSize + mtctx->targetSectionSize;
         unsigned forwardInputProgress = 0;
         DEBUGLOG(5, "ZSTDMT_compressStream_generic (endOp=%u, srcSize=%u)",
                     (U32)endOp, (U32)(input->size - input->pos));
    @@ -1271,7 +1399,6 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
             if (ZSTD_isError(cSize)) return cSize;
             input->pos = input->size;
             output->pos += cSize;
    -        ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->inBuff.buffer);  /* was allocated in initStream */
             mtctx->allJobsCompleted = 1;
             mtctx->frameEnded = 1;
             return 0;
    @@ -1281,18 +1408,19 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
         if ( (!mtctx->jobReady)
           && (input->size > input->pos) ) {   /* support NULL input */
             if (mtctx->inBuff.buffer.start == NULL) {
    -            mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool);  /* note : allocation can fail, in which case, buffer.start==NULL */
    -            mtctx->inBuff.filled = 0;
    -            if ( (mtctx->inBuff.buffer.start == NULL)        /* allocation failure */
    -              && (mtctx->doneJobID == mtctx->nextJobID) ) {  /* and nothing to flush */
    -                return ERROR(memory_allocation);    /* no forward progress possible => output an error */
    +            assert(mtctx->inBuff.filled == 0); /* Can't fill an empty buffer */
    +            if (!ZSTDMT_tryGetInputRange(mtctx)) {
    +                /* It is only possible for this operation to fail if there are
    +                 * still compression jobs ongoing.
    +                 */
    +                assert(mtctx->doneJobID != mtctx->nextJobID);
                 }
    -            assert(mtctx->inBuff.buffer.capacity >= mtctx->inBuff.targetCapacity);  /* pool must provide a buffer >= targetCapacity */
             }
    -        if (mtctx->inBuff.buffer.start != NULL) {   /* no buffer for input, but it's possible to flush, and then reclaim the buffer */
    -            size_t const toLoad = MIN(input->size - input->pos, mtctx->inBuff.targetCapacity - mtctx->inBuff.filled);
    +        if (mtctx->inBuff.buffer.start != NULL) {
    +            size_t const toLoad = MIN(input->size - input->pos, mtctx->targetSectionSize - mtctx->inBuff.filled);
    +            assert(mtctx->inBuff.buffer.capacity >= mtctx->targetSectionSize);
                 DEBUGLOG(5, "ZSTDMT_compressStream_generic: adding %u bytes on top of %u to buffer of size %u",
    -                        (U32)toLoad, (U32)mtctx->inBuff.filled, (U32)mtctx->inBuff.targetCapacity);
    +                        (U32)toLoad, (U32)mtctx->inBuff.filled, (U32)mtctx->targetSectionSize);
                 memcpy((char*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, (const char*)input->src + input->pos, toLoad);
                 input->pos += toLoad;
                 mtctx->inBuff.filled += toLoad;
    @@ -1303,10 +1431,11 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
         }
     
         if ( (mtctx->jobReady)
    -      || (mtctx->inBuff.filled >= newJobThreshold)  /* filled enough : let's compress */
    +      || (mtctx->inBuff.filled >= mtctx->targetSectionSize)  /* filled enough : let's compress */
           || ((endOp != ZSTD_e_continue) && (mtctx->inBuff.filled > 0))  /* something to flush : let's go */
           || ((endOp == ZSTD_e_end) && (!mtctx->frameEnded)) ) {   /* must finish the frame with a zero-size block */
    -        size_t const jobSize = MIN(mtctx->inBuff.filled - mtctx->inBuff.prefixSize, mtctx->targetSectionSize);
    +        size_t const jobSize = mtctx->inBuff.filled;
    +        assert(mtctx->inBuff.filled <= mtctx->targetSectionSize);
             CHECK_F( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp) );
         }
     
    @@ -1323,13 +1452,13 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_in
         CHECK_F( ZSTDMT_compressStream_generic(mtctx, output, input, ZSTD_e_continue) );
     
         /* recommended next input size : fill current input buffer */
    -    return mtctx->inBuff.targetCapacity - mtctx->inBuff.filled;   /* note : could be zero when input buffer is fully filled and no more availability to create new job */
    +    return mtctx->targetSectionSize - mtctx->inBuff.filled;   /* note : could be zero when input buffer is fully filled and no more availability to create new job */
     }
     
     
     static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_EndDirective endFrame)
     {
    -    size_t const srcSize = mtctx->inBuff.filled - mtctx->inBuff.prefixSize;
    +    size_t const srcSize = mtctx->inBuff.filled;
         DEBUGLOG(5, "ZSTDMT_flushStream_internal");
     
         if ( mtctx->jobReady     /* one job ready for a worker to pick up */
    diff --git a/tests/playTests.sh b/tests/playTests.sh
    index c170ba9f4..1847f57d7 100755
    --- a/tests/playTests.sh
    +++ b/tests/playTests.sh
    @@ -213,8 +213,8 @@ roundTripTest -g512K
     roundTripTest -g512K " --zstd=slen=3,tlen=48,strat=6"
     roundTripTest -g512K " --zstd=strat=6,wlog=23,clog=23,hlog=22,slog=6"
     roundTripTest -g512K " --zstd=windowLog=23,chainLog=23,hashLog=22,searchLog=6,searchLength=3,targetLength=48,strategy=6"
    -roundTripTest -g512K " --long --zstd=ldmHashLog=20,ldmSearchLength=64,ldmBucketSizeLog=1,ldmHashEveryLog=7"
    -roundTripTest -g512K " --long --zstd=ldmhlog=20,ldmslen=64,ldmblog=1,ldmhevery=7"
    +roundTripTest -g512K " --single-thread --long --zstd=ldmHashLog=20,ldmSearchLength=64,ldmBucketSizeLog=1,ldmHashEveryLog=7"
    +roundTripTest -g512K " --single-thread --long --zstd=ldmhlog=20,ldmslen=64,ldmblog=1,ldmhevery=7"
     roundTripTest -g512K 19
     
     
    @@ -630,12 +630,12 @@ roundTripTest -g516K 19   # btopt
     fileRoundTripTest -g500K
     
     $ECHO "\n===>  zstd long distance matching round-trip tests "
    -roundTripTest -g0 "2 --long"
    -roundTripTest -g1000K "1 --long"
    -roundTripTest -g517K "6 --long"
    -roundTripTest -g516K "16 --long"
    -roundTripTest -g518K "19 --long"
    -fileRoundTripTest -g5M "3 --long"
    +roundTripTest -g0 "2 --single-thread --long"
    +roundTripTest -g1000K "1 --single-thread --long"
    +roundTripTest -g517K "6 --single-thread --long"
    +roundTripTest -g516K "16 --single-thread --long"
    +roundTripTest -g518K "19 --single-thread --long"
    +fileRoundTripTest -g5M "3 --single-thread --long"
     
     
     roundTripTest -g96K "5 --single-thread"
    @@ -648,7 +648,7 @@ then
         fileRoundTripTest -g4M "19 -T2 -B1M"
     
         $ECHO "\n===>  zstdmt long distance matching round-trip tests "
    -    roundTripTest -g8M "3 --long -T2"
    +    roundTripTest -g8M "3 --long=24 -T2"
     else
         $ECHO "\n===>  no multithreading, skipping zstdmt tests "
     fi
    @@ -699,13 +699,13 @@ rm tmp*
     
     
     $ECHO "\n===>   zstd long distance matching tests "
    -roundTripTest -g0 " --long"
    -roundTripTest -g9M "2 --long"
    +roundTripTest -g0 " --single-thread --long"
    +roundTripTest -g9M "2 --single-thread --long"
     # Test parameter parsing
    -roundTripTest -g1M -P50 "1 --long=29" " --memory=512MB"
    -roundTripTest -g1M -P50 "1 --long=29 --zstd=wlog=28" " --memory=256MB"
    -roundTripTest -g1M -P50 "1 --long=29" " --long=28 --memory=512MB"
    -roundTripTest -g1M -P50 "1 --long=29" " --zstd=wlog=28 --memory=512MB"
    +roundTripTest -g1M -P50 "1 --single-thread --long=29" " --memory=512MB"
    +roundTripTest -g1M -P50 "1 --single-thread --long=29 --zstd=wlog=28" " --memory=256MB"
    +roundTripTest -g1M -P50 "1 --single-thread --long=29" " --long=28 --memory=512MB"
    +roundTripTest -g1M -P50 "1 --single-thread --long=29" " --zstd=wlog=28 --memory=512MB"
     
     
     if [ "$1" != "--test-large-data" ]; then
    @@ -746,13 +746,13 @@ fileRoundTripTest -g4193M -P99 1
     
     
     $ECHO "\n===>   zstd long, long distance matching round-trip tests "
    -roundTripTest -g270000000 "1 --long"
    -roundTripTest -g130000000 -P60 "5 --long"
    -roundTripTest -g35000000 -P70 "8 --long"
    -roundTripTest -g18000001 -P80  "18 --long"
    +roundTripTest -g270000000 "1 --single-thread --long"
    +roundTripTest -g130000000 -P60 "5 --single-thread --long"
    +roundTripTest -g35000000 -P70 "8 --single-thread --long"
    +roundTripTest -g18000001 -P80  "18 --single-thread --long"
     # Test large window logs
    -roundTripTest -g700M -P50 "1 --long=29"
    -roundTripTest -g600M -P50 "1 --long --zstd=wlog=29,clog=28"
    +roundTripTest -g700M -P50 "1 --single-thread --long=29"
    +roundTripTest -g600M -P50 "1 --single-thread --long --zstd=wlog=29,clog=28"
     
     
     if [ -n "$hasMT" ]
    
    From 38cbcb5f1a0b50d35edaa90acebb26fa64ab807c Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 15 Mar 2018 16:26:08 -0700
    Subject: [PATCH 189/255] removed LRM exploratory experiment
    
    ---
     contrib/long_distance_matching/Makefile     |  36 -
     contrib/long_distance_matching/README.md    | 102 ---
     contrib/long_distance_matching/ldm.c        | 856 --------------------
     contrib/long_distance_matching/ldm.h        | 197 -----
     contrib/long_distance_matching/ldm_common.c | 109 ---
     contrib/long_distance_matching/ldm_params.h |  12 -
     contrib/long_distance_matching/main.c       | 269 ------
     7 files changed, 1581 deletions(-)
     delete mode 100644 contrib/long_distance_matching/Makefile
     delete mode 100644 contrib/long_distance_matching/README.md
     delete mode 100644 contrib/long_distance_matching/ldm.c
     delete mode 100644 contrib/long_distance_matching/ldm.h
     delete mode 100644 contrib/long_distance_matching/ldm_common.c
     delete mode 100644 contrib/long_distance_matching/ldm_params.h
     delete mode 100644 contrib/long_distance_matching/main.c
    
    diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile
    deleted file mode 100644
    index 6ed1fab45..000000000
    --- a/contrib/long_distance_matching/Makefile
    +++ /dev/null
    @@ -1,36 +0,0 @@
    -# ################################################################
    -# Copyright (c) 2017-present, 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).
    -# ################################################################
    -
    -# This Makefile presumes libzstd is installed, using `sudo make install`
    -
    -CPPFLAGS+= -I../../lib/common
    -CFLAGS  ?= -O3
    -DEBUGFLAGS = -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \
    -            -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \
    -            -Wstrict-prototypes -Wundef -Wpointer-arith -Wformat-security \
    -            -Wvla -Wformat=2 -Winit-self -Wfloat-equal -Wwrite-strings \
    -            -Wredundant-decls
    -CFLAGS  += $(DEBUGFLAGS) $(MOREFLAGS)
    -FLAGS    = $(CPPFLAGS) $(CFLAGS)
    -
    -LDFLAGS += -lzstd
    -
    -.PHONY: default all clean
    -
    -default: all
    -
    -all: ldm
    -
    -ldm: ldm_common.c ldm.c main.c
    -	$(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@
    -
    -clean:
    -	@rm -f core *.o tmp* result* *.ldm *.ldm.dec \
    -	ldm
    -	@echo Cleaning completed
    diff --git a/contrib/long_distance_matching/README.md b/contrib/long_distance_matching/README.md
    deleted file mode 100644
    index 771a6c3ce..000000000
    --- a/contrib/long_distance_matching/README.md
    +++ /dev/null
    @@ -1,102 +0,0 @@
    -This is a compression algorithm focused on finding long distance matches.
    -
    -It is based upon lz4 and uses nearly the same block format (github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md). The number of bytes to encode the offset is four instead of two in lz4 to reflect the longer distance matching. The block format is described in `ldm.h`.
    -
    -### Build
    -
    -Run `make`.
    -
    -### Compressing a file
    -
    -`ldm `
    -
    -Decompression and verification can be enabled by defining `DECOMPRESS_AND_VERIFY` in `main.c`.
    -The output file names are as follows:
    -- `.ldm` : compressed file
    -- `.ldm.dec` : decompressed file
    -
    -### Parameters
    -
    -There are various parameters that can be tuned. These parameters can be tuned in `ldm.h` or, alternatively if `ldm_params.h` is included, in `ldm_params.h` (for easier configuration).
    -
    -The parameters are as follows and must all be defined:
    -- `LDM_MEMORY_USAGE` : the memory usage of the underlying hash table in bytes.
    -- `HASH_BUCKET_SIZE_LOG` : the log size of each bucket in the hash table (used in collision resolution).
    -- `LDM_LAG` : the lag (in bytes) in inserting entries into the hash table.
    -- `LDM_WINDOW_SIZE_LOG` : the log maximum window size when searching for matches.
    -- `LDM_MIN_MATCH_LENGTH` : the minimum match length.
    -- `INSERT_BY_TAG` : insert entries into the hash table as a function of the hash. This increases speed by reducing the number of hash table lookups and match comparisons. Certain hashes will never be inserted.
    -- `USE_CHECKSUM`  : store a checksum with the hash table entries for faster comparison. This halves the number of entries the hash table can contain.
    -
    -The optional parameter `HASH_ONLY_EVERY_LOG` is the log inverse frequency of insertion into the hash table. That is, an entry is inserted approximately every `1 << HASH_ONLY_EVERY_LOG` times. If this parameter is not defined, the value is computed as a function of the window size and memory usage to approximate an even coverage of the window.
    -
    -
    -### Benchmark
    -
    -Below is a comparison of various compression methods on a tar of four versions of llvm (versions `3.9.0`, `3.9.1`, `4.0.0`, `4.0.1`) with a total size of `727900160` B.
    -
    -| Method | Size | Ratio |
    -|:---|---:|---:|
    -|lrzip -p 32 -n -w 1 | `369968714` | `1.97`|
    -|ldm | `209391361` | `3.48`|
    -|lz4 | `189954338` | `3.83`|
    -|lrzip -p 32 -l -w 1 | `163940343` | `4.44`|
    -|zstd -1 | `126080293` | `5.77`|
    -|lrzip -p 32 -n | `124821009` | `5.83`|
    -|lrzip -p 32 -n -w 1 & zstd -1 | `120317909` | `6.05`|
    -|zstd -3 -o | `115290952` | `6.31`|
    -|lrzip -p 32 -g -L 9 -w 1 | `107168979` | `6.79`|
    -|zstd -6 -o | `102772098` | `7.08`|
    -|zstd -T16 -9 | `98040470` | `7.42`|
    -|lrzip -p 32 -n -w 1 & zstd -T32 -19 | `88050289` | `8.27`|
    -|zstd -T32 -19 | `83626098` | `8.70`|
    -|lrzip -p 32 -n & zstd -1 | `36335117` | `20.03`|
    -|ldm & zstd -6 | `32856232` | `22.15`|
    -|lrzip -p 32 -g -L 9 | `32243594` | `22.58`|
    -|lrzip -p 32 -n & zstd -6 | `30954572` | `23.52`|
    -|lrzip -p 32 -n & zstd -T32 -19 | `26472064` | `27.50`|
    -
    -The method marked `ldm` was run with the following parameters:
    -
    -| Parameter | Value |
    -|:---|---:|
    -| `LDM_MEMORY_USAGE`    |   `23`|
    -|`HASH_BUCKET_SIZE_LOG` |    `3`|
    -|`LDM_LAG`              |    `0`|
    -|`LDM_WINDOW_SIZE_LOG`  |   `28`|
    -|`LDM_MIN_MATCH_LENGTH`|   `64`|
    -|`INSERT_BY_TAG`        |    `1`|
    -|`USE_CHECKSUM`         |    `1`|
    -
    -The compression speed was `220.5 MB/s`.
    -
    -### Parameter selection
    -
    -Below is a brief discussion of the effects of the parameters on the speed and compression ratio.
    -
    -#### Speed
    -
    -A large bottleneck in terms of speed is finding the matches and comparing to see if they are greater than the minimum match length. Generally:
    -- The fewer matches found (or the lower the percentage of the literals matched), the slower the algorithm will behave.
    -- Increasing `HASH_ONLY_EVERY_LOG` results in fewer inserts and, if `INSERT_BY_TAG` is set, fewer lookups in the table. This has a large effect on speed, as well as compression ratio.
    -- If `HASH_ONLY_EVERY_LOG` is not set, its value is calculated based on `LDM_WINDOW_SIZE_LOG` and `LDM_MEMORY_USAGE`. Increasing `LDM_WINDOW_SIZE_LOG` has the effect of increasing `HASH_ONLY_EVERY_LOG` and increasing `LDM_MEMORY_USAGE` decreases `HASH_ONLY_EVERY_LOG`.
    -- `USE_CHECKSUM` generally improves speed with hash table lookups.
    -
    -#### Compression ratio
    -
    -The compression ratio is highly correlated with the coverage of matches. As a long distance matcher, the algorithm was designed to "optimize" for long distance matches outside the zstd compression window. The compression ratio after recompressing the output of the long-distance matcher with zstd was a more important signal in development than the raw compression ratio itself.
    -
    -Generally, increasing `LDM_MEMORY_USAGE` will improve the compression ratio. However when using the default computed value of `HASH_ONLY_EVERY_LOG`, this increases the frequency of insertion and lookup in the table and thus may result in a decrease in speed. 
    -
    -Below is a table showing the speed and compression ratio when compressing the llvm tar (as described above) using different settings for `LDM_MEMORY_USAGE`. The other parameters were the same as used in the benchmark above.
    -
    -| `LDM_MEMORY_USAGE` | Ratio | Speed (MB/s) | Ratio after zstd -6  |
    -|---:| ---: | ---: | ---: |
    -| `18` | `1.85` | `232.4` | `10.92` |
    -| `21` | `2.79` | `233.9` | `15.92` |
    -| `23` | `3.48` | `220.5` | `18.29` |
    -| `25` | `4.56` | `140.8` | `19.21` |
    -
    -### Compression statistics
    -
    -Compression statistics (and the configuration) can be enabled/disabled via `COMPUTE_STATS` and `OUTPUT_CONFIGURATION` in `ldm.h`.
    diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c
    deleted file mode 100644
    index 37b188b72..000000000
    --- a/contrib/long_distance_matching/ldm.c
    +++ /dev/null
    @@ -1,856 +0,0 @@
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -
    -#include "ldm.h"
    -
    -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE))
    -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2)
    -#define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3)
    -
    -#if USE_CHECKSUM
    -  #define LDM_HASH_ENTRY_SIZE_LOG 3
    -#else
    -  #define LDM_HASH_ENTRY_SIZE_LOG 2
    -#endif
    -
    -// Entries are inserted into the table HASH_ONLY_EVERY + 1 times "on average".
    -#ifndef HASH_ONLY_EVERY_LOG
    -  #define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)))
    -#endif
    -
    -#define HASH_ONLY_EVERY ((1 << (HASH_ONLY_EVERY_LOG)) - 1)
    -
    -#define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG))
    -#define NUM_HASH_BUCKETS_LOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG))
    -
    -#define HASH_CHAR_OFFSET 10
    -
    -// Take the first match in the hash bucket only.
    -//#define ZSTD_SKIP
    -
    -static const U64 prime8bytes = 11400714785074694791ULL;
    -
    -// Type of the small hash used to index into the hash table.
    -typedef U32 hash_t;
    -
    -#if USE_CHECKSUM
    -typedef struct LDM_hashEntry {
    -  U32 offset;
    -  U32 checksum;
    -} LDM_hashEntry;
    -#else
    -typedef struct LDM_hashEntry {
    -  U32 offset;
    -} LDM_hashEntry;
    -#endif
    -
    -struct LDM_compressStats {
    -  U32 windowSizeLog, hashTableSizeLog;
    -  U32 numMatches;
    -  U64 totalMatchLength;
    -  U64 totalLiteralLength;
    -  U64 totalOffset;
    -
    -  U32 matchLengthHistogram[32];
    -
    -  U32 minOffset, maxOffset;
    -  U32 offsetHistogram[32];
    -};
    -
    -typedef struct LDM_hashTable LDM_hashTable;
    -
    -struct LDM_CCtx {
    -  size_t isize;             /* Input size */
    -  size_t maxOSize;          /* Maximum output size */
    -
    -  const BYTE *ibase;        /* Base of input */
    -  const BYTE *ip;           /* Current input position */
    -  const BYTE *iend;         /* End of input */
    -
    -  // Maximum input position such that hashing at the position does not exceed
    -  // end of input.
    -  const BYTE *ihashLimit;
    -
    -  // Maximum input position such that finding a match of at least the minimum
    -  // match length does not exceed end of input.
    -  const BYTE *imatchLimit;
    -
    -  const BYTE *obase;        /* Base of output */
    -  BYTE *op;                 /* Output */
    -
    -  const BYTE *anchor;       /* Anchor to start of current (match) block */
    -
    -  LDM_compressStats stats;            /* Compression statistics */
    -
    -  LDM_hashTable *hashTable;
    -
    -  const BYTE *lastPosHashed;          /* Last position hashed */
    -  U64 lastHash;
    -
    -  const BYTE *nextIp;                 // TODO: this is redundant (ip + step)
    -  const BYTE *nextPosHashed;
    -  U64 nextHash;
    -
    -  unsigned step;                      // ip step, should be 1.
    -
    -  const BYTE *lagIp;
    -  U64 lagHash;
    -};
    -
    -struct LDM_hashTable {
    -  U32 numBuckets;          // The number of buckets.
    -  U32 numEntries;          // numBuckets * HASH_BUCKET_SIZE.
    -
    -  LDM_hashEntry *entries;
    -  BYTE *bucketOffsets;     // A pointer (per bucket) to the next insert position.
    -};
    -
    -static void HASH_destroyTable(LDM_hashTable *table) {
    -  free(table->entries);
    -  free(table->bucketOffsets);
    -  free(table);
    -}
    -
    -/**
    - * Create a hash table that can contain size elements.
    - * The number of buckets is determined by size >> HASH_BUCKET_SIZE_LOG.
    - *
    - * Returns NULL if table creation failed.
    - */
    -static LDM_hashTable *HASH_createTable(U32 size) {
    -  LDM_hashTable *table = malloc(sizeof(LDM_hashTable));
    -  if (!table) return NULL;
    -
    -  table->numBuckets = size >> HASH_BUCKET_SIZE_LOG;
    -  table->numEntries = size;
    -  table->entries = calloc(size, sizeof(LDM_hashEntry));
    -  table->bucketOffsets = calloc(size >> HASH_BUCKET_SIZE_LOG, sizeof(BYTE));
    -
    -  if (!table->entries || !table->bucketOffsets) {
    -    HASH_destroyTable(table);
    -    return NULL;
    -  }
    -
    -  return table;
    -}
    -
    -static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) {
    -  return table->entries + (hash << HASH_BUCKET_SIZE_LOG);
    -}
    -
    -static unsigned ZSTD_NbCommonBytes (size_t val) {
    -  if (MEM_isLittleEndian()) {
    -    if (MEM_64bits()) {
    -#    if defined(_MSC_VER) && defined(_WIN64)
    -      unsigned long r = 0;
    -      _BitScanForward64( &r, (U64)val );
    -      return (unsigned)(r>>3);
    -#     elif defined(__GNUC__) && (__GNUC__ >= 3)
    -      return (__builtin_ctzll((U64)val) >> 3);
    -#     else
    -      static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,
    -                                               0, 3, 1, 3, 1, 4, 2, 7,
    -                                               0, 2, 3, 6, 1, 5, 3, 5,
    -                                               1, 3, 4, 4, 2, 5, 6, 7,
    -                                               7, 0, 1, 2, 3, 3, 4, 6,
    -                                               2, 6, 5, 5, 3, 4, 5, 6,
    -                                               7, 1, 2, 4, 6, 4, 4, 5,
    -                                               7, 2, 6, 5, 7, 6, 7, 7 };
    -      return DeBruijnBytePos[
    -          ((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
    -#     endif
    -  } else { /* 32 bits */
    -#     if defined(_MSC_VER)
    -      unsigned long r=0;
    -      _BitScanForward( &r, (U32)val );
    -      return (unsigned)(r>>3);
    -#     elif defined(__GNUC__) && (__GNUC__ >= 3)
    -      return (__builtin_ctz((U32)val) >> 3);
    -#     else
    -      static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,
    -                                               3, 2, 2, 1, 3, 2, 0, 1,
    -                                               3, 3, 1, 2, 2, 2, 2, 0,
    -                                               3, 1, 2, 0, 1, 0, 1, 1 };
    -      return DeBruijnBytePos[
    -          ((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
    -#     endif
    -    }
    -  } else {  /* Big Endian CPU */
    -    if (MEM_64bits()) {
    -#     if defined(_MSC_VER) && defined(_WIN64)
    -      unsigned long r = 0;
    -      _BitScanReverse64( &r, val );
    -      return (unsigned)(r>>3);
    -#     elif defined(__GNUC__) && (__GNUC__ >= 3)
    -      return (__builtin_clzll(val) >> 3);
    -#     else
    -      unsigned r;
    -      /* calculate this way due to compiler complaining in 32-bits mode */
    -      const unsigned n32 = sizeof(size_t)*4;
    -      if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
    -      if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
    -      r += (!val);
    -      return r;
    -#       endif
    -    } else { /* 32 bits */
    -#     if defined(_MSC_VER)
    -      unsigned long r = 0;
    -      _BitScanReverse( &r, (unsigned long)val );
    -      return (unsigned)(r>>3);
    -#     elif defined(__GNUC__) && (__GNUC__ >= 3)
    -      return (__builtin_clz((U32)val) >> 3);
    -#     else
    -      unsigned r;
    -      if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
    -      r += (!val);
    -      return r;
    -#     endif
    -    }
    -  }
    -}
    -
    -// From lib/compress/zstd_compress.c
    -static size_t ZSTD_count(const BYTE *pIn, const BYTE *pMatch,
    -                         const BYTE *const pInLimit) {
    -    const BYTE * const pStart = pIn;
    -    const BYTE * const pInLoopLimit = pInLimit - (sizeof(size_t)-1);
    -
    -    while (pIn < pInLoopLimit) {
    -        size_t const diff = MEM_readST(pMatch) ^ MEM_readST(pIn);
    -        if (!diff) {
    -          pIn += sizeof(size_t);
    -          pMatch += sizeof(size_t);
    -          continue;
    -        }
    -        pIn += ZSTD_NbCommonBytes(diff);
    -        return (size_t)(pIn - pStart);
    -    }
    -
    -    if (MEM_64bits()) {
    -      if ((pIn < (pInLimit - 3)) && (MEM_read32(pMatch) == MEM_read32(pIn))) {
    -        pIn += 4;
    -        pMatch += 4;
    -      }
    -    }
    -    if ((pIn < (pInLimit - 1)) && (MEM_read16(pMatch) == MEM_read16(pIn))) {
    -      pIn += 2;
    -      pMatch += 2;
    -    }
    -    if ((pIn < pInLimit) && (*pMatch == *pIn)) {
    -      pIn++;
    -    }
    -    return (size_t)(pIn - pStart);
    -}
    -
    -/**
    - * Count number of bytes that match backwards before pIn and pMatch.
    - *
    - * We count only bytes where pMatch > pBase and pIn > pAnchor.
    - */
    -static size_t countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor,
    -                                  const BYTE *pMatch, const BYTE *pBase) {
    -  size_t matchLength = 0;
    -  while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) {
    -    pIn--;
    -    pMatch--;
    -    matchLength++;
    -  }
    -  return matchLength;
    -}
    -
    -/**
    - * Returns a pointer to the entry in the hash table matching the hash and
    - * checksum with the "longest match length" as defined below. The forward and
    - * backward match lengths are written to *pForwardMatchLength and
    - * *pBackwardMatchLength.
    - *
    - * The match length is defined based on cctx->ip and the entry's offset.
    - * The forward match is computed from cctx->ip and entry->offset + cctx->ibase.
    - * The backward match is computed backwards from cctx->ip and
    - * cctx->ibase only if the forward match is longer than LDM_MIN_MATCH_LENGTH.
    - */
    -static LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx,
    -                                        const hash_t hash,
    -                                        const U32 checksum,
    -                                        U64 *pForwardMatchLength,
    -                                        U64 *pBackwardMatchLength) {
    -  LDM_hashTable *table = cctx->hashTable;
    -  LDM_hashEntry *bucket = getBucket(table, hash);
    -  LDM_hashEntry *cur;
    -  LDM_hashEntry *bestEntry = NULL;
    -  U64 bestMatchLength = 0;
    -#if !(USE_CHECKSUM)
    -  (void)checksum;
    -#endif
    -  for (cur = bucket; cur < bucket + HASH_BUCKET_SIZE; ++cur) {
    -    const BYTE *pMatch = cur->offset + cctx->ibase;
    -
    -    // Check checksum for faster check.
    -#if USE_CHECKSUM
    -    if (cur->checksum == checksum &&
    -        cctx->ip - pMatch <= LDM_WINDOW_SIZE) {
    -#else
    -    if (cctx->ip - pMatch <= LDM_WINDOW_SIZE) {
    -#endif
    -      U64 forwardMatchLength = ZSTD_count(cctx->ip, pMatch, cctx->iend);
    -      U64 backwardMatchLength, totalMatchLength;
    -
    -      // Only take matches where the forward match length is large enough
    -      // for speed.
    -      if (forwardMatchLength < LDM_MIN_MATCH_LENGTH) {
    -        continue;
    -      }
    -
    -      backwardMatchLength =
    -          countBackwardsMatch(cctx->ip, cctx->anchor,
    -                              cur->offset + cctx->ibase,
    -                              cctx->ibase);
    -
    -      totalMatchLength = forwardMatchLength + backwardMatchLength;
    -
    -      if (totalMatchLength >= bestMatchLength) {
    -        bestMatchLength = totalMatchLength;
    -        *pForwardMatchLength = forwardMatchLength;
    -        *pBackwardMatchLength = backwardMatchLength;
    -
    -        bestEntry = cur;
    -#ifdef ZSTD_SKIP
    -        return cur;
    -#endif
    -      }
    -    }
    -  }
    -  if (bestEntry != NULL) {
    -    return bestEntry;
    -  }
    -  return NULL;
    -}
    -
    -/**
    - * Insert an entry into the hash table. The table uses a "circular buffer",
    - * with the oldest entry overwritten.
    - */
    -static void HASH_insert(LDM_hashTable *table,
    -                        const hash_t hash, const LDM_hashEntry entry) {
    -  *(getBucket(table, hash) + table->bucketOffsets[hash]) = entry;
    -  table->bucketOffsets[hash]++;
    -  table->bucketOffsets[hash] &= HASH_BUCKET_SIZE - 1;
    -}
    -
    -static void HASH_outputTableOccupancy(const LDM_hashTable *table) {
    -  U32 ctr = 0;
    -  LDM_hashEntry *cur = table->entries;
    -  LDM_hashEntry *end = table->entries + (table->numBuckets * HASH_BUCKET_SIZE);
    -  for (; cur < end; ++cur) {
    -    if (cur->offset == 0) {
    -      ctr++;
    -    }
    -  }
    -
    -  // The number of buckets is repeated as a check for now.
    -  printf("Num buckets, bucket size: %d (2^%d), %d\n",
    -         table->numBuckets, NUM_HASH_BUCKETS_LOG, HASH_BUCKET_SIZE);
    -  printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n",
    -         table->numEntries, ctr,
    -         100.0 * (double)(ctr) / table->numEntries);
    -}
    -
    -// TODO: This can be done more efficiently, for example by using builtin
    -// functions (but it is not that important as it is only used for computing
    -// stats).
    -static int intLog2(U64 x) {
    -  int ret = 0;
    -  while (x >>= 1) {
    -    ret++;
    -  }
    -  return ret;
    -}
    -
    -void LDM_printCompressStats(const LDM_compressStats *stats) {
    -  printf("=====================\n");
    -  printf("Compression statistics\n");
    -  printf("Window size, hash table size (bytes): 2^%u, 2^%u\n",
    -          stats->windowSizeLog, stats->hashTableSizeLog);
    -  printf("num matches, total match length, %% matched: %u, %llu, %.3f\n",
    -          stats->numMatches,
    -          stats->totalMatchLength,
    -          100.0 * (double)stats->totalMatchLength /
    -              (double)(stats->totalMatchLength + stats->totalLiteralLength));
    -  printf("avg match length: %.1f\n", ((double)stats->totalMatchLength) /
    -                                         (double)stats->numMatches);
    -  printf("avg literal length, total literalLength: %.1f, %llu\n",
    -         ((double)stats->totalLiteralLength) / (double)stats->numMatches,
    -         stats->totalLiteralLength);
    -  printf("avg offset length: %.1f\n",
    -         ((double)stats->totalOffset) / (double)stats->numMatches);
    -  printf("min offset, max offset: %u, %u\n",
    -         stats->minOffset, stats->maxOffset);
    -
    -  printf("\n");
    -  printf("offset histogram | match length histogram\n");
    -  printf("offset/ML, num matches, %% of matches | num matches, %% of matches\n");
    -
    -  {
    -    int i;
    -    int logMaxOffset = intLog2(stats->maxOffset);
    -    for (i = 0; i <= logMaxOffset; i++) {
    -      printf("2^%*d: %10u    %6.3f%% |2^%*d:  %10u    %6.3f \n",
    -             2, i,
    -             stats->offsetHistogram[i],
    -             100.0 * (double) stats->offsetHistogram[i] /
    -                     (double) stats->numMatches,
    -             2, i,
    -             stats->matchLengthHistogram[i],
    -             100.0 * (double) stats->matchLengthHistogram[i] /
    -                     (double) stats->numMatches);
    -    }
    -  }
    -  printf("\n");
    -  printf("=====================\n");
    -}
    -
    -/**
    - * Return the upper (most significant) NUM_HASH_BUCKETS_LOG bits.
    - */
    -static hash_t getSmallHash(U64 hash) {
    -  return hash >> (64 - NUM_HASH_BUCKETS_LOG);
    -}
    -
    -/**
    - * Return the 32 bits after the upper NUM_HASH_BUCKETS_LOG bits.
    - */
    -static U32 getChecksum(U64 hash) {
    -  return (hash >> (64 - 32 - NUM_HASH_BUCKETS_LOG)) & 0xFFFFFFFF;
    -}
    -
    -#if INSERT_BY_TAG
    -static U32 lowerBitsFromHfHash(U64 hash) {
    -  // The number of bits used so far is NUM_HASH_BUCKETS_LOG + 32.
    -  // So there are 32 - NUM_HASH_BUCKETS_LOG bits left.
    -  // Occasional hashing requires HASH_ONLY_EVERY_LOG bits.
    -  // So if 32 - LDMHASHLOG < HASH_ONLY_EVERY_LOG, just return lower bits
    -  // allowing for reuse of bits.
    -  if (32 - NUM_HASH_BUCKETS_LOG < HASH_ONLY_EVERY_LOG) {
    -    return hash & HASH_ONLY_EVERY;
    -  } else {
    -    // Otherwise shift by
    -    // (32 - NUM_HASH_BUCKETS_LOG - HASH_ONLY_EVERY_LOG) bits first.
    -    return (hash >> (32 - NUM_HASH_BUCKETS_LOG - HASH_ONLY_EVERY_LOG)) &
    -           HASH_ONLY_EVERY;
    -  }
    -}
    -#endif
    -
    -/**
    - * Get a 64-bit hash using the first len bytes from buf.
    - *
    - * Giving bytes s = s_1, s_2, ... s_k, the hash is defined to be
    - * H(s) = s_1*(a^(k-1)) + s_2*(a^(k-2)) + ... + s_k*(a^0)
    - *
    - * where the constant a is defined to be prime8bytes.
    - *
    - * The implementation adds an offset to each byte, so
    - * H(s) = (s_1 + HASH_CHAR_OFFSET)*(a^(k-1)) + ...
    - */
    -static U64 getHash(const BYTE *buf, U32 len) {
    -  U64 ret = 0;
    -  U32 i;
    -  for (i = 0; i < len; i++) {
    -    ret *= prime8bytes;
    -    ret += buf[i] + HASH_CHAR_OFFSET;
    -  }
    -  return ret;
    -
    -}
    -
    -static U64 ipow(U64 base, U64 exp) {
    -  U64 ret = 1;
    -  while (exp) {
    -    if (exp & 1) {
    -      ret *= base;
    -    }
    -    exp >>= 1;
    -    base *= base;
    -  }
    -  return ret;
    -}
    -
    -static U64 updateHash(U64 hash, U32 len,
    -                      BYTE toRemove, BYTE toAdd) {
    -  // TODO: this relies on compiler optimization.
    -  // The exponential can be calculated explicitly as len is constant.
    -  hash -= ((toRemove + HASH_CHAR_OFFSET) *
    -          ipow(prime8bytes, len - 1));
    -  hash *= prime8bytes;
    -  hash += toAdd + HASH_CHAR_OFFSET;
    -  return hash;
    -}
    -
    -/**
    - * Update cctx->nextHash and cctx->nextPosHashed
    - * based on cctx->lastHash and cctx->lastPosHashed.
    - *
    - * This uses a rolling hash and requires that the last position hashed
    - * corresponds to cctx->nextIp - step.
    - */
    -static void setNextHash(LDM_CCtx *cctx) {
    -  cctx->nextHash = updateHash(
    -      cctx->lastHash, LDM_HASH_LENGTH,
    -      cctx->lastPosHashed[0],
    -      cctx->lastPosHashed[LDM_HASH_LENGTH]);
    -  cctx->nextPosHashed = cctx->nextIp;
    -
    -#if LDM_LAG
    -  if (cctx->ip - cctx->ibase > LDM_LAG) {
    -    cctx->lagHash = updateHash(
    -      cctx->lagHash, LDM_HASH_LENGTH,
    -      cctx->lagIp[0], cctx->lagIp[LDM_HASH_LENGTH]);
    -    cctx->lagIp++;
    -  }
    -#endif
    -}
    -
    -static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hash) {
    -  // Hash only every HASH_ONLY_EVERY times, based on cctx->ip.
    -  // Note: this works only when cctx->step is 1.
    -#if LDM_LAG
    -  if (cctx -> lagIp - cctx->ibase > 0) {
    -#if INSERT_BY_TAG
    -    U32 hashEveryMask = lowerBitsFromHfHash(cctx->lagHash);
    -    if (hashEveryMask == HASH_ONLY_EVERY) {
    -#else
    -    if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) {
    -#endif
    -      U32 smallHash = getSmallHash(cctx->lagHash);
    -
    -#   if USE_CHECKSUM
    -      U32 checksum = getChecksum(cctx->lagHash);
    -      const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, checksum };
    -#   else
    -      const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase };
    -#   endif
    -
    -      HASH_insert(cctx->hashTable, smallHash, entry);
    -    }
    -  } else {
    -#endif // LDM_LAG
    -#if INSERT_BY_TAG
    -    U32 hashEveryMask = lowerBitsFromHfHash(hash);
    -    if (hashEveryMask == HASH_ONLY_EVERY) {
    -#else
    -    if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) {
    -#endif
    -      U32 smallHash = getSmallHash(hash);
    -
    -#if USE_CHECKSUM
    -      U32 checksum = getChecksum(hash);
    -      const LDM_hashEntry entry = { cctx->ip - cctx->ibase, checksum };
    -#else
    -      const LDM_hashEntry entry = { cctx->ip - cctx->ibase };
    -#endif
    -      HASH_insert(cctx->hashTable, smallHash, entry);
    -    }
    -#if LDM_LAG
    -  }
    -#endif
    -
    -  cctx->lastPosHashed = cctx->ip;
    -  cctx->lastHash = hash;
    -}
    -
    -/**
    - * Copy over the cctx->lastHash, and cctx->lastPosHashed
    - * fields from the "next" fields.
    - *
    - * This requires that cctx->ip == cctx->nextPosHashed.
    - */
    -static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) {
    -  putHashOfCurrentPositionFromHash(cctx, cctx->nextHash);
    -}
    -
    -/**
    - * Insert hash of the current position into the hash table.
    - */
    -static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) {
    -  U64 hash = getHash(cctx->ip, LDM_HASH_LENGTH);
    -
    -  putHashOfCurrentPositionFromHash(cctx, hash);
    -}
    -
    -size_t LDM_initializeCCtx(LDM_CCtx *cctx,
    -                          const void *src, size_t srcSize,
    -                          void *dst, size_t maxDstSize) {
    -  cctx->isize = srcSize;
    -  cctx->maxOSize = maxDstSize;
    -
    -  cctx->ibase = (const BYTE *)src;
    -  cctx->ip = cctx->ibase;
    -  cctx->iend = cctx->ibase + srcSize;
    -
    -  cctx->ihashLimit = cctx->iend - LDM_HASH_LENGTH;
    -  cctx->imatchLimit = cctx->iend - LDM_MIN_MATCH_LENGTH;
    -
    -  cctx->obase = (BYTE *)dst;
    -  cctx->op = (BYTE *)dst;
    -
    -  cctx->anchor = cctx->ibase;
    -
    -  memset(&(cctx->stats), 0, sizeof(cctx->stats));
    -#if USE_CHECKSUM
    -  cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U64);
    -#else
    -  cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U32);
    -#endif
    -
    -  if (!cctx->hashTable) return 1;
    -
    -  cctx->stats.minOffset = UINT_MAX;
    -  cctx->stats.windowSizeLog = LDM_WINDOW_SIZE_LOG;
    -  cctx->stats.hashTableSizeLog = LDM_MEMORY_USAGE;
    -
    -  cctx->lastPosHashed = NULL;
    -
    -  cctx->step = 1;   // Fixed to be 1 for now. Changing may break things.
    -  cctx->nextIp = cctx->ip + cctx->step;
    -  cctx->nextPosHashed = 0;
    -
    -  return 0;
    -}
    -
    -void LDM_destroyCCtx(LDM_CCtx *cctx) {
    -  HASH_destroyTable(cctx->hashTable);
    -}
    -
    -/**
    - * Finds the "best" match.
    - *
    - * Returns 0 if successful and 1 otherwise (i.e. no match can be found
    - * in the remaining input that is long enough).
    - *
    - * forwardMatchLength contains the forward length of the match.
    - */
    -static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match,
    -                             U64 *forwardMatchLength, U64 *backwardMatchLength) {
    -
    -  LDM_hashEntry *entry = NULL;
    -  cctx->nextIp = cctx->ip + cctx->step;
    -
    -  while (entry == NULL) {
    -    U64 hash;
    -    hash_t smallHash;
    -    U32 checksum;
    -#if INSERT_BY_TAG
    -    U32 hashEveryMask;
    -#endif
    -    setNextHash(cctx);
    -
    -    hash = cctx->nextHash;
    -    smallHash = getSmallHash(hash);
    -    checksum = getChecksum(hash);
    -#if INSERT_BY_TAG
    -    hashEveryMask = lowerBitsFromHfHash(hash);
    -#endif
    -
    -    cctx->ip = cctx->nextIp;
    -    cctx->nextIp += cctx->step;
    -
    -    if (cctx->ip > cctx->imatchLimit) {
    -      return 1;
    -    }
    -#if INSERT_BY_TAG
    -    if (hashEveryMask == HASH_ONLY_EVERY) {
    -
    -      entry = HASH_getBestEntry(cctx, smallHash, checksum,
    -                                forwardMatchLength, backwardMatchLength);
    -    }
    -#else
    -    entry = HASH_getBestEntry(cctx, smallHash, checksum,
    -                              forwardMatchLength, backwardMatchLength);
    -#endif
    -
    -    if (entry != NULL) {
    -      *match = entry->offset + cctx->ibase;
    -    }
    -
    -    putHashOfCurrentPositionFromHash(cctx, hash);
    -
    -  }
    -  setNextHash(cctx);
    -  return 0;
    -}
    -
    -void LDM_encodeLiteralLengthAndLiterals(
    -    LDM_CCtx *cctx, BYTE *pToken, const U64 literalLength) {
    -  /* Encode the literal length. */
    -  if (literalLength >= RUN_MASK) {
    -    U64 len = (U64)literalLength - RUN_MASK;
    -    *pToken = (RUN_MASK << ML_BITS);
    -    for (; len >= 255; len -= 255) {
    -      *(cctx->op)++ = 255;
    -    }
    -    *(cctx->op)++ = (BYTE)len;
    -  } else {
    -    *pToken = (BYTE)(literalLength << ML_BITS);
    -  }
    -
    -  /* Encode the literals. */
    -  memcpy(cctx->op, cctx->anchor, literalLength);
    -  cctx->op += literalLength;
    -}
    -
    -void LDM_outputBlock(LDM_CCtx *cctx,
    -                     const U64 literalLength,
    -                     const U32 offset,
    -                     const U64 matchLength) {
    -  BYTE *pToken = cctx->op++;
    -
    -  /* Encode the literal length and literals. */
    -  LDM_encodeLiteralLengthAndLiterals(cctx, pToken, literalLength);
    -
    -  /* Encode the offset. */
    -  MEM_write32(cctx->op, offset);
    -  cctx->op += LDM_OFFSET_SIZE;
    -
    -  /* Encode the match length. */
    -  if (matchLength >= ML_MASK) {
    -    U64 matchLengthRemaining = matchLength;
    -    *pToken += ML_MASK;
    -    matchLengthRemaining -= ML_MASK;
    -    MEM_write32(cctx->op, 0xFFFFFFFF);
    -    while (matchLengthRemaining >= 4*0xFF) {
    -      cctx->op += 4;
    -      MEM_write32(cctx->op, 0xffffffff);
    -      matchLengthRemaining -= 4*0xFF;
    -    }
    -    cctx->op += matchLengthRemaining / 255;
    -    *(cctx->op)++ = (BYTE)(matchLengthRemaining % 255);
    -  } else {
    -    *pToken += (BYTE)(matchLength);
    -  }
    -}
    -
    -// TODO: maxDstSize is unused. This function may seg fault when writing
    -// beyond the size of dst, as it does not check maxDstSize. Writing to
    -// a buffer and performing checks is a possible solution.
    -//
    -// This is based upon lz4.
    -size_t LDM_compress(const void *src, size_t srcSize,
    -                    void *dst, size_t maxDstSize) {
    -  LDM_CCtx cctx;
    -  const BYTE *match = NULL;
    -  U64 forwardMatchLength = 0;
    -  U64 backwardsMatchLength = 0;
    -
    -  if (LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize)) {
    -    // Initialization failed.
    -    return 0;
    -  }
    -
    -#ifdef OUTPUT_CONFIGURATION
    -  LDM_outputConfiguration();
    -#endif
    -
    -  /* Hash the first position and put it into the hash table. */
    -  LDM_putHashOfCurrentPosition(&cctx);
    -
    -  cctx.lagIp = cctx.ip;
    -  cctx.lagHash = cctx.lastHash;
    -
    -  /**
    -   * Find a match.
    -   * If no more matches can be found (i.e. the length of the remaining input
    -   * is less than the minimum match length), then stop searching for matches
    -   * and encode the final literals.
    -   */
    -  while (!LDM_findBestMatch(&cctx, &match, &forwardMatchLength,
    -                           &backwardsMatchLength)) {
    -
    -#ifdef COMPUTE_STATS
    -    cctx.stats.numMatches++;
    -#endif
    -
    -     cctx.ip -= backwardsMatchLength;
    -     match -= backwardsMatchLength;
    -
    -    /**
    -     * Write current block (literals, literal length, match offset, match
    -     * length) and update pointers and hashes.
    -     */
    -    {
    -      const U64 literalLength = cctx.ip - cctx.anchor;
    -      const U32 offset = cctx.ip - match;
    -      const U64 matchLength = forwardMatchLength +
    -                              backwardsMatchLength -
    -                              LDM_MIN_MATCH_LENGTH;
    -
    -      LDM_outputBlock(&cctx, literalLength, offset, matchLength);
    -
    -#ifdef COMPUTE_STATS
    -      cctx.stats.totalLiteralLength += literalLength;
    -      cctx.stats.totalOffset += offset;
    -      cctx.stats.totalMatchLength += matchLength + LDM_MIN_MATCH_LENGTH;
    -      cctx.stats.minOffset =
    -          offset < cctx.stats.minOffset ? offset : cctx.stats.minOffset;
    -      cctx.stats.maxOffset =
    -          offset > cctx.stats.maxOffset ? offset : cctx.stats.maxOffset;
    -      cctx.stats.offsetHistogram[(U32)intLog2(offset)]++;
    -      cctx.stats.matchLengthHistogram[
    -          (U32)intLog2(matchLength + LDM_MIN_MATCH_LENGTH)]++;
    -#endif
    -
    -      // Move ip to end of block, inserting hashes at each position.
    -      cctx.nextIp = cctx.ip + cctx.step;
    -      while (cctx.ip < cctx.anchor + LDM_MIN_MATCH_LENGTH +
    -                       matchLength + literalLength) {
    -        if (cctx.ip > cctx.lastPosHashed) {
    -          // TODO: Simplify.
    -          LDM_updateLastHashFromNextHash(&cctx);
    -          setNextHash(&cctx);
    -        }
    -        cctx.ip++;
    -        cctx.nextIp++;
    -      }
    -    }
    -
    -    // Set start of next block to current input pointer.
    -    cctx.anchor = cctx.ip;
    -    LDM_updateLastHashFromNextHash(&cctx);
    -  }
    -
    -  /* Encode the last literals (no more matches). */
    -  {
    -    const U64 lastRun = cctx.iend - cctx.anchor;
    -    BYTE *pToken = cctx.op++;
    -    LDM_encodeLiteralLengthAndLiterals(&cctx, pToken, lastRun);
    -  }
    -
    -#ifdef COMPUTE_STATS
    -  LDM_printCompressStats(&cctx.stats);
    -  HASH_outputTableOccupancy(cctx.hashTable);
    -#endif
    -
    -  {
    -    const size_t ret = cctx.op - cctx.obase;
    -    LDM_destroyCCtx(&cctx);
    -    return ret;
    -  }
    -}
    -
    -void LDM_outputConfiguration(void) {
    -  printf("=====================\n");
    -  printf("Configuration\n");
    -  printf("LDM_WINDOW_SIZE_LOG: %d\n", LDM_WINDOW_SIZE_LOG);
    -  printf("LDM_MIN_MATCH_LENGTH, LDM_HASH_LENGTH: %d, %d\n",
    -         LDM_MIN_MATCH_LENGTH, LDM_HASH_LENGTH);
    -  printf("LDM_MEMORY_USAGE: %d\n", LDM_MEMORY_USAGE);
    -  printf("HASH_ONLY_EVERY_LOG: %d\n", HASH_ONLY_EVERY_LOG);
    -  printf("HASH_BUCKET_SIZE_LOG: %d\n", HASH_BUCKET_SIZE_LOG);
    -  printf("LDM_LAG: %d\n", LDM_LAG);
    -  printf("USE_CHECKSUM: %d\n", USE_CHECKSUM);
    -  printf("INSERT_BY_TAG: %d\n", INSERT_BY_TAG);
    -  printf("HASH_CHAR_OFFSET: %d\n", HASH_CHAR_OFFSET);
    -  printf("=====================\n");
    -}
    diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h
    deleted file mode 100644
    index 4adadbd0a..000000000
    --- a/contrib/long_distance_matching/ldm.h
    +++ /dev/null
    @@ -1,197 +0,0 @@
    -#ifndef LDM_H
    -#define LDM_H
    -
    -#include "mem.h"    // from /lib/common/mem.h
    -
    -//#include "ldm_params.h"
    -
    -// =============================================================================
    -// Modify the parameters in ldm_params.h if "ldm_params.h" is included.
    -// Otherwise, modify the parameters here.
    -// =============================================================================
    -
    -#ifndef LDM_PARAMS_H
    -  // Defines the size of the hash table.
    -  // Note that this is not the number of buckets.
    -  // Currently this should be less than WINDOW_SIZE_LOG + 4.
    -  #define LDM_MEMORY_USAGE 23
    -
    -  // The number of entries in a hash bucket.
    -  #define HASH_BUCKET_SIZE_LOG 3 // The maximum is 4 for now.
    -
    -  // Defines the lag in inserting elements into the hash table.
    -  #define LDM_LAG 0
    -
    -  // The maximum window size when searching for matches.
    -  // The maximum value is 30
    -  #define LDM_WINDOW_SIZE_LOG 28
    -
    -  // The minimum match length.
    -  // This should be a multiple of four.
    -  #define LDM_MIN_MATCH_LENGTH 64
    -
    -  // If INSERT_BY_TAG, insert entries into the hash table as a function of the
    -  // hash. Certain hashes will not be inserted.
    -  //
    -  // Otherwise, insert as a function of the position.
    -  #define INSERT_BY_TAG 1
    -
    -  // Store a checksum with the hash table entries for faster comparison.
    -  // This halves the number of entries the hash table can contain.
    -  #define USE_CHECKSUM 1
    -#endif
    -
    -// Output compression statistics.
    -#define COMPUTE_STATS
    -
    -// Output the configuration.
    -#define OUTPUT_CONFIGURATION
    -
    -// If defined, forces the probability of insertion to be approximately
    -// one per (1 << HASH_ONLY_EVERY_LOG). If not defined, the probability will be
    -// calculated based on the memory usage and window size for "even" insertion
    -// throughout the window.
    -
    -// #define HASH_ONLY_EVERY_LOG 8
    -
    -// =============================================================================
    -
    -// The number of bytes storing the compressed and decompressed size
    -// in the header.
    -#define LDM_COMPRESSED_SIZE 8
    -#define LDM_DECOMPRESSED_SIZE 8
    -#define LDM_HEADER_SIZE ((LDM_COMPRESSED_SIZE)+(LDM_DECOMPRESSED_SIZE))
    -
    -#define ML_BITS 4
    -#define ML_MASK ((1U<
    -
    -#include "ldm.h"
    -
    -/**
    - * This function reads the header at the beginning of src and writes
    - * the compressed and decompressed size to compressedSize and
    - * decompressedSize.
    - *
    - * The header consists of 16 bytes: 8 bytes each in little-endian format
    - * of the compressed size and the decompressed size.
    - */
    -void LDM_readHeader(const void *src, U64 *compressedSize,
    -                    U64 *decompressedSize) {
    -  const BYTE *ip = (const BYTE *)src;
    -  *compressedSize = MEM_readLE64(ip);
    -  *decompressedSize = MEM_readLE64(ip + 8);
    -}
    -
    -/**
    - * Writes the 16-byte header (8-bytes each of the compressedSize and
    - * decompressedSize in little-endian format) to memPtr.
    - */
    -void LDM_writeHeader(void *memPtr, U64 compressedSize,
    -                     U64 decompressedSize) {
    -  MEM_writeLE64(memPtr, compressedSize);
    -  MEM_writeLE64((BYTE *)memPtr + 8, decompressedSize);
    -}
    -
    -struct LDM_DCtx {
    -  size_t compressedSize;
    -  size_t maxDecompressedSize;
    -
    -  const BYTE *ibase;   /* Base of input */
    -  const BYTE *ip;      /* Current input position */
    -  const BYTE *iend;    /* End of source */
    -
    -  const BYTE *obase;   /* Base of output */
    -  BYTE *op;            /* Current output position */
    -  const BYTE *oend;    /* End of output */
    -};
    -
    -void LDM_initializeDCtx(LDM_DCtx *dctx,
    -                        const void *src, size_t compressedSize,
    -                        void *dst, size_t maxDecompressedSize) {
    -  dctx->compressedSize = compressedSize;
    -  dctx->maxDecompressedSize = maxDecompressedSize;
    -
    -  dctx->ibase = src;
    -  dctx->ip = (const BYTE *)src;
    -  dctx->iend = dctx->ip + dctx->compressedSize;
    -  dctx->op = dst;
    -  dctx->oend = dctx->op + dctx->maxDecompressedSize;
    -}
    -
    -size_t LDM_decompress(const void *src, size_t compressedSize,
    -                      void *dst, size_t maxDecompressedSize) {
    -
    -  LDM_DCtx dctx;
    -  LDM_initializeDCtx(&dctx, src, compressedSize, dst, maxDecompressedSize);
    -
    -  while (dctx.ip < dctx.iend) {
    -    BYTE *cpy;
    -    const BYTE *match;
    -    size_t length, offset;
    -
    -    /* Get the literal length. */
    -    const unsigned token = *(dctx.ip)++;
    -    if ((length = (token >> ML_BITS)) == RUN_MASK) {
    -      unsigned s;
    -      do {
    -        s = *(dctx.ip)++;
    -        length += s;
    -      } while (s == 255);
    -    }
    -
    -    /* Copy the literals. */
    -    cpy = dctx.op + length;
    -    memcpy(dctx.op, dctx.ip, length);
    -    dctx.ip += length;
    -    dctx.op = cpy;
    -
    -    //TODO: dynamic offset size?
    -    /* Encode the offset. */
    -    offset = MEM_read32(dctx.ip);
    -    dctx.ip += LDM_OFFSET_SIZE;
    -    match = dctx.op - offset;
    -
    -    /* Get the match length. */
    -    length = token & ML_MASK;
    -    if (length == ML_MASK) {
    -      unsigned s;
    -      do {
    -        s = *(dctx.ip)++;
    -        length += s;
    -      } while (s == 255);
    -    }
    -    length += LDM_MIN_MATCH_LENGTH;
    -
    -    /* Copy match. */
    -    cpy = dctx.op + length;
    -
    -    // TODO: this can be made more efficient.
    -    while (match < cpy - offset && dctx.op < dctx.oend) {
    -      *(dctx.op)++ = *match++;
    -    }
    -  }
    -  return dctx.op - (BYTE *)dst;
    -}
    diff --git a/contrib/long_distance_matching/ldm_params.h b/contrib/long_distance_matching/ldm_params.h
    deleted file mode 100644
    index a541581b0..000000000
    --- a/contrib/long_distance_matching/ldm_params.h
    +++ /dev/null
    @@ -1,12 +0,0 @@
    -#ifndef LDM_PARAMS_H
    -#define LDM_PARAMS_H
    -
    -#define LDM_MEMORY_USAGE 23
    -#define HASH_BUCKET_SIZE_LOG 3
    -#define LDM_LAG 0
    -#define LDM_WINDOW_SIZE_LOG 28
    -#define LDM_MIN_MATCH_LENGTH 64
    -#define INSERT_BY_TAG 1
    -#define USE_CHECKSUM 1
    -
    -#endif  // LDM_PARAMS_H
    diff --git a/contrib/long_distance_matching/main.c b/contrib/long_distance_matching/main.c
    deleted file mode 100644
    index 7c7086a59..000000000
    --- a/contrib/long_distance_matching/main.c
    +++ /dev/null
    @@ -1,269 +0,0 @@
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -
    -#include 
    -#include "ldm.h"
    -#include "zstd.h"
    -
    -// #define DECOMPRESS_AND_VERIFY
    -
    -/* Compress file given by fname and output to oname.
    - * Returns 0 if successful, error code otherwise.
    - *
    - * This adds a header from LDM_writeHeader to the beginning of the output.
    - *
    - * This might seg fault if the compressed size is > the decompress
    - * size due to the mmapping and output file size allocated to be the input size
    - * The compress function should check before writing or buffer writes.
    - */
    -static int compress(const char *fname, const char *oname) {
    -  int fdin, fdout;
    -  struct stat statbuf;
    -  char *src, *dst;
    -  size_t maxCompressedSize, compressedSize;
    -
    -  struct timeval tv1, tv2;
    -  double timeTaken;
    -
    -
    -  /* Open the input file. */
    -  if ((fdin = open(fname, O_RDONLY)) < 0) {
    -    perror("Error in file opening");
    -    return 1;
    -  }
    -
    -  /* Open the output file. */
    -  if ((fdout = open(oname, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600)) < 0) {
    -    perror("Can't create output file");
    -    return 1;
    -  }
    -
    -  /* Find the size of the input file. */
    -  if (fstat (fdin, &statbuf) < 0) {
    -    perror("Fstat error");
    -    return 1;
    -  }
    -
    -  maxCompressedSize = (statbuf.st_size + LDM_HEADER_SIZE);
    -
    -  // Handle case where compressed size is > decompressed size.
    -  // TODO: The compress function should check before writing or buffer writes.
    -  maxCompressedSize += statbuf.st_size / 255;
    -
    -  ftruncate(fdout, maxCompressedSize);
    -
    -  /* mmap the input file. */
    -  if ((src = mmap(0, statbuf.st_size, PROT_READ,  MAP_SHARED, fdin, 0))
    -          == (caddr_t) - 1) {
    -      perror("mmap error for input");
    -      return 1;
    -  }
    -
    -  /* mmap the output file. */
    -  if ((dst = mmap(0, maxCompressedSize, PROT_READ | PROT_WRITE,
    -                  MAP_SHARED, fdout, 0)) == (caddr_t) - 1) {
    -      perror("mmap error for output");
    -      return 1;
    -  }
    -
    -  gettimeofday(&tv1, NULL);
    -
    -  compressedSize = LDM_HEADER_SIZE +
    -      LDM_compress(src, statbuf.st_size,
    -                   dst + LDM_HEADER_SIZE, maxCompressedSize);
    -
    -  gettimeofday(&tv2, NULL);
    -
    -  // Write the header.
    -  LDM_writeHeader(dst, compressedSize, statbuf.st_size);
    -
    -  // Truncate file to compressedSize.
    -  ftruncate(fdout, compressedSize);
    -
    -  printf("%25s : %10lu -> %10lu - %s \n", fname,
    -         (size_t)statbuf.st_size, (size_t)compressedSize, oname);
    -  printf("Compression ratio: %.2fx --- %.1f%%\n",
    -         (double)statbuf.st_size / (double)compressedSize,
    -         (double)compressedSize / (double)(statbuf.st_size) * 100.0);
    -
    -  timeTaken = (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 +
    -              (double) (tv2.tv_sec - tv1.tv_sec),
    -
    -  printf("Total compress time = %.3f seconds, Average scanning speed: %.3f MB/s\n",
    -         timeTaken,
    -         ((double)statbuf.st_size / (double) (1 << 20)) / timeTaken);
    -
    -  // Close files.
    -  close(fdin);
    -  close(fdout);
    -  return 0;
    -}
    -
    -#ifdef DECOMPRESS_AND_VERIFY
    -/* Decompress file compressed using LDM_compress.
    - * The input file should have the LDM_HEADER followed by payload.
    - * Returns 0 if succesful, and an error code otherwise.
    - */
    -static int decompress(const char *fname, const char *oname) {
    -  int fdin, fdout;
    -  struct stat statbuf;
    -  char *src, *dst;
    -  U64 compressedSize, decompressedSize;
    -  size_t outSize;
    -
    -  /* Open the input file. */
    -  if ((fdin = open(fname, O_RDONLY)) < 0) {
    -    perror("Error in file opening");
    -    return 1;
    -  }
    -
    -  /* Open the output file. */
    -  if ((fdout = open(oname, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600)) < 0) {
    -    perror("Can't create output file");
    -    return 1;
    -  }
    -
    -  /* Find the size of the input file. */
    -  if (fstat (fdin, &statbuf) < 0) {
    -    perror("Fstat error");
    -    return 1;
    -  }
    -
    -  /* mmap the input file. */
    -  if ((src = mmap(0, statbuf.st_size, PROT_READ,  MAP_SHARED, fdin, 0))
    -          == (caddr_t) - 1) {
    -      perror("mmap error for input");
    -      return 1;
    -  }
    -
    -  /* Read the header. */
    -  LDM_readHeader(src, &compressedSize, &decompressedSize);
    -
    -  ftruncate(fdout, decompressedSize);
    -
    -  /* mmap the output file */
    -  if ((dst = mmap(0, decompressedSize, PROT_READ | PROT_WRITE,
    -                  MAP_SHARED, fdout, 0)) == (caddr_t) - 1) {
    -      perror("mmap error for output");
    -      return 1;
    -  }
    -
    -  outSize = LDM_decompress(
    -      src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE,
    -      dst, decompressedSize);
    -  printf("Ret size out: %zu\n", outSize);
    -
    -  close(fdin);
    -  close(fdout);
    -  return 0;
    -}
    -
    -/* Compare two files.
    - * Returns 0 iff they are the same.
    - */
    -static int compare(FILE *fp0, FILE *fp1) {
    -  int result = 0;
    -  while (result == 0) {
    -    char b0[1024];
    -    char b1[1024];
    -    const size_t r0 = fread(b0, 1, sizeof(b0), fp0);
    -    const size_t r1 = fread(b1, 1, sizeof(b1), fp1);
    -
    -    result = (int)r0 - (int)r1;
    -
    -    if (0 == r0 || 0 == r1) break;
    -
    -    if (0 == result) result = memcmp(b0, b1, r0);
    -  }
    -  return result;
    -}
    -
    -/* Verify the input file is the same as the decompressed file. */
    -static int verify(const char *inpFilename, const char *decFilename) {
    -  FILE *inpFp, *decFp;
    -
    -  if ((inpFp = fopen(inpFilename, "rb")) == NULL) {
    -    perror("Could not open input file\n");
    -    return 1;
    -  }
    -
    -  if ((decFp = fopen(decFilename, "rb")) == NULL) {
    -    perror("Could not open decompressed file\n");
    -    return 1;
    -  }
    -
    -  printf("verify : %s <-> %s\n", inpFilename, decFilename);
    -  {
    -    const int cmp = compare(inpFp, decFp);
    -    if(0 == cmp) {
    -      printf("verify : OK\n");
    -    } else {
    -      printf("verify : NG\n");
    -      return 1;
    -    }
    -  }
    -
    -	fclose(decFp);
    -	fclose(inpFp);
    -  return 0;
    -}
    -#endif
    -
    -int main(int argc, const char *argv[]) {
    -  const char * const exeName = argv[0];
    -  char inpFilename[256] = { 0 };
    -  char ldmFilename[256] = { 0 };
    -  char decFilename[256] = { 0 };
    -
    -  if (argc < 2) {
    -    printf("Wrong arguments\n");
    -    printf("Usage:\n");
    -    printf("%s FILE\n", exeName);
    -    return 1;
    -  }
    -
    -  snprintf(inpFilename, 256, "%s", argv[1]);
    -  snprintf(ldmFilename, 256, "%s.ldm", argv[1]);
    -  snprintf(decFilename, 256, "%s.ldm.dec", argv[1]);
    -
    - 	printf("inp = [%s]\n", inpFilename);
    -	printf("ldm = [%s]\n", ldmFilename);
    -	printf("dec = [%s]\n", decFilename);
    -
    -  /* Compress */
    -  {
    -    if (compress(inpFilename, ldmFilename)) {
    -        printf("Compress error\n");
    -        return 1;
    -    }
    -  }
    -
    -#ifdef DECOMPRESS_AND_VERIFY
    -  /* Decompress */
    -  {
    -    struct timeval tv1, tv2;
    -    gettimeofday(&tv1, NULL);
    -    if (decompress(ldmFilename, decFilename)) {
    -        printf("Decompress error\n");
    -        return 1;
    -    }
    -    gettimeofday(&tv2, NULL);
    -    printf("Total decompress time = %f seconds\n",
    -          (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 +
    -          (double) (tv2.tv_sec - tv1.tv_sec));
    -  }
    -  /* verify */
    -  if (verify(inpFilename, decFilename)) {
    -    printf("Verification error\n");
    -    return 1;
    -  }
    -#endif
    -  return 0;
    -}
    
    From 355cb645bfb78133804b362b4a0fe4b7bee3e76d Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 15 Mar 2018 16:29:28 -0700
    Subject: [PATCH 190/255] fixed seekable format example
    
    ---
     contrib/seekable_format/zstdseek_compress.c   | 2 +-
     contrib/seekable_format/zstdseek_decompress.c | 4 ++--
     2 files changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/contrib/seekable_format/zstdseek_compress.c b/contrib/seekable_format/zstdseek_compress.c
    index df2074988..4ab666bdb 100644
    --- a/contrib/seekable_format/zstdseek_compress.c
    +++ b/contrib/seekable_format/zstdseek_compress.c
    @@ -147,7 +147,7 @@ size_t ZSTD_seekable_initCStream(ZSTD_seekable_CStream* zcs,
     
         /* make sure maxFrameSize has a reasonable value */
         if (maxFrameSize > ZSTD_SEEKABLE_MAX_FRAME_DECOMPRESSED_SIZE) {
    -        return ERROR(compressionParameter_unsupported);
    +        return ERROR(frameParameter_unsupported);
         }
     
         zcs->maxFrameSize = maxFrameSize
    diff --git a/contrib/seekable_format/zstdseek_decompress.c b/contrib/seekable_format/zstdseek_decompress.c
    index d740e16be..c67653338 100644
    --- a/contrib/seekable_format/zstdseek_decompress.c
    +++ b/contrib/seekable_format/zstdseek_decompress.c
    @@ -125,7 +125,7 @@ static int ZSTD_seekable_seek_buff(void* opaque, S64 offset, int origin)
             newOffset = (unsigned long long)buff->size - offset;
             break;
         }
    -    if (newOffset < 0 || newOffset > buff->size) {
    +    if (newOffset > buff->size) {
             return -1;
         }
         buff->pos = newOffset;
    @@ -145,7 +145,7 @@ typedef struct {
         int checksumFlag;
     } seekTable_t;
     
    -#define SEEKABLE_BUFF_SIZE ZSTD_BLOCKSIZE_ABSOLUTEMAX
    +#define SEEKABLE_BUFF_SIZE ZSTD_BLOCKSIZE_MAX
     
     struct ZSTD_seekable_s {
         ZSTD_DStream* dstream;
    
    From 97816400ca3642de858266822ecad9049b193f96 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 15 Mar 2018 16:39:59 -0700
    Subject: [PATCH 191/255] added /contrib projects to `make all`
    
    ---
     Makefile             | 9 ++++++++-
     doc/zstd_manual.html | 6 +++---
     2 files changed, 11 insertions(+), 4 deletions(-)
    
    diff --git a/Makefile b/Makefile
    index 47746ab1d..5f4b7935a 100644
    --- a/Makefile
    +++ b/Makefile
    @@ -27,7 +27,7 @@ endif
     default: lib-release zstd-release
     
     .PHONY: all
    -all: | allmost examples manual
    +all: | allmost examples manual contrib
     
     .PHONY: allmost
     allmost: allzstd
    @@ -92,6 +92,11 @@ examples:
     manual:
     	$(MAKE) -C contrib/gen_html $@
     
    +.PHONY: contrib
    +contrib: lib
    +	$(MAKE) -C contrib/pzstd all
    +	$(MAKE) -C contrib/seekable_format/examples all
    +
     .PHONY: cleanTabs
     cleanTabs:
     	cd contrib; ./cleanTabs
    @@ -104,6 +109,8 @@ clean:
     	@$(MAKE) -C $(ZWRAPDIR) $@ > $(VOID)
     	@$(MAKE) -C examples/ $@ > $(VOID)
     	@$(MAKE) -C contrib/gen_html $@ > $(VOID)
    +	@$(MAKE) -C contrib/pzstd $@ > $(VOID)
    +	@$(MAKE) -C contrib/seekable_format/examples $@ > $(VOID)
     	@$(RM) zstd$(EXT) zstdmt$(EXT) tmp*
     	@$(RM) -r lz4
     	@echo Cleaning completed
    diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
    index 7c55363f0..302b53595 100644
    --- a/doc/zstd_manual.html
    +++ b/doc/zstd_manual.html
    @@ -825,9 +825,9 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
     
         ZSTD_p_compressLiterals=1000, /* control huffman compression of literals (enabled) by default.
                                   * disabling it improves speed and decreases compression ratio by a large amount.
    -                              * note : this setting is updated when changing compression level.
    -                              *        positive compression levels set literalCompression to 1.
    -                              *        negative compression levels set literalCompression to 0. */
    +                              * note : this setting is automatically updated when changing compression level.
    +                              *        positive compression levels set ZSTD_p_compressLiterals to 1.
    +                              *        negative compression levels set ZSTD_p_compressLiterals to 0. */
     
         ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize,
                                   * even when referencing into Dictionary content (default:0) */
    
    From 5373e44ba7e6534aabbf646eb2c1bf8f148d7467 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 15 Mar 2018 17:10:15 -0700
    Subject: [PATCH 192/255] fixed contrib/adaptive-compression
    
    ---
     Makefile                                |  2 ++
     contrib/adaptive-compression/.gitignore |  3 +++
     contrib/adaptive-compression/adapt.c    | 21 ++++++---------------
     3 files changed, 11 insertions(+), 15 deletions(-)
     create mode 100644 contrib/adaptive-compression/.gitignore
    
    diff --git a/Makefile b/Makefile
    index 5f4b7935a..320fc6807 100644
    --- a/Makefile
    +++ b/Makefile
    @@ -96,6 +96,7 @@ manual:
     contrib: lib
     	$(MAKE) -C contrib/pzstd all
     	$(MAKE) -C contrib/seekable_format/examples all
    +	$(MAKE) -C contrib/adaptive-compression all
     
     .PHONY: cleanTabs
     cleanTabs:
    @@ -111,6 +112,7 @@ clean:
     	@$(MAKE) -C contrib/gen_html $@ > $(VOID)
     	@$(MAKE) -C contrib/pzstd $@ > $(VOID)
     	@$(MAKE) -C contrib/seekable_format/examples $@ > $(VOID)
    +	@$(MAKE) -C contrib/adaptive-compression $@ > $(VOID)
     	@$(RM) zstd$(EXT) zstdmt$(EXT) tmp*
     	@$(RM) -r lz4
     	@echo Cleaning completed
    diff --git a/contrib/adaptive-compression/.gitignore b/contrib/adaptive-compression/.gitignore
    new file mode 100644
    index 000000000..95df8a1a7
    --- /dev/null
    +++ b/contrib/adaptive-compression/.gitignore
    @@ -0,0 +1,3 @@
    +# binaries generated
    +adapt
    +datagen
    diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c
    index 8f9c678c5..49f1d4d57 100644
    --- a/contrib/adaptive-compression/adapt.c
    +++ b/contrib/adaptive-compression/adapt.c
    @@ -40,7 +40,6 @@ static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL;
     static UTIL_time_t g_startTime;
     static size_t g_streamedSize = 0;
     static unsigned g_useProgressBar = 1;
    -static UTIL_freq_t g_ticksPerSecond;
     static unsigned g_forceCompressionLevel = 0;
     static unsigned g_minCLevel = 1;
     static unsigned g_maxCLevel;
    @@ -576,13 +575,12 @@ static void* compressionThread(void* arg)
                 /* begin compression */
                 {
                     size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize);
    -                size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1);
                     ZSTD_parameters params = ZSTD_getParams(cLevel, 0, useDictSize);
                     params.cParams.windowLog = 23;
                     {
                         size_t const initError = ZSTD_compressBegin_advanced(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, params, 0);
    -                    size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1);
    -                    if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) {
    +                    size_t const windowSizeError = ZSTD_CCtx_setParameter(ctx->cctx, ZSTD_p_forceMaxWindow, 1);
    +                    if (ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) {
                             DISPLAY("Error: something went wrong while starting compression\n");
                             signalErrorToThreads(ctx);
                             return arg;
    @@ -644,21 +642,17 @@ static void* compressionThread(void* arg)
     
     static void displayProgress(unsigned cLevel, unsigned last)
     {
    -    UTIL_time_t currTime;
    -    UTIL_getTime(&currTime);
    +    UTIL_time_t currTime = UTIL_getTime();
         if (!g_useProgressBar) return;
    -    {
    -        double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0);
    +    {   double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_startTime, currTime) / 1000.0);
             double const sizeMB = (double)g_streamedSize / (1 << 20);
             double const avgCompRate = sizeMB * 1000 / timeElapsed;
             fprintf(stderr, "\r| Comp. Level: %2u | Time Elapsed: %7.2f s | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate);
             if (last) {
                 fprintf(stderr, "\n");
    -        }
    -        else {
    +        } else {
                 fflush(stderr);
    -        }
    -    }
    +    }   }
     }
     
     static void* outputThread(void* arg)
    @@ -971,7 +965,6 @@ static int compressFilename(const char* const srcFilename, const char* const dst
     {
         int ret = 0;
         fcResources fcr = createFileCompressionResources(srcFilename, dstFilenameOrNull);
    -    UTIL_getTime(&g_startTime);
         g_streamedSize = 0;
         ret |= performCompression(fcr.ctx, fcr.srcFile, fcr.otArg);
         ret |= freeFileCompressionResources(&fcr);
    @@ -1044,8 +1037,6 @@ int main(int argCount, const char* argv[])
         filenameTable[0] = stdinmark;
         g_maxCLevel = ZSTD_maxCLevel();
     
    -    UTIL_initTimer(&g_ticksPerSecond);
    -
         if (filenameTable == NULL) {
             DISPLAY("Error: could not allocate sapce for filename table.\n");
             return 1;
    
    From cbc71e40f6dda6b42ddcbbd80a3035ec88491a53 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Thu, 15 Mar 2018 17:22:40 -0700
    Subject: [PATCH 193/255] moving LRM parameters out of experimental section
    
    into "normal" range,
    start pinned at 160.
    ---
     lib/zstd.h | 67 +++++++++++++++++++++++++++---------------------------
     1 file changed, 34 insertions(+), 33 deletions(-)
    
    diff --git a/lib/zstd.h b/lib/zstd.h
    index 3ea049eb6..4b1b92c18 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -990,6 +990,40 @@ typedef enum {
                                   * resulting in stronger and slower compression.
                                   * Special: value 0 means "do not change strategy". */
     
    +    ZSTD_p_enableLongDistanceMatching=160, /* Enable long distance matching.
    +                                         * This parameter is designed to improve the compression
    +                                         * ratio for large inputs with long distance matches.
    +                                         * This increases the memory usage as well as window size.
    +                                         * Note: setting this parameter sets all the LDM parameters
    +                                         * as well as ZSTD_p_windowLog. It should be set after
    +                                         * ZSTD_p_compressionLevel and before ZSTD_p_windowLog and
    +                                         * other LDM parameters. Setting the compression level
    +                                         * after this parameter overrides the window log, though LDM
    +                                         * will remain enabled until explicitly disabled. */
    +    ZSTD_p_ldmHashLog,       /* Size of the table for long distance matching, as a power of 2.
    +                              * Larger values increase memory usage and compression ratio, but decrease
    +                              * compression speed.
    +                              * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
    +                              * (default: windowlog - 7).
    +                              * Special: value 0 means "do not change ldmHashLog". */
    +    ZSTD_p_ldmMinMatch,      /* Minimum size of searched matches for long distance matcher.
    +                              * Larger/too small values usually decrease compression ratio.
    +                              * Must be clamped between ZSTD_LDM_MINMATCH_MIN
    +                              * and ZSTD_LDM_MINMATCH_MAX (default: 64).
    +                              * Special: value 0 means "do not change ldmMinMatch". */
    +    ZSTD_p_ldmBucketSizeLog, /* Log size of each bucket in the LDM hash table for collision resolution.
    +                              * Larger values usually improve collision resolution but may decrease
    +                              * compression speed.
    +                              * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX (default: 3).
    +                              * note : 0 is a valid value */
    +    ZSTD_p_ldmHashEveryLog,  /* Frequency of inserting/looking up entries in the LDM hash table.
    +                              * The default is MAX(0, (windowLog - ldmHashLog)) to
    +                              * optimize hash table usage.
    +                              * Larger values improve compression speed. Deviating far from the
    +                              * default value will likely result in a decrease in compression ratio.
    +                              * Must be clamped between 0 and ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN.
    +                              * note : 0 is a valid value */
    +
         /* frame parameters */
         ZSTD_p_contentSizeFlag=200, /* Content size will be written into frame header _whenever known_ (default:1)
                                   * Content size must be known at the beginning of compression,
    @@ -1026,39 +1060,6 @@ typedef enum {
         ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize,
                                   * even when referencing into Dictionary content (default:0) */
     
    -    ZSTD_p_enableLongDistanceMatching=1200, /* Enable long distance matching.
    -                                         * This parameter is designed to improve the compression
    -                                         * ratio for large inputs with long distance matches.
    -                                         * This increases the memory usage as well as window size.
    -                                         * Note: setting this parameter sets all the LDM parameters
    -                                         * as well as ZSTD_p_windowLog. It should be set after
    -                                         * ZSTD_p_compressionLevel and before ZSTD_p_windowLog and
    -                                         * other LDM parameters. Setting the compression level
    -                                         * after this parameter overrides the window log, though LDM
    -                                         * will remain enabled until explicitly disabled. */
    -    ZSTD_p_ldmHashLog,       /* Size of the table for long distance matching, as a power of 2.
    -                              * Larger values increase memory usage and compression ratio, but decrease
    -                              * compression speed.
    -                              * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
    -                              * (default: windowlog - 7).
    -                              * Special: value 0 means "do not change ldmHashLog". */
    -    ZSTD_p_ldmMinMatch,      /* Minimum size of searched matches for long distance matcher.
    -                              * Larger/too small values usually decrease compression ratio.
    -                              * Must be clamped between ZSTD_LDM_MINMATCH_MIN
    -                              * and ZSTD_LDM_MINMATCH_MAX (default: 64).
    -                              * Special: value 0 means "do not change ldmMinMatch". */
    -    ZSTD_p_ldmBucketSizeLog, /* Log size of each bucket in the LDM hash table for collision resolution.
    -                              * Larger values usually improve collision resolution but may decrease
    -                              * compression speed.
    -                              * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX (default: 3).
    -                              * note : 0 is a valid value */
    -    ZSTD_p_ldmHashEveryLog,  /* Frequency of inserting/looking up entries in the LDM hash table.
    -                              * The default is MAX(0, (windowLog - ldmHashLog)) to
    -                              * optimize hash table usage.
    -                              * Larger values improve compression speed. Deviating far from the
    -                              * default value will likely result in a decrease in compression ratio.
    -                              * Must be clamped between 0 and ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN.
    -                              * note : 0 is a valid value */
     
     } ZSTD_cParameter;
     
    
    From 4af1fafeb80db846d7931a5fdfd6c24108761770 Mon Sep 17 00:00:00 2001
    From: Nick Terrell 
    Date: Thu, 15 Mar 2018 18:52:38 -0700
    Subject: [PATCH 194/255] Restore setting loadedDictEnd
    
    Setting `loadedDictEnd` was accidently removed from `ZSTD_loadDictionaryContent()`,
    which means that dictionary compression will only be able to reference the parts of
    the dictionary within the window. The spec allows us to reference the entire
    dictionary so long as even one byte is in the window.
    
    `ZSTD_enforceMaxDist()` incorrectly always allowed offsets up to `loadedDictEnd`
    beyond the window, even once the dictionary was out of range.
    
    When overflow protection kicked in, the check `current > loadedDictEnd + maxDist`
    is incorrect if `loadedDictEnd` isn't reset back to zero. `current` could be reset
    below the value, which would incorrectly allow references beyond the window. This
    bug is present in `master`, but is very hard to trigger, since it requires both
    dictionaries and data which triggers overflow correction.
    ---
     lib/compress/zstd_compress.c          |  4 +++-
     lib/compress/zstd_compress_internal.h | 21 ++++++++++++++++-----
     lib/compress/zstd_ldm.c               |  2 +-
     3 files changed, 20 insertions(+), 7 deletions(-)
    
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index e70c2a54d..db8d7a8c3 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -1931,8 +1931,9 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx,
                 ZSTD_reduceIndex(cctx, correction);
                 if (ms->nextToUpdate < correction) ms->nextToUpdate = 0;
                 else ms->nextToUpdate -= correction;
    +            ms->loadedDictEnd = 0;
             }
    -        ZSTD_window_enforceMaxDist(&ms->window, ip + blockSize, ms->loadedDictEnd + maxDist);
    +        ZSTD_window_enforceMaxDist(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd);
             if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit;
     
             {   size_t cSize = ZSTD_compressBlock_internal(cctx,
    @@ -2118,6 +2119,7 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, ZSTD_CCtx_params
         ZSTD_compressionParameters const* cParams = ¶ms->cParams;
     
         ZSTD_window_update(&ms->window, src, srcSize);
    +    ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base);
     
         if (srcSize <= HASH_READ_SIZE) return 0;
     
    diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
    index 0d8bbf296..865c080cc 100644
    --- a/lib/compress/zstd_compress_internal.h
    +++ b/lib/compress/zstd_compress_internal.h
    @@ -562,15 +562,24 @@ MEM_STATIC U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog,
     
     /**
      * ZSTD_window_enforceMaxDist():
    - * Sets lowLimit such that indices earlier than (srcEnd - base) - lowLimit are
    - * invalid. This allows a simple check index >= lowLimit to see if it is valid.
    - * Source pointers past srcEnd are not guaranteed to be valid.
    + * Updates lowLimit so that:
    + *    (srcEnd - base) - lowLimit == maxDist + loadedDictEnd
    + * This allows a simple check that index >= lowLimit to see if index is valid.
    + * This must be called before a block compression call, with srcEnd as the block
    + * source end.
    + * If loadedDictEndPtr is not NULL, we set it to zero once we update lowLimit.
    + * This is because dictionaries are allowed to be referenced as long as the last
    + * byte of the dictionary is in the window, but once they are out of range,
    + * they cannot be referenced. If loadedDictEndPtr is NULL, we use
    + * loadedDictEnd == 0.
      */
     MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window,
    -                                           void const* srcEnd, U32 maxDist)
    +                                           void const* srcEnd, U32 maxDist,
    +                                           U32* loadedDictEndPtr)
     {
         U32 const current = (U32)((BYTE const*)srcEnd - window->base);
    -    if (current > maxDist) {
    +    U32 loadedDictEnd = loadedDictEndPtr != NULL ? *loadedDictEndPtr : 0;
    +    if (current > maxDist + loadedDictEnd) {
             U32 const newLowLimit = current - maxDist;
             if (window->lowLimit < newLowLimit) window->lowLimit = newLowLimit;
             if (window->dictLimit < window->lowLimit) {
    @@ -578,6 +587,8 @@ MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window,
                          window->lowLimit);
                 window->dictLimit = window->lowLimit;
             }
    +        if (loadedDictEndPtr)
    +            *loadedDictEndPtr = 0;
         }
     }
     
    diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c
    index fa395ed7d..5c9c0d2b6 100644
    --- a/lib/compress/zstd_ldm.c
    +++ b/lib/compress/zstd_ldm.c
    @@ -514,7 +514,7 @@ size_t ZSTD_ldm_generateSequences(
              *       * Try invalidation after the sequence generation and test the
              *         the offset against maxDist directly.
              */
    -        ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist);
    +        ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, NULL);
             /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */
             newLeftoverSize = ZSTD_ldm_generateSequences_internal(
                 ldmState, sequences, params, chunkStart, chunkSize);
    
    From 7616200eaf97c78912bf5db3f43952be7b35c2fa Mon Sep 17 00:00:00 2001
    From: Varunram 
    Date: Sun, 18 Mar 2018 14:53:48 +0530
    Subject: [PATCH 195/255] Add Dockerfile
    
    Dockerfile initially proposed by @gyscos at #880
    ---
     contrib/docker/Dockerfile | 20 ++++++++++++++++++++
     1 file changed, 20 insertions(+)
     create mode 100644 contrib/docker/Dockerfile
    
    diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile
    new file mode 100644
    index 000000000..e06a32c0d
    --- /dev/null
    +++ b/contrib/docker/Dockerfile
    @@ -0,0 +1,20 @@
    +# Dockerfile
    +# First image to build the binary
    +FROM alpine as builder
    +
    +RUN apk --no-cache add make gcc libc-dev
    +COPY . /src
    +RUN mkdir /pkg && cd /src && make && make DESTDIR=/pkg install
    +
    +# Second minimal image to only keep the built binary
    +FROM alpine
    +
    +# Copy the built files
    +COPY --from=builder /pkg /
    +
    +# Copy the license as well
    +RUN mkdir -p /usr/local/share/licenses/zstd
    +COPY --from=builder /src/LICENSE /usr/local/share/licences/zstd/
    +
    +# Just run `zstd` if no other command is given
    +CMD ["/usr/local/bin/zstd"]
    
    From 90c598f089efd47a797e52de719d5079395e5efb Mon Sep 17 00:00:00 2001
    From: Varunram 
    Date: Sun, 18 Mar 2018 15:48:58 +0530
    Subject: [PATCH 196/255] Add missing checks to CMakeLists;closes #1023
    
    ---
     build/cmake/lib/CMakeLists.txt | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt
    index 945fdb922..2b11226d8 100644
    --- a/build/cmake/lib/CMakeLists.txt
    +++ b/build/cmake/lib/CMakeLists.txt
    @@ -122,14 +122,14 @@ ENDIF (MSVC)
     
     # Add multi-threading support definitions
     
    -IF (ZSTD_MULTITHREAD_SUPPORT)
    +IF (ZSTD_MULTITHREAD_SUPPORT AND ZSTD_BUILD_SHARED AND ZSTD_BUILD_STATIC)
         SET_PROPERTY(TARGET libzstd_shared libzstd_static APPEND PROPERTY COMPILE_DEFINITIONS "ZSTD_MULTITHREAD")
     
         IF (UNIX)
             TARGET_LINK_LIBRARIES(libzstd_shared ${THREADS_LIBS})
             TARGET_LINK_LIBRARIES(libzstd_static ${THREADS_LIBS})
         ENDIF ()
    -ENDIF (ZSTD_MULTITHREAD_SUPPORT)
    +ENDIF (ZSTD_MULTITHREAD_SUPPORT AND ZSTD_BUILD_SHARED AND ZSTD_BUILD_STATIC)
     
     # With MSVC static library needs to be renamed to avoid conflict with import library
     IF (MSVC)
    
    From 9618c0c8045e95e8c30b3bf2cfc5b9dd0fc61cad Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Mon, 19 Mar 2018 11:07:04 -0700
    Subject: [PATCH 197/255] make it possible to specify LDM parameters in any
     order
    
    ---
     lib/compress/zstd_compress.c | 12 ++++--------
     lib/compress/zstd_ldm.c      | 12 +++++++-----
     lib/compress/zstd_ldm.h      | 12 ++++++------
     lib/zstd.h                   | 37 ++++++++++++++++--------------------
     4 files changed, 33 insertions(+), 40 deletions(-)
    
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index e70c2a54d..42bcc4c05 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -478,17 +478,15 @@ size_t ZSTD_CCtxParam_setParameter(
             return ZSTD_ldm_initializeParameters(&CCtxParams->ldmParams, value);
     
         case ZSTD_p_ldmHashLog :
    -        if (value) { /* 0 : does not change current ldmHashLog */
    +        if (value>0)   /* 0 ==> auto */
                 CLAMPCHECK(value, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX);
    -            CCtxParams->ldmParams.hashLog = value;
    -        }
    +        CCtxParams->ldmParams.hashLog = value;
             return CCtxParams->ldmParams.hashLog;
     
         case ZSTD_p_ldmMinMatch :
    -        if (value) { /* 0 : does not change current ldmMinMatch */
    +        if (value>0)   /* 0 ==> default */
                 CLAMPCHECK(value, ZSTD_LDM_MINMATCH_MIN, ZSTD_LDM_MINMATCH_MAX);
    -            CCtxParams->ldmParams.minMatchLength = value;
    -        }
    +        CCtxParams->ldmParams.minMatchLength = value;
             return CCtxParams->ldmParams.minMatchLength;
     
         case ZSTD_p_ldmBucketSizeLog :
    @@ -988,8 +986,6 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
                                     zbuff, pledgedSrcSize)) {
                 DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog1=%u, blockSize1=%u)",
                             zc->appliedParams.cParams.windowLog, (U32)zc->blockSize);
    -            assert(!(params.ldmParams.enableLdm &&
    -                     params.ldmParams.hashEveryLog == ZSTD_LDM_HASHEVERYLOG_NOTSET));
                 return ZSTD_continueCCtx(zc, params, pledgedSrcSize);
         }   }
         DEBUGLOG(4, "ZSTD_equivalentParams()==0 -> reset CCtx");
    diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c
    index fa395ed7d..281260712 100644
    --- a/lib/compress/zstd_ldm.c
    +++ b/lib/compress/zstd_ldm.c
    @@ -21,10 +21,8 @@ size_t ZSTD_ldm_initializeParameters(ldmParams_t* params, U32 enableLdm)
     {
         ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);
         params->enableLdm = enableLdm>0;
    -    params->hashLog = 0;
    -    params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
    -    params->minMatchLength = LDM_MIN_MATCH_LENGTH;
    -    params->hashEveryLog = ZSTD_LDM_HASHEVERYLOG_NOTSET;
    +    if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
    +    if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH;
         return 0;
     }
     
    @@ -32,6 +30,9 @@ void ZSTD_ldm_adjustParameters(ldmParams_t* params,
                                    ZSTD_compressionParameters const* cParams)
     {
         U32 const windowLog = cParams->windowLog;
    +    DEBUGLOG(4, "ZSTD_ldm_adjustParameters");
    +    if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
    +    if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH;
         if (cParams->strategy >= ZSTD_btopt) {
           /* Get out of the way of the optimal parser */
           U32 const minMatch = MAX(cParams->targetLength, params->minMatchLength);
    @@ -43,7 +44,7 @@ void ZSTD_ldm_adjustParameters(ldmParams_t* params,
             params->hashLog = MAX(ZSTD_HASHLOG_MIN, windowLog - LDM_HASH_RLOG);
             assert(params->hashLog <= ZSTD_HASHLOG_MAX);
         }
    -    if (params->hashEveryLog == ZSTD_LDM_HASHEVERYLOG_NOTSET) {
    +    if (params->hashEveryLog == 0) {
             params->hashEveryLog =
                     windowLog < params->hashLog ? 0 : windowLog - params->hashLog;
         }
    @@ -183,6 +184,7 @@ static U64 ZSTD_ldm_ipow(U64 base, U64 exp)
     }
     
     U64 ZSTD_ldm_getHashPower(U32 minMatchLength) {
    +    DEBUGLOG(4, "ZSTD_ldm_getHashPower: mml=%u", minMatchLength);
         assert(minMatchLength >= ZSTD_LDM_MINMATCH_MIN);
         return ZSTD_ldm_ipow(prime8bytes, minMatchLength - 1);
     }
    diff --git a/lib/compress/zstd_ldm.h b/lib/compress/zstd_ldm.h
    index 9d2f7c391..d719a3c04 100644
    --- a/lib/compress/zstd_ldm.h
    +++ b/lib/compress/zstd_ldm.h
    @@ -22,7 +22,6 @@ extern "C" {
     ***************************************/
     
     #define ZSTD_LDM_DEFAULT_WINDOW_LOG ZSTD_WINDOWLOG_DEFAULTMAX
    -#define ZSTD_LDM_HASHEVERYLOG_NOTSET 9999
     
     /**
      * ZSTD_ldm_generateSequences():
    @@ -39,8 +38,8 @@ extern "C" {
      *       sequences.
      */
     size_t ZSTD_ldm_generateSequences(
    -        ldmState_t* ldms, rawSeqStore_t* sequences,
    -        ldmParams_t const* params, void const* src, size_t srcSize);
    +            ldmState_t* ldms, rawSeqStore_t* sequences,
    +            ldmParams_t const* params, void const* src, size_t srcSize);
     
     /**
      * ZSTD_ldm_blockCompress():
    @@ -61,9 +60,10 @@ size_t ZSTD_ldm_generateSequences(
      * NOTE: This function does not return any errors.
      */
     size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
    -    ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
    -    ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize,
    -    int const extDict);
    +        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
    +        ZSTD_compressionParameters const* cParams,
    +        void const* src, size_t srcSize,
    +        int const extDict);
     
     
     /** ZSTD_ldm_initializeParameters() :
    diff --git a/lib/zstd.h b/lib/zstd.h
    index 4b1b92c18..367eab905 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -991,38 +991,33 @@ typedef enum {
                                   * Special: value 0 means "do not change strategy". */
     
         ZSTD_p_enableLongDistanceMatching=160, /* Enable long distance matching.
    -                                         * This parameter is designed to improve the compression
    -                                         * ratio for large inputs with long distance matches.
    -                                         * This increases the memory usage as well as window size.
    -                                         * Note: setting this parameter sets all the LDM parameters
    -                                         * as well as ZSTD_p_windowLog. It should be set after
    -                                         * ZSTD_p_compressionLevel and before ZSTD_p_windowLog and
    -                                         * other LDM parameters. Setting the compression level
    -                                         * after this parameter overrides the window log, though LDM
    -                                         * will remain enabled until explicitly disabled. */
    +                                         * This parameter is designed to improve compression ratio
    +                                         * for large inputs, thanks to long distance matches.
    +                                         * It increases memory usage and window size.
    +                                         * Note: setting this parameter sets ZSTD_p_windowLog.
    +                                         * Setting compression level after LDM overrides the window log,
    +                                         * though LDM will remain enabled until explicitly disabled. */
         ZSTD_p_ldmHashLog,       /* Size of the table for long distance matching, as a power of 2.
                                   * Larger values increase memory usage and compression ratio, but decrease
                                   * compression speed.
                                   * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
                                   * (default: windowlog - 7).
    -                              * Special: value 0 means "do not change ldmHashLog". */
    +                              * Special: value 0 means "automatically determine hashlog". */
         ZSTD_p_ldmMinMatch,      /* Minimum size of searched matches for long distance matcher.
                                   * Larger/too small values usually decrease compression ratio.
    -                              * Must be clamped between ZSTD_LDM_MINMATCH_MIN
    -                              * and ZSTD_LDM_MINMATCH_MAX (default: 64).
    -                              * Special: value 0 means "do not change ldmMinMatch". */
    +                              * Must be clamped between ZSTD_LDM_MINMATCH_MIN and ZSTD_LDM_MINMATCH_MAX.
    +                              * Special: value 0 means "use default value" (default: 64). */
         ZSTD_p_ldmBucketSizeLog, /* Log size of each bucket in the LDM hash table for collision resolution.
    -                              * Larger values usually improve collision resolution but may decrease
    -                              * compression speed.
    -                              * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX (default: 3).
    -                              * note : 0 is a valid value */
    +                              * Larger values improve collision resolution but decrease compression speed.
    +                              * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX .
    +                              * Special: value 0 means "use default value" (default: 3). */
         ZSTD_p_ldmHashEveryLog,  /* Frequency of inserting/looking up entries in the LDM hash table.
                                   * The default is MAX(0, (windowLog - ldmHashLog)) to
                                   * optimize hash table usage.
    -                              * Larger values improve compression speed. Deviating far from the
    -                              * default value will likely result in a decrease in compression ratio.
    -                              * Must be clamped between 0 and ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN.
    -                              * note : 0 is a valid value */
    +                              * Larger values improve compression speed.
    +                              * Deviating far from default value will likely result in a compression ratio decrease.
    +                              * Must be clamped between 0 and (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN).
    +                              * Special: value 0 means "automatically determine hashEveryLog". */
     
         /* frame parameters */
         ZSTD_p_contentSizeFlag=200, /* Content size will be written into frame header _whenever known_ (default:1)
    
    From 2253d01b271ed7a0d2ebe359b55aa5b3c0f1f904 Mon Sep 17 00:00:00 2001
    From: Nick Terrell 
    Date: Wed, 28 Feb 2018 20:10:44 -0800
    Subject: [PATCH 198/255] Move XXH64_update() into worker threads
    
    * Computes the XXH hash in the worker threads.
    * Workers get a sequence number and wait until ther number shows up. On
      error, ensures that its sequence is finished, so future threads don't
      get blocked.
    * Sets up for ldm integration, which will go in the same spot.
    ---
     lib/compress/zstdmt_compress.c | 117 ++++++++++++++++++++++++++-------
     1 file changed, 94 insertions(+), 23 deletions(-)
    
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index 4b2369007..e5bad4210 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -304,16 +304,81 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx)
         ZSTD_pthread_mutex_unlock(&pool->poolMutex);
     }
     
    -
    -/* ------------------------------------------ */
    -/* =====          Worker thread         ===== */
    -/* ------------------------------------------ */
    +/* ====   Serial State   ==== */
     
     typedef struct {
         void const* start;
         size_t size;
     } range_t;
     
    +typedef struct {
    +    ZSTD_pthread_mutex_t mutex;
    +    ZSTD_pthread_cond_t cond;
    +    ZSTD_CCtx_params params;
    +    XXH64_state_t xxhState;
    +    unsigned nextJobID;
    +} serialState_t;
    +
    +static void ZSTDMT_serialState_reset(serialState_t* serialState, ZSTD_CCtx_params params)
    +{
    +    serialState->nextJobID = 0;
    +    if (params.fParams.checksumFlag)
    +        XXH64_reset(&serialState->xxhState, 0);
    +    serialState->params = params;
    +}
    +
    +static int ZSTDMT_serialState_init(serialState_t* serialState)
    +{
    +    int initError = 0;
    +    initError |= ZSTD_pthread_mutex_init(&serialState->mutex, NULL);
    +    initError |= ZSTD_pthread_cond_init(&serialState->cond, NULL);
    +    return initError;
    +}
    +
    +static void ZSTDMT_serialState_free(serialState_t* serialState)
    +{
    +    ZSTD_pthread_mutex_destroy(&serialState->mutex);
    +    ZSTD_pthread_cond_destroy(&serialState->cond);
    +}
    +
    +static void ZSTDMT_serialState_update(serialState_t* serialState, range_t src, unsigned jobID)
    +{
    +    /* Wait for our turn */
    +    ZSTD_PTHREAD_MUTEX_LOCK(&serialState->mutex);
    +    while (serialState->nextJobID < jobID) {
    +        ZSTD_pthread_cond_wait(&serialState->cond, &serialState->mutex);
    +    }
    +    /* A future job may error and skip our job */
    +    if (serialState->nextJobID == jobID) {
    +        /* It is now our turn, do any processing necessary */
    +        if (serialState->params.fParams.checksumFlag && src.size > 0)
    +            XXH64_update(&serialState->xxhState, src.start, src.size);
    +    }
    +    /* Now it is the next jobs turn */
    +    serialState->nextJobID++;
    +    ZSTD_pthread_cond_broadcast(&serialState->cond);
    +    ZSTD_pthread_mutex_unlock(&serialState->mutex);
    +}
    +
    +static void ZSTDMT_serialState_ensureFinished(serialState_t* serialState,
    +                                              unsigned jobID, size_t cSize)
    +{
    +    ZSTD_PTHREAD_MUTEX_LOCK(&serialState->mutex);
    +    if (serialState->nextJobID <= jobID) {
    +        assert(ZSTD_isError(cSize)); (void)cSize;
    +        DEBUGLOG(5, "Skipping past job %u because of error", jobID);
    +        serialState->nextJobID = jobID + 1;
    +        ZSTD_pthread_cond_broadcast(&serialState->cond);
    +    }
    +    ZSTD_pthread_mutex_unlock(&serialState->mutex);
    +
    +}
    +
    +
    +/* ------------------------------------------ */
    +/* =====          Worker thread         ===== */
    +/* ------------------------------------------ */
    +
     static const range_t kNullRange = { NULL, 0 };
     
     typedef struct {
    @@ -323,9 +388,11 @@ typedef struct {
         ZSTD_pthread_cond_t job_cond;        /* Thread-safe - used by mtctx and worker */
         ZSTDMT_CCtxPool* cctxPool;           /* Thread-safe - used by mtctx and (all) workers */
         ZSTDMT_bufferPool* bufPool;          /* Thread-safe - used by mtctx and (all) workers */
    +    serialState_t* serial;               /* Thread-safe - used by mtctx and (all) workers */
         buffer_t dstBuff;                    /* set by worker (or mtctx), then read by worker & mtctx, then modified by mtctx => no barrier */
         range_t prefix;                      /* set by mtctx, then read by worker & mtctx => no barrier */
         range_t src;                         /* set by mtctx, then read by worker & mtctx => no barrier */
    +    unsigned jobID;                      /* set by mtctx, then read by worker => no barrier */
         unsigned firstJob;                   /* set by mtctx, then read by worker => no barrier */
         unsigned lastJob;                    /* set by mtctx, then read by worker => no barrier */
         ZSTD_CCtx_params params;             /* set by mtctx, then read by worker => no barrier */
    @@ -339,9 +406,13 @@ typedef struct {
     void ZSTDMT_compressionJob(void* jobDescription)
     {
         ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription;
    +    ZSTD_CCtx_params jobParams = job->params;   /* do not modify job->params ! copy it, modify the copy */
         ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool);
         buffer_t dstBuff = job->dstBuff;
     
    +    /* Don't compute the checksum for chunks, but write it in the header */
    +    if (job->jobID != 0) jobParams.fParams.checksumFlag = 0;
    +
         /* ressources */
         if (cctx==NULL) {
             job->cSize = ERROR(memory_allocation);
    @@ -358,12 +429,11 @@ void ZSTDMT_compressionJob(void* jobDescription)
     
         /* init */
         if (job->cdict) {
    -        size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dm_auto, job->cdict, job->params, job->fullFrameSize);
    +        size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dm_auto, job->cdict, jobParams, job->fullFrameSize);
             assert(job->firstJob);  /* only allowed for first job */
             if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; }
         } else {  /* srcStart points at reloaded section */
             U64 const pledgedSrcSize = job->firstJob ? job->fullFrameSize : job->src.size;
    -        ZSTD_CCtx_params jobParams = job->params;   /* do not modify job->params ! copy it, modify the copy */
             {   size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstJob);
                 if (ZSTD_isError(forceWindowError)) {
                     job->cSize = forceWindowError;
    @@ -377,6 +447,10 @@ void ZSTDMT_compressionJob(void* jobDescription)
                     job->cSize = initError;
                     goto _endJob;
         }   }   }
    +
    +    /* Perform serial step as early as possible */
    +    ZSTDMT_serialState_update(job->serial, job->src, job->jobID);
    +
         if (!job->firstJob) {  /* flush and overwrite frame header when it's not first job */
             size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0);
             if (ZSTD_isError(hSize)) { job->cSize = hSize; /* save error code */ goto _endJob; }
    @@ -425,6 +499,7 @@ void ZSTDMT_compressionJob(void* jobDescription)
         }   }
     
     _endJob:
    +    ZSTDMT_serialState_ensureFinished(job->serial, job->jobID, job->cSize);
         if (job->prefix.size > 0)
             DEBUGLOG(5, "Finished with prefix: %zx", (size_t)job->prefix.start);
         DEBUGLOG(5, "Finished with source: %zx", (size_t)job->src.start);
    @@ -475,7 +550,7 @@ struct ZSTDMT_CCtx_s {
         roundBuff_t roundBuff;
         inBuff_t inBuff;
         int jobReady;        /* 1 => one job is already prepared, but pool has shortage of workers. Don't create another one. */
    -    XXH64_state_t xxhState;
    +    serialState_t serial;
         unsigned singleBlockingThread;
         unsigned jobIDMask;
         unsigned doneJobID;
    @@ -540,6 +615,7 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem)
     {
         ZSTDMT_CCtx* mtctx;
         U32 nbJobs = nbWorkers + 2;
    +    int initError;
         DEBUGLOG(3, "ZSTDMT_createCCtx_advanced (nbWorkers = %u)", nbWorkers);
     
         if (nbWorkers < 1) return NULL;
    @@ -559,8 +635,9 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem)
         mtctx->jobIDMask = nbJobs - 1;
         mtctx->bufPool = ZSTDMT_createBufferPool(nbWorkers, cMem);
         mtctx->cctxPool = ZSTDMT_createCCtxPool(nbWorkers, cMem);
    +    initError = ZSTDMT_serialState_init(&mtctx->serial);
         mtctx->roundBuff = kNullRoundBuff;
    -    if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool) {
    +    if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool | initError) {
             ZSTDMT_freeCCtx(mtctx);
             return NULL;
         }
    @@ -615,6 +692,7 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx)
         ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem);
         ZSTDMT_freeBufferPool(mtctx->bufPool);
         ZSTDMT_freeCCtxPool(mtctx->cctxPool);
    +    ZSTDMT_serialState_free(&mtctx->serial);
         ZSTD_freeCDict(mtctx->cdictLocal);
         if (mtctx->roundBuff.buffer)
             ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem);
    @@ -779,7 +857,6 @@ static size_t ZSTDMT_compress_advanced_internal(
         size_t remainingSrcSize = srcSize;
         unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbJobs : (unsigned)(dstCapacity / ZSTD_compressBound(avgJobSize));  /* presumes avgJobSize >= 256 KB, which should be the case */
         size_t frameStartPos = 0, dstBufferPos = 0;
    -    XXH64_state_t xxh64;
         assert(jobParams.nbWorkers == 0);
         assert(mtctx->cctxPool->totalCCtx == params.nbWorkers);
     
    @@ -795,7 +872,7 @@ static size_t ZSTDMT_compress_advanced_internal(
     
         assert(avgJobSize >= 256 KB);  /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), required to compress directly into Dst (no additional buffer) */
         ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgJobSize) );
    -    XXH64_reset(&xxh64, 0);
    +    ZSTDMT_serialState_reset(&mtctx->serial, params);
     
         if (nbJobs > mtctx->jobIDMask+1) {  /* enlarge job table */
             U32 jobsTableSize = nbJobs;
    @@ -825,17 +902,14 @@ static size_t ZSTDMT_compress_advanced_internal(
                 mtctx->jobs[u].fullFrameSize = srcSize;
                 mtctx->jobs[u].params = jobParams;
                 /* do not calculate checksum within sections, but write it in header for first section */
    -            if (u!=0) mtctx->jobs[u].params.fParams.checksumFlag = 0;
                 mtctx->jobs[u].dstBuff = dstBuffer;
                 mtctx->jobs[u].cctxPool = mtctx->cctxPool;
                 mtctx->jobs[u].bufPool = mtctx->bufPool;
    +            mtctx->jobs[u].serial = &mtctx->serial;
    +            mtctx->jobs[u].jobID = u;
                 mtctx->jobs[u].firstJob = (u==0);
                 mtctx->jobs[u].lastJob = (u==nbJobs-1);
     
    -            if (params.fParams.checksumFlag) {
    -                XXH64_update(&xxh64, srcStart + frameStartPos, jobSize);
    -            }
    -
                 DEBUGLOG(5, "ZSTDMT_compress_advanced_internal: posting job %u  (%u bytes)", u, (U32)jobSize);
                 DEBUG_PRINTHEX(6, mtctx->jobs[u].prefix.start, 12);
                 POOL_add(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[u]);
    @@ -876,7 +950,7 @@ static size_t ZSTDMT_compress_advanced_internal(
     
             DEBUGLOG(4, "checksumFlag : %u ", params.fParams.checksumFlag);
             if (params.fParams.checksumFlag) {
    -            U32 const checksum = (U32)XXH64_digest(&xxh64);
    +            U32 const checksum = (U32)XXH64_digest(&mtctx->serial.xxhState);
                 if (dstPos + 4 > dstCapacity) {
                     error = ERROR(dstSize_tooSmall);
                 } else {
    @@ -1016,7 +1090,7 @@ size_t ZSTDMT_initCStream_internal(
         mtctx->allJobsCompleted = 0;
         mtctx->consumed = 0;
         mtctx->produced = 0;
    -    if (params.fParams.checksumFlag) XXH64_reset(&mtctx->xxhState, 0);
    +    ZSTDMT_serialState_reset(&mtctx->serial, params);
         return 0;
     }
     
    @@ -1113,21 +1187,18 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS
             mtctx->jobs[jobID].consumed = 0;
             mtctx->jobs[jobID].cSize = 0;
             mtctx->jobs[jobID].params = mtctx->params;
    -        /* do not calculate checksum within sections, but write it in header for first section */
    -        if (mtctx->nextJobID) mtctx->jobs[jobID].params.fParams.checksumFlag = 0;
             mtctx->jobs[jobID].cdict = mtctx->nextJobID==0 ? mtctx->cdict : NULL;
             mtctx->jobs[jobID].fullFrameSize = mtctx->frameContentSize;
             mtctx->jobs[jobID].dstBuff = g_nullBuffer;
             mtctx->jobs[jobID].cctxPool = mtctx->cctxPool;
             mtctx->jobs[jobID].bufPool = mtctx->bufPool;
    +        mtctx->jobs[jobID].serial = &mtctx->serial;
    +        mtctx->jobs[jobID].jobID = mtctx->nextJobID;
             mtctx->jobs[jobID].firstJob = (mtctx->nextJobID==0);
             mtctx->jobs[jobID].lastJob = endFrame;
             mtctx->jobs[jobID].frameChecksumNeeded = endFrame && (mtctx->nextJobID>0) && mtctx->params.fParams.checksumFlag;
             mtctx->jobs[jobID].dstFlushed = 0;
     
    -        if (mtctx->params.fParams.checksumFlag && srcSize > 0)
    -            XXH64_update(&mtctx->xxhState, src, srcSize);
    -
             /* Update the round buffer pos and clear the input buffer to be reset */
             mtctx->roundBuff.pos += srcSize;
             mtctx->inBuff.buffer = g_nullBuffer;
    @@ -1214,7 +1285,7 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
             assert(srcConsumed <= srcSize);
             if ( (srcConsumed == srcSize)   /* job completed -> worker no longer active */
               && mtctx->jobs[wJobID].frameChecksumNeeded ) {
    -            U32 const checksum = (U32)XXH64_digest(&mtctx->xxhState);
    +            U32 const checksum = (U32)XXH64_digest(&mtctx->serial.xxhState);
                 DEBUGLOG(4, "ZSTDMT_flushProduced: writing checksum : %08X \n", checksum);
                 MEM_writeLE32((char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].cSize, checksum);
                 cSize += 4;
    
    From 6f4d0778a5c23a0174078d054dee766c38f2d821 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Mon, 19 Mar 2018 14:41:23 -0700
    Subject: [PATCH 199/255] make it possible to express compression parameters in
     any order
    
    ---
     lib/compress/zstd_compress.c          | 156 ++++++++++----------------
     lib/compress/zstd_compress_internal.h |  10 +-
     lib/compress/zstd_ldm.c               |  10 +-
     lib/compress/zstd_ldm.h               |   4 -
     lib/compress/zstdmt_compress.c        |  14 +--
     lib/compress/zstdmt_compress.h        |   5 +-
     lib/zstd.h                            |  61 +++++-----
     7 files changed, 110 insertions(+), 150 deletions(-)
    
    diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
    index 42bcc4c05..7b45a9719 100644
    --- a/lib/compress/zstd_compress.c
    +++ b/lib/compress/zstd_compress.c
    @@ -143,28 +143,19 @@ size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs)
     /* private API call, for dictBuilder only */
     const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStore); }
     
    -static ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(
    -        ZSTD_CCtx_params CCtxParams, U64 srcSizeHint, size_t dictSize)
    +ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(
    +        const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize)
     {
    -    DEBUGLOG(5, "ZSTD_getCParamsFromCCtxParams: srcSize = %u, dictSize = %u",
    -                (U32)srcSizeHint, (U32)dictSize);
    -    return (CCtxParams.compressionLevel == ZSTD_CLEVEL_CUSTOM) ?
    -                CCtxParams.cParams :
    -                ZSTD_getCParams(CCtxParams.compressionLevel, srcSizeHint, dictSize);
    -}
    -
    -static void ZSTD_cLevelToCCtxParams_srcSize(ZSTD_CCtx_params* CCtxParams, U64 srcSize)
    -{
    -    DEBUGLOG(4, "ZSTD_cLevelToCCtxParams_srcSize: srcSize = %u",
    -                (U32)srcSize);
    -    CCtxParams->cParams = ZSTD_getCParamsFromCCtxParams(*CCtxParams, srcSize, 0);
    -    CCtxParams->compressionLevel = ZSTD_CLEVEL_CUSTOM;
    -}
    -
    -static void ZSTD_cLevelToCCtxParams(ZSTD_CCtx_params* CCtxParams)
    -{
    -    DEBUGLOG(4, "ZSTD_cLevelToCCtxParams");
    -    ZSTD_cLevelToCCtxParams_srcSize(CCtxParams, ZSTD_CONTENTSIZE_UNKNOWN);
    +    ZSTD_compressionParameters cParams = ZSTD_getCParams(CCtxParams->compressionLevel, srcSizeHint, dictSize);
    +    if (CCtxParams->ldmParams.enableLdm) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG;
    +    if (CCtxParams->cParams.windowLog) cParams.windowLog = CCtxParams->cParams.windowLog;
    +    if (CCtxParams->cParams.hashLog) cParams.hashLog = CCtxParams->cParams.hashLog;
    +    if (CCtxParams->cParams.chainLog) cParams.chainLog = CCtxParams->cParams.chainLog;
    +    if (CCtxParams->cParams.searchLog) cParams.searchLog = CCtxParams->cParams.searchLog;
    +    if (CCtxParams->cParams.searchLength) cParams.searchLength = CCtxParams->cParams.searchLength;
    +    if (CCtxParams->cParams.targetLength) cParams.targetLength = CCtxParams->cParams.targetLength;
    +    if (CCtxParams->cParams.strategy) cParams.strategy = CCtxParams->cParams.strategy;
    +    return cParams;
     }
     
     static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams(
    @@ -173,7 +164,9 @@ static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams(
         ZSTD_CCtx_params cctxParams;
         memset(&cctxParams, 0, sizeof(cctxParams));
         cctxParams.cParams = cParams;
    -    cctxParams.compressionLevel = ZSTD_CLEVEL_CUSTOM;
    +    cctxParams.compressionLevel = ZSTD_CLEVEL_DEFAULT;  /* should not matter, as all cParams are presumed properly defined */
    +    assert(!ZSTD_checkCParams(cParams));
    +    cctxParams.fParams.contentSizeFlag = 1;
         return cctxParams;
     }
     
    @@ -187,6 +180,7 @@ static ZSTD_CCtx_params* ZSTD_createCCtxParams_advanced(
         if (!params) { return NULL; }
         params->customMem = customMem;
         params->compressionLevel = ZSTD_CLEVEL_DEFAULT;
    +    params->fParams.contentSizeFlag = 1;
         return params;
     }
     
    @@ -211,6 +205,7 @@ size_t ZSTD_initCCtxParams(ZSTD_CCtx_params* cctxParams, int compressionLevel) {
         if (!cctxParams) { return ERROR(GENERIC); }
         memset(cctxParams, 0, sizeof(*cctxParams));
         cctxParams->compressionLevel = compressionLevel;
    +    cctxParams->fParams.contentSizeFlag = 1;
         return 0;
     }
     
    @@ -221,17 +216,21 @@ size_t ZSTD_initCCtxParams_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameter
         memset(cctxParams, 0, sizeof(*cctxParams));
         cctxParams->cParams = params.cParams;
         cctxParams->fParams = params.fParams;
    -    cctxParams->compressionLevel = ZSTD_CLEVEL_CUSTOM;
    +    cctxParams->compressionLevel = ZSTD_CLEVEL_DEFAULT;   /* should not matter, as all cParams are presumed properly defined */
    +    assert(!ZSTD_checkCParams(params.cParams));
         return 0;
     }
     
    +/* ZSTD_assignParamsToCCtxParams() :
    + * params is presumed valid at this stage */
     static ZSTD_CCtx_params ZSTD_assignParamsToCCtxParams(
             ZSTD_CCtx_params cctxParams, ZSTD_parameters params)
     {
         ZSTD_CCtx_params ret = cctxParams;
         ret.cParams = params.cParams;
         ret.fParams = params.fParams;
    -    ret.compressionLevel = ZSTD_CLEVEL_CUSTOM;
    +    ret.compressionLevel = ZSTD_CLEVEL_DEFAULT;   /* should not matter, as all cParams are presumed properly defined */
    +    assert(!ZSTD_checkCParams(params.cParams));
         return ret;
     }
     
    @@ -301,9 +300,6 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v
         case ZSTD_p_targetLength:
         case ZSTD_p_compressionStrategy:
             if (cctx->cdict) return ERROR(stage_wrong);
    -        if (value>0) {
    -            ZSTD_cLevelToCCtxParams_srcSize(&cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1); /* Optimize cParams when srcSize is known */
    -        }
             return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
     
         case ZSTD_p_compressLiterals:
    @@ -328,11 +324,6 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v
             return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
     
         case ZSTD_p_enableLongDistanceMatching:
    -        if (cctx->cdict) return ERROR(stage_wrong);
    -        if (value>0)
    -            ZSTD_cLevelToCCtxParams_srcSize(&cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1); /* Optimize cParams when srcSize is known */
    -        return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
    -
         case ZSTD_p_ldmHashLog:
         case ZSTD_p_ldmMinMatch:
         case ZSTD_p_ldmBucketSizeLog:
    @@ -368,65 +359,50 @@ size_t ZSTD_CCtxParam_setParameter(
         }
     
         case ZSTD_p_windowLog :
    -        DEBUGLOG(4, "ZSTD_CCtxParam_setParameter: set windowLog=%u", value);
    -        if (value) {  /* 0 : does not change current windowLog */
    +        if (value)   /* 0 => use default */
                 CLAMPCHECK(value, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX);
    -            ZSTD_cLevelToCCtxParams(CCtxParams);
    -            CCtxParams->cParams.windowLog = value;
    -        }
    +        CCtxParams->cParams.windowLog = value;
             return CCtxParams->cParams.windowLog;
     
         case ZSTD_p_hashLog :
    -        if (value) { /* 0 : does not change current hashLog */
    +        if (value)   /* 0 => use default */
                 CLAMPCHECK(value, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX);
    -            ZSTD_cLevelToCCtxParams(CCtxParams);
    -            CCtxParams->cParams.hashLog = value;
    -        }
    +        CCtxParams->cParams.hashLog = value;
             return CCtxParams->cParams.hashLog;
     
         case ZSTD_p_chainLog :
    -        if (value) { /* 0 : does not change current chainLog */
    +        if (value)   /* 0 => use default */
                 CLAMPCHECK(value, ZSTD_CHAINLOG_MIN, ZSTD_CHAINLOG_MAX);
    -            ZSTD_cLevelToCCtxParams(CCtxParams);
    -            CCtxParams->cParams.chainLog = value;
    -        }
    +        CCtxParams->cParams.chainLog = value;
             return CCtxParams->cParams.chainLog;
     
         case ZSTD_p_searchLog :
    -        if (value) { /* 0 : does not change current searchLog */
    +        if (value)   /* 0 => use default */
                 CLAMPCHECK(value, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX);
    -            ZSTD_cLevelToCCtxParams(CCtxParams);
    -            CCtxParams->cParams.searchLog = value;
    -        }
    +        CCtxParams->cParams.searchLog = value;
             return value;
     
         case ZSTD_p_minMatch :
    -        if (value) { /* 0 : does not change current minMatch length */
    +        if (value)   /* 0 => use default */
                 CLAMPCHECK(value, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX);
    -            ZSTD_cLevelToCCtxParams(CCtxParams);
    -            CCtxParams->cParams.searchLength = value;
    -        }
    +        CCtxParams->cParams.searchLength = value;
             return CCtxParams->cParams.searchLength;
     
         case ZSTD_p_targetLength :
    -        if (value) { /* 0 : does not change current sufficient_len */
    +        if (value)   /* 0 => use default */
                 CLAMPCHECK(value, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX);
    -            ZSTD_cLevelToCCtxParams(CCtxParams);
    -            CCtxParams->cParams.targetLength = value;
    -        }
    +        CCtxParams->cParams.targetLength = value;
             return CCtxParams->cParams.targetLength;
     
         case ZSTD_p_compressionStrategy :
    -        if (value) { /* 0 : does not change currentstrategy */
    +        if (value)   /* 0 => use default */
                 CLAMPCHECK(value, (unsigned)ZSTD_fast, (unsigned)ZSTD_btultra);
    -            ZSTD_cLevelToCCtxParams(CCtxParams);
    -            CCtxParams->cParams.strategy = (ZSTD_strategy)value;
    -        }
    +        CCtxParams->cParams.strategy = (ZSTD_strategy)value;
             return (size_t)CCtxParams->cParams.strategy;
     
         case ZSTD_p_compressLiterals:
             CCtxParams->disableLiteralCompression = !value;
    -        return !!value;
    +        return !CCtxParams->disableLiteralCompression;
     
         case ZSTD_p_contentSizeFlag :
             /* Content size written in frame header _when known_ (default:1) */
    @@ -441,7 +417,7 @@ size_t ZSTD_CCtxParam_setParameter(
     
         case ZSTD_p_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */
             DEBUGLOG(4, "set dictIDFlag = %u", (value>0));
    -        CCtxParams->fParams.noDictIDFlag = (value == 0);
    +        CCtxParams->fParams.noDictIDFlag = !value;
             return !CCtxParams->fParams.noDictIDFlag;
     
         case ZSTD_p_forceMaxWindow :
    @@ -471,11 +447,8 @@ size_t ZSTD_CCtxParam_setParameter(
     #endif
     
         case ZSTD_p_enableLongDistanceMatching :
    -        if (value) {
    -            ZSTD_cLevelToCCtxParams(CCtxParams);
    -            CCtxParams->cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG;
    -        }
    -        return ZSTD_ldm_initializeParameters(&CCtxParams->ldmParams, value);
    +        CCtxParams->ldmParams.enableLdm = (value>0);
    +        return CCtxParams->ldmParams.enableLdm;
     
         case ZSTD_p_ldmHashLog :
             if (value>0)   /* 0 ==> auto */
    @@ -490,18 +463,16 @@ size_t ZSTD_CCtxParam_setParameter(
             return CCtxParams->ldmParams.minMatchLength;
     
         case ZSTD_p_ldmBucketSizeLog :
    -        if (value > ZSTD_LDM_BUCKETSIZELOG_MAX) {
    +        if (value > ZSTD_LDM_BUCKETSIZELOG_MAX)
                 return ERROR(parameter_outOfBound);
    -        }
             CCtxParams->ldmParams.bucketSizeLog = value;
    -        return value;
    +        return CCtxParams->ldmParams.bucketSizeLog;
     
         case ZSTD_p_ldmHashEveryLog :
    -        if (value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN) {
    +        if (value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN)
                 return ERROR(parameter_outOfBound);
    -        }
             CCtxParams->ldmParams.hashEveryLog = value;
    -        return value;
    +        return CCtxParams->ldmParams.hashEveryLog;
     
         default: return ERROR(parameter_unsupported);
         }
    @@ -545,7 +516,7 @@ size_t ZSTD_CCtx_loadDictionary_advanced(
             cctx->cdict = NULL;
         } else {
             ZSTD_compressionParameters const cParams =
    -                ZSTD_getCParamsFromCCtxParams(cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, dictSize);
    +                ZSTD_getCParamsFromCCtxParams(&cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, dictSize);
             cctx->cdictLocal = ZSTD_createCDict_advanced(
                                     dict, dictSize,
                                     dictLoadMethod, dictMode,
    @@ -718,14 +689,14 @@ size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params)
         /* Estimate CCtx size is supported for single-threaded compression only. */
         if (params->nbWorkers > 0) { return ERROR(GENERIC); }
         {   ZSTD_compressionParameters const cParams =
    -                ZSTD_getCParamsFromCCtxParams(*params, 0, 0);
    +                ZSTD_getCParamsFromCCtxParams(params, 0, 0);
             size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog);
             U32    const divider = (cParams.searchLength==3) ? 3 : 4;
             size_t const maxNbSeq = blockSize / divider;
             size_t const tokenSpace = blockSize + 11*maxNbSeq;
             size_t const entropySpace = HUF_WORKSPACE_SIZE;
             size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t);
    -        size_t const matchStateSize = ZSTD_sizeof_matchState(¶ms->cParams, /* forCCtx */ 1);
    +        size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 1);
     
             size_t const ldmSpace = ZSTD_ldm_getTableSize(params->ldmParams);
             size_t const ldmSeqSpace = ZSTD_ldm_getMaxNbSeq(params->ldmParams, blockSize) * sizeof(rawSeq);
    @@ -2093,7 +2064,7 @@ size_t ZSTD_compressContinue (ZSTD_CCtx* cctx,
     size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx)
     {
         ZSTD_compressionParameters const cParams =
    -            ZSTD_getCParamsFromCCtxParams(cctx->appliedParams, 0, 0);
    +            ZSTD_getCParamsFromCCtxParams(&(cctx->appliedParams), 0, 0);
         return MIN (ZSTD_BLOCKSIZE_MAX, (U32)1 << cParams.windowLog);
     }
     
    @@ -2536,6 +2507,7 @@ static size_t ZSTD_initCDict_internal(
                         ZSTD_compressionParameters cParams)
     {
         DEBUGLOG(3, "ZSTD_initCDict_internal, mode %u", (U32)dictMode);
    +    assert(!ZSTD_checkCParams(cParams));
         cdict->cParams = cParams;
         if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) {
             cdict->dictBuffer = NULL;
    @@ -2551,8 +2523,7 @@ static size_t ZSTD_initCDict_internal(
     
         /* Reset the state to no dictionary */
         ZSTD_reset_compressedBlockState(&cdict->cBlockState);
    -    {
    -        void* const end = ZSTD_reset_matchState(
    +    {   void* const end = ZSTD_reset_matchState(
                     &cdict->matchState,
                     (U32*)cdict->workspace + HUF_WORKSPACE_SIZE_U32,
                     &cParams, ZSTDcrp_continue, /* forCCtx */ 0);
    @@ -2562,15 +2533,13 @@ static size_t ZSTD_initCDict_internal(
         /* (Maybe) load the dictionary
          * Skips loading the dictionary if it is <= 8 bytes.
          */
    -    {
    -        ZSTD_CCtx_params params;
    +    {   ZSTD_CCtx_params params;
             memset(¶ms, 0, sizeof(params));
             params.compressionLevel = ZSTD_CLEVEL_DEFAULT;
             params.fParams.contentSizeFlag = 1;
             params.cParams = cParams;
    -        {
    -            size_t const dictID = ZSTD_compress_insertDictionary(
    -                    &cdict->cBlockState, &cdict->matchState,¶ms,
    +        {   size_t const dictID = ZSTD_compress_insertDictionary(
    +                    &cdict->cBlockState, &cdict->matchState, ¶ms,
                         cdict->dictContent, cdict->dictContentSize,
                         dictMode, cdict->workspace);
                 if (ZSTD_isError(dictID)) return dictID;
    @@ -2830,7 +2799,7 @@ size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize)
         DEBUGLOG(4, "ZSTD_resetCStream: pledgedSrcSize = %u", (U32)pledgedSrcSize);
         if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
         params.fParams.contentSizeFlag = 1;
    -    params.cParams = ZSTD_getCParamsFromCCtxParams(params, pledgedSrcSize, 0);
    +    params.cParams = ZSTD_getCParamsFromCCtxParams(¶ms, pledgedSrcSize, 0);
         return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, zcs->cdict, params, pledgedSrcSize);
     }
     
    @@ -2867,9 +2836,6 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs,
             zcs->cdict = cdict;
         }
     
    -    params.compressionLevel = ZSTD_CLEVEL_CUSTOM; /* enforce usage of cParams, instead of a dynamic derivation from cLevel (but does that happen ?) */
    -    zcs->requestedParams = params;
    -
         return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, zcs->cdict, params, pledgedSrcSize);
     }
     
    @@ -2899,20 +2865,22 @@ size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict)
         return ZSTD_initCStream_usingCDict_advanced(zcs, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN);  /* note : will check that cdict != NULL */
     }
     
    +
     /* ZSTD_initCStream_advanced() :
    - * pledgedSrcSize must be correct.
    + * pledgedSrcSize must be exact.
      * if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN.
      * dict is loaded with default parameters ZSTD_dm_auto and ZSTD_dlm_byCopy. */
     size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
                                      const void* dict, size_t dictSize,
                                      ZSTD_parameters params, unsigned long long pledgedSrcSize)
     {
    -    ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params);
         DEBUGLOG(4, "ZSTD_initCStream_advanced: pledgedSrcSize=%u, flag=%u",
                     (U32)pledgedSrcSize, params.fParams.contentSizeFlag);
         CHECK_F( ZSTD_checkCParams(params.cParams) );
         if ((pledgedSrcSize==0) && (params.fParams.contentSizeFlag==0)) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;  /* for compatibility with older programs relying on this behavior. Users should now specify ZSTD_CONTENTSIZE_UNKNOWN. This line will be removed in the future. */
    -    return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL /*cdict*/, cctxParams, pledgedSrcSize);
    +    {   ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params);
    +        return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL /*cdict*/, cctxParams, pledgedSrcSize);
    +    }
     }
     
     size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel)
    @@ -3124,7 +3092,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx,
             DEBUGLOG(4, "ZSTD_compress_generic : transparent init stage");
             if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = input->size + 1;  /* auto-fix pledgedSrcSize */
             params.cParams = ZSTD_getCParamsFromCCtxParams(
    -                cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/);
    +                &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/);
     
     #ifdef ZSTD_MULTITHREAD
             if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) {
    @@ -3164,7 +3132,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx,
     #ifdef ZSTD_MULTITHREAD
         if (cctx->appliedParams.nbWorkers > 0) {
             if (cctx->cParamsChanged) {
    -            ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, cctx->requestedParams.compressionLevel, cctx->requestedParams.cParams);
    +            ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams);
                 cctx->cParamsChanged = 0;
             }
             {   size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp);
    diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
    index 0d8bbf296..b726df86d 100644
    --- a/lib/compress/zstd_compress_internal.h
    +++ b/lib/compress/zstd_compress_internal.h
    @@ -32,7 +32,6 @@ extern "C" {
     ***************************************/
     #define kSearchStrength      8
     #define HASH_READ_SIZE       8
    -#define ZSTD_CLEVEL_CUSTOM 999
     #define ZSTD_DUBT_UNSORTED_MARK 1   /* For btlazy2 strategy, index 1 now means "unsorted".
                                            It could be confused for a real successor at index "1", if sorted as larger than its predecessor.
                                            It's not a big deal though : candidate will just be sorted again.
    @@ -183,7 +182,7 @@ struct ZSTD_CCtx_params_s {
         /* Long distance matching parameters */
         ldmParams_t ldmParams;
     
    -    /* For use with createCCtxParams() and freeCCtxParams() only */
    +    /* Internal use, for createCCtxParams() and freeCCtxParams() only */
         ZSTD_customMem customMem;
     };  /* typedef'd to ZSTD_CCtx_params within "zstd.h" */
     
    @@ -629,6 +628,13 @@ MEM_STATIC U32 ZSTD_window_update(ZSTD_window_t* window,
      * These prototypes shall only be called from within lib/compress
      * ============================================================== */
     
    +/* ZSTD_getCParamsFromCCtxParams() :
    + * cParams are built depending on compressionLevel, src size hints, 
    + * LDM and manually set compression parameters.
    + */
    +ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(
    +        const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize);
    +
     /*! ZSTD_initCStream_internal() :
      *  Private use only. Init streaming operation.
      *  expects params to be valid.
    diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c
    index 281260712..4d18e7dca 100644
    --- a/lib/compress/zstd_ldm.c
    +++ b/lib/compress/zstd_ldm.c
    @@ -17,19 +17,11 @@
     #define LDM_HASH_RLOG 7
     #define LDM_HASH_CHAR_OFFSET 10
     
    -size_t ZSTD_ldm_initializeParameters(ldmParams_t* params, U32 enableLdm)
    -{
    -    ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);
    -    params->enableLdm = enableLdm>0;
    -    if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
    -    if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH;
    -    return 0;
    -}
    -
     void ZSTD_ldm_adjustParameters(ldmParams_t* params,
                                    ZSTD_compressionParameters const* cParams)
     {
         U32 const windowLog = cParams->windowLog;
    +    ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);
         DEBUGLOG(4, "ZSTD_ldm_adjustParameters");
         if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
         if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH;
    diff --git a/lib/compress/zstd_ldm.h b/lib/compress/zstd_ldm.h
    index d719a3c04..88ee60286 100644
    --- a/lib/compress/zstd_ldm.h
    +++ b/lib/compress/zstd_ldm.h
    @@ -66,10 +66,6 @@ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
             int const extDict);
     
     
    -/** ZSTD_ldm_initializeParameters() :
    - *  Initialize the long distance matching parameters to their default values. */
    -size_t ZSTD_ldm_initializeParameters(ldmParams_t* params, U32 enableLdm);
    -
     /** ZSTD_ldm_getTableSize() :
      *  Estimate the space needed for long distance matching tables or 0 if LDM is
      *  disabled.
    diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
    index a5b6e0143..666193b5f 100644
    --- a/lib/compress/zstdmt_compress.c
    +++ b/lib/compress/zstdmt_compress.c
    @@ -667,19 +667,19 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params)
     }
     
     /*! ZSTDMT_updateCParams_whileCompressing() :
    - *  Update compression level and parameters (except wlog)
    - *  while compression is ongoing.
    + *  Updates only a selected set of compression parameters, to remain compatible with current frame.
      *  New parameters will be applied to next compression job. */
    -void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, int compressionLevel, ZSTD_compressionParameters cParams)
    +void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* cctxParams)
     {
         U32 const saved_wlog = mtctx->params.cParams.windowLog;   /* Do not modify windowLog while compressing */
    +    int const compressionLevel = cctxParams->compressionLevel;
         DEBUGLOG(5, "ZSTDMT_updateCParams_whileCompressing (level:%i)",
                     compressionLevel);
         mtctx->params.compressionLevel = compressionLevel;
    -    if (compressionLevel != ZSTD_CLEVEL_CUSTOM)
    -        cParams = ZSTD_getCParams(compressionLevel, mtctx->frameContentSize, 0 /* dictSize */ );
    -    cParams.windowLog = saved_wlog;
    -    mtctx->params.cParams = cParams;
    +    {   ZSTD_compressionParameters cParams = ZSTD_getCParamsFromCCtxParams(cctxParams, 0, 0);
    +        cParams.windowLog = saved_wlog;
    +        mtctx->params.cParams = cParams;
    +    }
     }
     
     /* ZSTDMT_getNbWorkers():
    diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h
    index 4364f100d..87800ba1c 100644
    --- a/lib/compress/zstdmt_compress.h
    +++ b/lib/compress/zstdmt_compress.h
    @@ -122,10 +122,9 @@ size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_param
     size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers);
     
     /*! ZSTDMT_updateCParams_whileCompressing() :
    - *  Update compression level and parameters (except wlog)
    - *  while compression is ongoing.
    + *  Updates only a selected set of compression parameters, to remain compatible with current frame.
      *  New parameters will be applied to next compression job. */
    -void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, int compressionLevel, ZSTD_compressionParameters cParams);
    +void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* cctxParams);
     
     /* ZSTDMT_getNbWorkers():
      * @return nb threads currently active in mtctx.
    diff --git a/lib/zstd.h b/lib/zstd.h
    index 367eab905..e817c5287 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -905,9 +905,8 @@ ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
      *   and then applied on all subsequent compression jobs.
      *   When no parameter is ever provided, CCtx is created with compression level ZSTD_CLEVEL_DEFAULT.
      *
    - *   This API is intended to replace all others experimental API.
    - *   It can basically do all other use cases, and even new ones.
    - *   It stands a reasonable chance to become "stable", after a good testing period.
    + *   This API is intended to replace all others advanced / experimental API entry points.
    + *   But it stands a reasonable chance to become "stable", after a reasonable testing period.
      */
     
     /* note on naming convention :
    @@ -924,12 +923,12 @@ ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
      * All enum will be pinned to explicit values before reaching "stable API" status */
     
     typedef enum {
    -    /* Question : should we have a format ZSTD_f_auto ?
    -     * For the time being, it would mean exactly the same as ZSTD_f_zstd1.
    -     * But, in the future, should several formats be supported,
    +    /* Opened question : should we have a format ZSTD_f_auto ?
    +     * Today, it would mean exactly the same as ZSTD_f_zstd1.
    +     * But, in the future, should several formats become supported,
          * on the compression side, it would mean "default format".
    -     * On the decompression side, it would mean "multi format",
    -     * and ZSTD_f_zstd1 could be reserved to mean "accept *only* zstd frames".
    +     * On the decompression side, it would mean "automatic format detection",
    +     * so that ZSTD_f_zstd1 would mean "accept *only* zstd frames".
          * Since meaning is a little different, another option could be to define different enums for compression and decompression.
          * This question could be kept for later, when there are actually multiple formats to support,
          * but there is also the question of pinning enum values, and pinning value `0` is especially important */
    @@ -949,33 +948,34 @@ typedef enum {
                                   * Default level is ZSTD_CLEVEL_DEFAULT==3.
                                   * Special: value 0 means "do not change cLevel".
                                   * Note 1 : it's possible to pass a negative compression level by casting it to unsigned type.
    -                              * Note 2 : setting compressionLevel automatically updates ZSTD_p_compressLiterals. */
    +                              * Note 2 : setting a level sets all default values of other compression parameters.
    +                              * Note 3 : setting compressionLevel automatically updates ZSTD_p_compressLiterals. */
         ZSTD_p_windowLog,        /* Maximum allowed back-reference distance, expressed as power of 2.
                                   * Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX.
    -                              * Special: value 0 means "do not change windowLog".
    +                              * Special: value 0 means "use default windowLog".
                                   * Note: Using a window size greater than ZSTD_MAXWINDOWSIZE_DEFAULT (default: 2^27)
    -                              * requires setting the maximum window size at least as large during decompression. */
    +                              *       requires explicitly allowing such window size during decompression stage. */
         ZSTD_p_hashLog,          /* Size of the probe table, as a power of 2.
                                   * Resulting table size is (1 << (hashLog+2)).
                                   * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX.
                                   * Larger tables improve compression ratio of strategies <= dFast,
                                   * and improve speed of strategies > dFast.
    -                              * Special: value 0 means "do not change hashLog". */
    +                              * Special: value 0 means "use default hashLog". */
         ZSTD_p_chainLog,         /* Size of the full-search table, as a power of 2.
                                   * Resulting table size is (1 << (chainLog+2)).
                                   * Larger tables result in better and slower compression.
                                   * This parameter is useless when using "fast" strategy.
    -                              * Special: value 0 means "do not change chainLog". */
    +                              * Special: value 0 means "use default chainLog". */
         ZSTD_p_searchLog,        /* Number of search attempts, as a power of 2.
                                   * More attempts result in better and slower compression.
                                   * This parameter is useless when using "fast" and "dFast" strategies.
    -                              * Special: value 0 means "do not change searchLog". */
    +                              * Special: value 0 means "use default searchLog". */
         ZSTD_p_minMatch,         /* Minimum size of searched matches (note : repCode matches can be smaller).
                                   * Larger values make faster compression and decompression, but decrease ratio.
                                   * Must be clamped between ZSTD_SEARCHLENGTH_MIN and ZSTD_SEARCHLENGTH_MAX.
                                   * Note that currently, for all strategies < btopt, effective minimum is 4.
    -                              * Note that currently, for all strategies > fast, effective maximum is 6.
    -                              * Special: value 0 means "do not change minMatchLength". */
    +                              *                    , for all strategies > fast, effective maximum is 6.
    +                              * Special: value 0 means "use default minMatchLength". */
         ZSTD_p_targetLength,     /* Impact of this field depends on strategy.
                                   * For strategies btopt & btultra:
                                   *     Length of Match considered "good enough" to stop search.
    @@ -983,27 +983,26 @@ typedef enum {
                                   * For strategy fast:
                                   *     Distance between match sampling.
                                   *     Larger values make compression faster, and weaker.
    -                              * Special: value 0 means "do not change targetLength". */
    +                              * Special: value 0 means "use default targetLength". */
         ZSTD_p_compressionStrategy, /* See ZSTD_strategy enum definition.
                                   * Cast selected strategy as unsigned for ZSTD_CCtx_setParameter() compatibility.
                                   * The higher the value of selected strategy, the more complex it is,
                                   * resulting in stronger and slower compression.
    -                              * Special: value 0 means "do not change strategy". */
    +                              * Special: value 0 means "use default strategy". */
     
         ZSTD_p_enableLongDistanceMatching=160, /* Enable long distance matching.
                                              * This parameter is designed to improve compression ratio
    -                                         * for large inputs, thanks to long distance matches.
    +                                         * for large inputs, by finding large matches at long distance.
                                              * It increases memory usage and window size.
    -                                         * Note: setting this parameter sets ZSTD_p_windowLog.
    -                                         * Setting compression level after LDM overrides the window log,
    -                                         * though LDM will remain enabled until explicitly disabled. */
    +                                         * Note: enabling this parameter increases ZSTD_p_windowLog to 128 MB
    +                                         * except when expressly set to a different value. */
         ZSTD_p_ldmHashLog,       /* Size of the table for long distance matching, as a power of 2.
    -                              * Larger values increase memory usage and compression ratio, but decrease
    -                              * compression speed.
    +                              * Larger values increase memory usage and compression ratio,
    +                              * but decrease compression speed.
                                   * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
    -                              * (default: windowlog - 7).
    +                              * default: windowlog - 7.
                                   * Special: value 0 means "automatically determine hashlog". */
    -    ZSTD_p_ldmMinMatch,      /* Minimum size of searched matches for long distance matcher.
    +    ZSTD_p_ldmMinMatch,      /* Minimum match size for long distance matcher.
                                   * Larger/too small values usually decrease compression ratio.
                                   * Must be clamped between ZSTD_LDM_MINMATCH_MIN and ZSTD_LDM_MINMATCH_MAX.
                                   * Special: value 0 means "use default value" (default: 64). */
    @@ -1012,11 +1011,10 @@ typedef enum {
                                   * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX .
                                   * Special: value 0 means "use default value" (default: 3). */
         ZSTD_p_ldmHashEveryLog,  /* Frequency of inserting/looking up entries in the LDM hash table.
    -                              * The default is MAX(0, (windowLog - ldmHashLog)) to
    -                              * optimize hash table usage.
    +                              * Must be clamped between 0 and (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN).
    +                              * Default is MAX(0, (windowLog - ldmHashLog)), optimizing hash table usage.
                                   * Larger values improve compression speed.
                                   * Deviating far from default value will likely result in a compression ratio decrease.
    -                              * Must be clamped between 0 and (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN).
                                   * Special: value 0 means "automatically determine hashEveryLog". */
     
         /* frame parameters */
    @@ -1044,7 +1042,9 @@ typedef enum {
         ZSTD_p_overlapSizeLog,   /* Size of previous input reloaded at the beginning of each job.
                                   * 0 => no overlap, 6(default) => use 1/8th of windowSize, >=9 => use full windowSize */
     
    -    /* advanced parameters - may not remain available after API update */
    +    /* =================================================================== */
    +    /* experimental parameters - no stability guaranteed                   */
    +    /* =================================================================== */
     
         ZSTD_p_compressLiterals=1000, /* control huffman compression of literals (enabled) by default.
                                   * disabling it improves speed and decreases compression ratio by a large amount.
    @@ -1055,7 +1055,6 @@ typedef enum {
         ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize,
                                   * even when referencing into Dictionary content (default:0) */
     
    -
     } ZSTD_cParameter;
     
     
    
    From c8b3d389fddd67f24475483b2086f109cdbc1806 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Mon, 19 Mar 2018 15:07:26 -0700
    Subject: [PATCH 200/255] updated CCtxParams API
    
    to respect naming convention :
    ZSTD_CCtxParams_*()
    ---
     doc/zstd_manual.html         | 27 ++++++++++++++-------------
     lib/compress/zstd_compress.c |  8 ++++----
     lib/zstd.h                   | 35 ++++++++++++++++++-----------------
     3 files changed, 36 insertions(+), 34 deletions(-)
    
    diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
    index 302b53595..e16bb1913 100644
    --- a/doc/zstd_manual.html
    +++ b/doc/zstd_manual.html
    @@ -936,6 +936,16 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t
                Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode. 
     


    +
    void ZSTD_CCtx_reset(ZSTD_CCtx* cctx);
    +

    Return a CCtx to clean state. + Useful after an error, or to interrupt an ongoing compression job and start a new one. + Any internal data not yet flushed is cancelled. + Dictionary (if any) is dropped. + All parameters are back to default values. + It's possible to modify compression parameters after a reset. + +


    +
    typedef enum {
         ZSTD_e_continue=0, /* collect more data, encoder decides when to output compressed result, for optimal conditions */
         ZSTD_e_flush,      /* flush any data provided so far - frame will continue, future data can still reference previous data for better compression */
    @@ -968,16 +978,6 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t
      
     


    -
    void ZSTD_CCtx_reset(ZSTD_CCtx* cctx);
    -

    Return a CCtx to clean state. - Useful after an error, or to interrupt an ongoing compression job and start a new one. - Any internal data not yet flushed is cancelled. - Dictionary (if any) is dropped. - All parameters are back to default values. - It's possible to modify compression parameters after a reset. - -


    -
    size_t ZSTD_compress_generic_simpleArgs (
                     ZSTD_CCtx* cctx,
                     void* dst, size_t dstCapacity, size_t* dstPos,
    @@ -992,6 +992,7 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t
     


    ZSTD_CCtx_params* ZSTD_createCCtxParams(void);
    +size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params);
     

    Quick howto : - ZSTD_createCCtxParams() : Create a ZSTD_CCtx_params structure - ZSTD_CCtxParam_setParameter() : Push parameters one by one into @@ -1010,18 +1011,18 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t


    -
    size_t ZSTD_resetCCtxParams(ZSTD_CCtx_params* params);
    +
    size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params);
     

    Reset params to default values.


    -
    size_t ZSTD_initCCtxParams(ZSTD_CCtx_params* cctxParams, int compressionLevel);
    +
    size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel);
     

    Initializes the compression parameters of cctxParams according to compression level. All other parameters are reset to their default values.


    -
    size_t ZSTD_initCCtxParams_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params);
    +
    size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params);
     

    Initializes the compression and frame parameters of cctxParams according to params. All other parameters are reset to their default values. diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index db8d7a8c3..d7cf5bd14 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -202,19 +202,19 @@ size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params) return 0; } -size_t ZSTD_resetCCtxParams(ZSTD_CCtx_params* params) +size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params) { - return ZSTD_initCCtxParams(params, ZSTD_CLEVEL_DEFAULT); + return ZSTD_CCtxParams_init(params, ZSTD_CLEVEL_DEFAULT); } -size_t ZSTD_initCCtxParams(ZSTD_CCtx_params* cctxParams, int compressionLevel) { +size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) { if (!cctxParams) { return ERROR(GENERIC); } memset(cctxParams, 0, sizeof(*cctxParams)); cctxParams->compressionLevel = compressionLevel; return 0; } -size_t ZSTD_initCCtxParams_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params) +size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params) { if (!cctxParams) { return ERROR(GENERIC); } CHECK_F( ZSTD_checkCParams(params.cParams) ); diff --git a/lib/zstd.h b/lib/zstd.h index 3ea049eb6..bb16673b6 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -1132,6 +1132,16 @@ ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize); ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictMode_e dictMode); +/*! ZSTD_CCtx_reset() : + * Return a CCtx to clean state. + * Useful after an error, or to interrupt an ongoing compression job and start a new one. + * Any internal data not yet flushed is cancelled. + * Dictionary (if any) is dropped. + * All parameters are back to default values. + * It's possible to modify compression parameters after a reset. + */ +ZSTDLIB_API void ZSTD_CCtx_reset(ZSTD_CCtx* cctx); + typedef enum { @@ -1166,16 +1176,6 @@ ZSTDLIB_API size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, ZSTD_inBuffer* input, ZSTD_EndDirective endOp); -/*! ZSTD_CCtx_reset() : - * Return a CCtx to clean state. - * Useful after an error, or to interrupt an ongoing compression job and start a new one. - * Any internal data not yet flushed is cancelled. - * Dictionary (if any) is dropped. - * All parameters are back to default values. - * It's possible to modify compression parameters after a reset. - */ -ZSTDLIB_API void ZSTD_CCtx_reset(ZSTD_CCtx* cctx); - /*! ZSTD_compress_generic_simpleArgs() : * Same as ZSTD_compress_generic(), @@ -1209,25 +1209,26 @@ ZSTDLIB_API size_t ZSTD_compress_generic_simpleArgs ( * for static allocation for single-threaded compression. */ ZSTDLIB_API ZSTD_CCtx_params* ZSTD_createCCtxParams(void); +ZSTDLIB_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params); -/*! ZSTD_resetCCtxParams() : + +/*! ZSTD_CCtxParams_reset() : * Reset params to default values. */ -ZSTDLIB_API size_t ZSTD_resetCCtxParams(ZSTD_CCtx_params* params); +ZSTDLIB_API size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params); -/*! ZSTD_initCCtxParams() : +/*! ZSTD_CCtxParams_init() : * Initializes the compression parameters of cctxParams according to * compression level. All other parameters are reset to their default values. */ -ZSTDLIB_API size_t ZSTD_initCCtxParams(ZSTD_CCtx_params* cctxParams, int compressionLevel); +ZSTDLIB_API size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel); -/*! ZSTD_initCCtxParams_advanced() : +/*! ZSTD_CCtxParams_init_advanced() : * Initializes the compression and frame parameters of cctxParams according to * params. All other parameters are reset to their default values. */ -ZSTDLIB_API size_t ZSTD_initCCtxParams_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params); +ZSTDLIB_API size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params); -ZSTDLIB_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params); /*! ZSTD_CCtxParam_setParameter() : * Similar to ZSTD_CCtx_setParameter. From aa4dbd09a19e27bb671f594905361f5c174c5405 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 19 Mar 2018 15:56:36 -0700 Subject: [PATCH 201/255] Pull job/overlap log logic into common function (#1055) Prepares for LDM integration by separating the job size and overlap logic into helper functions. --- lib/compress/zstdmt_compress.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index e5bad4210..99131260b 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -825,9 +825,20 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx) /* ===== Multi-threaded compression ===== */ /* ------------------------------------------ */ -static unsigned ZSTDMT_computeNbJobs(size_t srcSize, unsigned windowLog, unsigned nbWorkers) { +static size_t ZSTDMT_computeTargetJobLog(ZSTD_CCtx_params const params) +{ + return params.cParams.windowLog + 2; +} + +static size_t ZSTDMT_computeOverlapLog(ZSTD_CCtx_params const params) +{ + unsigned const overlapRLog = (params.overlapSizeLog>9) ? 0 : 9-params.overlapSizeLog; + return overlapRLog >= 9 ? 0 : (params.cParams.windowLog - overlapRLog); +} + +static unsigned ZSTDMT_computeNbJobs(ZSTD_CCtx_params params, size_t srcSize, unsigned nbWorkers) { assert(nbWorkers>0); - { size_t const jobSizeTarget = (size_t)1 << (windowLog + 2); + { size_t const jobSizeTarget = (size_t)1 << ZSTDMT_computeTargetJobLog(params); size_t const jobMaxSize = jobSizeTarget << 2; size_t const passSizeMax = jobMaxSize * nbWorkers; unsigned const multiplier = (unsigned)(srcSize / passSizeMax) + 1; @@ -848,9 +859,8 @@ static size_t ZSTDMT_compress_advanced_internal( ZSTD_CCtx_params const params) { ZSTD_CCtx_params const jobParams = ZSTDMT_initJobCCtxParams(params); - unsigned const overlapRLog = (params.overlapSizeLog>9) ? 0 : 9-params.overlapSizeLog; - size_t const overlapSize = (overlapRLog>=9) ? 0 : (size_t)1 << (params.cParams.windowLog - overlapRLog); - unsigned const nbJobs = ZSTDMT_computeNbJobs(srcSize, params.cParams.windowLog, params.nbWorkers); + size_t const overlapSize = (size_t)1 << ZSTDMT_computeOverlapLog(params); + unsigned const nbJobs = ZSTDMT_computeNbJobs(params, srcSize, params.nbWorkers); size_t const proposedJobSize = (srcSize + (nbJobs-1)) / nbJobs; size_t const avgJobSize = (((proposedJobSize-1) & 0x1FFFF) < 0x7FFF) ? proposedJobSize + 0xFFFF : proposedJobSize; /* avoid too small last block */ const char* const srcStart = (const char*)src; @@ -1016,7 +1026,7 @@ size_t ZSTDMT_initCStream_internal( if (params.cParams.windowLog >= 29) params.jobSize = ZSTDMT_JOBSIZE_MAX; else - params.jobSize = 1 << (params.cParams.windowLog + 2); + params.jobSize = 1U << ZSTDMT_computeTargetJobLog(params); } if (params.jobSize > ZSTDMT_JOBSIZE_MAX) params.jobSize = ZSTDMT_JOBSIZE_MAX; @@ -1053,8 +1063,7 @@ size_t ZSTDMT_initCStream_internal( mtctx->cdict = cdict; } - assert(params.overlapSizeLog <= 9); - mtctx->targetPrefixSize = (params.overlapSizeLog==0) ? 0 : (size_t)1 << (params.cParams.windowLog - (9 - params.overlapSizeLog)); + mtctx->targetPrefixSize = (size_t)1 << ZSTDMT_computeOverlapLog(params); DEBUGLOG(4, "overlapLog=%u => %u KB", params.overlapSizeLog, (U32)(mtctx->targetPrefixSize>>10)); mtctx->targetSectionSize = params.jobSize; if (mtctx->targetSectionSize < ZSTDMT_JOBSIZE_MIN) mtctx->targetSectionSize = ZSTDMT_JOBSIZE_MIN; From e5ddfaede829522d85869ea23e0191e097382f4e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 19 Mar 2018 16:02:51 -0700 Subject: [PATCH 202/255] add a test for unordered parameters --- tests/fuzzer.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index cae509707..cebb2655c 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -1024,6 +1024,45 @@ static int basicUnitTests(U32 seed, double compressibility) ZSTD_freeCCtx(cctx); } + /* parameters order test */ + { size_t const inputSize = CNBuffSize / 2; + U64 xxh64; + + { ZSTD_CCtx* cctx = ZSTD_createCCtx(); + DISPLAYLEVEL(3, "test%3i : parameters in order : ", testNb++); + CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, 2) ); + CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_enableLongDistanceMatching, 1) ); + CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_windowLog, 18) ); + { ZSTD_inBuffer in = { CNBuffer, inputSize, 0 }; + ZSTD_outBuffer out = { compressedBuffer, ZSTD_compressBound(inputSize), 0 }; + size_t const result = ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); + if (result != 0) goto _output_error; + if (in.pos != in.size) goto _output_error; + cSize = out.pos; + xxh64 = XXH64(out.dst, out.pos, 0); + } + DISPLAYLEVEL(3, "OK (compress : %u -> %u bytes)\n", (U32)inputSize, (U32)cSize); + ZSTD_freeCCtx(cctx); + } + + { ZSTD_CCtx* cctx = ZSTD_createCCtx(); + DISPLAYLEVEL(3, "test%3i : parameters disordered : ", testNb++); + CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_windowLog, 18) ); + CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_enableLongDistanceMatching, 1) ); + CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, 2) ); + { ZSTD_inBuffer in = { CNBuffer, inputSize, 0 }; + ZSTD_outBuffer out = { compressedBuffer, ZSTD_compressBound(inputSize), 0 }; + size_t const result = ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); + if (result != 0) goto _output_error; + if (in.pos != in.size) goto _output_error; + if (out.pos != cSize) goto _output_error; /* must result in same compressed result, hence same size */ + if (XXH64(out.dst, out.pos, 0) != xxh64) goto _output_error; /* must result in exactly same content, hence same hash */ + DISPLAYLEVEL(3, "OK (compress : %u -> %u bytes)\n", (U32)inputSize, (U32)out.pos); + } + ZSTD_freeCCtx(cctx); + } + } + /* custom formats tests */ { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); size_t const inputSize = CNBuffSize / 2; /* won't cause pb with small dict size */ From a7b532a34710dc0a0b90c330f48f2d7609b38603 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 19 Mar 2018 16:13:12 -0700 Subject: [PATCH 203/255] added docker readme --- contrib/docker/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 contrib/docker/README.md diff --git a/contrib/docker/README.md b/contrib/docker/README.md new file mode 100644 index 000000000..299117787 --- /dev/null +++ b/contrib/docker/README.md @@ -0,0 +1,20 @@ + +## Requirement + +The `Dockerfile` script requires a version of `docker` >= 17.05 + +## Installing docker + +The officiel docker install docs use a ppa with a modern version available: +https://docs.docker.com/install/linux/docker-ce/ubuntu/ + +## How to run + +`docker build -t zstd .` + +## test + +``` +echo foo | docker run -i --rm zstd | docker run -i --rm zstd zstdcat +foo +``` From 9e6ba883638006e100b82e06760793159a5d7622 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 19 Mar 2018 16:15:06 -0700 Subject: [PATCH 204/255] moved snap to /contrib --- {snap => contrib/snap}/snapcraft.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {snap => contrib/snap}/snapcraft.yaml (100%) diff --git a/snap/snapcraft.yaml b/contrib/snap/snapcraft.yaml similarity index 100% rename from snap/snapcraft.yaml rename to contrib/snap/snapcraft.yaml From 94c77710a90c982ce22d776fa34ab5f3dc5e0446 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 6 Mar 2018 19:51:21 -0800 Subject: [PATCH 205/255] Integrate ldm with zstdmt Integrate ldm into zstdmt by running it in serial and in order in the first step of each job, in the same place as the hash gets updated. The input buffer is sized to fit the whole LDM window and 2 full buffers of slack. Input buffers cannot be reused until the LDM step is done with them. After the LDM step is finished, the jobs don't actually have access to the full window, only the overlap. Tested on a few different multi-GB files with and without sanitizers, and with different numbers of threads. --- lib/compress/zstdmt_compress.c | 282 +++++++++++++++++++++++++++++++-- 1 file changed, 266 insertions(+), 16 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 99131260b..3225f9829 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -27,8 +27,14 @@ #include "pool.h" /* threadpool */ #include "threading.h" /* mutex */ #include "zstd_compress_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */ +#include "zstd_ldm.h" #include "zstdmt_compress.h" +/* Guards code to support resizing the SeqPool. + * We will want to resize the SeqPool to save memory in the future. + * Until then, comment the code out since it is unused. + */ +#define ZSTD_RESIZE_SEQPOOL 0 /* ====== Debug ====== */ #if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=2) @@ -194,6 +200,32 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool) } } +#if ZSTD_RESIZE_SEQPOOL +/** ZSTDMT_resizeBuffer() : + * assumption : bufPool must be valid + * @return : a buffer that is at least the buffer pool buffer size. + * If a reallocation happens, the data in the input buffer is copied. + */ +static buffer_t ZSTDMT_resizeBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buffer) +{ + size_t const bSize = bufPool->bufferSize; + if (buffer.capacity < bSize) { + void* const start = ZSTD_malloc(bSize, bufPool->cMem); + buffer_t newBuffer; + newBuffer.start = start; + newBuffer.capacity = start == NULL ? 0 : bSize; + if (start != NULL) { + assert(newBuffer.capacity >= buffer.capacity); + memcpy(newBuffer.start, buffer.start, buffer.capacity); + DEBUGLOG(5, "ZSTDMT_resizeBuffer: created buffer of size %u", (U32)bSize); + return newBuffer; + } + DEBUGLOG(5, "ZSTDMT_resizeBuffer: buffer allocation failure !!"); + } + return buffer; +} +#endif + /* store buffer for later re-use, up to pool capacity */ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf) { @@ -214,6 +246,72 @@ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf) } +/* ===== Seq Pool Wrapper ====== */ + +static rawSeqStore_t kNullRawSeqStore = {NULL, 0, 0, 0}; + +typedef ZSTDMT_bufferPool ZSTDMT_seqPool; + +static size_t ZSTDMT_sizeof_seqPool(ZSTDMT_seqPool* seqPool) +{ + return ZSTDMT_sizeof_bufferPool(seqPool); +} + +static rawSeqStore_t bufferToSeq(buffer_t buffer) +{ + rawSeqStore_t seq = {NULL, 0, 0, 0}; + seq.seq = (rawSeq*)buffer.start; + seq.capacity = buffer.capacity / sizeof(rawSeq); + return seq; +} + +static buffer_t seqToBuffer(rawSeqStore_t seq) +{ + buffer_t buffer; + buffer.start = seq.seq; + buffer.capacity = seq.capacity * sizeof(rawSeq); + return buffer; +} + +static rawSeqStore_t ZSTDMT_getSeq(ZSTDMT_seqPool* seqPool) +{ + if (seqPool->bufferSize == 0) { + return kNullRawSeqStore; + } + return bufferToSeq(ZSTDMT_getBuffer(seqPool)); +} + +#if ZSTD_RESIZE_SEQPOOL +static rawSeqStore_t ZSTDMT_resizeSeq(ZSTDMT_seqPool* seqPool, rawSeqStore_t seq) +{ + return bufferToSeq(ZSTDMT_resizeBuffer(seqPool, seqToBuffer(seq))); +} +#endif + +static void ZSTDMT_releaseSeq(ZSTDMT_seqPool* seqPool, rawSeqStore_t seq) +{ + ZSTDMT_releaseBuffer(seqPool, seqToBuffer(seq)); +} + +static void ZSTDMT_setNbSeq(ZSTDMT_seqPool* const seqPool, size_t const nbSeq) +{ + ZSTDMT_setBufferSize(seqPool, nbSeq * sizeof(rawSeq)); +} + +static ZSTDMT_seqPool* ZSTDMT_createSeqPool(unsigned nbWorkers, ZSTD_customMem cMem) +{ + ZSTDMT_seqPool* seqPool = ZSTDMT_createBufferPool(nbWorkers, cMem); + ZSTDMT_setNbSeq(seqPool, 0); + return seqPool; +} + +static void ZSTDMT_freeSeqPool(ZSTDMT_seqPool* seqPool) +{ + ZSTDMT_freeBufferPool(seqPool); +} + + + /* ===== CCtx Pool ===== */ /* a single CCtx Pool can be invoked from multiple threads in parallel */ @@ -312,36 +410,95 @@ typedef struct { } range_t; typedef struct { + /* All variables in the struct are protected by mutex. */ ZSTD_pthread_mutex_t mutex; ZSTD_pthread_cond_t cond; ZSTD_CCtx_params params; + ldmState_t ldmState; XXH64_state_t xxhState; unsigned nextJobID; + /* Protects ldmWindow. + * Must be acquired after the main mutex when acquiring both. + */ + ZSTD_pthread_mutex_t ldmWindowMutex; + ZSTD_pthread_cond_t ldmWindowCond; /* Signaled when ldmWindow is udpated */ + ZSTD_window_t ldmWindow; /* A thread-safe copy of ldmState.window */ } serialState_t; -static void ZSTDMT_serialState_reset(serialState_t* serialState, ZSTD_CCtx_params params) +static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* seqPool, ZSTD_CCtx_params params) { + /* Adjust parameters */ + if (params.ldmParams.enableLdm) { + DEBUGLOG(4, "LDM window size = %u KB", (1U << params.cParams.windowLog) >> 10); + params.ldmParams.windowLog = params.cParams.windowLog; + ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams); + assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog); + assert(params.ldmParams.hashEveryLog < 32); + serialState->ldmState.hashPower = + ZSTD_ldm_getHashPower(params.ldmParams.minMatchLength); + } serialState->nextJobID = 0; if (params.fParams.checksumFlag) XXH64_reset(&serialState->xxhState, 0); + if (params.ldmParams.enableLdm) { + ZSTD_customMem cMem = params.customMem; + unsigned const hashLog = params.ldmParams.hashLog; + size_t const hashSize = ((size_t)1 << hashLog) * sizeof(ldmEntry_t); + unsigned const bucketLog = + params.ldmParams.hashLog - params.ldmParams.bucketSizeLog; + size_t const bucketSize = (size_t)1 << bucketLog; + unsigned const prevBucketLog = + serialState->params.ldmParams.hashLog - + serialState->params.ldmParams.bucketSizeLog; + /* Size the seq pool tables */ + ZSTDMT_setNbSeq(seqPool, ZSTD_ldm_getMaxNbSeq(params.ldmParams, params.jobSize)); + /* Reset the window */ + ZSTD_window_clear(&serialState->ldmState.window); + serialState->ldmWindow = serialState->ldmState.window; + /* Resize tables and output space if necessary. */ + if (serialState->params.ldmParams.hashLog < hashLog) { + ZSTD_free(serialState->ldmState.hashTable, cMem); + serialState->ldmState.hashTable = (ldmEntry_t*)ZSTD_malloc(hashSize, cMem); + } + if (prevBucketLog < bucketLog) { + ZSTD_free(serialState->ldmState.bucketOffsets, cMem); + serialState->ldmState.bucketOffsets = (BYTE*)ZSTD_malloc(bucketSize, cMem); + } + if (!serialState->ldmState.hashTable || !serialState->ldmState.bucketOffsets) + return 1; + /* Zero the tables */ + memset(serialState->ldmState.hashTable, 0, hashSize); + memset(serialState->ldmState.bucketOffsets, 0, bucketSize); + } serialState->params = params; + return 0; } static int ZSTDMT_serialState_init(serialState_t* serialState) { int initError = 0; + memset(serialState, 0, sizeof(*serialState)); initError |= ZSTD_pthread_mutex_init(&serialState->mutex, NULL); initError |= ZSTD_pthread_cond_init(&serialState->cond, NULL); + initError |= ZSTD_pthread_mutex_init(&serialState->ldmWindowMutex, NULL); + initError |= ZSTD_pthread_cond_init(&serialState->ldmWindowCond, NULL); return initError; } static void ZSTDMT_serialState_free(serialState_t* serialState) { + ZSTD_customMem cMem = serialState->params.customMem; ZSTD_pthread_mutex_destroy(&serialState->mutex); ZSTD_pthread_cond_destroy(&serialState->cond); + ZSTD_pthread_mutex_destroy(&serialState->ldmWindowMutex); + ZSTD_pthread_cond_destroy(&serialState->ldmWindowCond); + ZSTD_free(serialState->ldmState.hashTable, cMem); + ZSTD_free(serialState->ldmState.bucketOffsets, cMem); } -static void ZSTDMT_serialState_update(serialState_t* serialState, range_t src, unsigned jobID) +static void ZSTDMT_serialState_update(serialState_t* serialState, + ZSTD_CCtx* jobCCtx, rawSeqStore_t seqStore, + range_t src, unsigned jobID) { /* Wait for our turn */ ZSTD_PTHREAD_MUTEX_LOCK(&serialState->mutex); @@ -351,6 +508,24 @@ static void ZSTDMT_serialState_update(serialState_t* serialState, range_t src, u /* A future job may error and skip our job */ if (serialState->nextJobID == jobID) { /* It is now our turn, do any processing necessary */ + if (serialState->params.ldmParams.enableLdm) { + size_t error; + assert(seqStore.seq != NULL && seqStore.pos == 0 && + seqStore.size == 0 && seqStore.capacity > 0); + ZSTD_window_update(&serialState->ldmState.window, src.start, src.size); + error = ZSTD_ldm_generateSequences( + &serialState->ldmState, &seqStore, + &serialState->params.ldmParams, src.start, src.size); + /* We provide a large enough buffer to never fail. */ + assert(!ZSTD_isError(error)); (void)error; + /* Update ldmWindow to match the ldmState.window and signal the main + * thread if it is waiting for a buffer. + */ + ZSTD_PTHREAD_MUTEX_LOCK(&serialState->ldmWindowMutex); + serialState->ldmWindow = serialState->ldmState.window; + ZSTD_pthread_cond_signal(&serialState->ldmWindowCond); + ZSTD_pthread_mutex_unlock(&serialState->ldmWindowMutex); + } if (serialState->params.fParams.checksumFlag && src.size > 0) XXH64_update(&serialState->xxhState, src.start, src.size); } @@ -358,6 +533,14 @@ static void ZSTDMT_serialState_update(serialState_t* serialState, range_t src, u serialState->nextJobID++; ZSTD_pthread_cond_broadcast(&serialState->cond); ZSTD_pthread_mutex_unlock(&serialState->mutex); + + if (seqStore.size > 0) { + size_t const err = ZSTD_referenceExternalSequences( + jobCCtx, seqStore.seq, seqStore.size); + assert(serialState->params.ldmParams.enableLdm); + assert(!ZSTD_isError(err)); + (void)err; + } } static void ZSTDMT_serialState_ensureFinished(serialState_t* serialState, @@ -369,6 +552,11 @@ static void ZSTDMT_serialState_ensureFinished(serialState_t* serialState, DEBUGLOG(5, "Skipping past job %u because of error", jobID); serialState->nextJobID = jobID + 1; ZSTD_pthread_cond_broadcast(&serialState->cond); + + ZSTD_PTHREAD_MUTEX_LOCK(&serialState->ldmWindowMutex); + ZSTD_window_clear(&serialState->ldmWindow); + ZSTD_pthread_cond_signal(&serialState->ldmWindowCond); + ZSTD_pthread_mutex_unlock(&serialState->ldmWindowMutex); } ZSTD_pthread_mutex_unlock(&serialState->mutex); @@ -388,6 +576,7 @@ typedef struct { ZSTD_pthread_cond_t job_cond; /* Thread-safe - used by mtctx and worker */ ZSTDMT_CCtxPool* cctxPool; /* Thread-safe - used by mtctx and (all) workers */ ZSTDMT_bufferPool* bufPool; /* Thread-safe - used by mtctx and (all) workers */ + ZSTDMT_seqPool* seqPool; /* Thread-safe - used by mtctx and (all) workers */ serialState_t* serial; /* Thread-safe - used by mtctx and (all) workers */ buffer_t dstBuff; /* set by worker (or mtctx), then read by worker & mtctx, then modified by mtctx => no barrier */ range_t prefix; /* set by mtctx, then read by worker & mtctx => no barrier */ @@ -408,10 +597,15 @@ void ZSTDMT_compressionJob(void* jobDescription) ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */ ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool); + rawSeqStore_t rawSeqStore = ZSTDMT_getSeq(job->seqPool); buffer_t dstBuff = job->dstBuff; - /* Don't compute the checksum for chunks, but write it in the header */ + /* Don't compute the checksum for chunks, since we compute it externally, + * but write it in the header. + */ if (job->jobID != 0) jobParams.fParams.checksumFlag = 0; + /* Don't run LDM for the chunks, since we handle it externally */ + jobParams.ldmParams.enableLdm = 0; /* ressources */ if (cctx==NULL) { @@ -448,8 +642,8 @@ void ZSTDMT_compressionJob(void* jobDescription) goto _endJob; } } } - /* Perform serial step as early as possible */ - ZSTDMT_serialState_update(job->serial, job->src, job->jobID); + /* Perform serial step as early as possible, but after CCtx initialization */ + ZSTDMT_serialState_update(job->serial, cctx, rawSeqStore, job->src, job->jobID); if (!job->firstJob) { /* flush and overwrite frame header when it's not first job */ size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0); @@ -504,6 +698,7 @@ _endJob: DEBUGLOG(5, "Finished with prefix: %zx", (size_t)job->prefix.start); DEBUGLOG(5, "Finished with source: %zx", (size_t)job->src.start); /* release resources */ + ZSTDMT_releaseSeq(job->seqPool, rawSeqStore); ZSTDMT_releaseCCtx(job->cctxPool, cctx); /* report */ ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex); @@ -544,6 +739,7 @@ struct ZSTDMT_CCtx_s { ZSTDMT_jobDescription* jobs; ZSTDMT_bufferPool* bufPool; ZSTDMT_CCtxPool* cctxPool; + ZSTDMT_seqPool* seqPool; ZSTD_CCtx_params params; size_t targetSectionSize; size_t targetPrefixSize; @@ -635,9 +831,10 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem) mtctx->jobIDMask = nbJobs - 1; mtctx->bufPool = ZSTDMT_createBufferPool(nbWorkers, cMem); mtctx->cctxPool = ZSTDMT_createCCtxPool(nbWorkers, cMem); + mtctx->seqPool = ZSTDMT_createSeqPool(nbWorkers, cMem); initError = ZSTDMT_serialState_init(&mtctx->serial); mtctx->roundBuff = kNullRoundBuff; - if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool | initError) { + if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool | !mtctx->seqPool | initError) { ZSTDMT_freeCCtx(mtctx); return NULL; } @@ -692,6 +889,7 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem); ZSTDMT_freeBufferPool(mtctx->bufPool); ZSTDMT_freeCCtxPool(mtctx->cctxPool); + ZSTDMT_freeSeqPool(mtctx->seqPool); ZSTDMT_serialState_free(&mtctx->serial); ZSTD_freeCDict(mtctx->cdictLocal); if (mtctx->roundBuff.buffer) @@ -708,6 +906,7 @@ size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx) + ZSTDMT_sizeof_bufferPool(mtctx->bufPool) + (mtctx->jobIDMask+1) * sizeof(ZSTDMT_jobDescription) + ZSTDMT_sizeof_CCtxPool(mtctx->cctxPool) + + ZSTDMT_sizeof_seqPool(mtctx->seqPool) + ZSTD_sizeof_CDict(mtctx->cdictLocal) + mtctx->roundBuff.capacity; } @@ -761,7 +960,6 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) jobParams.compressionLevel = params.compressionLevel; jobParams.disableLiteralCompression = params.disableLiteralCompression; - jobParams.ldmParams = params.ldmParams; return jobParams; } @@ -827,12 +1025,16 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx) static size_t ZSTDMT_computeTargetJobLog(ZSTD_CCtx_params const params) { + if (params.ldmParams.enableLdm) + return MAX(21, params.cParams.chainLog + 4); return params.cParams.windowLog + 2; } static size_t ZSTDMT_computeOverlapLog(ZSTD_CCtx_params const params) { unsigned const overlapRLog = (params.overlapSizeLog>9) ? 0 : 9-params.overlapSizeLog; + if (params.ldmParams.enableLdm) + return (MIN(params.cParams.windowLog, ZSTDMT_computeTargetJobLog(params) - 2) - overlapRLog); return overlapRLog >= 9 ? 0 : (params.cParams.windowLog - overlapRLog); } @@ -856,7 +1058,7 @@ static size_t ZSTDMT_compress_advanced_internal( void* dst, size_t dstCapacity, const void* src, size_t srcSize, const ZSTD_CDict* cdict, - ZSTD_CCtx_params const params) + ZSTD_CCtx_params params) { ZSTD_CCtx_params const jobParams = ZSTDMT_initJobCCtxParams(params); size_t const overlapSize = (size_t)1 << ZSTDMT_computeOverlapLog(params); @@ -870,6 +1072,7 @@ static size_t ZSTDMT_compress_advanced_internal( assert(jobParams.nbWorkers == 0); assert(mtctx->cctxPool->totalCCtx == params.nbWorkers); + params.jobSize = (U32)avgJobSize; DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbJobs=%2u (rawSize=%u bytes; fixedSize=%u) ", nbJobs, (U32)proposedJobSize, (U32)avgJobSize); @@ -882,7 +1085,8 @@ static size_t ZSTDMT_compress_advanced_internal( assert(avgJobSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), required to compress directly into Dst (no additional buffer) */ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgJobSize) ); - ZSTDMT_serialState_reset(&mtctx->serial, params); + if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params)) + return ERROR(memory_allocation); if (nbJobs > mtctx->jobIDMask+1) { /* enlarge job table */ U32 jobsTableSize = nbJobs; @@ -915,6 +1119,7 @@ static size_t ZSTDMT_compress_advanced_internal( mtctx->jobs[u].dstBuff = dstBuffer; mtctx->jobs[u].cctxPool = mtctx->cctxPool; mtctx->jobs[u].bufPool = mtctx->bufPool; + mtctx->jobs[u].seqPool = mtctx->seqPool; mtctx->jobs[u].serial = &mtctx->serial; mtctx->jobs[u].jobID = u; mtctx->jobs[u].firstJob = (u==0); @@ -1023,10 +1228,7 @@ size_t ZSTDMT_initCStream_internal( /* init */ if (params.jobSize == 0) { - if (params.cParams.windowLog >= 29) - params.jobSize = ZSTDMT_JOBSIZE_MAX; - else - params.jobSize = 1U << ZSTDMT_computeTargetJobLog(params); + params.jobSize = 1U << ZSTDMT_computeTargetJobLog(params); } if (params.jobSize > ZSTDMT_JOBSIZE_MAX) params.jobSize = ZSTDMT_JOBSIZE_MAX; @@ -1072,11 +1274,15 @@ size_t ZSTDMT_initCStream_internal( DEBUGLOG(4, "inBuff Size : %u KB", (U32)(mtctx->targetSectionSize>>10)); ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(mtctx->targetSectionSize)); { + /* If ldm is enabled we need windowSize space. */ + size_t const windowSize = mtctx->params.ldmParams.enableLdm ? (1U << mtctx->params.cParams.windowLog) : 0; /* Two buffers of slack, plus extra space for the overlap */ size_t const nbWorkers = MAX(mtctx->params.nbWorkers, 1); size_t const nbSlackBuffers = MIN(nbWorkers, 2) + (mtctx->targetPrefixSize > 0); - size_t const nbSections = nbWorkers + nbSlackBuffers; - size_t const capacity = mtctx->targetSectionSize * nbSections; + size_t const slackSize = mtctx->targetSectionSize * nbSlackBuffers; + /* Compute the total size, and always have enough slack */ + size_t const sectionsSize = mtctx->targetSectionSize * nbWorkers; + size_t const capacity = MAX(windowSize, sectionsSize) + slackSize; if (mtctx->roundBuff.capacity < capacity) { if (mtctx->roundBuff.buffer) ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem); @@ -1099,7 +1305,8 @@ size_t ZSTDMT_initCStream_internal( mtctx->allJobsCompleted = 0; mtctx->consumed = 0; mtctx->produced = 0; - ZSTDMT_serialState_reset(&mtctx->serial, params); + if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params)) + return ERROR(memory_allocation); return 0; } @@ -1201,6 +1408,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS mtctx->jobs[jobID].dstBuff = g_nullBuffer; mtctx->jobs[jobID].cctxPool = mtctx->cctxPool; mtctx->jobs[jobID].bufPool = mtctx->bufPool; + mtctx->jobs[jobID].seqPool = mtctx->seqPool; mtctx->jobs[jobID].serial = &mtctx->serial; mtctx->jobs[jobID].jobID = mtctx->nextJobID; mtctx->jobs[jobID].firstJob = (mtctx->nextJobID==0); @@ -1387,6 +1595,44 @@ static int ZSTDMT_isOverlapped(buffer_t buffer, range_t range) return bufferStart < rangeEnd && rangeStart < bufferEnd; } +static int ZSTDMT_doesOverlapWindow(buffer_t buffer, ZSTD_window_t window) +{ + range_t extDict; + range_t prefix; + + extDict.start = window.dictBase + window.lowLimit; + extDict.size = window.dictLimit - window.lowLimit; + + prefix.start = window.base + window.dictLimit; + prefix.size = window.nextSrc - (window.base + window.dictLimit); + DEBUGLOG(5, "extDict [0x%zx, 0x%zx)", + (size_t)extDict.start, + (size_t)extDict.start + extDict.size); + DEBUGLOG(5, "prefix [0x%zx, 0x%zx)", + (size_t)prefix.start, + (size_t)prefix.start + prefix.size); + + return ZSTDMT_isOverlapped(buffer, extDict) + || ZSTDMT_isOverlapped(buffer, prefix); +} + +static void ZSTDMT_waitForLdmComplete(ZSTDMT_CCtx* mtctx, buffer_t buffer) +{ + if (mtctx->params.ldmParams.enableLdm) { + ZSTD_pthread_mutex_t* mutex = &mtctx->serial.ldmWindowMutex; + DEBUGLOG(5, "source [0x%zx, 0x%zx)", + (size_t)buffer.start, + (size_t)buffer.start + buffer.capacity); + ZSTD_PTHREAD_MUTEX_LOCK(mutex); + while (ZSTDMT_doesOverlapWindow(buffer, mtctx->serial.ldmWindow)) { + DEBUGLOG(6, "Waiting for LDM to finish..."); + ZSTD_pthread_cond_wait(&mtctx->serial.ldmWindowCond, mutex); + } + DEBUGLOG(6, "Done waiting for LDM to finish"); + ZSTD_pthread_mutex_unlock(mutex); + } +} + /** * Attempts to set the inBuff to the next section to fill. * If any part of the new section is still in use we give up. @@ -1415,6 +1661,7 @@ static int ZSTDMT_tryGetInputRange(ZSTDMT_CCtx* mtctx) DEBUGLOG(6, "Waiting for buffer..."); return 0; } + ZSTDMT_waitForLdmComplete(mtctx, buffer); memmove(start, mtctx->inBuff.prefix.start, prefixSize); mtctx->inBuff.prefix.start = start; mtctx->roundBuff.pos = prefixSize; @@ -1427,6 +1674,9 @@ static int ZSTDMT_tryGetInputRange(ZSTDMT_CCtx* mtctx) return 0; } assert(!ZSTDMT_isOverlapped(buffer, mtctx->inBuff.prefix)); + + ZSTDMT_waitForLdmComplete(mtctx, buffer); + DEBUGLOG(5, "Using prefix range [%zx, %zx)", (size_t)mtctx->inBuff.prefix.start, (size_t)mtctx->inBuff.prefix.start + mtctx->inBuff.prefix.size); From 4b92574feb7be219aa8c89e3b7ae770a145ecbd8 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 19 Mar 2018 17:54:04 -0700 Subject: [PATCH 206/255] Fix corner cases exposed by zstreamtest --- lib/compress/zstdmt_compress.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 3225f9829..9d54da0d8 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -456,11 +456,11 @@ static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* ZSTD_window_clear(&serialState->ldmState.window); serialState->ldmWindow = serialState->ldmState.window; /* Resize tables and output space if necessary. */ - if (serialState->params.ldmParams.hashLog < hashLog) { + if (serialState->ldmState.hashTable == NULL || serialState->params.ldmParams.hashLog < hashLog) { ZSTD_free(serialState->ldmState.hashTable, cMem); serialState->ldmState.hashTable = (ldmEntry_t*)ZSTD_malloc(hashSize, cMem); } - if (prevBucketLog < bucketLog) { + if (serialState->ldmState.bucketOffsets == NULL || prevBucketLog < bucketLog) { ZSTD_free(serialState->ldmState.bucketOffsets, cMem); serialState->ldmState.bucketOffsets = (BYTE*)ZSTD_malloc(bucketSize, cMem); } @@ -1591,6 +1591,9 @@ static int ZSTDMT_isOverlapped(buffer_t buffer, range_t range) if (rangeStart == NULL || bufferStart == NULL) return 0; + /* Empty ranges cannot overlap */ + if (bufferStart == bufferEnd || rangeStart == rangeEnd) + return 0; return bufferStart < rangeEnd && rangeStart < bufferEnd; } From 24d9edbdd866be330161359db3badba5a73c3510 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 19 Mar 2018 18:23:54 -0700 Subject: [PATCH 207/255] Set ldmParams to 0 when disabled --- lib/compress/zstdmt_compress.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 9d54da0d8..b0d97c70a 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -436,6 +436,8 @@ static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* assert(params.ldmParams.hashEveryLog < 32); serialState->ldmState.hashPower = ZSTD_ldm_getHashPower(params.ldmParams.minMatchLength); + } else { + memset(¶ms.ldmParams, 0, sizeof(params.ldmParams)); } serialState->nextJobID = 0; if (params.fParams.checksumFlag) From d19f803a3b18d46acf565e99214d33c9fbbb13a0 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 19 Mar 2018 18:56:39 -0700 Subject: [PATCH 208/255] Fix window size for 1 worker + flushing --- lib/compress/zstdmt_compress.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index b0d97c70a..975e4149c 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1278,11 +1278,16 @@ size_t ZSTDMT_initCStream_internal( { /* If ldm is enabled we need windowSize space. */ size_t const windowSize = mtctx->params.ldmParams.enableLdm ? (1U << mtctx->params.cParams.windowLog) : 0; - /* Two buffers of slack, plus extra space for the overlap */ - size_t const nbWorkers = MAX(mtctx->params.nbWorkers, 1); - size_t const nbSlackBuffers = MIN(nbWorkers, 2) + (mtctx->targetPrefixSize > 0); + /* Two buffers of slack, plus extra space for the overlap + * This is the minimum slack that LDM works with. One extra because + * flush might waste up to targetSectionSize-1 bytes. Another extra + * for the overlap (if > 0), then one to fill which doesn't overlap + * with the LDM window. + */ + size_t const nbSlackBuffers = 2 + (mtctx->targetPrefixSize > 0); size_t const slackSize = mtctx->targetSectionSize * nbSlackBuffers; /* Compute the total size, and always have enough slack */ + size_t const nbWorkers = MAX(mtctx->params.nbWorkers, 1); size_t const sectionsSize = mtctx->targetSectionSize * nbWorkers; size_t const capacity = MAX(windowSize, sectionsSize) + slackSize; if (mtctx->roundBuff.capacity < capacity) { From 2ed5af0766b6e6ae8f24118f75c75a8d58c68f8b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 19 Mar 2018 19:10:31 -0700 Subject: [PATCH 209/255] merge bmi2 implementation of encodeSequence into zstd_compress.c --- lib/compress/zstd_compress.c | 127 +++++++++++++++++++++++++++--- lib/compress/zstd_compress_impl.h | 106 ------------------------- 2 files changed, 117 insertions(+), 116 deletions(-) delete mode 100644 lib/compress/zstd_compress_impl.h diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index db8d7a8c3..89f71795c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1582,19 +1582,126 @@ size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, } } -#define FUNCTION(fn) fn##_default -#define TARGET -#include "zstd_compress_impl.h" -#undef TARGET -#undef FUNCTION +FORCE_INLINE_TEMPLATE size_t +ZSTD_encodeSequences_body( + void* dst, size_t dstCapacity, + FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, + FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, + FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, + seqDef const* sequences, size_t nbSeq, int longOffsets) +{ + BIT_CStream_t blockStream; + FSE_CState_t stateMatchLength; + FSE_CState_t stateOffsetBits; + FSE_CState_t stateLitLength; + + CHECK_E(BIT_initCStream(&blockStream, dst, dstCapacity), dstSize_tooSmall); /* not enough space remaining */ + + /* first symbols */ + FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]); + FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]); + FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]); + BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]); + if (MEM_32bits()) BIT_flushBits(&blockStream); + BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]); + if (MEM_32bits()) BIT_flushBits(&blockStream); + if (longOffsets) { + U32 const ofBits = ofCodeTable[nbSeq-1]; + int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); + if (extraBits) { + BIT_addBits(&blockStream, sequences[nbSeq-1].offset, extraBits); + BIT_flushBits(&blockStream); + } + BIT_addBits(&blockStream, sequences[nbSeq-1].offset >> extraBits, + ofBits - extraBits); + } else { + BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]); + } + BIT_flushBits(&blockStream); + + { size_t n; + for (n=nbSeq-2 ; n= 64-7-(LLFSELog+MLFSELog+OffFSELog))) + BIT_flushBits(&blockStream); /* (7)*/ + BIT_addBits(&blockStream, sequences[n].litLength, llBits); + if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream); + BIT_addBits(&blockStream, sequences[n].matchLength, mlBits); + if (MEM_32bits() || (ofBits+mlBits+llBits > 56)) BIT_flushBits(&blockStream); + if (longOffsets) { + int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); + if (extraBits) { + BIT_addBits(&blockStream, sequences[n].offset, extraBits); + BIT_flushBits(&blockStream); /* (7)*/ + } + BIT_addBits(&blockStream, sequences[n].offset >> extraBits, + ofBits - extraBits); /* 31 */ + } else { + BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ + } + BIT_flushBits(&blockStream); /* (7)*/ + } } + + DEBUGLOG(6, "ZSTD_encodeSequences: flushing ML state with %u bits", stateMatchLength.stateLog); + FSE_flushCState(&blockStream, &stateMatchLength); + DEBUGLOG(6, "ZSTD_encodeSequences: flushing Off state with %u bits", stateOffsetBits.stateLog); + FSE_flushCState(&blockStream, &stateOffsetBits); + DEBUGLOG(6, "ZSTD_encodeSequences: flushing LL state with %u bits", stateLitLength.stateLog); + FSE_flushCState(&blockStream, &stateLitLength); + + { size_t const streamSize = BIT_closeCStream(&blockStream); + if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */ + return streamSize; + } +} + +static size_t +ZSTD_encodeSequences_default( + void* dst, size_t dstCapacity, + FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, + FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, + FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, + seqDef const* sequences, size_t nbSeq, int longOffsets) +{ + return ZSTD_encodeSequences_body(dst, dstCapacity, + CTable_MatchLength, mlCodeTable, + CTable_OffsetBits, ofCodeTable, + CTable_LitLength, llCodeTable, + sequences, nbSeq, longOffsets); +} + #if DYNAMIC_BMI2 -#define FUNCTION(fn) fn##_bmi2 -#define TARGET TARGET_ATTRIBUTE("bmi2") -#include "zstd_compress_impl.h" -#undef TARGET -#undef FUNCTION +static TARGET_ATTRIBUTE("bmi2") size_t +ZSTD_encodeSequences_bmi2( + void* dst, size_t dstCapacity, + FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, + FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, + FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, + seqDef const* sequences, size_t nbSeq, int longOffsets) +{ + return ZSTD_encodeSequences_body(dst, dstCapacity, + CTable_MatchLength, mlCodeTable, + CTable_OffsetBits, ofCodeTable, + CTable_LitLength, llCodeTable, + sequences, nbSeq, longOffsets); +} #endif diff --git a/lib/compress/zstd_compress_impl.h b/lib/compress/zstd_compress_impl.h deleted file mode 100644 index e6a30b1d0..000000000 --- a/lib/compress/zstd_compress_impl.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2018-present, 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. - */ - -#ifndef FUNCTION -# error "FUNCTION(name) must be defined" -#endif - -#ifndef TARGET -# error "TARGET must be defined" -#endif - - -MEM_STATIC TARGET -size_t FUNCTION(ZSTD_encodeSequences)( - void* dst, size_t dstCapacity, - FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, - FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, - FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, - seqDef const* sequences, size_t nbSeq, int longOffsets) -{ - BIT_CStream_t blockStream; - FSE_CState_t stateMatchLength; - FSE_CState_t stateOffsetBits; - FSE_CState_t stateLitLength; - - CHECK_E(BIT_initCStream(&blockStream, dst, dstCapacity), dstSize_tooSmall); /* not enough space remaining */ - - /* first symbols */ - FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]); - FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]); - FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]); - BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]); - if (MEM_32bits()) BIT_flushBits(&blockStream); - BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]); - if (MEM_32bits()) BIT_flushBits(&blockStream); - if (longOffsets) { - U32 const ofBits = ofCodeTable[nbSeq-1]; - int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); - if (extraBits) { - BIT_addBits(&blockStream, sequences[nbSeq-1].offset, extraBits); - BIT_flushBits(&blockStream); - } - BIT_addBits(&blockStream, sequences[nbSeq-1].offset >> extraBits, - ofBits - extraBits); - } else { - BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]); - } - BIT_flushBits(&blockStream); - - { size_t n; - for (n=nbSeq-2 ; n= 64-7-(LLFSELog+MLFSELog+OffFSELog))) - BIT_flushBits(&blockStream); /* (7)*/ - BIT_addBits(&blockStream, sequences[n].litLength, llBits); - if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream); - BIT_addBits(&blockStream, sequences[n].matchLength, mlBits); - if (MEM_32bits() || (ofBits+mlBits+llBits > 56)) BIT_flushBits(&blockStream); - if (longOffsets) { - int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); - if (extraBits) { - BIT_addBits(&blockStream, sequences[n].offset, extraBits); - BIT_flushBits(&blockStream); /* (7)*/ - } - BIT_addBits(&blockStream, sequences[n].offset >> extraBits, - ofBits - extraBits); /* 31 */ - } else { - BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ - } - BIT_flushBits(&blockStream); /* (7)*/ - } } - - DEBUGLOG(6, "ZSTD_encodeSequences: flushing ML state with %u bits", stateMatchLength.stateLog); - FSE_flushCState(&blockStream, &stateMatchLength); - DEBUGLOG(6, "ZSTD_encodeSequences: flushing Off state with %u bits", stateOffsetBits.stateLog); - FSE_flushCState(&blockStream, &stateOffsetBits); - DEBUGLOG(6, "ZSTD_encodeSequences: flushing LL state with %u bits", stateLitLength.stateLog); - FSE_flushCState(&blockStream, &stateLitLength); - - { size_t const streamSize = BIT_closeCStream(&blockStream); - if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */ - return streamSize; - } -} From 353117c5d77f937394e43daef133f70763f884d5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Mar 2018 13:40:29 -0700 Subject: [PATCH 210/255] implemented ZSTD_DCtx_loadDictionary*() this required updating ZSTD_createDDict_advanced() to accept a dictContentType parameter (raw, full, auto). --- doc/zstd_manual.html | 2 +- lib/decompress/zstd_decompress.c | 80 +++++++++++++++++++++++--------- lib/zstd.h | 7 +-- programs/platform.h | 3 +- 4 files changed, 64 insertions(+), 28 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 302b53595..d48250d6e 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -1047,7 +1047,7 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t


    Advanced parameters for decompression API


    -
    size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);   /* not implemented */
    +
    size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
     size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);   /* not implemented */
     size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode);   /* not implemented */
     

    Create an internal DDict from dict buffer, diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 4cd1f3da6..f0b2cc95b 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -14,8 +14,9 @@ *****************************************************************/ /*! * HEAPMODE : - * Select how default decompression function ZSTD_decompress() will allocate memory, - * in memory stack (0), or in memory heap (1, requires malloc()) + * Select how default decompression function ZSTD_decompress() allocates its context, + * on stack (0), or into heap (1, default; requires malloc()). + * Note that functions with explicit context such as ZSTD_decompressDCtx() are unaffected. */ #ifndef ZSTD_HEAPMODE # define ZSTD_HEAPMODE 1 @@ -30,10 +31,11 @@ #endif /*! -* MAXWINDOWSIZE_DEFAULT : -* maximum window size accepted by DStream, by default. -* Frames requiring more memory will be rejected. -*/ + * MAXWINDOWSIZE_DEFAULT : + * maximum window size accepted by DStream __by default__. + * Frames requiring more memory will be rejected. + * It's possible to set a different limit using ZSTD_DCtx_setMaxWindowSize(). + */ #ifndef ZSTD_MAXWINDOWSIZE_DEFAULT # define ZSTD_MAXWINDOWSIZE_DEFAULT (((U32)1 << ZSTD_WINDOWLOG_DEFAULTMAX) + 1) #endif @@ -2335,13 +2337,23 @@ size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dstDCtx, const ZSTD_DDict* ddi return 0; } -static size_t ZSTD_loadEntropy_inDDict(ZSTD_DDict* ddict) +static size_t ZSTD_loadEntropy_inDDict(ZSTD_DDict* ddict, ZSTD_dictMode_e dictContentType) { ddict->dictID = 0; ddict->entropyPresent = 0; - if (ddict->dictSize < 8) return 0; + if (dictContentType == ZSTD_dm_rawContent) return 0; + + if (ddict->dictSize < 8) { + if (dictContentType == ZSTD_dm_fullDict) + return ERROR(dictionary_corrupted); /* only accept specified dictionaries */ + return 0; /* pure content mode */ + } { U32 const magic = MEM_readLE32(ddict->dictContent); - if (magic != ZSTD_MAGIC_DICTIONARY) return 0; /* pure content mode */ + if (magic != ZSTD_MAGIC_DICTIONARY) { + if (dictContentType == ZSTD_dm_fullDict) + return ERROR(dictionary_corrupted); /* only accept specified dictionaries */ + return 0; /* pure content mode */ + } } ddict->dictID = MEM_readLE32((const char*)ddict->dictContent + ZSTD_frameIdSize); @@ -2352,7 +2364,10 @@ static size_t ZSTD_loadEntropy_inDDict(ZSTD_DDict* ddict) } -static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod) +static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict, + const void* dict, size_t dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictMode_e dictContentType) { if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dict) || (!dictSize)) { ddict->dictBuffer = NULL; @@ -2368,12 +2383,15 @@ static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict, const void* dict, size_ ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ /* parse dictionary content */ - CHECK_F( ZSTD_loadEntropy_inDDict(ddict) ); + CHECK_F( ZSTD_loadEntropy_inDDict(ddict, dictContentType) ); return 0; } -ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_customMem customMem) +ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictMode_e dictContentType, + ZSTD_customMem customMem) { if (!customMem.customAlloc ^ !customMem.customFree) return NULL; @@ -2381,7 +2399,7 @@ ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_di if (!ddict) return NULL; ddict->cMem = customMem; - if (ZSTD_isError( ZSTD_initDDict_internal(ddict, dict, dictSize, dictLoadMethod) )) { + if (ZSTD_isError( ZSTD_initDDict_internal(ddict, dict, dictSize, dictLoadMethod, dictContentType) )) { ZSTD_freeDDict(ddict); return NULL; } @@ -2397,7 +2415,7 @@ ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_di ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize) { ZSTD_customMem const allocator = { NULL, NULL, NULL }; - return ZSTD_createDDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, allocator); + return ZSTD_createDDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dm_auto, allocator); } /*! ZSTD_createDDict_byReference() : @@ -2407,7 +2425,7 @@ ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize) ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize) { ZSTD_customMem const allocator = { NULL, NULL, NULL }; - return ZSTD_createDDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, allocator); + return ZSTD_createDDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dm_auto, allocator); } @@ -2427,7 +2445,7 @@ const ZSTD_DDict* ZSTD_initStaticDDict( memcpy(ddict+1, dict, dictSize); /* local copy */ dict = ddict+1; } - if (ZSTD_isError( ZSTD_initDDict_internal(ddict, dict, dictSize, ZSTD_dlm_byRef) )) + if (ZSTD_isError( ZSTD_initDDict_internal(ddict, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dm_auto) )) return NULL; return ddict; } @@ -2546,17 +2564,35 @@ size_t ZSTD_freeDStream(ZSTD_DStream* zds) size_t ZSTD_DStreamInSize(void) { return ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize; } size_t ZSTD_DStreamOutSize(void) { return ZSTD_BLOCKSIZE_MAX; } +size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode) +{ + ZSTD_freeDDict(dctx->ddictLocal); + if (dict && dictSize >= 8) { + dctx->ddictLocal = ZSTD_createDDict_advanced(dict, dictSize, dictLoadMethod, dictMode, dctx->customMem); + if (dctx->ddictLocal == NULL) return ERROR(memory_allocation); + } else { + dctx->ddictLocal = NULL; + } + dctx->ddict = dctx->ddictLocal; + return 0; +} + +size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) +{ + return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dm_auto); +} + +size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) +{ + return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dm_auto); +} + size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize) { DEBUGLOG(4, "ZSTD_initDStream_usingDict"); zds->streamStage = zdss_loadHeader; zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; - ZSTD_freeDDict(zds->ddictLocal); - if (dict && dictSize >= 8) { - zds->ddictLocal = ZSTD_createDDict(dict, dictSize); - if (zds->ddictLocal == NULL) return ERROR(memory_allocation); - } else zds->ddictLocal = NULL; - zds->ddict = zds->ddictLocal; + CHECK_F( ZSTD_DCtx_loadDictionary(zds, dict, dictSize) ); zds->legacyVersion = 0; zds->hostageByte = 0; return ZSTD_frameHeaderSize_prefix; diff --git a/lib/zstd.h b/lib/zstd.h index 3ea049eb6..8a927f427 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -605,6 +605,7 @@ ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictS ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictMode_e dictContentType, ZSTD_customMem customMem); @@ -1270,9 +1271,9 @@ ZSTDLIB_API size_t ZSTD_CCtx_setParametersUsingCCtxParams( * Note 3 : Use ZSTD_DCtx_loadDictionary_advanced() to select * how dictionary content will be interpreted and loaded. */ -ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); /* not implemented */ -ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); /* not implemented */ -ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode); /* not implemented */ +ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); +ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); +ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode); /*! ZSTD_DCtx_refDDict() : diff --git a/programs/platform.h b/programs/platform.h index 58ff16dc4..c86d289f5 100644 --- a/programs/platform.h +++ b/programs/platform.h @@ -106,8 +106,7 @@ extern "C" { # include /* _isatty */ # include /* DeviceIoControl, HANDLE, FSCTL_SET_SPARSE */ # include /* FILE */ -static __inline int IS_CONSOLE(FILE* stdStream) -{ +static __inline int IS_CONSOLE(FILE* stdStream) { DWORD dummy; return _isatty(_fileno(stdStream)) && GetConsoleMode((HANDLE)_get_osfhandle(_fileno(stdStream)), &dummy); } From 136b9e2392995a2e975237f832703362e3c6d523 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 20 Mar 2018 14:31:43 -0700 Subject: [PATCH 211/255] Fix external sequence corner cases * Clear external sequences when we reset the `ZSTD_CCtx`. * Skip external sequences when a block is too small to compress. --- lib/compress/zstd_compress.c | 6 ++- lib/compress/zstd_ldm.c | 73 ++++++++++++++++++------------------ lib/compress/zstd_ldm.h | 10 +++++ 3 files changed, 52 insertions(+), 37 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index db8d7a8c3..642296fb6 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -922,6 +922,7 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pl cctx->dictID = 0; if (params.ldmParams.enableLdm) ZSTD_window_clear(&cctx->ldmState.window); + ZSTD_referenceExternalSequences(cctx, NULL, 0); ZSTD_invalidateMatchState(&cctx->blockState.matchState); ZSTD_reset_compressedBlockState(cctx->blockState.prevCBlock); XXH64_reset(&cctx->xxhState, 0); @@ -1108,6 +1109,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ptr = zc->ldmState.bucketOffsets + ldmBucketSize; ZSTD_window_clear(&zc->ldmState.window); } + ZSTD_referenceExternalSequences(zc, NULL, 0); /* buffers */ zc->inBuffSize = buffInSize; @@ -1818,8 +1820,10 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, ZSTD_matchState_t* const ms = &zc->blockState.matchState; DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)", (U32)dstCapacity, ms->window.dictLimit, ms->nextToUpdate); - if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) + if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) { + ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize, zc->appliedParams.cParams.searchLength); return 0; /* don't even attempt compression below a certain srcSize */ + } ZSTD_resetSeqStore(&(zc->seqStore)); /* limited update after a very long match */ diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index 5c9c0d2b6..aff9dd2be 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -536,6 +536,34 @@ size_t ZSTD_ldm_generateSequences( return 0; } +void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch) { + while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) { + rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos; + if (srcSize <= seq->litLength) { + /* Skip past srcSize literals */ + seq->litLength -= srcSize; + return; + } + srcSize -= seq->litLength; + seq->litLength = 0; + if (srcSize < seq->matchLength) { + /* Skip past the first srcSize of the match */ + seq->matchLength -= srcSize; + if (seq->matchLength < minMatch) { + /* The match is too short, omit it */ + if (rawSeqStore->pos + 1 < rawSeqStore->size) { + seq[1].litLength += seq[0].matchLength; + } + rawSeqStore->pos++; + } + return; + } + srcSize -= seq->matchLength; + seq->matchLength = 0; + rawSeqStore->pos++; + } +} + /** * If the sequence length is longer than remaining then the sequence is split * between this block and the next. @@ -546,51 +574,24 @@ size_t ZSTD_ldm_generateSequences( static rawSeq maybeSplitSequence(rawSeqStore_t* rawSeqStore, U32 const remaining, U32 const minMatch) { - size_t const pos = rawSeqStore->pos; rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos]; assert(sequence.offset > 0); - /* Handle partial sequences */ + /* Likely: No partial sequence */ + if (remaining >= sequence.litLength + sequence.matchLength) { + rawSeqStore->pos++; + return sequence; + } + /* Cut the sequence short (offset == 0 ==> rest is literals). */ if (remaining <= sequence.litLength) { - /* Split the literals that we have out of the sequence. - * They will become the last literals of this block. - * The next block starts off with the remaining literals. - */ - rawSeqStore->seq[pos].litLength -= remaining; sequence.offset = 0; } else if (remaining < sequence.litLength + sequence.matchLength) { - /* Split the match up into two sequences. One in this block, and one - * in the next with no literals. If either match would be shorter - * than searchLength we omit it. - */ - U32 const matchPrefix = remaining - sequence.litLength; - U32 const matchSuffix = sequence.matchLength - matchPrefix; - - assert(remaining > sequence.litLength); - assert(matchPrefix < sequence.matchLength); - assert(matchPrefix + matchSuffix == sequence.matchLength); - /* Update the first sequence */ - sequence.matchLength = matchPrefix; - /* Update the second sequence */ - if (matchSuffix >= minMatch) { - /* Update the second sequence, since the suffix is long enough */ - rawSeqStore->seq[pos].litLength = 0; - rawSeqStore->seq[pos].matchLength = matchSuffix; - } else { - /* Omit the second sequence since the match suffix is too short. - * Add to the next sequences literals (if any). - */ - if (pos + 1 < rawSeqStore->size) - rawSeqStore->seq[pos + 1].litLength += matchSuffix; - rawSeqStore->pos++; /* Consume the sequence */ - } + sequence.matchLength = remaining - sequence.litLength; if (sequence.matchLength < minMatch) { - /* Skip the current sequence if it is too short */ sequence.offset = 0; } - } else { - /* No partial sequence */ - rawSeqStore->pos++; /* Consume the sequence */ } + /* Skip past `remaining` bytes for the future sequences. */ + ZSTD_ldm_skipSequences(rawSeqStore, remaining, minMatch); return sequence; } diff --git a/lib/compress/zstd_ldm.h b/lib/compress/zstd_ldm.h index 9d2f7c391..84d3723ce 100644 --- a/lib/compress/zstd_ldm.h +++ b/lib/compress/zstd_ldm.h @@ -65,6 +65,16 @@ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize, int const extDict); +/** + * ZSTD_ldm_skipSequences(): + * + * Skip past `srcSize` bytes worth of sequences in `rawSeqStore`. + * Avoids emitting matches less than `minMatch` bytes. + * Must be called for data with is not passed to ZSTD_ldm_blockCompress(). + */ +void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, + U32 const minMatch); + /** ZSTD_ldm_initializeParameters() : * Initialize the long distance matching parameters to their default values. */ From 31b54b6eea9051cb44f239ecd9d52b453df8880a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Mar 2018 14:52:02 -0700 Subject: [PATCH 212/255] updated ZSTD_initStaticDDict() prototype can also specify dictContentType. --- doc/zstd_manual.html | 4 ++-- lib/decompress/zstd_decompress.c | 5 +++-- lib/zstd.h | 3 ++- tests/fuzzer.c | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index d48250d6e..4daf4d7ed 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -1048,8 +1048,8 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t

    Advanced parameters for decompression API


    size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
    -size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);   /* not implemented */
    -size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode);   /* not implemented */
    +size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
    +size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode);
     

    Create an internal DDict from dict buffer, to be used to decompress next frames. @result : 0, or an error code (which can be tested with ZSTD_isError()). diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index f0b2cc95b..ca44179ff 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2432,7 +2432,8 @@ ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize const ZSTD_DDict* ZSTD_initStaticDDict( void* workspace, size_t workspaceSize, const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod) + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictMode_e dictContentType) { size_t const neededSpace = sizeof(ZSTD_DDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize); @@ -2445,7 +2446,7 @@ const ZSTD_DDict* ZSTD_initStaticDDict( memcpy(ddict+1, dict, dictSize); /* local copy */ dict = ddict+1; } - if (ZSTD_isError( ZSTD_initDDict_internal(ddict, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dm_auto) )) + if (ZSTD_isError( ZSTD_initDDict_internal(ddict, dict, dictSize, ZSTD_dlm_byRef, dictContentType) )) return NULL; return ddict; } diff --git a/lib/zstd.h b/lib/zstd.h index 8a927f427..278ed7247 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -580,7 +580,8 @@ ZSTDLIB_API const ZSTD_CDict* ZSTD_initStaticCDict( ZSTDLIB_API const ZSTD_DDict* ZSTD_initStaticDDict( void* workspace, size_t workspaceSize, const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod); + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictMode_e dictMode); /*! Custom memory allocation : * These prototypes make it possible to pass your own allocation/free functions. diff --git a/tests/fuzzer.c b/tests/fuzzer.c index cae509707..9b7553ba5 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -649,7 +649,7 @@ static int basicUnitTests(U32 seed, double compressibility) { size_t const ddictBufferSize = ZSTD_estimateDDictSize(dictSize, ZSTD_dlm_byCopy); void* ddictBuffer = malloc(ddictBufferSize); if (ddictBuffer == NULL) goto _output_error; - { const ZSTD_DDict* const ddict = ZSTD_initStaticDDict(ddictBuffer, ddictBufferSize, CNBuffer, dictSize, ZSTD_dlm_byCopy); + { const ZSTD_DDict* const ddict = ZSTD_initStaticDDict(ddictBuffer, ddictBufferSize, CNBuffer, dictSize, ZSTD_dlm_byCopy, ZSTD_dm_auto); size_t const r = ZSTD_decompress_usingDDict(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize, ddict); if (r != CNBuffSize - dictSize) goto _output_error; } From 6873fec658f4d035a9a420e706cdf827888d77ed Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Mar 2018 15:13:14 -0700 Subject: [PATCH 213/255] changed dictMore for dictContentType which seems clearer to describe what the variable/argument is about. --- doc/zstd_manual.html | 16 +++--- lib/compress/zstd_compress.c | 72 +++++++++++++-------------- lib/compress/zstd_compress_internal.h | 4 +- lib/compress/zstdmt_compress.c | 16 +++--- lib/compress/zstdmt_compress.h | 2 +- lib/decompress/zstd_decompress.c | 26 +++++----- lib/zstd.h | 24 ++++----- tests/fuzzer.c | 18 +++---- tests/zstreamtest.c | 6 +-- zlibWrapper/examples/zwrapbench.c | 2 +- 10 files changed, 93 insertions(+), 93 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 4daf4d7ed..d533aefe4 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -345,10 +345,10 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB } ZSTD_parameters;


    typedef enum {
    -    ZSTD_dm_auto=0,      /* dictionary is "full" if it starts with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */
    -    ZSTD_dm_rawContent,  /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */
    -    ZSTD_dm_fullDict     /* refuses to load a dictionary if it does not respect Zstandard's specification */
    -} ZSTD_dictMode_e;
    +    ZSTD_dct_auto=0,      /* dictionary is "full" when starting with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */
    +    ZSTD_dct_rawContent,  /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */
    +    ZSTD_dct_fullDict     /* refuses to load a dictionary if it does not respect Zstandard's specification */
    +} ZSTD_dictContentType_e;
     

    typedef enum {
         ZSTD_dlm_byCopy = 0, /**< Copy dictionary content internally */
    @@ -890,7 +890,7 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
     
     
    size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
     size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
    -size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode);
    +size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
     

    Create an internal CDict from `dict` buffer. Decompression will have to use same dictionary. @result : 0, or an error code (which can be tested with ZSTD_isError()). @@ -921,7 +921,7 @@ size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size


    size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize);
    -size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictMode_e dictMode);
    +size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
     

    Reference a prefix (single-usage dictionary) for next compression job. Decompression need same prefix to properly regenerate data. Prefix is **only used once**. Tables are discarded at end of compression job. @@ -1049,7 +1049,7 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t

    Advanced parameters for decompression API


    size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
     size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
    -size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode);
    +size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
     

    Create an internal DDict from dict buffer, to be used to decompress next frames. @result : 0, or an error code (which can be tested with ZSTD_isError()). @@ -1078,7 +1078,7 @@ size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size


    size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize);   /* not implemented */
    -size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictMode_e dictMode);   /* not implemented */
    +size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);   /* not implemented */
     

    Reference a prefix (single-usage dictionary) for next compression job. Prefix is **only used once**. It must be explicitly referenced before each frame. If there is a need to use same prefix multiple times, consider embedding it into a ZSTD_DDict instead. diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index db8d7a8c3..85678a8dc 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -536,7 +536,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long lo size_t ZSTD_CCtx_loadDictionary_advanced( ZSTD_CCtx* cctx, const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode) + ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) { if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); if (cctx->staticSize) return ERROR(memory_allocation); /* no malloc for static CCtx */ @@ -550,7 +550,7 @@ size_t ZSTD_CCtx_loadDictionary_advanced( ZSTD_getCParamsFromCCtxParams(cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, dictSize); cctx->cdictLocal = ZSTD_createCDict_advanced( dict, dictSize, - dictLoadMethod, dictMode, + dictLoadMethod, dictContentType, cParams, cctx->customMem); cctx->cdict = cctx->cdictLocal; if (cctx->cdictLocal == NULL) @@ -563,13 +563,13 @@ ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference( ZSTD_CCtx* cctx, const void* dict, size_t dictSize) { return ZSTD_CCtx_loadDictionary_advanced( - cctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dm_auto); + cctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto); } ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize) { return ZSTD_CCtx_loadDictionary_advanced( - cctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dm_auto); + cctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto); } @@ -583,17 +583,17 @@ size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize) { - return ZSTD_CCtx_refPrefix_advanced(cctx, prefix, prefixSize, ZSTD_dm_rawContent); + return ZSTD_CCtx_refPrefix_advanced(cctx, prefix, prefixSize, ZSTD_dct_rawContent); } size_t ZSTD_CCtx_refPrefix_advanced( - ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictMode_e dictMode) + ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType) { if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); cctx->cdict = NULL; /* prefix discards any prior cdict */ cctx->prefixDict.dict = prefix; cctx->prefixDict.dictSize = prefixSize; - cctx->prefixDict.dictMode = dictMode; + cctx->prefixDict.dictContentType = dictContentType; return 0; } @@ -2268,7 +2268,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_matc static size_t ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_matchState_t* ms, ZSTD_CCtx_params const* params, const void* dict, size_t dictSize, - ZSTD_dictMode_e dictMode, + ZSTD_dictContentType_e dictContentType, void* workspace) { DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize); @@ -2277,15 +2277,15 @@ static size_t ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, ZS ZSTD_reset_compressedBlockState(bs); /* dict restricted modes */ - if (dictMode==ZSTD_dm_rawContent) + if (dictContentType == ZSTD_dct_rawContent) return ZSTD_loadDictionaryContent(ms, params, dict, dictSize); if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) { - if (dictMode == ZSTD_dm_auto) { + if (dictContentType == ZSTD_dct_auto) { DEBUGLOG(4, "raw content dictionary detected"); return ZSTD_loadDictionaryContent(ms, params, dict, dictSize); } - if (dictMode == ZSTD_dm_fullDict) + if (dictContentType == ZSTD_dct_fullDict) return ERROR(dictionary_wrong); assert(0); /* impossible */ } @@ -2298,7 +2298,7 @@ static size_t ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, ZS * @return : 0, or an error code */ size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, - ZSTD_dictMode_e dictMode, + ZSTD_dictContentType_e dictContentType, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, U64 pledgedSrcSize, ZSTD_buffered_policy_e zbuff) @@ -2319,7 +2319,7 @@ size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, { size_t const dictID = ZSTD_compress_insertDictionary( cctx->blockState.prevCBlock, &cctx->blockState.matchState, - ¶ms, dict, dictSize, dictMode, cctx->entropyWorkspace); + ¶ms, dict, dictSize, dictContentType, cctx->entropyWorkspace); if (ZSTD_isError(dictID)) return dictID; assert(dictID <= (size_t)(U32)-1); cctx->dictID = (U32)dictID; @@ -2329,7 +2329,7 @@ size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, - ZSTD_dictMode_e dictMode, + ZSTD_dictContentType_e dictContentType, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize) @@ -2338,7 +2338,7 @@ size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx, /* compression parameters verification and optimization */ CHECK_F( ZSTD_checkCParams(params.cParams) ); return ZSTD_compressBegin_internal(cctx, - dict, dictSize, dictMode, + dict, dictSize, dictContentType, cdict, params, pledgedSrcSize, ZSTDb_not_buffered); @@ -2353,7 +2353,7 @@ size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params); return ZSTD_compressBegin_advanced_internal(cctx, - dict, dictSize, ZSTD_dm_auto, + dict, dictSize, ZSTD_dct_auto, NULL /*cdict*/, cctxParams, pledgedSrcSize); } @@ -2364,7 +2364,7 @@ size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t di ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params); DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (U32)dictSize); - return ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dm_auto, NULL, + return ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered); } @@ -2475,7 +2475,7 @@ size_t ZSTD_compress_advanced_internal( { DEBUGLOG(4, "ZSTD_compress_advanced_internal (srcSize:%u)", (U32)srcSize); - CHECK_F( ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dm_auto, NULL, + CHECK_F( ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, NULL, params, srcSize, ZSTDb_not_buffered) ); return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); } @@ -2538,10 +2538,10 @@ static size_t ZSTD_initCDict_internal( ZSTD_CDict* cdict, const void* dictBuffer, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictMode, + ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams) { - DEBUGLOG(3, "ZSTD_initCDict_internal, mode %u", (U32)dictMode); + DEBUGLOG(3, "ZSTD_initCDict_internal, mode %u", (U32)dictContentType); cdict->cParams = cParams; if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) { cdict->dictBuffer = NULL; @@ -2578,7 +2578,7 @@ static size_t ZSTD_initCDict_internal( size_t const dictID = ZSTD_compress_insertDictionary( &cdict->cBlockState, &cdict->matchState,¶ms, cdict->dictContent, cdict->dictContentSize, - dictMode, cdict->workspace); + dictContentType, cdict->workspace); if (ZSTD_isError(dictID)) return dictID; assert(dictID <= (size_t)(U32)-1); cdict->dictID = (U32)dictID; @@ -2590,10 +2590,10 @@ static size_t ZSTD_initCDict_internal( ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictMode, + ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams, ZSTD_customMem customMem) { - DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (U32)dictMode); + DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (U32)dictContentType); if (!customMem.customAlloc ^ !customMem.customFree) return NULL; { ZSTD_CDict* const cdict = (ZSTD_CDict*)ZSTD_malloc(sizeof(ZSTD_CDict), customMem); @@ -2610,7 +2610,7 @@ ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize, cdict->workspaceSize = workspaceSize; if (ZSTD_isError( ZSTD_initCDict_internal(cdict, dictBuffer, dictSize, - dictLoadMethod, dictMode, + dictLoadMethod, dictContentType, cParams) )) { ZSTD_freeCDict(cdict); return NULL; @@ -2624,7 +2624,7 @@ ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionL { ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, 0, dictSize); return ZSTD_createCDict_advanced(dict, dictSize, - ZSTD_dlm_byCopy, ZSTD_dm_auto, + ZSTD_dlm_byCopy, ZSTD_dct_auto, cParams, ZSTD_defaultCMem); } @@ -2632,7 +2632,7 @@ ZSTD_CDict* ZSTD_createCDict_byReference(const void* dict, size_t dictSize, int { ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, 0, dictSize); return ZSTD_createCDict_advanced(dict, dictSize, - ZSTD_dlm_byRef, ZSTD_dm_auto, + ZSTD_dlm_byRef, ZSTD_dct_auto, cParams, ZSTD_defaultCMem); } @@ -2664,7 +2664,7 @@ const ZSTD_CDict* ZSTD_initStaticCDict( void* workspace, size_t workspaceSize, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictMode, + ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams) { size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0); @@ -2689,7 +2689,7 @@ const ZSTD_CDict* ZSTD_initStaticCDict( if (ZSTD_isError( ZSTD_initCDict_internal(cdict, dict, dictSize, - ZSTD_dlm_byRef, dictMode, + ZSTD_dlm_byRef, dictContentType, cParams) )) return NULL; @@ -2723,7 +2723,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced( } params.fParams = fParams; return ZSTD_compressBegin_internal(cctx, - NULL, 0, ZSTD_dm_auto, + NULL, 0, ZSTD_dct_auto, cdict, params, pledgedSrcSize, ZSTDb_not_buffered); @@ -2802,7 +2802,7 @@ size_t ZSTD_CStreamOutSize(void) } static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx, - const void* const dict, size_t const dictSize, ZSTD_dictMode_e const dictMode, + const void* const dict, size_t const dictSize, ZSTD_dictContentType_e const dictContentType, const ZSTD_CDict* const cdict, ZSTD_CCtx_params const params, unsigned long long const pledgedSrcSize) { @@ -2813,7 +2813,7 @@ static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx, assert(!((dict) && (cdict))); /* either dict or cdict, not both */ CHECK_F( ZSTD_compressBegin_internal(cctx, - dict, dictSize, dictMode, + dict, dictSize, dictContentType, cdict, params, pledgedSrcSize, ZSTDb_buffered) ); @@ -2837,7 +2837,7 @@ size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize) if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; params.fParams.contentSizeFlag = 1; params.cParams = ZSTD_getCParamsFromCCtxParams(params, pledgedSrcSize, 0); - return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, zcs->cdict, params, pledgedSrcSize); + return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dct_auto, zcs->cdict, params, pledgedSrcSize); } /*! ZSTD_initCStream_internal() : @@ -2860,7 +2860,7 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, } ZSTD_freeCDict(zcs->cdictLocal); zcs->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize, - ZSTD_dlm_byCopy, ZSTD_dm_auto, + ZSTD_dlm_byCopy, ZSTD_dct_auto, params.cParams, zcs->customMem); zcs->cdict = zcs->cdictLocal; if (zcs->cdictLocal == NULL) return ERROR(memory_allocation); @@ -2876,7 +2876,7 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, params.compressionLevel = ZSTD_CLEVEL_CUSTOM; /* enforce usage of cParams, instead of a dynamic derivation from cLevel (but does that happen ?) */ zcs->requestedParams = params; - return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, zcs->cdict, params, pledgedSrcSize); + return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dct_auto, zcs->cdict, params, pledgedSrcSize); } /* ZSTD_initCStream_usingCDict_advanced() : @@ -3152,14 +3152,14 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbWorkers=%u", params.nbWorkers); CHECK_F( ZSTDMT_initCStream_internal( cctx->mtctx, - prefixDict.dict, prefixDict.dictSize, ZSTD_dm_rawContent, + prefixDict.dict, prefixDict.dictSize, ZSTD_dct_rawContent, cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) ); cctx->streamStage = zcss_load; cctx->appliedParams.nbWorkers = params.nbWorkers; } else #endif { CHECK_F( ZSTD_resetCStream_internal(cctx, - prefixDict.dict, prefixDict.dictSize, prefixDict.dictMode, + prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType, cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) ); assert(cctx->streamStage == zcss_load); diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 865c080cc..c09c2333b 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -50,7 +50,7 @@ typedef enum { zcss_init=0, zcss_load, zcss_flush } ZSTD_cStreamStage; typedef struct ZSTD_prefixDict_s { const void* dict; size_t dictSize; - ZSTD_dictMode_e dictMode; + ZSTD_dictContentType_e dictContentType; } ZSTD_prefixDict; typedef struct { @@ -665,7 +665,7 @@ ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict); * Private use only. To be called from zstdmt_compress.c. */ size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, - ZSTD_dictMode_e dictMode, + ZSTD_dictContentType_e dictContentType, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 99131260b..94de683e2 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -429,7 +429,7 @@ void ZSTDMT_compressionJob(void* jobDescription) /* init */ if (job->cdict) { - size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dm_auto, job->cdict, jobParams, job->fullFrameSize); + size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dct_auto, job->cdict, jobParams, job->fullFrameSize); assert(job->firstJob); /* only allowed for first job */ if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ @@ -440,7 +440,7 @@ void ZSTDMT_compressionJob(void* jobDescription) goto _endJob; } } { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, - job->prefix.start, job->prefix.size, ZSTD_dm_rawContent, /* load dictionary in "content-only" mode (no header analysis) */ + job->prefix.start, job->prefix.size, ZSTD_dct_rawContent, /* load dictionary in "content-only" mode (no header analysis) */ NULL, /*cdict*/ jobParams, pledgedSrcSize); if (ZSTD_isError(initError)) { @@ -1010,7 +1010,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, size_t ZSTDMT_initCStream_internal( ZSTDMT_CCtx* mtctx, - const void* dict, size_t dictSize, ZSTD_dictMode_e dictMode, + const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize) { @@ -1053,7 +1053,7 @@ size_t ZSTDMT_initCStream_internal( if (dict) { ZSTD_freeCDict(mtctx->cdictLocal); mtctx->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize, - ZSTD_dlm_byCopy, dictMode, /* note : a loadPrefix becomes an internal CDict */ + ZSTD_dlm_byCopy, dictContentType, /* note : a loadPrefix becomes an internal CDict */ params.cParams, mtctx->cMem); mtctx->cdict = mtctx->cdictLocal; if (mtctx->cdictLocal == NULL) return ERROR(memory_allocation); @@ -1112,7 +1112,7 @@ size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, DEBUGLOG(4, "ZSTDMT_initCStream_advanced (pledgedSrcSize=%u)", (U32)pledgedSrcSize); cctxParams.cParams = params.cParams; cctxParams.fParams = params.fParams; - return ZSTDMT_initCStream_internal(mtctx, dict, dictSize, ZSTD_dm_auto, NULL, + return ZSTDMT_initCStream_internal(mtctx, dict, dictSize, ZSTD_dct_auto, NULL, cctxParams, pledgedSrcSize); } @@ -1125,7 +1125,7 @@ size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, if (cdict==NULL) return ERROR(dictionary_wrong); /* method incompatible with NULL cdict */ cctxParams.cParams = ZSTD_getCParamsFromCDict(cdict); cctxParams.fParams = fParams; - return ZSTDMT_initCStream_internal(mtctx, NULL, 0 /*dictSize*/, ZSTD_dm_auto, cdict, + return ZSTDMT_initCStream_internal(mtctx, NULL, 0 /*dictSize*/, ZSTD_dct_auto, cdict, cctxParams, pledgedSrcSize); } @@ -1137,7 +1137,7 @@ size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize) { if (!pledgedSrcSize) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; - return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dm_auto, 0, mtctx->params, + return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dct_auto, 0, mtctx->params, pledgedSrcSize); } @@ -1147,7 +1147,7 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel) { DEBUGLOG(4, "ZSTDMT_initCStream (cLevel=%i)", compressionLevel); cctxParams.cParams = params.cParams; cctxParams.fParams = params.fParams; - return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dm_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN); + return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dct_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN); } diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 4364f100d..7adaa803f 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -145,7 +145,7 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx); * must receive dict, or cdict, or none, but not both. * @return : 0, or an error code */ size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, - const void* dict, size_t dictSize, ZSTD_dictMode_e dictMode, + const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize); diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index ca44179ff..3aefcc6cf 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2337,20 +2337,20 @@ size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dstDCtx, const ZSTD_DDict* ddi return 0; } -static size_t ZSTD_loadEntropy_inDDict(ZSTD_DDict* ddict, ZSTD_dictMode_e dictContentType) +static size_t ZSTD_loadEntropy_inDDict(ZSTD_DDict* ddict, ZSTD_dictContentType_e dictContentType) { ddict->dictID = 0; ddict->entropyPresent = 0; - if (dictContentType == ZSTD_dm_rawContent) return 0; + if (dictContentType == ZSTD_dct_rawContent) return 0; if (ddict->dictSize < 8) { - if (dictContentType == ZSTD_dm_fullDict) + if (dictContentType == ZSTD_dct_fullDict) return ERROR(dictionary_corrupted); /* only accept specified dictionaries */ return 0; /* pure content mode */ } { U32 const magic = MEM_readLE32(ddict->dictContent); if (magic != ZSTD_MAGIC_DICTIONARY) { - if (dictContentType == ZSTD_dm_fullDict) + if (dictContentType == ZSTD_dct_fullDict) return ERROR(dictionary_corrupted); /* only accept specified dictionaries */ return 0; /* pure content mode */ } @@ -2367,7 +2367,7 @@ static size_t ZSTD_loadEntropy_inDDict(ZSTD_DDict* ddict, ZSTD_dictMode_e dictCo static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictContentType) + ZSTD_dictContentType_e dictContentType) { if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dict) || (!dictSize)) { ddict->dictBuffer = NULL; @@ -2390,7 +2390,7 @@ static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict, ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictContentType, + ZSTD_dictContentType_e dictContentType, ZSTD_customMem customMem) { if (!customMem.customAlloc ^ !customMem.customFree) return NULL; @@ -2415,7 +2415,7 @@ ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize) { ZSTD_customMem const allocator = { NULL, NULL, NULL }; - return ZSTD_createDDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dm_auto, allocator); + return ZSTD_createDDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto, allocator); } /*! ZSTD_createDDict_byReference() : @@ -2425,7 +2425,7 @@ ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize) ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize) { ZSTD_customMem const allocator = { NULL, NULL, NULL }; - return ZSTD_createDDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dm_auto, allocator); + return ZSTD_createDDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto, allocator); } @@ -2433,7 +2433,7 @@ const ZSTD_DDict* ZSTD_initStaticDDict( void* workspace, size_t workspaceSize, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictContentType) + ZSTD_dictContentType_e dictContentType) { size_t const neededSpace = sizeof(ZSTD_DDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize); @@ -2565,11 +2565,11 @@ size_t ZSTD_freeDStream(ZSTD_DStream* zds) size_t ZSTD_DStreamInSize(void) { return ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize; } size_t ZSTD_DStreamOutSize(void) { return ZSTD_BLOCKSIZE_MAX; } -size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode) +size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) { ZSTD_freeDDict(dctx->ddictLocal); if (dict && dictSize >= 8) { - dctx->ddictLocal = ZSTD_createDDict_advanced(dict, dictSize, dictLoadMethod, dictMode, dctx->customMem); + dctx->ddictLocal = ZSTD_createDDict_advanced(dict, dictSize, dictLoadMethod, dictContentType, dctx->customMem); if (dctx->ddictLocal == NULL) return ERROR(memory_allocation); } else { dctx->ddictLocal = NULL; @@ -2580,12 +2580,12 @@ size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) { - return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dm_auto); + return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto); } size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) { - return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dm_auto); + return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto); } size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize) diff --git a/lib/zstd.h b/lib/zstd.h index 278ed7247..c90e8d226 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -433,10 +433,10 @@ typedef struct { typedef struct ZSTD_CCtx_params_s ZSTD_CCtx_params; typedef enum { - ZSTD_dm_auto=0, /* dictionary is "full" if it starts with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */ - ZSTD_dm_rawContent, /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */ - ZSTD_dm_fullDict /* refuses to load a dictionary if it does not respect Zstandard's specification */ -} ZSTD_dictMode_e; + ZSTD_dct_auto=0, /* dictionary is "full" when starting with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */ + ZSTD_dct_rawContent, /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */ + ZSTD_dct_fullDict /* refuses to load a dictionary if it does not respect Zstandard's specification */ +} ZSTD_dictContentType_e; typedef enum { ZSTD_dlm_byCopy = 0, /**< Copy dictionary content internally */ @@ -574,14 +574,14 @@ ZSTDLIB_API const ZSTD_CDict* ZSTD_initStaticCDict( void* workspace, size_t workspaceSize, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictMode, + ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams); ZSTDLIB_API const ZSTD_DDict* ZSTD_initStaticDDict( void* workspace, size_t workspaceSize, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictMode); + ZSTD_dictContentType_e dictContentType); /*! Custom memory allocation : * These prototypes make it possible to pass your own allocation/free functions. @@ -600,13 +600,13 @@ ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem); ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictMode, + ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams, ZSTD_customMem customMem); ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, - ZSTD_dictMode_e dictContentType, + ZSTD_dictContentType_e dictContentType, ZSTD_customMem customMem); @@ -1103,7 +1103,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long lo * to precisely select how dictionary content must be interpreted. */ ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); -ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode); +ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType); /*! ZSTD_CCtx_refCDict() : @@ -1132,7 +1132,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); * Note 3 : By default, the prefix is treated as raw content (ZSTD_dm_rawContent). * Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode. */ ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize); -ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictMode_e dictMode); +ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); @@ -1274,7 +1274,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_setParametersUsingCCtxParams( */ ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); -ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictMode_e dictMode); +ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType); /*! ZSTD_DCtx_refDDict() : @@ -1301,7 +1301,7 @@ ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); * Note 4 : Referencing a raw content prefix has almost no cpu nor memory cost. */ ZSTDLIB_API size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize); /* not implemented */ -ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictMode_e dictMode); /* not implemented */ +ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); /* not implemented */ /*! ZSTD_DCtx_setMaxWindowSize() : diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 9b7553ba5..82b686052 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -649,7 +649,7 @@ static int basicUnitTests(U32 seed, double compressibility) { size_t const ddictBufferSize = ZSTD_estimateDDictSize(dictSize, ZSTD_dlm_byCopy); void* ddictBuffer = malloc(ddictBufferSize); if (ddictBuffer == NULL) goto _output_error; - { const ZSTD_DDict* const ddict = ZSTD_initStaticDDict(ddictBuffer, ddictBufferSize, CNBuffer, dictSize, ZSTD_dlm_byCopy, ZSTD_dm_auto); + { const ZSTD_DDict* const ddict = ZSTD_initStaticDDict(ddictBuffer, ddictBufferSize, CNBuffer, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto); size_t const r = ZSTD_decompress_usingDDict(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize, ddict); if (r != CNBuffSize - dictSize) goto _output_error; } @@ -753,7 +753,7 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "test%3i : compress with CDict ", testNb++); { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBuffSize, dictSize); ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize, - ZSTD_dlm_byRef, ZSTD_dm_auto, + ZSTD_dlm_byRef, ZSTD_dct_auto, cParams, ZSTD_defaultCMem); DISPLAYLEVEL(3, "(size : %u) : ", (U32)ZSTD_sizeof_CDict(cdict)); cSize = ZSTD_compress_usingCDict(cctx, compressedBuffer, compressedBufferSize, @@ -788,7 +788,7 @@ static int basicUnitTests(U32 seed, double compressibility) { const ZSTD_CDict* const cdict = ZSTD_initStaticCDict( cdictBuffer, cdictSize, dictBuffer, dictSize, - ZSTD_dlm_byCopy, ZSTD_dm_auto, + ZSTD_dlm_byCopy, ZSTD_dct_auto, cParams); if (cdict == NULL) { DISPLAY("ZSTD_initStaticCDict failed "); @@ -808,7 +808,7 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "test%3i : ZSTD_compress_usingCDict_advanced, no contentSize, no dictID : ", testNb++); { ZSTD_frameParameters const fParams = { 0 /* frameSize */, 1 /* checksum */, 1 /* noDictID*/ }; ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBuffSize, dictSize); - ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dm_auto, cParams, ZSTD_defaultCMem); + ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto, cParams, ZSTD_defaultCMem); cSize = ZSTD_compress_usingCDict_advanced(cctx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize, cdict, fParams); ZSTD_freeCDict(cdict); @@ -859,7 +859,7 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "test%3i : Building cdict w/ ZSTD_dm_fullDict on a good dictionary : ", testNb++); { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBuffSize, dictSize); - ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dm_fullDict, cParams, ZSTD_defaultCMem); + ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dct_fullDict, cParams, ZSTD_defaultCMem); if (cdict==NULL) goto _output_error; ZSTD_freeCDict(cdict); } @@ -867,7 +867,7 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "test%3i : Building cdict w/ ZSTD_dm_fullDict on a rawContent (must fail) : ", testNb++); { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBuffSize, dictSize); - ZSTD_CDict* const cdict = ZSTD_createCDict_advanced((const char*)dictBuffer+1, dictSize-1, ZSTD_dlm_byRef, ZSTD_dm_fullDict, cParams, ZSTD_defaultCMem); + ZSTD_CDict* const cdict = ZSTD_createCDict_advanced((const char*)dictBuffer+1, dictSize-1, ZSTD_dlm_byRef, ZSTD_dct_fullDict, cParams, ZSTD_defaultCMem); if (cdict!=NULL) goto _output_error; ZSTD_freeCDict(cdict); } @@ -878,7 +878,7 @@ static int basicUnitTests(U32 seed, double compressibility) size_t ret; MEM_writeLE32((char*)dictBuffer+2, ZSTD_MAGIC_DICTIONARY); ret = ZSTD_CCtx_loadDictionary_advanced( - cctx, (const char*)dictBuffer+2, dictSize-2, ZSTD_dlm_byRef, ZSTD_dm_auto); + cctx, (const char*)dictBuffer+2, dictSize-2, ZSTD_dlm_byRef, ZSTD_dct_auto); if (!ZSTD_isError(ret)) goto _output_error; } DISPLAYLEVEL(3, "OK \n"); @@ -888,7 +888,7 @@ static int basicUnitTests(U32 seed, double compressibility) size_t ret; MEM_writeLE32((char*)dictBuffer+2, ZSTD_MAGIC_DICTIONARY); ret = ZSTD_CCtx_loadDictionary_advanced( - cctx, (const char*)dictBuffer+2, dictSize-2, ZSTD_dlm_byRef, ZSTD_dm_rawContent); + cctx, (const char*)dictBuffer+2, dictSize-2, ZSTD_dlm_byRef, ZSTD_dct_rawContent); if (ZSTD_isError(ret)) goto _output_error; } DISPLAYLEVEL(3, "OK \n"); @@ -924,7 +924,7 @@ static int basicUnitTests(U32 seed, double compressibility) BYTE data[1024]; ZSTD_compressionParameters const cParams = ZSTD_getCParams(19, CNBuffSize, dictSize); ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize, - ZSTD_dlm_byRef, ZSTD_dm_auto, + ZSTD_dlm_byRef, ZSTD_dct_auto, cParams, ZSTD_defaultCMem); memset(data, 'x', sizeof(data)); cSize = ZSTD_compress_usingCDict(cctx, compressedBuffer, compressedBufferSize, diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index db87e2c5b..703c67834 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -610,7 +610,7 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "test%3i : ZSTD_initCStream_usingCDict_advanced with masked dictID : ", testNb++); { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBufferSize, dictionary.filled); ZSTD_frameParameters const fParams = { 1 /* contentSize */, 1 /* checksum */, 1 /* noDictID */}; - ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dm_auto, cParams, ZSTD_defaultCMem); + ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dct_auto, cParams, ZSTD_defaultCMem); size_t const initError = ZSTD_initCStream_usingCDict_advanced(zc, cdict, fParams, CNBufferSize); if (ZSTD_isError(initError)) goto _output_error; cSize = 0; @@ -776,7 +776,7 @@ static int basicUnitTests(U32 seed, double compressibility) inBuff.pos = 0; } { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, 4 KB, dictionary.filled); /* intentionnally lies on estimatedSrcSize, to push cdict into targeting a small window size */ - ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dm_fullDict, cParams, ZSTD_defaultCMem); + ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dct_fullDict, cParams, ZSTD_defaultCMem); DISPLAYLEVEL(5, "cParams.windowLog = %u : ", cParams.windowLog); CHECK_Z( ZSTD_CCtx_refCDict(zc, cdict) ); CHECK_Z( ZSTD_compress_generic(zc, &outBuff, &inBuff, ZSTD_e_end) ); @@ -819,7 +819,7 @@ static int basicUnitTests(U32 seed, double compressibility) XXH64_reset(&xxh, 0); cParams.windowLog = kMaxWindowLog; - cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dm_fullDict, cParams, ZSTD_defaultCMem); + cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dct_fullDict, cParams, ZSTD_defaultCMem); ddict = ZSTD_createDDict(dictionary.start, dictionary.filled); if (!cdict || !ddict) goto _output_error; diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c index 9226932b5..a4dfbb6e8 100644 --- a/zlibWrapper/examples/zwrapbench.c +++ b/zlibWrapper/examples/zwrapbench.c @@ -233,7 +233,7 @@ static int BMK_benchMem(z_const void* srcBuffer, size_t srcSize, if (compressor == BMK_ZSTD) { ZSTD_parameters const zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize); ZSTD_customMem const cmem = { NULL, NULL, NULL }; - ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, ZSTD_dlm_byRef, ZSTD_dm_auto, zparams.cParams, cmem); + ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, ZSTD_dlm_byRef, ZSTD_dct_auto, zparams.cParams, cmem); if (cdict==NULL) EXM_THROW(1, "ZSTD_createCDict_advanced() allocation failure"); do { From a3b76a77ef9b9313c063bf6731f4259faef9978b Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 20 Mar 2018 15:34:40 -0700 Subject: [PATCH 214/255] Quiet appveyor warnings --- lib/compress/zstd_ldm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index aff9dd2be..1684ac113 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -541,14 +541,14 @@ void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 cons rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos; if (srcSize <= seq->litLength) { /* Skip past srcSize literals */ - seq->litLength -= srcSize; + seq->litLength -= (U32)srcSize; return; } srcSize -= seq->litLength; seq->litLength = 0; if (srcSize < seq->matchLength) { /* Skip past the first srcSize of the match */ - seq->matchLength -= srcSize; + seq->matchLength -= (U32)srcSize; if (seq->matchLength < minMatch) { /* The match is too short, omit it */ if (rawSeqStore->pos + 1 < rawSeqStore->size) { From 569b8ba4d9934a53fbf070a17f53f6bbdaf0df07 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Mar 2018 15:43:49 -0700 Subject: [PATCH 215/255] implemented ZSTD_DCtx_refDDict() --- lib/decompress/zstd_decompress.c | 21 ++++++++++++++++----- lib/zstd.h | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 3aefcc6cf..d3cd7a8c9 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2567,6 +2567,7 @@ size_t ZSTD_DStreamOutSize(void) { return ZSTD_BLOCKSIZE_MAX; } size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) { + if (dctx->streamStage != zdss_init) return ERROR(stage_wrong); ZSTD_freeDDict(dctx->ddictLocal); if (dict && dictSize >= 8) { dctx->ddictLocal = ZSTD_createDDict_advanced(dict, dictSize, dictLoadMethod, dictContentType, dctx->customMem); @@ -2588,15 +2589,15 @@ size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSi return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto); } +/* ZSTD_initDStream_usingDict() : + * return : expected size, aka ZSTD_frameHeaderSize_prefix. + * this function cannot fail */ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize) { DEBUGLOG(4, "ZSTD_initDStream_usingDict"); - zds->streamStage = zdss_loadHeader; - zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; + zds->streamStage = zdss_init; CHECK_F( ZSTD_DCtx_loadDictionary(zds, dict, dictSize) ); - zds->legacyVersion = 0; - zds->hostageByte = 0; - return ZSTD_frameHeaderSize_prefix; + return ZSTD_resetDStream(zds); } /* note : this variant can't fail */ @@ -2606,6 +2607,13 @@ size_t ZSTD_initDStream(ZSTD_DStream* zds) return ZSTD_initDStream_usingDict(zds, NULL, 0); } +size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) +{ + if (dctx->streamStage != zdss_init) return ERROR(stage_wrong); + dctx->ddict = ddict; + return 0; +} + /* ZSTD_initDStream_usingDDict() : * ddict will just be referenced, and must outlive decompression session * this function cannot fail */ @@ -2616,6 +2624,9 @@ size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict) return initResult; } +/* ZSTD_resetDStream() : + * return : expected size, aka ZSTD_frameHeaderSize_prefix. + * this function cannot fail */ size_t ZSTD_resetDStream(ZSTD_DStream* zds) { DEBUGLOG(4, "ZSTD_resetDStream"); diff --git a/lib/zstd.h b/lib/zstd.h index c90e8d226..4b825742a 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -1286,7 +1286,7 @@ ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void * Special : adding a NULL DDict means "return to no-dictionary mode". * Note 2 : DDict is just referenced, its lifetime must outlive its usage from DCtx. */ -ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); /* not implemented */ +ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); /*! ZSTD_DCtx_refPrefix() : From 0dadb6b70dc4ef38dd99ebb057dd2a9d386c3032 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Mar 2018 15:45:56 -0700 Subject: [PATCH 216/255] implemented ZSTD_DCtx_refPrefix*() --- lib/decompress/zstd_decompress.c | 11 +++++++++++ lib/zstd.h | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index d3cd7a8c9..60a7f3bd8 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2589,6 +2589,17 @@ size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSi return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto); } +size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType) +{ + return ZSTD_DCtx_loadDictionary_advanced(dctx, prefix, prefixSize, ZSTD_dlm_byRef, dictContentType); +} + +size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize) +{ + return ZSTD_DCtx_refPrefix_advanced(dctx, prefix, prefixSize, ZSTD_dct_rawContent); +} + + /* ZSTD_initDStream_usingDict() : * return : expected size, aka ZSTD_frameHeaderSize_prefix. * this function cannot fail */ diff --git a/lib/zstd.h b/lib/zstd.h index 4b825742a..5d1466e76 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -1300,8 +1300,8 @@ ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); * Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode. * Note 4 : Referencing a raw content prefix has almost no cpu nor memory cost. */ -ZSTDLIB_API size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize); /* not implemented */ -ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); /* not implemented */ +ZSTDLIB_API size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize); +ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); /*! ZSTD_DCtx_setMaxWindowSize() : From 2af41592eaa355a3d8357b062efc691eb221e95a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Mar 2018 15:59:25 -0700 Subject: [PATCH 217/255] added a test with ZSTD_decompress_generic() + ZSTD_DCtx_loadDictionary_byReference() --- tests/zstreamtest.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 703c67834..3c6eb9d87 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -579,14 +579,16 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "test%3i : dictionary source size and level : ", testNb++); { ZSTD_DCtx* const dctx = ZSTD_createDCtx(); - ZSTD_DDict* const ddict = ZSTD_createDDict(dictionary.start, dictionary.filled); - int const maxLevel = ZSTD_maxCLevel(); + int const maxLevel = 15; int level; + assert(maxLevel < ZSTD_maxCLevel()); + CHECK_Z( ZSTD_DCtx_loadDictionary_byReference(dctx, dictionary.start, dictionary.filled) ); for (level = 1; level <= maxLevel; ++level) { ZSTD_CDict* const cdict = ZSTD_createCDict(dictionary.start, dictionary.filled, level); - int const maxSize = MIN(1 MB, CNBufferSize); - int size; + size_t const maxSize = MIN(1 MB, CNBufferSize); + size_t size; for (size = 512; size <= maxSize; size <<= 1) { + U64 const crcOrig = XXH64(CNBuffer, size, 0); ZSTD_CCtx* const cctx = ZSTD_createCCtx(); outBuff.dst = compressedBuffer; outBuff.size = compressedBufferSize; @@ -597,12 +599,18 @@ static int basicUnitTests(U32 seed, double compressibility) CHECK_Z(ZSTD_CCtx_refCDict(cctx, cdict)); CHECK_Z(ZSTD_compress_generic(cctx, &outBuff, &inBuff, ZSTD_e_end)); if (inBuff.pos != inBuff.size) goto _output_error; - CHECK_Z(ZSTD_decompress_usingDDict(dctx, decodedBuffer, size, outBuff.dst, outBuff.pos, ddict)); + { ZSTD_outBuffer decOut = {decodedBuffer, size, 0}; + ZSTD_inBuffer decIn = {outBuff.dst, outBuff.pos, 0}; + CHECK_Z( ZSTD_decompress_generic(dctx, &decOut, &decIn) ); + if (decIn.pos != decIn.size) goto _output_error; + if (decOut.pos != size) goto _output_error; + { U64 const crcDec = XXH64(decOut.dst, decOut.pos, 0); + if (crcDec != crcOrig) goto _output_error; + } } ZSTD_freeCCtx(cctx); } ZSTD_freeCDict(cdict); } - ZSTD_freeDDict(ddict); ZSTD_freeDCtx(dctx); } DISPLAYLEVEL(3, "OK\n"); From 6cda8c932c5d4ac7fa154c4be713b47ee134ae84 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Mar 2018 16:16:13 -0700 Subject: [PATCH 218/255] added test with ZSTD_decompress_generic() + ZSTD_DCtx_refPrefix() also : clarified stage condition to accept new parameters, fixed initializers correspondingly. --- lib/decompress/zstd_decompress.c | 42 ++++++++++++++------------------ tests/zstreamtest.c | 23 ++++++++++------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 60a7f3bd8..008e4174f 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2608,7 +2608,7 @@ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t di DEBUGLOG(4, "ZSTD_initDStream_usingDict"); zds->streamStage = zdss_init; CHECK_F( ZSTD_DCtx_loadDictionary(zds, dict, dictSize) ); - return ZSTD_resetDStream(zds); + return ZSTD_frameHeaderSize_prefix; } /* note : this variant can't fail */ @@ -2628,38 +2628,36 @@ size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) /* ZSTD_initDStream_usingDDict() : * ddict will just be referenced, and must outlive decompression session * this function cannot fail */ -size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict) +size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict) { - size_t const initResult = ZSTD_initDStream(zds); - zds->ddict = ddict; + size_t const initResult = ZSTD_initDStream(dctx); + dctx->ddict = ddict; return initResult; } /* ZSTD_resetDStream() : * return : expected size, aka ZSTD_frameHeaderSize_prefix. * this function cannot fail */ -size_t ZSTD_resetDStream(ZSTD_DStream* zds) +size_t ZSTD_resetDStream(ZSTD_DStream* dctx) { DEBUGLOG(4, "ZSTD_resetDStream"); - zds->streamStage = zdss_loadHeader; - zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; - zds->legacyVersion = 0; - zds->hostageByte = 0; + dctx->streamStage = zdss_loadHeader; + dctx->lhSize = dctx->inPos = dctx->outStart = dctx->outEnd = 0; + dctx->legacyVersion = 0; + dctx->hostageByte = 0; return ZSTD_frameHeaderSize_prefix; } -size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, +size_t ZSTD_setDStreamParameter(ZSTD_DStream* dctx, ZSTD_DStreamParameter_e paramType, unsigned paramValue) { - ZSTD_STATIC_ASSERT((unsigned)zdss_loadHeader >= (unsigned)zdss_init); - if ((unsigned)zds->streamStage > (unsigned)zdss_loadHeader) - return ERROR(stage_wrong); + if (dctx->streamStage != zdss_init) return ERROR(stage_wrong); switch(paramType) { default : return ERROR(parameter_unsupported); case DStream_p_maxWindowSize : DEBUGLOG(4, "setting maxWindowSize = %u KB", paramValue >> 10); - zds->maxWindowSize = paramValue ? paramValue : (U32)(-1); + dctx->maxWindowSize = paramValue ? paramValue : (U32)(-1); break; } return 0; @@ -2667,9 +2665,7 @@ size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize) { - ZSTD_STATIC_ASSERT((unsigned)zdss_loadHeader >= (unsigned)zdss_init); - if ((unsigned)dctx->streamStage > (unsigned)zdss_loadHeader) - return ERROR(stage_wrong); + if (dctx->streamStage != zdss_init) return ERROR(stage_wrong); dctx->maxWindowSize = maxWindowSize; return 0; } @@ -2677,17 +2673,15 @@ size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize) size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format) { DEBUGLOG(4, "ZSTD_DCtx_setFormat : %u", (unsigned)format); - ZSTD_STATIC_ASSERT((unsigned)zdss_loadHeader >= (unsigned)zdss_init); - if ((unsigned)dctx->streamStage > (unsigned)zdss_loadHeader) - return ERROR(stage_wrong); + if (dctx->streamStage != zdss_init) return ERROR(stage_wrong); dctx->format = format; return 0; } -size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds) +size_t ZSTD_sizeof_DStream(const ZSTD_DStream* dctx) { - return ZSTD_sizeof_DCtx(zds); + return ZSTD_sizeof_DCtx(dctx); } size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize) @@ -2968,8 +2962,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB return 1; } /* nextSrcSizeHint==0 */ nextSrcSizeHint += ZSTD_blockHeaderSize * (ZSTD_nextInputType(zds) == ZSTDnit_block); /* preload header of next block */ - if (zds->inPos > nextSrcSizeHint) return ERROR(GENERIC); /* should never happen */ - nextSrcSizeHint -= zds->inPos; /* already loaded*/ + assert(zds->inPos <= nextSrcSizeHint); + nextSrcSizeHint -= zds->inPos; /* part already loaded*/ return nextSrcSizeHint; } } diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 3c6eb9d87..535b02d8f 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -576,10 +576,11 @@ static int basicUnitTests(U32 seed, double compressibility) { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (!ZSTD_isError(r)) goto _output_error; /* must fail : frame requires > 100 bytes */ DISPLAYLEVEL(3, "OK (%s)\n", ZSTD_getErrorName(r)); } + ZSTD_DCtx_reset(zd); /* leave zd in good shape for next tests */ DISPLAYLEVEL(3, "test%3i : dictionary source size and level : ", testNb++); { ZSTD_DCtx* const dctx = ZSTD_createDCtx(); - int const maxLevel = 15; + int const maxLevel = 16; /* first level with zstd_opt */ int level; assert(maxLevel < ZSTD_maxCLevel()); CHECK_Z( ZSTD_DCtx_loadDictionary_byReference(dctx, dictionary.start, dictionary.filled) ); @@ -662,14 +663,18 @@ static int basicUnitTests(U32 seed, double compressibility) cSize = outBuff.pos; DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBufferSize*100); - DISPLAYLEVEL(3, "test%3i : decompress with dictionary : ", testNb++); - { size_t const r = ZSTD_decompress_usingDict(zd, - decodedBuffer, CNBufferSize, - compressedBuffer, cSize, - dictionary.start, dictionary.filled); - if (ZSTD_isError(r)) goto _output_error; /* must fail : dictionary not used */ - DISPLAYLEVEL(3, "OK \n"); - } + DISPLAYLEVEL(3, "test%3i : decompress with ZSTD_DCtx_refPrefix : ", testNb++); + CHECK_Z( ZSTD_DCtx_refPrefix(zd, dictionary.start, dictionary.filled) ); + outBuff.dst = decodedBuffer; + outBuff.size = CNBufferSize; + outBuff.pos = 0; + inBuff.src = compressedBuffer; + inBuff.size = cSize; + inBuff.pos = 0; + CHECK_Z( ZSTD_decompress_generic(zd, &outBuff, &inBuff) ); + if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */ + if (outBuff.pos != CNBufferSize) goto _output_error; /* must regenerate whole input */ + DISPLAYLEVEL(3, "OK \n"); DISPLAYLEVEL(3, "test%3i : decompress without dictionary (should fail): ", testNb++); { size_t const r = ZSTD_decompress(decodedBuffer, CNBufferSize, compressedBuffer, cSize); From b18cb7e0b73ef53efb4043976cc4438b44e3c303 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Mar 2018 16:18:48 -0700 Subject: [PATCH 219/255] zstreamtest : slightly decreased time to build dictionary size --- tests/zstreamtest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 535b02d8f..b94f282f5 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -239,7 +239,7 @@ static int basicUnitTests(U32 seed, double compressibility) /* Create dictionary */ DISPLAYLEVEL(3, "creating dictionary for unit tests \n"); - dictionary = FUZ_createDictionary(CNBuffer, CNBufferSize / 2, 8 KB, 40 KB); + dictionary = FUZ_createDictionary(CNBuffer, CNBufferSize / 3, 16 KB, 48 KB); if (!dictionary.start) { DISPLAY("Error creating dictionary, aborting \n"); goto _output_error; From 878728dc26dc6c252344cf066acf3926f09a2932 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Mar 2018 16:35:14 -0700 Subject: [PATCH 220/255] fixed several comments by @terrelln --- lib/compress/zstd_compress.c | 20 ++++++++++---------- lib/compress/zstd_ldm.h | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 7b45a9719..52c03e741 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -359,43 +359,43 @@ size_t ZSTD_CCtxParam_setParameter( } case ZSTD_p_windowLog : - if (value) /* 0 => use default */ + if (value>0) /* 0 => use default */ CLAMPCHECK(value, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX); CCtxParams->cParams.windowLog = value; return CCtxParams->cParams.windowLog; case ZSTD_p_hashLog : - if (value) /* 0 => use default */ + if (value>0) /* 0 => use default */ CLAMPCHECK(value, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX); CCtxParams->cParams.hashLog = value; return CCtxParams->cParams.hashLog; case ZSTD_p_chainLog : - if (value) /* 0 => use default */ + if (value>0) /* 0 => use default */ CLAMPCHECK(value, ZSTD_CHAINLOG_MIN, ZSTD_CHAINLOG_MAX); CCtxParams->cParams.chainLog = value; return CCtxParams->cParams.chainLog; case ZSTD_p_searchLog : - if (value) /* 0 => use default */ + if (value>0) /* 0 => use default */ CLAMPCHECK(value, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX); CCtxParams->cParams.searchLog = value; return value; case ZSTD_p_minMatch : - if (value) /* 0 => use default */ + if (value>0) /* 0 => use default */ CLAMPCHECK(value, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX); CCtxParams->cParams.searchLength = value; return CCtxParams->cParams.searchLength; case ZSTD_p_targetLength : - if (value) /* 0 => use default */ + if (value>0) /* 0 => use default */ CLAMPCHECK(value, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX); CCtxParams->cParams.targetLength = value; return CCtxParams->cParams.targetLength; case ZSTD_p_compressionStrategy : - if (value) /* 0 => use default */ + if (value>0) /* 0 => use default */ CLAMPCHECK(value, (unsigned)ZSTD_fast, (unsigned)ZSTD_btultra); CCtxParams->cParams.strategy = (ZSTD_strategy)value; return (size_t)CCtxParams->cParams.strategy; @@ -426,7 +426,7 @@ size_t ZSTD_CCtxParam_setParameter( case ZSTD_p_nbWorkers : #ifndef ZSTD_MULTITHREAD - if (value > 0) return ERROR(parameter_unsupported); + if (value>0) return ERROR(parameter_unsupported); return 0; #else return ZSTDMT_CCtxParam_setNbWorkers(CCtxParams, value); @@ -2063,8 +2063,8 @@ size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx) { - ZSTD_compressionParameters const cParams = - ZSTD_getCParamsFromCCtxParams(&(cctx->appliedParams), 0, 0); + ZSTD_compressionParameters const cParams = cctx->appliedParams.cParams; + assert(!ZSTD_checkCParams(cParams)); return MIN (ZSTD_BLOCKSIZE_MAX, (U32)1 << cParams.windowLog); } diff --git a/lib/compress/zstd_ldm.h b/lib/compress/zstd_ldm.h index 88ee60286..175a42cc6 100644 --- a/lib/compress/zstd_ldm.h +++ b/lib/compress/zstd_ldm.h @@ -60,10 +60,10 @@ size_t ZSTD_ldm_generateSequences( * NOTE: This function does not return any errors. */ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, - ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_compressionParameters const* cParams, - void const* src, size_t srcSize, - int const extDict); + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, + void const* src, size_t srcSize, + int const extDict); /** ZSTD_ldm_getTableSize() : From e0cb8d19c66d5adb2416d724fe3b07c1a71982b6 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Mar 2018 17:48:22 -0700 Subject: [PATCH 221/255] fixed legacy test case --- lib/decompress/zstd_decompress.c | 27 ++++-- lib/legacy/zstd_legacy.h | 2 + lib/legacy/zstd_v04.c | 160 ++++++++----------------------- tests/legacy.c | 27 +++--- 4 files changed, 73 insertions(+), 143 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 008e4174f..3ec6a1cb3 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2748,23 +2748,25 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB } DEBUGLOG(5, "input size : %u", (U32)(input->size - input->pos)); -#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) - if (zds->legacyVersion) { - /* legacy support is incompatible with static dctx */ - if (zds->staticSize) return ERROR(memory_allocation); - return ZSTD_decompressLegacyStream(zds->legacyContext, zds->legacyVersion, output, input); - } -#endif - while (someMoreWork) { switch(zds->streamStage) { case zdss_init : + DEBUGLOG(5, "stage zdss_init => transparent reset "); ZSTD_resetDStream(zds); /* transparent reset on starting decoding a new frame */ /* fall-through */ case zdss_loadHeader : DEBUGLOG(5, "stage zdss_loadHeader (srcSize : %u)", (U32)(iend - ip)); +#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) + if (zds->legacyVersion) { + /* legacy support is incompatible with static dctx */ + if (zds->staticSize) return ERROR(memory_allocation); + { size_t const hint = ZSTD_decompressLegacyStream(zds->legacyContext, zds->legacyVersion, output, input); + if (hint==0) zds->streamStage = zdss_init; + return hint; + } } +#endif { size_t const hSize = ZSTD_getFrameHeader_internal(&zds->fParams, zds->headerBuffer, zds->lhSize, zds->format); DEBUGLOG(5, "header size : %u", (U32)hSize); if (ZSTD_isError(hSize)) { @@ -2773,14 +2775,17 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB if (legacyVersion) { const void* const dict = zds->ddict ? zds->ddict->dictContent : NULL; size_t const dictSize = zds->ddict ? zds->ddict->dictSize : 0; + DEBUGLOG(5, "ZSTD_decompressStream: detected legacy version v0.%u", legacyVersion); /* legacy support is incompatible with static dctx */ if (zds->staticSize) return ERROR(memory_allocation); CHECK_F(ZSTD_initLegacyStream(&zds->legacyContext, zds->previousLegacyVersion, legacyVersion, dict, dictSize)); zds->legacyVersion = zds->previousLegacyVersion = legacyVersion; - return ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input); - } + { size_t const hint = ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input); + if (hint==0) zds->streamStage = zdss_init; /* or stay in stage zdss_loadHeader */ + return hint; + } } #endif return hSize; /* error */ } @@ -2890,6 +2895,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB if (ip==iend) { someMoreWork = 0; break; } /* no more input */ zds->streamStage = zdss_load; /* fall-through */ + case zdss_load: { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds); size_t const toLoad = neededInSize - zds->inPos; @@ -2916,6 +2922,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB } } zds->streamStage = zdss_flush; /* fall-through */ + case zdss_flush: { size_t const toFlushSize = zds->outEnd - zds->outStart; size_t const flushedSize = ZSTD_limitCopy(op, oend-op, zds->outBuff + zds->outStart, toFlushSize); diff --git a/lib/legacy/zstd_legacy.h b/lib/legacy/zstd_legacy.h index 487ff0b28..5893cb965 100644 --- a/lib/legacy/zstd_legacy.h +++ b/lib/legacy/zstd_legacy.h @@ -246,6 +246,7 @@ MEM_STATIC size_t ZSTD_freeLegacyStreamContext(void* legacyContext, U32 version) MEM_STATIC size_t ZSTD_initLegacyStream(void** legacyContext, U32 prevVersion, U32 newVersion, const void* dict, size_t dictSize) { + DEBUGLOG(5, "ZSTD_initLegacyStream for v0.%u", newVersion); if (prevVersion != newVersion) ZSTD_freeLegacyStreamContext(*legacyContext, prevVersion); switch(newVersion) { @@ -304,6 +305,7 @@ MEM_STATIC size_t ZSTD_initLegacyStream(void** legacyContext, U32 prevVersion, U MEM_STATIC size_t ZSTD_decompressLegacyStream(void* legacyContext, U32 version, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { + DEBUGLOG(5, "ZSTD_decompressLegacyStream for v0.%u", version); switch(version) { default : diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c index 1ff7c95e0..fb6d1d4b1 100644 --- a/lib/legacy/zstd_v04.c +++ b/lib/legacy/zstd_v04.c @@ -16,37 +16,6 @@ /* ****************************************************************** mem.h - low-level memory access routines - Copyright (C) 2013-2015, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy - - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ #ifndef MEM_H_MODULE #define MEM_H_MODULE @@ -103,6 +72,44 @@ extern "C" { #endif +/*-************************************* +* Debug +***************************************/ +#if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=1) +# include +#else +# ifndef assert +# define assert(condition) ((void)0) +# endif +#endif + +#define ZSTD_STATIC_ASSERT(c) { enum { ZSTD_static_assert = 1/(int)(!!(c)) }; } + +#if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=2) +# include +extern int g_debuglog_enable; +/* recommended values for ZSTD_DEBUG display levels : + * 1 : no display, enables assert() only + * 2 : reserved for currently active debug path + * 3 : events once per object lifetime (CCtx, CDict, etc.) + * 4 : events once per frame + * 5 : events once per block + * 6 : events once per sequence (*very* verbose) */ +# define RAWLOG(l, ...) { \ + if ((g_debuglog_enable) & (l<=ZSTD_DEBUG)) { \ + fprintf(stderr, __VA_ARGS__); \ + } } +# define DEBUGLOG(l, ...) { \ + if ((g_debuglog_enable) & (l<=ZSTD_DEBUG)) { \ + fprintf(stderr, __FILE__ ": " __VA_ARGS__); \ + fprintf(stderr, " \n"); \ + } } +#else +# define RAWLOG(l, ...) {} /* disabled */ +# define DEBUGLOG(l, ...) {} /* disabled */ +#endif + + /**************************************************************** * Memory I/O *****************************************************************/ @@ -255,34 +262,6 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr) /* zstd - standard compression library Header File for static linking only - Copyright (C) 2014-2015, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - zstd source repository : https://github.com/Cyan4973/zstd - - ztsd public forum : https://groups.google.com/forum/#!forum/lz4c */ #ifndef ZSTD_STATIC_H #define ZSTD_STATIC_H @@ -392,34 +371,6 @@ static size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t maxDstS /* zstd_internal - common functions to include Header File for include - Copyright (C) 2014-2015, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - zstd source repository : https://github.com/Cyan4973/zstd - - ztsd public forum : https://groups.google.com/forum/#!forum/lz4c */ #ifndef ZSTD_CCOMMON_H_MODULE #define ZSTD_CCOMMON_H_MODULE @@ -507,36 +458,6 @@ static void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length) /* ****************************************************************** FSE : Finite State Entropy coder header file - Copyright (C) 2013-2015, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - Source repository : https://github.com/Cyan4973/FiniteStateEntropy - - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ #ifndef FSE_H #define FSE_H @@ -3528,12 +3449,14 @@ static size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbc, void* dst, size_t* maxDs char* const oend = ostart + *maxDstSizePtr; U32 notDone = 1; + DEBUGLOG(5, "ZBUFF_decompressContinue"); while (notDone) { switch(zbc->stage) { case ZBUFFds_init : + DEBUGLOG(5, "ZBUFF_decompressContinue: stage==ZBUFFds_init => ERROR(init_missing)"); return ERROR(init_missing); case ZBUFFds_readHeader : @@ -3733,7 +3656,7 @@ size_t ZSTDv04_decompressContinue(ZSTDv04_Dctx* dctx, void* dst, size_t maxDstSi ZBUFFv04_DCtx* ZBUFFv04_createDCtx(void) { return ZBUFF_createDCtx(); } -size_t ZBUFFv04_freeDCtx(ZBUFFv04_DCtx* dctx) { return ZBUFF_freeDCtx(dctx); } +size_t ZBUFFv04_freeDCtx(ZBUFFv04_DCtx* dctx) { return ZBUFF_freeDCtx(dctx); } size_t ZBUFFv04_decompressInit(ZBUFFv04_DCtx* dctx) { return ZBUFF_decompressInit(dctx); } size_t ZBUFFv04_decompressWithDictionary(ZBUFFv04_DCtx* dctx, const void* src, size_t srcSize) @@ -3741,6 +3664,7 @@ size_t ZBUFFv04_decompressWithDictionary(ZBUFFv04_DCtx* dctx, const void* src, s size_t ZBUFFv04_decompressContinue(ZBUFFv04_DCtx* dctx, void* dst, size_t* maxDstSizePtr, const void* src, size_t* srcSizePtr) { + DEBUGLOG(5, "ZBUFFv04_decompressContinue"); return ZBUFF_decompressContinue(dctx, dst, maxDstSizePtr, src, srcSizePtr); } diff --git a/tests/legacy.c b/tests/legacy.c index 46a8206c4..6cfd38b1e 100644 --- a/tests/legacy.c +++ b/tests/legacy.c @@ -92,23 +92,23 @@ int testStreamingAPI(void) while (1) { ZSTD_outBuffer output = {outBuff, outBuffSize, 0}; if (needsInit) { + DISPLAY("needsInit: ZSTD_initDStream(stream)\n"); size_t const ret = ZSTD_initDStream(stream); if (ZSTD_isError(ret)) { - DISPLAY("ERROR: %s\n", ZSTD_getErrorName(ret)); + DISPLAY("ERROR: ZSTD_initDStream: %s\n", ZSTD_getErrorName(ret)); return 1; - } - } - { - size_t const ret = ZSTD_decompressStream(stream, &output, &input); + } } + + DISPLAY("ZSTD_decompressStream(stream, output, input)\n"); + { size_t const ret = ZSTD_decompressStream(stream, &output, &input); if (ZSTD_isError(ret)) { - DISPLAY("ERROR: %s\n", ZSTD_getErrorName(ret)); + DISPLAY("ERROR: ZSTD_decompressStream: %s\n", ZSTD_getErrorName(ret)); return 1; } if (ret == 0) { needsInit = 1; - } - } + } } if (memcmp(outBuff, EXPECTED + outputPos, output.pos) != 0) { DISPLAY("ERROR: Wrong decoded output produced\n"); @@ -128,15 +128,12 @@ int testStreamingAPI(void) int main(void) { - int ret; - - ret = testSimpleAPI(); - if (ret) return ret; - ret = testStreamingAPI(); - if (ret) return ret; + { int const ret = testSimpleAPI(); + if (ret) return ret; } + { int const ret = testStreamingAPI(); + if (ret) return ret; } DISPLAY("OK\n"); - return 0; } From 453d487807cb32cba7463e23d3ffc1c38f28c658 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 20 Mar 2018 18:03:56 -0700 Subject: [PATCH 222/255] fixed minor declaration warning --- tests/Makefile | 4 ++-- tests/legacy.c | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index d6a253cc8..5b35ad406 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -100,11 +100,11 @@ zstd-nolegacy: gzstd: $(MAKE) -C $(PRGDIR) zstd HAVE_ZLIB=1 MOREFLAGS+="$(DEBUGFLAGS)" -.PHONY: +.PHONY: zstd-dll zstd-dll : $(MAKE) -C $(ZSTDDIR) libzstd -.PHONY: +.PHONY: zstd-staticLib zstd-staticLib : $(MAKE) -C $(ZSTDDIR) libzstd.a diff --git a/tests/legacy.c b/tests/legacy.c index 6cfd38b1e..847e1d25e 100644 --- a/tests/legacy.c +++ b/tests/legacy.c @@ -92,14 +92,12 @@ int testStreamingAPI(void) while (1) { ZSTD_outBuffer output = {outBuff, outBuffSize, 0}; if (needsInit) { - DISPLAY("needsInit: ZSTD_initDStream(stream)\n"); size_t const ret = ZSTD_initDStream(stream); if (ZSTD_isError(ret)) { DISPLAY("ERROR: ZSTD_initDStream: %s\n", ZSTD_getErrorName(ret)); return 1; } } - DISPLAY("ZSTD_decompressStream(stream, output, input)\n"); { size_t const ret = ZSTD_decompressStream(stream, &output, &input); if (ZSTD_isError(ret)) { DISPLAY("ERROR: ZSTD_decompressStream: %s\n", ZSTD_getErrorName(ret)); From 0034c759b045a946ac18b093df7d182114f2f9cc Mon Sep 17 00:00:00 2001 From: Varunram Date: Wed, 21 Mar 2018 14:10:42 +0530 Subject: [PATCH 223/255] Update man page variables in CMakeLists --- build/cmake/contrib/gen_html/CMakeLists.txt | 2 +- build/cmake/programs/CMakeLists.txt | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/cmake/contrib/gen_html/CMakeLists.txt b/build/cmake/contrib/gen_html/CMakeLists.txt index 01d190672..958e9d173 100644 --- a/build/cmake/contrib/gen_html/CMakeLists.txt +++ b/build/cmake/contrib/gen_html/CMakeLists.txt @@ -27,4 +27,4 @@ ADD_CUSTOM_TARGET(zstd_manual.html ALL ${GENHTML_BINARY} "${LIBVERSION}" "${LIBRARY_DIR}/zstd.h" "${PROJECT_BINARY_DIR}/zstd_manual.html" DEPENDS gen_html COMMENT "Update zstd manual") -INSTALL(FILES "${PROJECT_BINARY_DIR}/zstd_manual.html" DESTINATION "share/doc") +INSTALL(FILES "${PROJECT_BINARY_DIR}/zstd_manual.html" DESTINATION "${CMAKE_INSTALL_PREFIX}/${DOC_INSTALL_DIR}") diff --git a/build/cmake/programs/CMakeLists.txt b/build/cmake/programs/CMakeLists.txt index 6c0ed05a1..ad8628f9a 100644 --- a/build/cmake/programs/CMakeLists.txt +++ b/build/cmake/programs/CMakeLists.txt @@ -45,9 +45,10 @@ IF (UNIX) COMMENT "Copying manpage zstd.1") ADD_CUSTOM_TARGET(zstdcat.1 ALL ${CMAKE_COMMAND} -E create_symlink zstd.1 zstdcat.1 DEPENDS zstd.1 COMMENT "Creating zstdcat.1 symlink") ADD_CUSTOM_TARGET(unzstd.1 ALL ${CMAKE_COMMAND} -E create_symlink zstd.1 unzstd.1 DEPENDS zstd.1 COMMENT "Creating unzstd.1 symlink") - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/zstd.1 DESTINATION "share/man/man1") - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/zstdcat.1 DESTINATION "share/man/man1") - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/unzstd.1 DESTINATION "share/man/man1") + + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/zstd.1 DESTINATION "${MAN_INSTALL_DIR}") + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/zstdcat.1 DESTINATION "${MAN_INSTALL_DIR}") + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/unzstd.1 DESTINATION "${MAN_INSTALL_DIR}") ADD_EXECUTABLE(zstd-frugal ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/fileio.c) TARGET_LINK_LIBRARIES(zstd-frugal libzstd_static) From 321de5682c7e3ee435cb74e92d17492412a56ca4 Mon Sep 17 00:00:00 2001 From: Haydn Trigg Date: Wed, 21 Mar 2018 22:57:42 +1030 Subject: [PATCH 224/255] Generic Visual Studio 2017 build script --- build/VS_scripts/build.VS2017.cmd | 7 +++++++ build/VS_scripts/build.generic.cmd | 12 ++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 build/VS_scripts/build.VS2017.cmd diff --git a/build/VS_scripts/build.VS2017.cmd b/build/VS_scripts/build.VS2017.cmd new file mode 100644 index 000000000..a810faa53 --- /dev/null +++ b/build/VS_scripts/build.VS2017.cmd @@ -0,0 +1,7 @@ +@echo off + +rem build 32-bit +call "%~p0%build.generic.cmd" VS2017 Win32 Release v141 + +rem build 64-bit +call "%~p0%build.generic.cmd" VS2017 x64 Release v141 \ No newline at end of file diff --git a/build/VS_scripts/build.generic.cmd b/build/VS_scripts/build.generic.cmd index aa24053d0..a9bb179f1 100644 --- a/build/VS_scripts/build.generic.cmd +++ b/build/VS_scripts/build.generic.cmd @@ -29,11 +29,23 @@ EXIT /B 1 :build SET msbuild="%windir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" +SET msbuild_vs2017community="%programfiles(x86)%\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" +SET msbuild_vs2017professional="%programfiles(x86)%\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\MSBuild.exe" +SET msbuild_vs2017enterprise="%programfiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MSBuild.exe" IF %msbuild_version% == VS2013 SET msbuild="%programfiles(x86)%\MSBuild\12.0\Bin\MSBuild.exe" IF %msbuild_version% == VS2015 SET msbuild="%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" IF %msbuild_version% == VS2017Community SET msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" IF %msbuild_version% == VS2017Professional SET msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\MSBuild.exe" IF %msbuild_version% == VS2017Enterprise SET msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MSBuild.exe" +if exist %msbuild_vs2017enterprise% ( + IF %msbuild_version% == VS2017 SET msbuild=%msbuild_vs2017enterprise% +) +if exist %msbuild_vs2017professional% ( + IF %msbuild_version% == VS2017 SET msbuild=%msbuild_vs2017professional% +) +if exist %msbuild_vs2017community% ( + IF %msbuild_version% == VS2017 SET msbuild=%msbuild_vs2017community% +) SET project="%~p0\..\VS2010\zstd.sln" From 57e6b98eb11af763dc2d24bd066a0375f0dab8d9 Mon Sep 17 00:00:00 2001 From: Haydn Trigg Date: Thu, 22 Mar 2018 09:10:39 +1030 Subject: [PATCH 225/255] Refined build script --- build/VS_scripts/build.generic.cmd | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/build/VS_scripts/build.generic.cmd b/build/VS_scripts/build.generic.cmd index a9bb179f1..3f8630ccb 100644 --- a/build/VS_scripts/build.generic.cmd +++ b/build/VS_scripts/build.generic.cmd @@ -19,10 +19,10 @@ GOTO build :display_help echo Syntax: build.generic.cmd msbuild_version msbuild_platform msbuild_configuration msbuild_toolset -echo msbuild_version: VS installed version (VS2012, VS2013, VS2015, ...) +echo msbuild_version: VS installed version (VS2012, VS2013, VS2015, VS2017, ...) echo msbuild_platform: Platform (x64 or Win32) echo msbuild_configuration: VS configuration (Release or Debug) -echo msbuild_toolset: Platform Toolset (v100, v110, v120, v140) +echo msbuild_toolset: Platform Toolset (v100, v110, v120, v140, v141) EXIT /B 1 @@ -34,17 +34,13 @@ SET msbuild_vs2017professional="%programfiles(x86)%\Microsoft Visual Studio\2017 SET msbuild_vs2017enterprise="%programfiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MSBuild.exe" IF %msbuild_version% == VS2013 SET msbuild="%programfiles(x86)%\MSBuild\12.0\Bin\MSBuild.exe" IF %msbuild_version% == VS2015 SET msbuild="%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" -IF %msbuild_version% == VS2017Community SET msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" -IF %msbuild_version% == VS2017Professional SET msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\MSBuild.exe" -IF %msbuild_version% == VS2017Enterprise SET msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MSBuild.exe" -if exist %msbuild_vs2017enterprise% ( - IF %msbuild_version% == VS2017 SET msbuild=%msbuild_vs2017enterprise% -) -if exist %msbuild_vs2017professional% ( - IF %msbuild_version% == VS2017 SET msbuild=%msbuild_vs2017professional% -) -if exist %msbuild_vs2017community% ( - IF %msbuild_version% == VS2017 SET msbuild=%msbuild_vs2017community% +IF %msbuild_version% == VS2017Community SET msbuild="%msbuild_vs2017community% +IF %msbuild_version% == VS2017Professional SET msbuild=%msbuild_vs2017professional% +IF %msbuild_version% == VS2017Enterprise SET msbuild=%msbuild_vs2017enterprise% +IF %msbuild_version% == VS2017 ( + IF EXIST %msbuild_vs2017enterprise% SET msbuild=%msbuild_vs2017enterprise% + IF EXIST %msbuild_vs2017professional% SET msbuild=%msbuild_vs2017professional% + IF EXIST %msbuild_vs2017community% SET msbuild=%msbuild_vs2017community% ) SET project="%~p0\..\VS2010\zstd.sln" From 153bc1c0047f9c2bb503ced4f1b6f13b75e9c80a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 21 Mar 2018 15:50:05 -0700 Subject: [PATCH 226/255] removed limit ZSTD_TARGETLENGTH_MAX this makes it possible to specify extremely large negative compression levels, achieving the side effect as "no compression". It will also be possible to define larger targetlength for ultra compression mode. There is no adverse side effect due to removing this limit. --- lib/compress/zstd_compress.c | 8 ++++---- lib/zstd.h | 1 - programs/zstdcli.c | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 528291777..2aa26da4c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -389,8 +389,7 @@ size_t ZSTD_CCtxParam_setParameter( return CCtxParams->cParams.searchLength; case ZSTD_p_targetLength : - if (value>0) /* 0 => use default */ - CLAMPCHECK(value, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX); + /* all values are valid. 0 => use default */ CCtxParams->cParams.targetLength = value; return CCtxParams->cParams.targetLength; @@ -590,7 +589,8 @@ size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams) CLAMPCHECK(cParams.hashLog, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX); CLAMPCHECK(cParams.searchLog, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX); CLAMPCHECK(cParams.searchLength, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX); - CLAMPCHECK(cParams.targetLength, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX); + if ((U32)(cParams.targetLength) < ZSTD_TARGETLENGTH_MIN) + return ERROR(parameter_unsupported); if ((U32)(cParams.strategy) > (U32)ZSTD_btultra) return ERROR(parameter_unsupported); return 0; @@ -610,7 +610,7 @@ static ZSTD_compressionParameters ZSTD_clampCParams(ZSTD_compressionParameters c CLAMP(cParams.hashLog, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX); CLAMP(cParams.searchLog, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX); CLAMP(cParams.searchLength, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX); - CLAMP(cParams.targetLength, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX); + if ((U32)(cParams.targetLength) < ZSTD_TARGETLENGTH_MIN) cParams.targetLength = ZSTD_TARGETLENGTH_MIN; if ((U32)(cParams.strategy) > (U32)ZSTD_btultra) cParams.strategy = ZSTD_btultra; return cParams; } diff --git a/lib/zstd.h b/lib/zstd.h index a35091a58..6405da602 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -391,7 +391,6 @@ ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output #define ZSTD_SEARCHLENGTH_MAX 7 /* only for ZSTD_fast, other strategies are limited to 6 */ #define ZSTD_SEARCHLENGTH_MIN 3 /* only for ZSTD_btopt, other strategies are limited to 4 */ #define ZSTD_TARGETLENGTH_MIN 1 /* only used by btopt, btultra and btfast */ -#define ZSTD_TARGETLENGTH_MAX 999 /* only used by btopt, btultra and btfast */ #define ZSTD_LDM_MINMATCH_MIN 4 #define ZSTD_LDM_MINMATCH_MAX 4096 #define ZSTD_LDM_BUCKETSIZELOG_MAX 8 diff --git a/programs/zstdcli.c b/programs/zstdcli.c index bf82843aa..c35de7ccf 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -388,7 +388,7 @@ int main(int argCount, const char* argv[]) zstd_operation_mode operation = zom_compress; ZSTD_compressionParameters compressionParams; int cLevel = ZSTDCLI_CLEVEL_DEFAULT; - int cLevelLast = -10000; + int cLevelLast = -1000000000; unsigned recursive = 0; unsigned memLimit = 0; const char** filenameTable = (const char**)malloc(argCount * sizeof(const char*)); /* argCount >= 1 */ From 93b8262c021c89f8b6204c7b27aca0e23ade9786 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 21 Mar 2018 15:54:44 -0700 Subject: [PATCH 227/255] added large negative level test case --- tests/playTests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index 1847f57d7..41d8263b6 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -106,6 +106,7 @@ $ZSTD -99 -f tmp # too large compression level, automatic sized down $ECHO "test : --fast aka negative compression levels" $ZSTD --fast -f tmp # == -1 $ZSTD --fast=3 -f tmp # == -3 +$ZSTD --fast=200000 -f tmp # == no compression $ECHO "test : compress to stdout" $ZSTD tmp -c > tmpCompressed $ZSTD tmp --stdout > tmpCompressed # long command format From b7b80e156903750946f9bc9e99570a57d47529f5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 21 Mar 2018 16:09:15 -0700 Subject: [PATCH 228/255] fixed paramgrill --- doc/zstd_manual.html | 102 +++++++++++++++++++++---------------------- tests/paramgrill.c | 12 ++--- 2 files changed, 55 insertions(+), 59 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 66dd8de16..623fd611d 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -730,12 +730,12 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long

    New advanced API (experimental)

    
     
     
    typedef enum {
    -    /* Question : should we have a format ZSTD_f_auto ?
    -     * For the time being, it would mean exactly the same as ZSTD_f_zstd1.
    -     * But, in the future, should several formats be supported,
    +    /* Opened question : should we have a format ZSTD_f_auto ?
    +     * Today, it would mean exactly the same as ZSTD_f_zstd1.
    +     * But, in the future, should several formats become supported,
          * on the compression side, it would mean "default format".
    -     * On the decompression side, it would mean "multi format",
    -     * and ZSTD_f_zstd1 could be reserved to mean "accept *only* zstd frames".
    +     * On the decompression side, it would mean "automatic format detection",
    +     * so that ZSTD_f_zstd1 would mean "accept *only* zstd frames".
          * Since meaning is a little different, another option could be to define different enums for compression and decompression.
          * This question could be kept for later, when there are actually multiple formats to support,
          * but there is also the question of pinning enum values, and pinning value `0` is especially important */
    @@ -755,33 +755,34 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
                                   * Default level is ZSTD_CLEVEL_DEFAULT==3.
                                   * Special: value 0 means "do not change cLevel".
                                   * Note 1 : it's possible to pass a negative compression level by casting it to unsigned type.
    -                              * Note 2 : setting compressionLevel automatically updates ZSTD_p_compressLiterals. */
    +                              * Note 2 : setting a level sets all default values of other compression parameters.
    +                              * Note 3 : setting compressionLevel automatically updates ZSTD_p_compressLiterals. */
         ZSTD_p_windowLog,        /* Maximum allowed back-reference distance, expressed as power of 2.
                                   * Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX.
    -                              * Special: value 0 means "do not change windowLog".
    +                              * Special: value 0 means "use default windowLog".
                                   * Note: Using a window size greater than ZSTD_MAXWINDOWSIZE_DEFAULT (default: 2^27)
    -                              * requires setting the maximum window size at least as large during decompression. */
    +                              *       requires explicitly allowing such window size during decompression stage. */
         ZSTD_p_hashLog,          /* Size of the probe table, as a power of 2.
                                   * Resulting table size is (1 << (hashLog+2)).
                                   * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX.
                                   * Larger tables improve compression ratio of strategies <= dFast,
                                   * and improve speed of strategies > dFast.
    -                              * Special: value 0 means "do not change hashLog". */
    +                              * Special: value 0 means "use default hashLog". */
         ZSTD_p_chainLog,         /* Size of the full-search table, as a power of 2.
                                   * Resulting table size is (1 << (chainLog+2)).
                                   * Larger tables result in better and slower compression.
                                   * This parameter is useless when using "fast" strategy.
    -                              * Special: value 0 means "do not change chainLog". */
    +                              * Special: value 0 means "use default chainLog". */
         ZSTD_p_searchLog,        /* Number of search attempts, as a power of 2.
                                   * More attempts result in better and slower compression.
                                   * This parameter is useless when using "fast" and "dFast" strategies.
    -                              * Special: value 0 means "do not change searchLog". */
    +                              * Special: value 0 means "use default searchLog". */
         ZSTD_p_minMatch,         /* Minimum size of searched matches (note : repCode matches can be smaller).
                                   * Larger values make faster compression and decompression, but decrease ratio.
                                   * Must be clamped between ZSTD_SEARCHLENGTH_MIN and ZSTD_SEARCHLENGTH_MAX.
                                   * Note that currently, for all strategies < btopt, effective minimum is 4.
    -                              * Note that currently, for all strategies > fast, effective maximum is 6.
    -                              * Special: value 0 means "do not change minMatchLength". */
    +                              *                    , for all strategies > fast, effective maximum is 6.
    +                              * Special: value 0 means "use default minMatchLength". */
         ZSTD_p_targetLength,     /* Impact of this field depends on strategy.
                                   * For strategies btopt & btultra:
                                   *     Length of Match considered "good enough" to stop search.
    @@ -789,12 +790,39 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
                                   * For strategy fast:
                                   *     Distance between match sampling.
                                   *     Larger values make compression faster, and weaker.
    -                              * Special: value 0 means "do not change targetLength". */
    +                              * Special: value 0 means "use default targetLength". */
         ZSTD_p_compressionStrategy, /* See ZSTD_strategy enum definition.
                                   * Cast selected strategy as unsigned for ZSTD_CCtx_setParameter() compatibility.
                                   * The higher the value of selected strategy, the more complex it is,
                                   * resulting in stronger and slower compression.
    -                              * Special: value 0 means "do not change strategy". */
    +                              * Special: value 0 means "use default strategy". */
    +
    +    ZSTD_p_enableLongDistanceMatching=160, /* Enable long distance matching.
    +                                         * This parameter is designed to improve compression ratio
    +                                         * for large inputs, by finding large matches at long distance.
    +                                         * It increases memory usage and window size.
    +                                         * Note: enabling this parameter increases ZSTD_p_windowLog to 128 MB
    +                                         * except when expressly set to a different value. */
    +    ZSTD_p_ldmHashLog,       /* Size of the table for long distance matching, as a power of 2.
    +                              * Larger values increase memory usage and compression ratio,
    +                              * but decrease compression speed.
    +                              * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
    +                              * default: windowlog - 7.
    +                              * Special: value 0 means "automatically determine hashlog". */
    +    ZSTD_p_ldmMinMatch,      /* Minimum match size for long distance matcher.
    +                              * Larger/too small values usually decrease compression ratio.
    +                              * Must be clamped between ZSTD_LDM_MINMATCH_MIN and ZSTD_LDM_MINMATCH_MAX.
    +                              * Special: value 0 means "use default value" (default: 64). */
    +    ZSTD_p_ldmBucketSizeLog, /* Log size of each bucket in the LDM hash table for collision resolution.
    +                              * Larger values improve collision resolution but decrease compression speed.
    +                              * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX .
    +                              * Special: value 0 means "use default value" (default: 3). */
    +    ZSTD_p_ldmHashEveryLog,  /* Frequency of inserting/looking up entries in the LDM hash table.
    +                              * Must be clamped between 0 and (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN).
    +                              * Default is MAX(0, (windowLog - ldmHashLog)), optimizing hash table usage.
    +                              * Larger values improve compression speed.
    +                              * Deviating far from default value will likely result in a compression ratio decrease.
    +                              * Special: value 0 means "automatically determine hashEveryLog". */
     
         /* frame parameters */
         ZSTD_p_contentSizeFlag=200, /* Content size will be written into frame header _whenever known_ (default:1)
    @@ -821,7 +849,9 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
         ZSTD_p_overlapSizeLog,   /* Size of previous input reloaded at the beginning of each job.
                                   * 0 => no overlap, 6(default) => use 1/8th of windowSize, >=9 => use full windowSize */
     
    -    /* advanced parameters - may not remain available after API update */
    +    /* =================================================================== */
    +    /* experimental parameters - no stability guaranteed                   */
    +    /* =================================================================== */
     
         ZSTD_p_compressLiterals=1000, /* control huffman compression of literals (enabled) by default.
                                   * disabling it improves speed and decreases compression ratio by a large amount.
    @@ -832,40 +862,6 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
         ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize,
                                   * even when referencing into Dictionary content (default:0) */
     
    -    ZSTD_p_enableLongDistanceMatching=1200, /* Enable long distance matching.
    -                                         * This parameter is designed to improve the compression
    -                                         * ratio for large inputs with long distance matches.
    -                                         * This increases the memory usage as well as window size.
    -                                         * Note: setting this parameter sets all the LDM parameters
    -                                         * as well as ZSTD_p_windowLog. It should be set after
    -                                         * ZSTD_p_compressionLevel and before ZSTD_p_windowLog and
    -                                         * other LDM parameters. Setting the compression level
    -                                         * after this parameter overrides the window log, though LDM
    -                                         * will remain enabled until explicitly disabled. */
    -    ZSTD_p_ldmHashLog,       /* Size of the table for long distance matching, as a power of 2.
    -                              * Larger values increase memory usage and compression ratio, but decrease
    -                              * compression speed.
    -                              * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
    -                              * (default: windowlog - 7).
    -                              * Special: value 0 means "do not change ldmHashLog". */
    -    ZSTD_p_ldmMinMatch,      /* Minimum size of searched matches for long distance matcher.
    -                              * Larger/too small values usually decrease compression ratio.
    -                              * Must be clamped between ZSTD_LDM_MINMATCH_MIN
    -                              * and ZSTD_LDM_MINMATCH_MAX (default: 64).
    -                              * Special: value 0 means "do not change ldmMinMatch". */
    -    ZSTD_p_ldmBucketSizeLog, /* Log size of each bucket in the LDM hash table for collision resolution.
    -                              * Larger values usually improve collision resolution but may decrease
    -                              * compression speed.
    -                              * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX (default: 3).
    -                              * note : 0 is a valid value */
    -    ZSTD_p_ldmHashEveryLog,  /* Frequency of inserting/looking up entries in the LDM hash table.
    -                              * The default is MAX(0, (windowLog - ldmHashLog)) to
    -                              * optimize hash table usage.
    -                              * Larger values improve compression speed. Deviating far from the
    -                              * default value will likely result in a decrease in compression ratio.
    -                              * Must be clamped between 0 and ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN.
    -                              * note : 0 is a valid value */
    -
     } ZSTD_cParameter;
     

    size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned value);
    @@ -1067,7 +1063,7 @@ size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size
      
     


    -
    size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);   /* not implemented */
    +
    size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
     

    Reference a prepared dictionary, to be used to decompress next frames. The dictionary remains active for decompression of future frames using same DCtx. @result : 0, or an error code (which can be tested with ZSTD_isError()). @@ -1078,8 +1074,8 @@ size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size


    -
    size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize);   /* not implemented */
    -size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);   /* not implemented */
    +
    size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize);
    +size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
     

    Reference a prefix (single-usage dictionary) for next compression job. Prefix is **only used once**. It must be explicitly referenced before each frame. If there is a need to use same prefix multiple times, consider embedding it into a ZSTD_DDict instead. diff --git a/tests/paramgrill.c b/tests/paramgrill.c index ae14aa074..13b102b2d 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -547,12 +547,12 @@ static ZSTD_compressionParameters randomParams(void) U32 validated = 0; while (!validated) { /* totally random entry */ - p.chainLog = FUZ_rand(&g_rand) % (ZSTD_CHAINLOG_MAX+1 - ZSTD_CHAINLOG_MIN) + ZSTD_CHAINLOG_MIN; - p.hashLog = FUZ_rand(&g_rand) % (ZSTD_HASHLOG_MAX+1 - ZSTD_HASHLOG_MIN) + ZSTD_HASHLOG_MIN; - p.searchLog = FUZ_rand(&g_rand) % (ZSTD_SEARCHLOG_MAX+1 - ZSTD_SEARCHLOG_MIN) + ZSTD_SEARCHLOG_MIN; - p.windowLog = FUZ_rand(&g_rand) % (ZSTD_WINDOWLOG_MAX+1 - ZSTD_WINDOWLOG_MIN) + ZSTD_WINDOWLOG_MIN; - p.searchLength=FUZ_rand(&g_rand) % (ZSTD_SEARCHLENGTH_MAX+1 - ZSTD_SEARCHLENGTH_MIN) + ZSTD_SEARCHLENGTH_MIN; - p.targetLength=FUZ_rand(&g_rand) % (ZSTD_TARGETLENGTH_MAX+1 - ZSTD_TARGETLENGTH_MIN) + ZSTD_TARGETLENGTH_MIN; + p.chainLog = (FUZ_rand(&g_rand) % (ZSTD_CHAINLOG_MAX+1 - ZSTD_CHAINLOG_MIN)) + ZSTD_CHAINLOG_MIN; + p.hashLog = (FUZ_rand(&g_rand) % (ZSTD_HASHLOG_MAX+1 - ZSTD_HASHLOG_MIN)) + ZSTD_HASHLOG_MIN; + p.searchLog = (FUZ_rand(&g_rand) % (ZSTD_SEARCHLOG_MAX+1 - ZSTD_SEARCHLOG_MIN)) + ZSTD_SEARCHLOG_MIN; + p.windowLog = (FUZ_rand(&g_rand) % (ZSTD_WINDOWLOG_MAX+1 - ZSTD_WINDOWLOG_MIN)) + ZSTD_WINDOWLOG_MIN; + p.searchLength=(FUZ_rand(&g_rand) % (ZSTD_SEARCHLENGTH_MAX+1 - ZSTD_SEARCHLENGTH_MIN)) + ZSTD_SEARCHLENGTH_MIN; + p.targetLength=(FUZ_rand(&g_rand) % (512)) + ZSTD_TARGETLENGTH_MIN; p.strategy = (ZSTD_strategy) (FUZ_rand(&g_rand) % (ZSTD_btultra +1)); validated = !ZSTD_isError(ZSTD_checkCParams(p)); } From ad344033df0781c114e703d558a6e3dbac747fe5 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 21 Mar 2018 16:20:30 -0700 Subject: [PATCH 229/255] Fix broken assertion The `avgJobSize` must not be lower than 256 KB for single-pass mode. In `zstd.h` we say the minimum value for `ZSTD_p_jobSize` is 1 MB, so ensure that we always pick a size >= 1 MB. Found by libFuzzer fuzzer tests with large input limits. --- lib/compress/zstdmt_compress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index c7b08cbce..c7a205d8c 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1029,7 +1029,7 @@ static size_t ZSTDMT_computeTargetJobLog(ZSTD_CCtx_params const params) { if (params.ldmParams.enableLdm) return MAX(21, params.cParams.chainLog + 4); - return params.cParams.windowLog + 2; + return MAX(20, params.cParams.windowLog + 2); } static size_t ZSTDMT_computeOverlapLog(ZSTD_CCtx_params const params) From f15e2fd55ed632d947b93b3acc9ef5ad34658b46 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 21 Mar 2018 16:45:15 -0700 Subject: [PATCH 230/255] fixed fuzz test --- tests/fuzz/zstd_helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fuzz/zstd_helpers.c b/tests/fuzz/zstd_helpers.c index 602898479..6fc38361b 100644 --- a/tests/fuzz/zstd_helpers.c +++ b/tests/fuzz/zstd_helpers.c @@ -35,7 +35,7 @@ ZSTD_compressionParameters FUZZ_randomCParams(size_t srcSize, uint32_t *state) cParams.searchLength = FUZZ_rand32(state, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX); cParams.targetLength = FUZZ_rand32(state, ZSTD_TARGETLENGTH_MIN, - ZSTD_TARGETLENGTH_MAX); + 512); cParams.strategy = FUZZ_rand32(state, ZSTD_fast, ZSTD_btultra); return ZSTD_adjustCParams(cParams, srcSize, 0); } From 01082a39bde03f8c0e9346de194309e6087fa98a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 22 Mar 2018 17:49:46 -0700 Subject: [PATCH 231/255] restored simple status line during zstd compression the more advanced one, featuring amount of data buffered, is triggered on `-v`. --- programs/fileio.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index d3d8b7dc5..14569bb47 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -88,10 +88,12 @@ static const U64 g_refreshRate = SEC_TO_MICRO / 6; static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; #define READY_FOR_UPDATE() (UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) +#define DELAY_NEXT_UPDATE() { g_displayClock = UTIL_getTime(); } #define DISPLAYUPDATE(l, ...) { \ if (g_displayLevel>=l) { \ - if (READY_FOR_UPDATE() || (g_displayLevel>=4)) { \ - g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ + if (READY_FOR_UPDATE() || (g_displayLevel>=4)) { \ + DELAY_NEXT_UPDATE(); \ + DISPLAY(__VA_ARGS__); \ if (g_displayLevel>=4) fflush(stderr); \ } } } @@ -401,9 +403,9 @@ static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName) #ifndef ZSTD_NOCOMPRESS -/*-********************************************************************** -* Compression -************************************************************************/ +/* ********************************************************************** + * Compression + ************************************************************************/ typedef struct { FILE* srcFile; FILE* dstFile; @@ -782,12 +784,21 @@ FIO_compressZstdFrame(const cRess_t* ressPtr, } if (READY_FOR_UPDATE()) { ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx); - DISPLAYUPDATE(2, "\r(%i) Read :%6u MB - Consumed :%6u MB - Compressed :%6u MB => %.2f%%", + double const cShare = (double)zfp.produced / (zfp.consumed + !zfp.consumed/*avoid div0*/) * 100; + if (g_displayLevel >= 3) { + DISPLAYUPDATE(3, "\r(L%i) Buffered :%4u MB - Consumed :%4u MB - Compressed :%4u MB => %.2f%%", compressionLevel, - (U32)(zfp.ingested >> 20), + (U32)((zfp.ingested - zfp.consumed) >> 20), (U32)(zfp.consumed >> 20), (U32)(zfp.produced >> 20), - (double)zfp.produced / (zfp.consumed + !zfp.consumed/*avoid div0*/) * 100 ); + cShare ); + } else { /* g_displayLevel == 2 */ + DISPLAYLEVEL(2, "\rRead : %u ", (U32)(zfp.consumed >> 20)); + if (fileSize != UTIL_FILESIZE_UNKNOWN) + DISPLAYLEVEL(2, "/ %u ", (U32)(fileSize >> 20)); + DISPLAYLEVEL(2, "MB ==> %2.f%% ", cShare); + DELAY_NEXT_UPDATE(); + } } } } while (directive != ZSTD_e_end); From 184e1c8b8c2ce1032245b304838e38d3f7e7fc2a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 22 Mar 2018 18:22:10 -0700 Subject: [PATCH 232/255] updated benchmark figures --- README.md | 21 +++++++++++---------- doc/images/CSpeed2.png | Bin 0 -> 73335 bytes doc/images/Cspeed4.png | Bin 71276 -> 0 bytes doc/images/DSpeed3.png | Bin 0 -> 27123 bytes doc/images/Dspeed4.png | Bin 24692 -> 0 bytes 5 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 doc/images/CSpeed2.png delete mode 100644 doc/images/Cspeed4.png create mode 100644 doc/images/DSpeed3.png delete mode 100644 doc/images/Dspeed4.png diff --git a/README.md b/README.md index 808451d12..6478964ea 100644 --- a/README.md +++ b/README.md @@ -21,24 +21,25 @@ Development branch status : [![Build Status][travisDevBadge]][travisLink] [![B ### Benchmarks For reference, several fast compression algorithms were tested and compared -on a server running Linux Debian (`Linux version 4.8.0-1-amd64`), +on a server running Linux Debian (`Linux version 4.14.0-3-amd64`), with a Core i7-6700K CPU @ 4.0GHz, using [lzbench], an open-source in-memory benchmark by @inikep -compiled with GCC 6.3.0, +compiled with [gcc] 7.3.0, on the [Silesia compression corpus]. [lzbench]: https://github.com/inikep/lzbench [Silesia compression corpus]: http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia +[gcc]: https://gcc.gnu.org/ | Compressor name | Ratio | Compression| Decompress.| | --------------- | ------| -----------| ---------- | -| **zstd 1.1.3 -1** | 2.877 | 430 MB/s | 1110 MB/s | -| zlib 1.2.8 -1 | 2.743 | 110 MB/s | 400 MB/s | -| brotli 0.5.2 -0 | 2.708 | 400 MB/s | 430 MB/s | +| **zstd 1.3.4 -1** | 2.877 | 470 MB/s | 1380 MB/s | +| zlib 1.2.11 -1 | 2.743 | 110 MB/s | 400 MB/s | +| brotli 1.0.2 -0 | 2.701 | 410 MB/s | 430 MB/s | | quicklz 1.5.0 -1 | 2.238 | 550 MB/s | 710 MB/s | | lzo1x 2.09 -1 | 2.108 | 650 MB/s | 830 MB/s | -| lz4 1.7.5 | 2.101 | 720 MB/s | 3600 MB/s | -| snappy 1.1.3 | 2.091 | 500 MB/s | 1650 MB/s | +| lz4 1.8.1 | 2.101 | 750 MB/s | 3700 MB/s | +| snappy 1.1.4 | 2.091 | 530 MB/s | 1800 MB/s | | lzf 3.6 -1 | 2.077 | 400 MB/s | 860 MB/s | [zlib]:http://www.zlib.net/ @@ -50,15 +51,15 @@ Decompression speed is preserved and remains roughly the same at all settings, a property shared by most LZ compression algorithms, such as [zlib] or lzma. The following tests were run -on a server running Linux Debian (`Linux version 4.8.0-1-amd64`) +on a server running Linux Debian (`Linux version 4.14.0-3-amd64`) with a Core i7-6700K CPU @ 4.0GHz, using [lzbench], an open-source in-memory benchmark by @inikep -compiled with GCC 6.3.0, +compiled with [gcc] 7.3.0, on the [Silesia compression corpus]. Compression Speed vs Ratio | Decompression Speed ---------------------------|-------------------- -![Compression Speed vs Ratio](doc/images/Cspeed4.png "Compression Speed vs Ratio") | ![Decompression Speed](doc/images/Dspeed4.png "Decompression Speed") +![Compression Speed vs Ratio](doc/images/CSpeed2.png "Compression Speed vs Ratio") | ![Decompression Speed](doc/images/DSpeed3.png "Decompression Speed") A few other algorithms can produce higher compression ratios at slower speeds, falling outside of the graph. For a larger picture including slow modes, [click on this link](doc/images/DCspeed5.png). diff --git a/doc/images/CSpeed2.png b/doc/images/CSpeed2.png new file mode 100644 index 0000000000000000000000000000000000000000..42affa46ea4789c4d722fc47e3aa71d23058faf2 GIT binary patch literal 73335 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 Ds6bEP literal 0 HcmV?d00001 diff --git a/doc/images/Cspeed4.png b/doc/images/Cspeed4.png deleted file mode 100644 index 318204c00e96311529ed76351481db81d8212770..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71276 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 diff --git a/doc/images/DSpeed3.png b/doc/images/DSpeed3.png new file mode 100644 index 0000000000000000000000000000000000000000..4818b11180a74514e1f7af61e0a9dcd91e6b4b07 GIT binary patch literal 27123 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 literal 0 HcmV?d00001 diff --git a/doc/images/Dspeed4.png b/doc/images/Dspeed4.png deleted file mode 100644 index b7baef1ff3f7adc356d69bbabf2290bb38a1c994..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24692 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 From d6c6b62958f1793884240b7151a998a966d6ae6f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 22 Mar 2018 18:44:56 -0700 Subject: [PATCH 233/255] updated graph for dictionary compression --- doc/images/dict-cr.png | Bin 90047 -> 90412 bytes doc/images/dict-cs.png | Bin 93837 -> 91518 bytes doc/images/dict-ds.png | Bin 89590 -> 98316 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/images/dict-cr.png b/doc/images/dict-cr.png index f3a9ce2bdda64c7f370642d596a5cc1fdf50bf54..6da34da46bde23942c10e100a3354681399c3e66 100644 GIT binary patch literal 90412 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

    {@*J*NZDjzn?yQQnj4#-KFk6b;I^~ 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 R$iTqB;OXk;vd$@?2>{S6O1=O9 literal 90047 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

    bK|d*UojxRsRysT|DFd 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%0A2)%v<)@0Zk{i+cM0 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 literal 89590 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%0AxZ_Oby5xD{2baIw|6HyzK1CkVV|`uE^!eY6 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 Iol`;+0BoVPEdT%j From 6bb6e6178a2ac56ee46d8b4e8aa04007e70cd99c Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 23 Mar 2018 14:06:05 -0700 Subject: [PATCH 234/255] Add zstdmt long range mode graphs --- doc/images/linux-4.7-12-mt-compress.png | Bin 0 -> 106940 bytes doc/images/linux-git-mt-compress.png | Bin 0 -> 116645 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/images/linux-4.7-12-mt-compress.png create mode 100644 doc/images/linux-git-mt-compress.png diff --git a/doc/images/linux-4.7-12-mt-compress.png b/doc/images/linux-4.7-12-mt-compress.png new file mode 100644 index 0000000000000000000000000000000000000000..c66ac0d5e6b991425a92498b328f6ae12bd30f10 GIT binary patch literal 106940 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 literal 0 HcmV?d00001 diff --git a/doc/images/linux-git-mt-compress.png b/doc/images/linux-git-mt-compress.png new file mode 100644 index 0000000000000000000000000000000000000000..538b8a9fc626d472ab0388b1e1c4ba43d7f41621 GIT binary patch literal 116645 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

    TDhiaYKhQcgHFA=^! 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 Q0|Nttr>mdKI;Vst0O(Qf$^ZZW literal 0 HcmV?d00001 From 929608146a439c4bd28b989a5af47cb9afce2af5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 15:00:04 -0700 Subject: [PATCH 235/255] create .tar.zst source package --- appveyor.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 7d6ca99bb..1f092574c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -80,12 +80,17 @@ SET "LDFLAGS=../../zlib/libz.a" && sh -c "%SCRIPT%" && ( if [%COMPILER%]==[gcc] if [%ARTIFACT%]==[true] + ECHO Creating artifacts && + ECHO %cd% && + make clean && 7z a -ttar -so zstd-src.tar * && lib\dll\example\build_package.bat && - make -C programs DEBUGFLAGS= clean zstd && - cd programs\ && 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe && + cd programs\ && make DEBUGFLAGS= clean zstd && + 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe && appveyor PushArtifact zstd-win-binary-%PLATFORM%.zip && cp zstd.exe ..\bin\zstd.exe && - cd ..\bin\ && 7z a -tzip -mx9 zstd-win-release-%PLATFORM%.zip * && + cd .. && zstd --ultra -22 zstd-src.tar && + appveyor PushArtifact zstd-src.tar.zst && + cd bin\ && 7z a -tzip -mx9 zstd-win-release-%PLATFORM%.zip * && appveyor PushArtifact zstd-win-release-%PLATFORM%.zip ) ) From b1f898ec829a79b0adc7afb1e8ffd923c3a5432f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 16:15:56 -0700 Subject: [PATCH 236/255] added srcPackage to list of branch producing artifacts --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 1f092574c..07ce12ca0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,6 +4,7 @@ only: - dev - master + - srcPackage environment: matrix: - COMPILER: "gcc" From b94d08c566fb6f658a2c53d992662cfee1647480 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 16:23:23 -0700 Subject: [PATCH 237/255] commented src archive lines --- appveyor.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 07ce12ca0..c2760b0e8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -82,15 +82,17 @@ sh -c "%SCRIPT%" && ( if [%COMPILER%]==[gcc] if [%ARTIFACT%]==[true] ECHO Creating artifacts && - ECHO %cd% && - make clean && 7z a -ttar -so zstd-src.tar * && + REM ECHO %cd% && + REM make clean && + REM 7z a -ttar -so zstd-src.tar * && lib\dll\example\build_package.bat && cd programs\ && make DEBUGFLAGS= clean zstd && 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe && appveyor PushArtifact zstd-win-binary-%PLATFORM%.zip && cp zstd.exe ..\bin\zstd.exe && - cd .. && zstd --ultra -22 zstd-src.tar && - appveyor PushArtifact zstd-src.tar.zst && + cd .. && + REM programs\zstd --ultra -22 zstd-src.tar && + REM appveyor PushArtifact zstd-src.tar.zst && cd bin\ && 7z a -tzip -mx9 zstd-win-release-%PLATFORM%.zip * && appveyor PushArtifact zstd-win-release-%PLATFORM%.zip ) From 97bc1aec477508100e6d85dfa2fb7048363af16a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 16:24:54 -0700 Subject: [PATCH 238/255] removed src artifact lines --- appveyor.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c2760b0e8..7a660c39f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -82,17 +82,12 @@ sh -c "%SCRIPT%" && ( if [%COMPILER%]==[gcc] if [%ARTIFACT%]==[true] ECHO Creating artifacts && - REM ECHO %cd% && - REM make clean && - REM 7z a -ttar -so zstd-src.tar * && lib\dll\example\build_package.bat && cd programs\ && make DEBUGFLAGS= clean zstd && 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe && appveyor PushArtifact zstd-win-binary-%PLATFORM%.zip && cp zstd.exe ..\bin\zstd.exe && cd .. && - REM programs\zstd --ultra -22 zstd-src.tar && - REM appveyor PushArtifact zstd-src.tar.zst && cd bin\ && 7z a -tzip -mx9 zstd-win-release-%PLATFORM%.zip * && appveyor PushArtifact zstd-win-release-%PLATFORM%.zip ) From a75104d8f8ac3b8b96aa4c2ccf8dda4385ec38de Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 16:27:28 -0700 Subject: [PATCH 239/255] added path print --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 7a660c39f..5b914ae01 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -82,6 +82,7 @@ sh -c "%SCRIPT%" && ( if [%COMPILER%]==[gcc] if [%ARTIFACT%]==[true] ECHO Creating artifacts && + ECHO %cd% && lib\dll\example\build_package.bat && cd programs\ && make DEBUGFLAGS= clean zstd && 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe && From a59dac279a9d99095f0db4283a1133c0ada72618 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 16:36:18 -0700 Subject: [PATCH 240/255] added tar command --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 5b914ae01..76a5c99af 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -83,6 +83,8 @@ ( if [%COMPILER%]==[gcc] if [%ARTIFACT%]==[true] ECHO Creating artifacts && ECHO %cd% && + make clean && + 7z a -ttar zstd-src.tar * && lib\dll\example\build_package.bat && cd programs\ && make DEBUGFLAGS= clean zstd && 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe && From 17e937bcd03b5142340f22b3d11e48aea36fc67e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 16:49:34 -0700 Subject: [PATCH 241/255] git clone for clean src tar --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 76a5c99af..e39c9cd1a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -83,8 +83,8 @@ ( if [%COMPILER%]==[gcc] if [%ARTIFACT%]==[true] ECHO Creating artifacts && ECHO %cd% && - make clean && - 7z a -ttar zstd-src.tar * && + git clone --depth 1 --branch %APPVEYOR_REPO_BRANCH% https://github.com/facebook/zstd" + 7z a -ttar zstd-src.tar zstd && lib\dll\example\build_package.bat && cd programs\ && make DEBUGFLAGS= clean zstd && 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe && From b42ffb91f74d77d05d1b4b81a019ace498bbe981 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 16:52:32 -0700 Subject: [PATCH 242/255] git clone master branch (hardcoded) --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index e39c9cd1a..bcb62b74c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -83,7 +83,7 @@ ( if [%COMPILER%]==[gcc] if [%ARTIFACT%]==[true] ECHO Creating artifacts && ECHO %cd% && - git clone --depth 1 --branch %APPVEYOR_REPO_BRANCH% https://github.com/facebook/zstd" + git clone --depth 1 --branch master https://github.com/facebook/zstd" 7z a -ttar zstd-src.tar zstd && lib\dll\example\build_package.bat && cd programs\ && make DEBUGFLAGS= clean zstd && From 308f405c736480fc88274967b2213ccd6d2f2e93 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 17:06:11 -0700 Subject: [PATCH 243/255] export zstd/master from github using svn --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index bcb62b74c..3d964cc25 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -83,8 +83,8 @@ ( if [%COMPILER%]==[gcc] if [%ARTIFACT%]==[true] ECHO Creating artifacts && ECHO %cd% && - git clone --depth 1 --branch master https://github.com/facebook/zstd" - 7z a -ttar zstd-src.tar zstd && + svn export https://github.com/facebook/zstd/branches/master && + 7z a -ttar zstd-src.tar master && lib\dll\example\build_package.bat && cd programs\ && make DEBUGFLAGS= clean zstd && 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe && From fa294b85905c3c6658b9bd54a666355900685378 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 17:20:05 -0700 Subject: [PATCH 244/255] created zstd archive from git archive --- appveyor.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 3d964cc25..198347776 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -83,15 +83,18 @@ ( if [%COMPILER%]==[gcc] if [%ARTIFACT%]==[true] ECHO Creating artifacts && ECHO %cd% && - svn export https://github.com/facebook/zstd/branches/master && - 7z a -ttar zstd-src.tar master && lib\dll\example\build_package.bat && cd programs\ && make DEBUGFLAGS= clean zstd && 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe && appveyor PushArtifact zstd-win-binary-%PLATFORM%.zip && cp zstd.exe ..\bin\zstd.exe && - cd .. && - cd bin\ && 7z a -tzip -mx9 zstd-win-release-%PLATFORM%.zip * && + git clone --depth 1 --branch master https://github.com/facebook/zstd && + cd zstd && + git archive --format=tar master -o zstd-src.tar && + ..\zstd -19 zstd-src.tar && + appveyor PushArtifact zstd-src.tar.zst && + cd ..\..\bin\ && + 7z a -tzip -mx9 zstd-win-release-%PLATFORM%.zip * && appveyor PushArtifact zstd-win-release-%PLATFORM%.zip ) ) From 2489e71ff1b0ed39e403f891ae5337c53f7cadf3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 17:40:53 -0700 Subject: [PATCH 245/255] added sha256 sig --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 198347776..ae609df09 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -93,6 +93,8 @@ git archive --format=tar master -o zstd-src.tar && ..\zstd -19 zstd-src.tar && appveyor PushArtifact zstd-src.tar.zst && + certUtil -hashfile zstd-src.tar.zst SHA256 > zstd-src.sig.sha256 && + appveyor PushArtifact zstd-src.sig.sha256 && cd ..\..\bin\ && 7z a -tzip -mx9 zstd-win-release-%PLATFORM%.zip * && appveyor PushArtifact zstd-win-release-%PLATFORM%.zip From 956a3b7006e0a6c1a78c46517b45b51896ebf9fb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 17:52:16 -0700 Subject: [PATCH 246/255] clean certutil output --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index ae609df09..3cc657abd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -93,7 +93,7 @@ git archive --format=tar master -o zstd-src.tar && ..\zstd -19 zstd-src.tar && appveyor PushArtifact zstd-src.tar.zst && - certUtil -hashfile zstd-src.tar.zst SHA256 > zstd-src.sig.sha256 && + $(CertUtil -hashfile zstd-src.tar.zst SHA256)[1] -replace " ","" > zstd-src.sig.sha256 && appveyor PushArtifact zstd-src.sig.sha256 && cd ..\..\bin\ && 7z a -tzip -mx9 zstd-win-release-%PLATFORM%.zip * && From d9b2cbf6505c06516857b90328fb002eaaf1f8dc Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 17:57:11 -0700 Subject: [PATCH 247/255] restored certutil command line --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 3cc657abd..ae609df09 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -93,7 +93,7 @@ git archive --format=tar master -o zstd-src.tar && ..\zstd -19 zstd-src.tar && appveyor PushArtifact zstd-src.tar.zst && - $(CertUtil -hashfile zstd-src.tar.zst SHA256)[1] -replace " ","" > zstd-src.sig.sha256 && + certUtil -hashfile zstd-src.tar.zst SHA256 > zstd-src.sig.sha256 && appveyor PushArtifact zstd-src.sig.sha256 && cd ..\..\bin\ && 7z a -tzip -mx9 zstd-win-release-%PLATFORM%.zip * && From 6356b007fb5b970afd5cd37d0041d5c0be0c711f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 17:57:37 -0700 Subject: [PATCH 248/255] make artifacts ande extended tests only on master --- appveyor.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ae609df09..b098fd452 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,9 +2,7 @@ version: 1.0.{build} branches: only: - - dev - master - - srcPackage environment: matrix: - COMPILER: "gcc" From f991897bb1dbf6d55b9a1d3879f8ae34b6fed7cf Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 18:39:42 -0700 Subject: [PATCH 249/255] Revert "create .tar.zst source package" This reverts commit 929608146a439c4bd28b989a5af47cb9afce2af5. --- appveyor.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 1f092574c..7d6ca99bb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -80,17 +80,12 @@ SET "LDFLAGS=../../zlib/libz.a" && sh -c "%SCRIPT%" && ( if [%COMPILER%]==[gcc] if [%ARTIFACT%]==[true] - ECHO Creating artifacts && - ECHO %cd% && - make clean && 7z a -ttar -so zstd-src.tar * && lib\dll\example\build_package.bat && - cd programs\ && make DEBUGFLAGS= clean zstd && - 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe && + make -C programs DEBUGFLAGS= clean zstd && + cd programs\ && 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe && appveyor PushArtifact zstd-win-binary-%PLATFORM%.zip && cp zstd.exe ..\bin\zstd.exe && - cd .. && zstd --ultra -22 zstd-src.tar && - appveyor PushArtifact zstd-src.tar.zst && - cd bin\ && 7z a -tzip -mx9 zstd-win-release-%PLATFORM%.zip * && + cd ..\bin\ && 7z a -tzip -mx9 zstd-win-release-%PLATFORM%.zip * && appveyor PushArtifact zstd-win-release-%PLATFORM%.zip ) ) From 52ca7c6c565579bfc732184597a30fa904656493 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 18:45:42 -0700 Subject: [PATCH 250/255] make DYNAMIC_BMI2 support of clang conditional to __has_attribute() to support older clang versions such as 3.4 --- lib/common/compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/compiler.h b/lib/common/compiler.h index bb8730126..a6044c861 100644 --- a/lib/common/compiler.h +++ b/lib/common/compiler.h @@ -74,7 +74,7 @@ * Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default. */ #ifndef DYNAMIC_BMI2 - #if defined(__clang__) \ + #if (defined(__clang__) && __has_attribute(__target__)) \ || (defined(__GNUC__) \ && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) \ && (defined(__x86_64__) || defined(_M_X86)) \ From ad15c1b724f4c853ba7f23e234c85ef176ad2e27 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Mar 2018 19:04:48 -0700 Subject: [PATCH 251/255] added __has_attribute() define for non-clang compilers --- lib/common/compiler.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/common/compiler.h b/lib/common/compiler.h index a6044c861..e90a3bcde 100644 --- a/lib/common/compiler.h +++ b/lib/common/compiler.h @@ -64,6 +64,9 @@ #endif /* target attribute */ +#ifndef __has_attribute + #define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif #if defined(__GNUC__) # define TARGET_ATTRIBUTE(target) __attribute__((__target__(target))) #else From 2960f74c29b96c23d0d9ad91108060ba485e5aed Mon Sep 17 00:00:00 2001 From: Haydn Trigg Date: Mon, 26 Mar 2018 02:03:53 +1030 Subject: [PATCH 252/255] Swapped VS2017 preference order --- build/VS_scripts/build.generic.cmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/VS_scripts/build.generic.cmd b/build/VS_scripts/build.generic.cmd index 3f8630ccb..bae42fd7f 100644 --- a/build/VS_scripts/build.generic.cmd +++ b/build/VS_scripts/build.generic.cmd @@ -38,9 +38,9 @@ IF %msbuild_version% == VS2017Community SET msbuild="%msbuild_vs2017community% IF %msbuild_version% == VS2017Professional SET msbuild=%msbuild_vs2017professional% IF %msbuild_version% == VS2017Enterprise SET msbuild=%msbuild_vs2017enterprise% IF %msbuild_version% == VS2017 ( - IF EXIST %msbuild_vs2017enterprise% SET msbuild=%msbuild_vs2017enterprise% - IF EXIST %msbuild_vs2017professional% SET msbuild=%msbuild_vs2017professional% IF EXIST %msbuild_vs2017community% SET msbuild=%msbuild_vs2017community% + IF EXIST %msbuild_vs2017professional% SET msbuild=%msbuild_vs2017professional% + IF EXIST %msbuild_vs2017enterprise% SET msbuild=%msbuild_vs2017enterprise% ) SET project="%~p0\..\VS2010\zstd.sln" From 21eb26d664c40b65225b7a3fd2acf34fa5bd2ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Rebe?= Date: Sun, 25 Mar 2018 20:35:15 +0200 Subject: [PATCH 253/255] fixed legacy/zstd_v* with older gcc version, by guarding builtin_* like in other files --- lib/legacy/zstd_v06.c | 4 ++-- lib/legacy/zstd_v07.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/legacy/zstd_v06.c b/lib/legacy/zstd_v06.c index 19116f108..8b068b3e5 100644 --- a/lib/legacy/zstd_v06.c +++ b/lib/legacy/zstd_v06.c @@ -189,7 +189,7 @@ MEM_STATIC U32 MEM_swap32(U32 in) { #if defined(_MSC_VER) /* Visual Studio */ return _byteswap_ulong(in); -#elif defined (__GNUC__) +#elif defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403) return __builtin_bswap32(in); #else return ((in << 24) & 0xff000000 ) | @@ -203,7 +203,7 @@ MEM_STATIC U64 MEM_swap64(U64 in) { #if defined(_MSC_VER) /* Visual Studio */ return _byteswap_uint64(in); -#elif defined (__GNUC__) +#elif defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403) return __builtin_bswap64(in); #else return ((in << 56) & 0xff00000000000000ULL) | diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c index 114ce005b..70b170f0f 100644 --- a/lib/legacy/zstd_v07.c +++ b/lib/legacy/zstd_v07.c @@ -348,7 +348,7 @@ MEM_STATIC U32 MEM_swap32(U32 in) { #if defined(_MSC_VER) /* Visual Studio */ return _byteswap_ulong(in); -#elif defined (__GNUC__) +#elif defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403) return __builtin_bswap32(in); #else return ((in << 24) & 0xff000000 ) | @@ -362,7 +362,7 @@ MEM_STATIC U64 MEM_swap64(U64 in) { #if defined(_MSC_VER) /* Visual Studio */ return _byteswap_uint64(in); -#elif defined (__GNUC__) +#elif defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403) return __builtin_bswap64(in); #else return ((in << 56) & 0xff00000000000000ULL) | From bffb4b46a12366e0941f0f761bfd7b171d6e4b68 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 26 Mar 2018 09:55:10 -0700 Subject: [PATCH 254/255] updated VS studio readme --- README.md | 2 +- build/VS_scripts/README.md | 44 +++++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 6478964ea..58c9ab02c 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ A Meson project is provided within `contrib/meson`. Going into `build` directory, you will find additional possibilities: - Projects for Visual Studio 2005, 2008 and 2010. + VS2010 project is compatible with VS2012, VS2013, VS2015 and VS2017. -- Automated build scripts for Visual compiler by [@KrzysFR](https://github.com/KrzysFR) and [@HaydnTrigg](https://github.com/HaydnTrigg) , in `build/VS_scripts`, +- Automated build scripts for Visual compiler by [@KrzysFR](https://github.com/KrzysFR), in `build/VS_scripts`, which will build `zstd` cli and `libzstd` library without any need to open Visual Studio solution. diff --git a/build/VS_scripts/README.md b/build/VS_scripts/README.md index 3942e3811..6ccaca788 100644 --- a/build/VS_scripts/README.md +++ b/build/VS_scripts/README.md @@ -7,48 +7,58 @@ Here are a few command lines for reference : Running the following command will build both the `Release Win32` and `Release x64` versions: ```batch -build\build.VS2013.cmd +build.VS2013.cmd ``` -The result of each build will be in the corresponding `build\bin\Release\{ARCH}\` folder. +The result of each build will be in the corresponding `bin\Release\{ARCH}\` folder. If you want to only need one architecture: -- Win32: `build\build.generic.cmd VS2013 Win32 Release v120` -- x64: `build\build.generic.cmd VS2013 x64 Release v120` +- Win32: `build.generic.cmd VS2013 Win32 Release v120` +- x64: `build.generic.cmd VS2013 x64 Release v120` If you want a Debug build: -- Win32: `build\build.generic.cmd VS2013 Win32 Debug v120` -- x64: `build\build.generic.cmd VS2013 x64 Debug v120` +- Win32: `build.generic.cmd VS2013 Win32 Debug v120` +- x64: `build.generic.cmd VS2013 x64 Debug v120` ### Build with Visual Studio 2015 for msvcr140.dll Running the following command will build both the `Release Win32` and `Release x64` versions: ```batch -build\build.VS2015.cmd +build.VS2015.cmd ``` -The result of each build will be in the corresponding `build\bin\Release\{ARCH}\` folder. +The result of each build will be in the corresponding `bin\Release\{ARCH}\` folder. If you want to only need one architecture: -- Win32: `build\build.generic.cmd VS2015 Win32 Release v140` -- x64: `build\build.generic.cmd VS2015 x64 Release v140` +- Win32: `build.generic.cmd VS2015 Win32 Release v140` +- x64: `build.generic.cmd VS2015 x64 Release v140` If you want a Debug build: -- Win32: `build\build.generic.cmd VS2015 Win32 Debug v140` -- x64: `build\build.generic.cmd VS2015 x64 Debug v140` +- Win32: `build.generic.cmd VS2015 Win32 Debug v140` +- x64: `build.generic.cmd VS2015 x64 Debug v140` ### Build with Visual Studio 2015 for msvcr120.dll -You need to invoke `build\build.generic.cmd` with the proper arguments: +This capability is offered through `build.generic.cmd` using proper arguments: **For Win32** ```batch -build\build.generic.cmd VS2015 Win32 Release v120 +build.generic.cmd VS2015 Win32 Release v120 ``` -The result of the build will be in the `build\bin\Release\Win32\` folder. +The result of the build will be in the `bin\Release\Win32\` folder. **For x64** ```batch -build\build.generic.cmd VS2015 x64 Release v120 +build.generic.cmd VS2015 x64 Release v120 ``` -The result of the build will be in the `build\bin\Release\x64\` folder. +The result of the build will be in the `bin\Release\x64\` folder. If you want Debug builds, replace `Release` with `Debug`. + +### Build with Visual Studio 2017 + +`build.VS2017.cmd`, contributed by [@HaydnTrigg](https://github.com/HaydnTrigg), +will build both the `Release Win32` and `Release x64` versions +of the first VS2017 variant it finds, in this priority order : +Enterprise > Professional > Community + +Alternatively, it's possible to target a specific version, +using appropriate script, such as `build.VS2017Enterprise.cmd` for example. From eba13dba2e2bac09bef231164b3cc08ffda999d7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 26 Mar 2018 10:21:49 -0700 Subject: [PATCH 255/255] updated NEWS for v1.3.4 --- NEWS | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index f23203bb2..9e903e6bc 100644 --- a/NEWS +++ b/NEWS @@ -1,13 +1,20 @@ v1.3.4 -perf: faster speed for recent cpus (haswell+) -perf: faster decoding speed -perf: faster zstd_btlazy2 compression strategy (levels 13-15) -cli : asynchronous mode by default, for improved performance (use --single-thread for former behavior) +perf: faster speed (especially decoding speed) on recent cpus (haswell+) +perf: much better performance associating --long with multi-threading, by @terrelln +perf: better compression at levels 13-15 +cli : asynchronous compression by default, for faster experience (use --single-thread for former behavior) +cli : smoother status report in multi-threading mode +cli : added command --fast=#, for faster compression modes cli : fix crash when not overwriting existing files, by Pádraig Brady (@pixelb) api : `nbThreads` becomes `nbWorkers` : 1 triggers asynchronous mode +api : compression levels can be negative, for even more speed api : ZSTD_getFrameProgression() : get precise progress status of ZSTDMT anytime api : ZSTDMT can accept new compression parameters during compression +api : implemented all advanced dictionary decompression prototypes build: improved meson recipe, by Shawn Landden (@shawnl) +build: VS2017 scripts, by @HaydnTrigg +misc: all /contrib projects fixed +misc: added /contrib/docker script by @gyscos v1.3.3 perf: faster zstd_opt strategy (levels 16-19)