From ac7992896077695b576587431a1e254693d690c8 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 14:11:55 -0700 Subject: [PATCH 001/318] version one complete, can compress a file given input and output names --- contrib/adaptive-compression/Makefile | 26 ++++++++++ contrib/adaptive-compression/v1.c | 73 +++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 contrib/adaptive-compression/Makefile create mode 100644 contrib/adaptive-compression/v1.c diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile new file mode 100644 index 000000000..12607edff --- /dev/null +++ b/contrib/adaptive-compression/Makefile @@ -0,0 +1,26 @@ + +ZSTDDIR = ../../lib +ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c +ZSTDCOMP_FILES := $(ZSTDDIR)/compress/*.c +ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/*.c +ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) + +DEBUGFLAGS= -g -DZSTD_DEBUG=1 +CPPFLAGS += -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ + -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) +CFLAGS ?= -O3 +CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ + -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ + -Wstrict-prototypes -Wundef -Wformat-security \ + -Wvla -Wformat=2 -Winit-self -Wfloat-equal -Wwrite-strings \ + -Wredundant-decls +CFLAGS += $(DEBUGFLAGS) +CFLAGS += $(MOREFLAGS) +FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) + +adaptive: $(ZSTD_FILES) v1.c + $(CC) $(FLAGS) $^ -o $@ + +clean: + @$(RM) -f adaptive + @$(RM) -f tmp* diff --git a/contrib/adaptive-compression/v1.c b/contrib/adaptive-compression/v1.c new file mode 100644 index 000000000..ef8062d8f --- /dev/null +++ b/contrib/adaptive-compression/v1.c @@ -0,0 +1,73 @@ +#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define FILE_CHUNK_SIZE 4 << 20 +typedef unsigned char BYTE; + +#include +#include +#include "zstd.h" + + + +/* return 0 if successful, else return error */ +int main(int argCount, const char* argv[]) +{ + const char* const srcFilename = argv[1]; + const char* const dstFilename = argv[2]; + FILE* const srcFile = fopen(srcFilename, "rb"); + FILE* const dstFile = fopen(dstFilename, "wb"); + BYTE* const src = malloc(FILE_CHUNK_SIZE); + size_t const dstSize = ZSTD_compressBound(FILE_CHUNK_SIZE); + BYTE* const dst = malloc(dstSize); + int ret = 0; + + /* checking for errors */ + if (!srcFilename || !dstFilename || !src || !dst) { + DISPLAY("Error: initial variables could not be allocated\n"); + ret = 1; + goto cleanup; + } + + /* compressing in blocks */ + for ( ; ; ) { + size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); + if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { + DISPLAY("Error: could not read %d bytes\n", FILE_CHUNK_SIZE); + ret = 1; + goto cleanup; + } + { + size_t const compressedSize = ZSTD_compress(dst, dstSize, src, readSize, 6); + if (ZSTD_isError(compressedSize)) { + DISPLAY("Error: something went wrong during compression\n"); + ret = 1; + goto cleanup; + } + { + size_t const writeSize = fwrite(dst, 1, compressedSize, dstFile); + if (writeSize != compressedSize) { + DISPLAY("Error: could not write compressed data to file\n"); + ret = 1; + goto cleanup; + } + } + } + if (feof(srcFile)) { + /* reached end of file */ + break; + } + } + + /* file compression completed */ + { + int const error = fclose(srcFile); + if (ret != 0) { + DISPLAY("Error: could not close the file\n"); + ret = error; + goto cleanup; + } + } +cleanup: + if (src != NULL) free(src); + if (dst != NULL) free(dst); + return ret; +} From 00b5e6c512a2c9d8bc0db321ff1f79889d803c33 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 14:18:46 -0700 Subject: [PATCH 002/318] continuing work on v2 --- contrib/adaptive-compression/Makefile | 9 +++- contrib/adaptive-compression/v2.c | 73 +++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 contrib/adaptive-compression/v2.c diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 12607edff..b2c47654b 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -18,9 +18,14 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -adaptive: $(ZSTD_FILES) v1.c +all: clean v1 v2 +v1: $(ZSTD_FILES) v1.c + $(CC) $(FLAGS) $^ -o $@ + +v2: $(ZSTD_FILES) v2.c $(CC) $(FLAGS) $^ -o $@ clean: - @$(RM) -f adaptive + @$(RM) -f v1 v2 + @$(RM) -rf *.dSYM @$(RM) -f tmp* diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c new file mode 100644 index 000000000..ef8062d8f --- /dev/null +++ b/contrib/adaptive-compression/v2.c @@ -0,0 +1,73 @@ +#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define FILE_CHUNK_SIZE 4 << 20 +typedef unsigned char BYTE; + +#include +#include +#include "zstd.h" + + + +/* return 0 if successful, else return error */ +int main(int argCount, const char* argv[]) +{ + const char* const srcFilename = argv[1]; + const char* const dstFilename = argv[2]; + FILE* const srcFile = fopen(srcFilename, "rb"); + FILE* const dstFile = fopen(dstFilename, "wb"); + BYTE* const src = malloc(FILE_CHUNK_SIZE); + size_t const dstSize = ZSTD_compressBound(FILE_CHUNK_SIZE); + BYTE* const dst = malloc(dstSize); + int ret = 0; + + /* checking for errors */ + if (!srcFilename || !dstFilename || !src || !dst) { + DISPLAY("Error: initial variables could not be allocated\n"); + ret = 1; + goto cleanup; + } + + /* compressing in blocks */ + for ( ; ; ) { + size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); + if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { + DISPLAY("Error: could not read %d bytes\n", FILE_CHUNK_SIZE); + ret = 1; + goto cleanup; + } + { + size_t const compressedSize = ZSTD_compress(dst, dstSize, src, readSize, 6); + if (ZSTD_isError(compressedSize)) { + DISPLAY("Error: something went wrong during compression\n"); + ret = 1; + goto cleanup; + } + { + size_t const writeSize = fwrite(dst, 1, compressedSize, dstFile); + if (writeSize != compressedSize) { + DISPLAY("Error: could not write compressed data to file\n"); + ret = 1; + goto cleanup; + } + } + } + if (feof(srcFile)) { + /* reached end of file */ + break; + } + } + + /* file compression completed */ + { + int const error = fclose(srcFile); + if (ret != 0) { + DISPLAY("Error: could not close the file\n"); + ret = error; + goto cleanup; + } + } +cleanup: + if (src != NULL) free(src); + if (dst != NULL) free(dst); + return ret; +} From 0887e98d4bb9f8a62b29d0dcb9b0af7dd732aaa5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 17:28:59 -0700 Subject: [PATCH 003/318] finished main portion of code, now need to debug --- contrib/adaptive-compression/Makefile | 1 + contrib/adaptive-compression/v2.c | 285 +++++++++++++++++++++++--- 2 files changed, 253 insertions(+), 33 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index b2c47654b..fbab219c3 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -29,3 +29,4 @@ clean: @$(RM) -f v1 v2 @$(RM) -rf *.dSYM @$(RM) -f tmp* + @echo "finished cleaning" diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index ef8062d8f..f85ccfe19 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -2,72 +2,291 @@ #define FILE_CHUNK_SIZE 4 << 20 typedef unsigned char BYTE; -#include -#include +#include /* fprintf */ +#include /* malloc, free */ +#include /* pthread functions */ +#include /* memset */ #include "zstd.h" +typedef struct { + void* start; + size_t size; +} buffer_t; + +typedef struct { + buffer_t src; + buffer_t dst; + unsigned compressionLevel; + unsigned jobID; + unsigned jobCompleted; + unsigned jobReady; + pthread_mutex_t* jobCompleted_mutex; + pthread_cond_t* jobCompleted_cond; + pthread_mutex_t* jobReady_mutex; + pthread_cond_t* jobReady_cond; + size_t compressedSize; +} jobDescription; + +typedef struct { + unsigned compressionLevel; + unsigned numActiveThreads; + unsigned numJobs; + unsigned nextJobID; + unsigned threadError; + pthread_mutex_t jobCompleted_mutex; + pthread_cond_t jobCompleted_cond; + pthread_mutex_t jobReady_mutex; + pthread_cond_t jobReady_cond; + jobDescription* jobs; + FILE* dstFile; +} adaptCCtx; + +static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) +{ + adaptCCtx* ctx = malloc(sizeof(adaptCCtx)); + memset(ctx, 0, sizeof(adaptCCtx)); + ctx->compressionLevel = 6; /* default */ + pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); + pthread_cond_init(&ctx->jobCompleted_cond, NULL); + pthread_mutex_init(&ctx->jobReady_mutex, NULL); + pthread_cond_init(&ctx->jobReady_cond, NULL); + ctx->numJobs = numJobs; + ctx->jobs = malloc(numJobs*sizeof(jobDescription)); + ctx->nextJobID = 0; + ctx->threadError = 0; + if (!ctx->jobs) { + DISPLAY("Error: could not allocate space for jobs during context creation\n"); + return NULL; + } + { + FILE* dstFile = fopen(outFilename, "wb"); + if (dstFile == NULL) { + DISPLAY("Error: could not open output file\n"); + return NULL; + } + ctx->dstFile = dstFile; + } + return ctx; +} + +static void freeCompressionJobs(adaptCCtx* ctx) +{ + unsigned u; + for (u=0; unumJobs; u++) { + jobDescription job = ctx->jobs[u]; + if (job.dst.start) free(job.dst.start); + if (job.src.start) free(job.src.start); + } +} + +static int freeCCtx(adaptCCtx* ctx) +{ + int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); + int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); + int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); + int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); + int const fileError = fclose(ctx->dstFile); + freeCompressionJobs(ctx); + free(ctx->jobs); + return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError; +} + +static void* compressionThread(void* arg) +{ + adaptCCtx* ctx = (adaptCCtx*)arg; + unsigned currJob = 0; + for ( ; ; ) { + jobDescription* job = &ctx->jobs[currJob]; + pthread_mutex_lock(job->jobReady_mutex); + while(job->jobReady == 0) { + pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); + } + pthread_mutex_unlock(job->jobReady_mutex); + + /* compress the data */ + { + size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, job->compressionLevel); + if (ZSTD_isError(compressedSize)) { + ctx->threadError = 1; + DISPLAY("Error: somethign went wrong during compression\n"); + return arg; + } + job->compressedSize = compressedSize; + } + currJob++; + if (currJob >= ctx->numJobs || ctx->threadError) { + /* finished compressing all jobs */ + break; + } + } + return arg; +} + +static void* outputThread(void* arg) +{ + adaptCCtx* ctx = (adaptCCtx*)arg; + unsigned currJob = 0; + for ( ; ; ) { + jobDescription* job = &ctx->jobs[currJob]; + pthread_mutex_lock(job->jobCompleted_mutex); + while (job->jobCompleted == 0) { + pthread_cond_wait(job->jobCompleted_cond, job->jobCompleted_mutex); + } + pthread_mutex_unlock(job->jobCompleted_mutex); + { + size_t const compressedSize = job->compressedSize; + if (ZSTD_isError(compressedSize)) { + DISPLAY("Error: an error occurred during compression\n"); + return arg; /* TODO: return something else if error */ + } + { + size_t const writeSize = fwrite(ctx->jobs[currJob].dst.start, 1, compressedSize, ctx->dstFile); + if (writeSize != compressedSize) { + DISPLAY("Error: an error occurred during file write operation\n"); + return arg; /* TODO: return something else if error */ + } + } + } + currJob++; + if (currJob >= ctx->numJobs || ctx->threadError) { + /* finished with all jobs */ + break; + } + } + return arg; +} + + +static size_t getFileSize(const char* const filename) +{ + FILE* fd = fopen(filename, "rb"); + if (fd == NULL) { + DISPLAY("Error: could not open file in order to get file size\n"); + return -1; /* intentional underflow */ + } + if (fseek(fd, 0, SEEK_END) != 0) { + DISPLAY("Error: fseek failed during file size computation\n"); + return -1; + } + { + size_t const fileSize = ftell(fd); + if (fclose(fd) != 0) { + DISPLAY("Error: could not close file during file size computation\n"); + return -1; + } + return fileSize; + } +} + +static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) +{ + unsigned const nextJob = ctx->nextJobID; + jobDescription job = ctx->jobs[nextJob]; + job.compressionLevel = ctx->compressionLevel; + job.src.start = malloc(srcSize); + job.src.size = srcSize; + job.dst.size = ZSTD_compressBound(srcSize); + job.dst.start = malloc(job.dst.size); + job.jobCompleted = 0; + job.jobCompleted_cond = &ctx->jobCompleted_cond; + job.jobCompleted_mutex = &ctx->jobCompleted_mutex; + job.jobReady_cond = &ctx->jobReady_cond; + job.jobReady_mutex = &ctx->jobReady_mutex; + job.jobID = nextJob; + if (!job.src.start || !job.dst.start) { + /* problem occurred, free things then return */ + if (job.src.start) free(job.src.start); + if (job.dst.start) free(job.dst.start); + return 1; + } + memcpy(job.src.start, data, srcSize); + ctx->nextJobID++; + return 0; +} + /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { const char* const srcFilename = argv[1]; const char* const dstFilename = argv[2]; - FILE* const srcFile = fopen(srcFilename, "rb"); - FILE* const dstFile = fopen(dstFilename, "wb"); BYTE* const src = malloc(FILE_CHUNK_SIZE); - size_t const dstSize = ZSTD_compressBound(FILE_CHUNK_SIZE); - BYTE* const dst = malloc(dstSize); + FILE* const srcFile = fopen(srcFilename, "rb"); + size_t fileSize = getFileSize(srcFilename); + size_t const numJobsPrelim = (fileSize / FILE_CHUNK_SIZE) + 1; + size_t const numJobs = (numJobsPrelim * FILE_CHUNK_SIZE) == fileSize ? numJobsPrelim : numJobsPrelim + 1; int ret = 0; + adaptCCtx* ctx = NULL; + /* checking for errors */ - if (!srcFilename || !dstFilename || !src || !dst) { + if (fileSize == (size_t)(-1)) { + ret = 1; + goto cleanup; + } + if (!srcFilename || !dstFilename || !src) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; goto cleanup; } - /* compressing in blocks */ - for ( ; ; ) { - size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); - if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { - DISPLAY("Error: could not read %d bytes\n", FILE_CHUNK_SIZE); + /* creating context */ + ctx = createCCtx(numJobs, dstFilename); + if (ctx == NULL) { + ret = 1; + goto cleanup; + } + + /* create output thread */ + { + pthread_t out; + if (pthread_create(&out, NULL, &outputThread, ctx)) { + DISPLAY("Error: could not create output thread\n"); ret = 1; goto cleanup; } + } + /* create compression thread */ + { + pthread_t compression; + if (pthread_create(&compression, NULL, &compressionThread, ctx)) { + DISPLAY("Error: could not create compression thread\n"); + ret = 1; + goto cleanup; + } + } + + /* creating jobs */ + for ( ; ; ) { + size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); + if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { + DISPLAY("Error: problem occurred during read from src file\n"); + ret = 1; + goto cleanup; + } + + /* reading was fine, now create the compression job */ { - size_t const compressedSize = ZSTD_compress(dst, dstSize, src, readSize, 6); - if (ZSTD_isError(compressedSize)) { - DISPLAY("Error: something went wrong during compression\n"); - ret = 1; + int const error = createCompressionJob(ctx, src, readSize); + if (error != 0) { + ret = error; goto cleanup; } - { - size_t const writeSize = fwrite(dst, 1, compressedSize, dstFile); - if (writeSize != compressedSize) { - DISPLAY("Error: could not write compressed data to file\n"); - ret = 1; - goto cleanup; - } - } - } - if (feof(srcFile)) { - /* reached end of file */ - break; } } /* file compression completed */ { - int const error = fclose(srcFile); - if (ret != 0) { - DISPLAY("Error: could not close the file\n"); - ret = error; + int const fileCloseError = fclose(srcFile); + int const cctxReleaseError = freeCCtx(ctx); + if (fileCloseError | cctxReleaseError) { + ret = 1; goto cleanup; } } cleanup: if (src != NULL) free(src); - if (dst != NULL) free(dst); + if (ctx != NULL) freeCCtx(ctx); return ret; } From dd96efa9efc20b298ec152daed9c9b18d641e3f0 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 17:44:22 -0700 Subject: [PATCH 004/318] added print statements for debugging, fixed long memset by changing to calloc --- contrib/adaptive-compression/run.sh | 2 ++ contrib/adaptive-compression/v2 | Bin 0 -> 467088 bytes contrib/adaptive-compression/v2.c | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100755 contrib/adaptive-compression/run.sh create mode 100755 contrib/adaptive-compression/v2 diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh new file mode 100755 index 000000000..f9e276730 --- /dev/null +++ b/contrib/adaptive-compression/run.sh @@ -0,0 +1,2 @@ +make clean v2 +./v2 tests/test2048.pdf tmp.zst diff --git a/contrib/adaptive-compression/v2 b/contrib/adaptive-compression/v2 new file mode 100755 index 0000000000000000000000000000000000000000..974745ab75ce65419904f0cdf236697bc45f05ca GIT binary patch literal 467088 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
>M___i%!>umw~Jd3_HL^9|L!ZcYrQE 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⩔ybu8UsnrAI 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%0A0lBswVoEpIEG!#g!J!0Q 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

1Jy*JLZ-VHWG!S`1Cj!| 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 Pn>WobB7G$Z=z>cCDk!J8 literal 0 HcmV?d00001 diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index f85ccfe19..4a171bd09 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -45,6 +45,7 @@ typedef struct { static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) { + adaptCCtx* ctx = malloc(sizeof(adaptCCtx)); memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = 6; /* default */ @@ -53,7 +54,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); ctx->numJobs = numJobs; - ctx->jobs = malloc(numJobs*sizeof(jobDescription)); + ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); ctx->nextJobID = 0; ctx->threadError = 0; if (!ctx->jobs) { @@ -95,6 +96,7 @@ static int freeCCtx(adaptCCtx* ctx) static void* compressionThread(void* arg) { + DISPLAY("started compression thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; unsigned currJob = 0; for ( ; ; ) { @@ -126,6 +128,7 @@ static void* compressionThread(void* arg) static void* outputThread(void* arg) { + DISPLAY("started output thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; unsigned currJob = 0; for ( ; ; ) { @@ -202,6 +205,10 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) return 1; } memcpy(job.src.start, data, srcSize); + pthread_mutex_lock(job.jobReady_mutex); + job.jobReady = 1; + pthread_cond_signal(job.jobReady_cond); + pthread_mutex_unlock(job.jobReady_mutex); ctx->nextJobID++; return 0; } @@ -209,6 +216,10 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { + if (argCount < 2) { + DISPLAY("Error: not enough arguments\n"); + return 1; + } const char* const srcFilename = argv[1]; const char* const dstFilename = argv[2]; BYTE* const src = malloc(FILE_CHUNK_SIZE); @@ -228,7 +239,7 @@ int main(int argCount, const char* argv[]) if (!srcFilename || !dstFilename || !src) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; - goto cleanup; + goto cleanup; } /* creating context */ From ff9ac637d9f11e9e8e574ccad6989aafb8018a37 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 17:44:40 -0700 Subject: [PATCH 005/318] removed unnecessary files --- contrib/adaptive-compression/v2 | Bin 467088 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 contrib/adaptive-compression/v2 diff --git a/contrib/adaptive-compression/v2 b/contrib/adaptive-compression/v2 deleted file mode 100755 index 974745ab75ce65419904f0cdf236697bc45f05ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 467088 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
>M___i%!>umw~Jd3_HL^9|L!ZcYrQE z0L^iAw!MH1=zx~CfS51NzJTU$=$zvRkM0JL1UP?j!p%Vp-=H~hFQi@b-J=`a!g=wY z3+mA3ogiaap;s#Lw?T>+&;$;|{N@*|;EDClMv#X<_2%&gkY_+i22@0WT6ZrySU_zm z@X{=hDv%hwE&`2IfrF~~0ILV22LIsEJqe@^)aOnEI|MY*TXZ9zL6!QG&4*ALA*YCstnwigv1 z!XR@HaREv~(Dn&j1vm~s3qrxM1>&Kr067R@21@Yk0C(a+`3mGKSiWLLz448|9kjW| zqn8!5E*Ew^1gw-nYfIe#-PP~{w6x2k({;tmNKj6IF2n$}QarkSFTAJ%4F-Z_Av;q+ zYuG@`;X-eKWI+z~>23f8>WhOgg+lOJ3Oerc5_Dez_?m{fPf-&HWG3zbs47JcR`9ei zD8;Z&0B1PxZR;yQ>GI5J1_sFW3LvYScYq>_g};?15d)rCgU1y(CO~dNNyVVba|JZr zLEDNDKS35hfN~kw3!TSb2tHz904+KIEu*;cLijeQZL^@;b%C|(5=6QB0W?Pgx``X) zJr9Ugpi~0lLUK5m2g@*^c?4p!^kg<@+0u9roSi`fGoU3q;9DPFG~WVG&4SavM<=+n z2YCxpM1txUcy@r)rg%~`TqUTf3F5$K@f(kTDjHDzB=+n7|BB!L|4aS(|KIX21V8=v z|G)SD|Nr;@|NkF!9o+Q4|Nn>o{r?|)Kq=~dXD8E-g@;h`=^+R_DXt^*b zQmi{bQiuqA4T?ZxxISn(AqClYbPN=v9*qYfN#gtqyEUNYY-d1W1ddkF%6;%kKa~A= zp#BDWgs~d-i8Mr z_;>|ySiy$pK{W|z1s!Pr=!F*|5a)nKNI}j4bpt`Y9|y?#aSZ>078Y#22(AVohjN5H z#1@O;piqY_W^jQpA29H@fQPT4Efa9d7`%QF+-a}9@PY@$q~oqXz>}n)>I>2%fo3%D z$T7Gf3hJGq?dQaBFJuKBD9(|~+ZP34;3oNxmlr{<1FgQ}yACP@?ljikF<)ZvhQ9?v zi!DscJg^qm7m&m5kH5GAI?15B_CseoBq7#*0Iykxc9UOBh3LBg>gc@Ccz_&QFhBah zOoS|70k!@?%U58ne^A;!-T)dh1ve?c#UO0f3dL8mLLt6F2_n??GIV_tcs(g7E}x$V z$K?aito@5FP_quca0R;N$6FNO-V9b_0M#LHn{HT2UtaK>e!j2^uhuPB4J5 zUUW~;fv`SwPJjl(i*A_ght3Hg_r3&;PD9)WiYaJ`@&OWyARe|P4OwE29$x|A{Rhx> zvz?(&@NY$M;oo-9#qbbhGr|Q#SqQchw3=WCD2&0I5iWqY5@6koQ1}X*kZypA8KfQ@ z*bwNr5wy)A=+QX=5>=ob4HF!=`uAl<%CUO@8u@fV7Bq1gbM%Wk~* z4=Ph&IUID~1h~QiP1PZmJwaBCqlZrda(Ue8d#Abf4g+)^7m{ef8-E~YY`|j013Hld z8chO?#&tS!yacUg0p$Wvta-3P{Df;cd*DlO_<)9rKp7Q!q5zcX$N`#RLJBr;#}~Bl z0^U5Rz2E^W9ywz2ipi5sX6iD(QRn(feJ_P zdNk009yr8cK?te75@HWy)F%dpmppn|&ohG7okPa}z#V8%EaCERA!G*wct{LA{0(-2 zOQl}cDI^&w2R9O>{|g#lgQj)xivCXD8!nx$SDI_DKzp{}LD^2<9k7*5pjHED+-nDX z`xB@N`v5*m0n|ine!%X5SP2FVo|mA}8xK(ITf3e>j68wUH)wrDGswDD(2Nc|%3KdX zE$D_exjT=)IP-@Ew14n`N8>?IuJQowyXbV?uoE00-L;^*Kp%h_?=J#Dp6xvTLh}}K z#K9Z`jyTXrEJ{*`tZ#zs&uy;#g6jPjFz@eLYZg$x>k*9W5PAAmHD zYCpUXxbXkKEu`WDErf>+et-rx!1)1uugmclFVBN2Nz@I|=;;A6ND0}~hHMsS#X8um z?g~&b{K2EUK;Q-FrWp7RYtUu^%=+BHqu2F<;mKWK{qUp#nl|b@2a4qv%YK7m0hD|} zBdxUuUL;=t*J+Tvvh4;m$dTOw8fWNseE_!^G_V8-@8&lFpz*OoIEQxOc?Dz{Vptb6 z3JY5|!@$7c(;a%k2eiTKg&F89F!0DObbdp@qnGvLUyze=bq-yyof#xQwLukxPzI234D43 zbU)z^&}0N?1_R2+Xn%EwemLF)+T{hw6QLhGjyE;HHhqCQ3&)!(P`MGP+zH6s?p>fA zOD`^g9aj4SRB+zlZvhXUf*X2GHsD4Aq+|So8#Evb87u;611G*_@IEN?!r2ehO@@x8 zf_Be=l!FQbu$Jxy5Es&{XasG21Bo7QSORw#X#8XajCb7i2WYSXsR@Qwz6n4|FwpD( zsQ5kS(Ru2{LGbV{Xq_Tv`UmA-c>KKpUknaf+X*V$Kr>LCZBQq@fW$g#e1gZ$z-hU= z_6KCU9V8;ayZjIl0p=ni0?b831elA62#`q#--CEC-(wwr?JRxM8Tz6-^iAs}$W8-z z+vm^!|Nmctq6|?{ce}pmbbay?RI%*<^;JO}P)u~YJ^?TI10|WxF3^5NUf!>e>v zP;zdrJ;8vO$^v)wAc|^lya27s0}XIOlN+cULoJUR-+)#d!L0|4CwGTF@aPr-_eYt- z1i&k1P=q)0;%5N(G8k^0&nL5C9me%H4OYM z_Mk}=@X#|T|HFpJSYT7fo#1<~`P9a#-Kqx6jN~B+4=#LML>z3 z6*9PhJ{Af}p5R4h=Uya($~V{wl5Lkz0~R!!^y8%r$hPL%1q}QxrjR}jWC56VBg7og zat{y&T{aea!b9^S=!k%}?V#v@b)!M!N#Ma7P?BVB0_*E81qB$Wt9Qaf^A~84Y~>dg z2FD$s`UbLP8PxDQ;i35nJkP>B4XmpZLV+V8hyxrkonZnV)}Xa(tuH{U%Gy9x0@xH# z!UV4>dkJpVf@Z}!TfwW$x}lWs2M?$(K+D)c4MTlU2orYp43K+&eg@S{z6-!J4V_(J z_kpGbK#O2O>!~_hLCf4;#(~c52gOG74p4M3@V9_>_CQ3ybH@BF&p@FCTEPUJn*ueo zts6ng`J2GYAi6=F1JI0*?+x(W)v*_vklyG7@G5-J{10d}T=OF+@Y_M_*r2`L4d8`Q zaAQG3r=VlcKlpTafeXXVLof6pn+_mzZksNkrUB3t+mDwLT;L&j&=ezRN)Oa*?C@ZA z1&w2aECAI5pfPe#=>sY8LCF}zgKS!3cp1q7TfYq+({TOZ(Y*m=|BFcIH-Pf%%h|91WCf?8&f_n7k0H;gKz4D!(-^e^%0u4SXCfblC=Y&f~@8^U&Y{6@MO(D@(zS{081J43~gLRS8PlP?1cXwcQ8vlqM=5EPIe-C${u7d$!}!5TmY zb~iwnz8^fS8$m@qqVxmr+lz-e3u-e169a!Urp+fnYYrhcGcYlLGY(h?YBPupoznqD zuLsmtX2_L?%?AWLAk!)y&`S_O!Pwc@0h+UbI1;q6-NPEJoWBX&I)#XXcDQ?3gAb13 zZ=MYy|!0DlW4uB zPq2Tr93huiHtz*>P#E}IA>$OFh8{>Q$aAnlAOtd_2I~8R*VjQWlxR``M||yv z7gt#k(7O-VP@Wpqaxh1H>`5`C7dw`~$K0qiJd@O%b1E;<`Q zjZH*wgQn+RNp{C~L6z|i4|r{lCHr`EPX(*(Jm%3k z6|}V;v>dv-7sPr|1`1rr{q`Q+;H?;lIv&CWl?Kd>V3&46D5yH?R*$Ox*k|An1Z9om;H&}SzLW$nErFx~aEl(cKC2FNc zjrxH{w}*fSc+J0u0IYWgKJpI~TrlHXp#cL^{U3HP>hT7U{U8HDdO`HdGO*KXKfEx! z14^e%{P5d9l7Dz)|7Z4K273^CJvt=M><6V+4{NXlBCCVCk}tutV31G%E9!=9s{ySj zgRr{6D||ssA8>!F@d&8z1nygweg6Ny?eqWt&wl>@-}L+c|Cqnf^{UYIN6q^|v)}Ce zko*6@hjPODqoDaaNdFzWN)TKgTKmHG7K0oFI_VZV3i!YSJRjzI0XFgoKg0_>^M2z+ z_zQ3g9W?HF!UMd$5otXo|29_DH!R>08i@T+_koVX1ocO$b-pI9^ZH;z4a4>PP0;#y z&^Abq-d511mpsEz4d!`t z_Wl5MYr7#7b1zt~v-bl;5Pbb+XYUIL4@@!lLJb4m&*#w%k?ZV*+&R|^wzj(yybq@n zdchEsnhKUFfpHiq{R(0G_Vt>;=v0d%y+36E~f`J0P}$wRd+y#+4j-V0GY73w#Lr;fb{J_9b2z?XY=LoWVQI1I|r4E!yi_PIxQD_E|x zRRh$@*((TY3qpjLTft^^f~jWk?V}9*Eq_7xzkoR`{Jo$h=H1}f=xm+y=l_4mAP%T~ z0p)|r+HSB^XX_lWJHhTh-UypfJ>JUk_djUs4ny-^5Q~|=*#>knAy^o+%Mu)h-7Q_9 z8Nkki-4O9Z9-Tc`GyeYn58;E)ZUpIm2_B;B-75gLsudizovom2_&mB>q45P41Z~D> z1zjQvYPEqCLm~n!4!WDvqkAjZ_uZ|~bOGjpqz}E&2i-8$*~$Rcw--dcICBshrl4k{ z$8m6Af}1!w;JaO0!43kQG<{IRqZ{lc(7+C8*~zgN4WR4IdXbb^z?B3EfKQV-_96yd zjR!&v2ULxP3`Zx3Pxy?5+`!(LlIK$rp^-K{?$ObLi{!0`w&`K8@|P~3u;P$%}H z2dpqZYybf2(V5an9_`2QD5%H)dlV$q)e0iv9{mMcz}dU^0LU>Ak3#(P;wm`Uw}P)x z>;-SuK%^s3VbDDli{mK>Loln_ku1L^5|?u>XCSKZUyx+d^%e}4N9L* za0|<)b1LW#O`p!W6To^Q8kk$b=6AMsKm@@&=2oyxovjTJL9o%x5MF0%1w;_sPXQg@ z-U+=h4cfC|@PM0{0Z|EcU;=~()^6Pj(hlG50_x*~7r?v&wb;8`!5Xc>u18S=&L*cI zYQPS*2HVZw20quL7vdMxo>cc#P*6bI);`^>pvI<8cP}_7x~GDI!lzrpr&C77r#phf zr!z){!>2n!0K^agpI_hUq9OsBAyRm;L<2On+X`~)OE<6^pj|eo#|>UMX@eH`?gfP* z2Y;JBxK#uRX7DNa;Lf2(H>XGQK@N}ZsUT$@&96ZFH9b1dy>JC-?uNtzG&Z3D(GAfE zS}V~CiT7Tx=er?oXy~BlRB&q^O##T8uu)NHt^m0o9F9%kUR^6VTA(p*-3kg2{&w)X zMc7E>UWjE|!3w*lf|ACIhWnt#O)ofoboPRb^yqAD0F~-GAX8yl!Nz%XPX%k$yx`H< z8UaoOXhwnrU+6-N1Y5@33lj3^>;@k4~^7JRm&>kIr7O@!b%`*1e!> zxI8*rE5O;B4-^T_dqIw7=5Njcwd7kv{y}PyZZNC!poeuYNS?nLbebnz5aMcwE)y;! zA&`3@n}By89eRwK#@`H? zZ-;7v?lcBROc$sk0$T^Jpc$b_v_s(KPS6|~fE?NUf&()HK35cMp$hQz36 zSF50<<qID5m3?E3E79+y%*I; z&`#YK><}ZtGR(aoAy6?4PFWBh#A-+_3CRiIbnVdznX>H$@3r=@?ggjD&ejZ2@zD;g zB|+-pwd5CgEeT?Ef@(>SJgk<43PM~BsU_h;pvZ*Ok{}^)EeT@4YDq|VfQR@|YDrL} zAZkg~hb#;py}WiHs(UI(4qQt@=9j^qaRAqn5X(TdBq->iwIoCkTuVYl!Byk`|Nk92 zDnU%BD5z?Lss%A2wt#9$kUXN61o2>H0ceV^8xqZ($6x$cM5-l`+=g6Bf^@@bNswh8 z5bZC7L0JVl0R&3=@bnDQ0Iem#OlWZcIl~COmIRv!&Ec?G5-bL>1vZ!pjd)0}^d{)i zdg$mWLV_8xR~)`&9GXNxhQVq{5ZePf-U_QF!A^u%haeufmINz>3xjxQwIoOg8WfhQ`1#uoyI`M$qzyO*)0o7jJdpCgkk4>9UYe|qGtd^9AG>5@Lc&oMepa1_KZ(Z{T z(k5))3u3`X7C;=($U@Iq&w79h!=~RFSP_Y0z6#&HX zXgmTMPXnC_FyS%_1NwS5(0H2%Xav8v^$I8=JHZoH9-YvGB*1m5M)9fy>8kSiuOE zgO9L5Gb>oIyAwLX25u!lYC#X!A=Thz9MG!K13tnADhr|Wa-iWANC^ya{IM6DJHaDq zpniZy_g)b7;@W!Dv zZJ=dz^Ii}Oo@zlHP^tyr^no-U1{Md8hk^Qq-C*tDR11;-r&^C*a9;(Kyum(#v_qhg z14_P!Uc`c&(h2KogF3M^=<Y;L{lknrVCC!3;ju&ZEqcLsPLR5vF=?*#Cqoi_NS7!Ssepv!nXx_uXTfaX46YY;wwi=tPc&OPYl zO>5T+@JSoUSwH6?QX4$1U1#vOfv4hnArsPQyCZ$N!AIR4@aYcSL2RQ^6Li&hr|XB8 znP6K$>qbGF(+pl*09{BAnf>SBZ-d;A3EocvI&J9&q7?}acEniAO^`Owd8Lqbf}jAn z0U3IP!~rNc5zEPd%xP%{Xqe>x|Nk#k&w)yFaI}2@orm`K|NoaoAd_LrL1zSoc7Pj- zuArq$8$7x}xkYs^h>!><%Xb%T$`1~>e`+rGPf zI~+i*KHnGMfe{c7)W`#I!E}d17pQp$<}-PKRab)UAbjD_0iwZe@NN@ue85|G;OGJE zD`Ei$Wasf0S`tW&I+(pkEjr&0@WNN{A;+M-@?N0i3EHIqZpDE%Pq}tL+HYX9K}!}v zxeqe90p2(XQVhx{&EWGiAvpx(Dp2Np0UELBJoLf@Vk%sS*%h=N4x*~l73n@1Yu5$* z&Cp5H4)7h72N^+`7}N}NeSr{o(IN2iG-#LtGDi%`MWBQONtVz7iv8ew?;&vnnhpk? zrUa_cyZ3@-$6g4mgqHNsaS4zhtQOOSWD~FuN>lP>3h4MpSXBlpi9L>kdxPLQ;v0BO zjs;$mfmraG48#E?sGhT+8Vl6^0*ip!U!d~86W0C$iGmXvhyhM$u=W?Ung^HHpb88; zLj{^m05!;X zDuk%}SY!B+?hqZlkClI$2l@B03h*I?jOEo~ty}Cnh@NYMI zQ3E>N2Yjp+6KLtj3&`d^@VS31;G=9h!#O%#uRs>#!_I$(?SG~3{h5KF<>sJ;LY<7T ztySRj?LcckAggRX443;e(?E;OAzmfo{!EX?Hy1#~d*=`6`G1{LML^T8U=q5nr}H{= z`0|An4`?anpMU@VzsToiVAuuPPre_tiVC#B&-HqDD@e-n0)N{rP?H-vy3y&{aon{9 z)UN@zmYNS}fR^y_^Dr>*Z#&W13qDAyySBrl`MAf6pWIMUaJvp7YVqPR$VT z2hrBkED)WbC7az~{hjB!c{V}LJ%qHDJ5P1T?SNe~S_$Xb2osH$i%lkg>eZUeNg0izF@vhMl0m*aZ)a zQ{Al~Da#Z5&5#8WAgz$oabJR}=@)uf^mO)uYBflI!=snC`Ya0r#;QlY;agR>$xzjH; zazbtn0N+5-yceXKfxiW^0jsv-MG$B#r1c5tJ{*`FWa(>dNB35c;uj5|wfoHAwUp3h zpD#fhusnKMkDXy*fZYJu%`>4BvI-~T5Hoa00zx%|z01Jg0-kw-@R<2q zp^L>kJh~ksJeprHdUS&O=r18_*de~^1|6sCaxelahnUU*j|DV?$8eEvpu6aie8__t zG6VvhPk`9R$PZpe3{L#r4hf*ft4F6}29zHG%7=4($RSV(%F)SPU-Qtff6a`z_jkI zpj`B#A2d1D37*{tFYtjbiw4d1gX0A>(*$l$LS}2hvxuPP{|h&ENK$J79sdWpAe0&0 zNNoNg!QTe)UTw#V`=EFDv#tSFR-KL{QLhO?A`9r4v+4j z3J+%JHMCPfLpdH0(E<-<2v-{%w4e)U8~a-1Q5npad`11i7&D_zP1uNEz;X2HXe$73)ecAy8oex(&?Y1wY98UR#f2;4*#V zQAnBYd!*a<45T#oJ<#oY0u=qzJAL;=hpgH?I-@?JjU;1J-JPbeAr9@f{Q& z%;0|AikINI^j=nHkehsufC3Lv{{HAL-2vKa11fxvfi!ea1<#a&3g6HbFBXFi^#UC` z0?my-UIc<|%mJ+i1Vt}66E)W!VdQTIo!0`sYz4B?7EV(?V=@{`6v_?j9%k1A$R*~F7i&QmkhJ=MboR17ILyKT z$~vdORc>?b8BkdX8tO!L%7GUrLE}xG;8kGAP64fwfjI@l_UP^f=>->*z879JgIYm9 z82DQuX>$vri2U(F9qbAdL=pJ|QbdMc03B}#z7)1QsKA3cbb^Pr?*nkL2-&#rVeLDC zAH1&;5*^Ue5Hzj<9%um_o(($V1Qa2l67YuysC)tMxIX^E5#$e0q(Mr+AD|W`=n8}v zH$b@u6l(CK3|ch~8`TCKVtD{m0)jRC0X2L<=QSY}f37z?IzeTh>jRHYP{9XsjvHth z1$cO%@d&7~3_h|O(VqwHcmD7Kw2B{G^g=GMsQvK5why{ju(S3DY*^{Ti}J~!83g3J z#8A$++5y^N23aJ|zxRLhcX`bpp!M&dngYC@+z`C99dTYb_&fs085N+?w!8Gp3sum9 z8u0ihDEWPPkqY92#-*Sq8ldb?-T}U>0%8Dx`|LdqzGuXCr#*^!p!MhY?z1Q4KahTq zM?AXiA<4plqnl?(r|$>;ZLXg{yW4T>&+jb#0@4QZKlsQ$WdDO|FF3V~Id+Cc#&^gzjL%4o;bh1Nwxu6rVP{Lye z_|`F^!WUcEVweY-j{}_>@6qjQ;L{r_;A(i=rSmrANY&B@;JxIXz88EtT~GLcZd~XD zjVZgn@MLE4=ycuS+0Eq1?8E^&-_QdTAdqTf0r=z_@KP4gj%(2PI|D=mXdCbbPwglH z4@S^#2ha%R0?=(FK9H6TVxj;%oW>2wlM_%b_JRzcgKlf^01eB7hDO_AW9#5OAy*-p z@CA5!p&7hB5j<)Na)tr8^agFP0S)jSdhr{g7Cvwa+L*)42p-=DR}3$pWtc0-v<>{t zpyOge*Ry~xp*;n;A3?yO%Mk2&@U#_ZIQ4}?hr!F^;6ws#gL!~X#sZDLgExv>UgU3k z3(iC6(g#5M$U)LDAA!d`nHd=v3=e?1XP{F%K^xL*J3Qd0l6QjIkf5#w_zp`CW-ks8 zYu_FG?ckMZu$chZ!~xX80FDz}NPI zPUI;4@Z$Rx@PSOQ<1|pu(?H3up#40c&Hs?`sUP6f-wnR<<^|*k1SszeSl`Yn$Ch$fzP!=y?<9>2Y4?7{M=e}_qyYCZ{wQ{plAY( zuYhi^E&buqd`!awbj45SchDigFW7$n{|_1n@x1~*-@f^nhezl47au@Zscky}I?EhX zDm5Ro0EyoE_5Z&|uc`2EP@~zlW*4;4dNeouQzU(Kj&gw}8)oYOdYD z%-;&yi2yC@p`CHphL@l*6L5(Ex$DLmQfi%ikegZ*Z~$$!#FMZ3vpO z0`24jH8L>Th8tcI**1im;L*z}z89QPH-M(;x z`9yAzf%7*c3xef5pm*AV+lCKbeEJTJ14!HO2(#-6)VAToSD+pP=welmUe+(WL2-Kp zlq^79>Dnuxwjp@752=}Y;>B93tRLET~`El%8eK4P^UoKhI^3P zhTLFRfJUsq@r%@cYXsSa)_%+V@&Eq|38-MZA(tU=oBSJydQ#U90)(B7?2C9#{eGhmr`+{zj08R5BfXwfMwl;&5fzs{@PUwj^ z$6X=EuBwBMfo5f3VCV!Lz*GS zNAp2u(9&zj&CxJJ!BA=LbGD z2jmFf6_6A1S9tV>g66DGcyyPpcwy8FG7egMLC&~X-vf0jc!(A0j0-o2xhFgzr=@^S z0a@YE{DK+MN`aVR?YaWq*nk9!0Qd@~2GF4r(0if5Q^3%L!2Io?d#pWrS$~3C3=k=_ zbDz<6HG}r+gR&=RG52TioqMhmUP8{i1=Yr&>J@UrKX^BKFGT6F7a5R%oA46c&coO% zeWFXSyR@V8qetg4{%x0DoSFj)7uN|dL0k7BnF@9+v_06RwG&>nbwdLR8mFM6QGUEs z084{T--Vu{3)%w?p2vk93%$VuR7iIof3dX?G)NB`JOiKN0zXg%5|oI2@?bA?!y~Y{ z7BtKaN@*{_gf^&SiF^<>Tc7e)!=*;`K6}=RGvPdocb2 z*VmoXz}q!J%@9yU-8l`s;0(k?yEvuu1lIDU@y!8n)Uw81163TL6V-lrbnXQ$SoP@K z3SMK>ITgHCx*N7E9lXK}y1M|rfE~Pdp?NPzB?muvRg}kZ@X~YeA-;($3=BI!cUC%f z9`NXf?CSw-R)TKvf!>qXco1^m;#PcaLt?gfpJzWCOP!=Jn$qq@Nxi%{bbHdWMofXBlcy#EL@ z;33D_gC$}9Za&BZULXYCopihvH24Z?0XFXiv6%UL!Q&c`-1y>QHK;(10R=Hc#H0B* zi$^DTMQZb2kT?T>3#eh;-3sDB)_48;|DV4JJtu=iE z0_+a(3LKE+%RER*ffNwE;Nl6?HwTwA9?-om-Mo`7g32<`vg03+%g8`GMfqF7c6Yae zI3A5hK;>TS;TKZNSs2pb=UYO@vp#?p6dZ2{o$d?@0|*avt}~41(OfTpbkh+?_$7GY z89bw3!SP~$6{u74rm^-7=A|7 z!xJV<0H1fdqlK9Pd|CwRcp~IJZSVyYSyf4jPZdtsnWmZf!0GhR)+J zj)10P!PbC=o6y>~$m**>kq=gga1XNlY%KE7`?KKZ#)8(o`hwTIf|sj;jCcMIqw{7$ZJ-z}ZTEU1xN=u22Q7w6p~g{H>s4`MX_bG#`)vO>uPlLJn?hKJ?qe@*;mTym>%K#MzfcqCu&V7v&rGq&4xN%I5tPFK){ zAv<0ufm;8tFah0}3Jw#H|GwphU<^U20<7iWLq@PJu!;%Ru50*vKv}ZebxODI1kHn;z95Yd zUEQt|K+DNpCp6biVBqfsr9Hy~+O88iU3*@F4xj7>GrL`Tx_vi*bZ+Q&osiafsM{4e z_&`0p4{4nugxj@%?JK?fYPky36Fyhm^%-3moCw~)>*p519WyaND`K2 zn?OP6yQKM{JS5rf`}6;QbL|F3{uWTcfYvjDM@m7b(KpvFQ9$HXurTOSm2TGspmiDD zzAri%L2-Y89V1DAodW8OftFwTqF%d(UOs(zQ4hLr9yBoo8Ck0R@S+xUE}ZKh=mxIZ z4=-{+0>@vJf+$z$8QY*vKfgTEd`sh-4d8m;RluiPbk!*q2H$QWpUzMmZFWfe+6S^D z*#~m09B8i-xZ;L1b|K3aJRl8H#D%4x1}V6p(*_rG;I$3>+c-d{xI(7$8^LWl(8Lgb zlPcIr5b@?0^&YUMA*8+4e1Os8Ab61rs1@kZ4OMr5(Zd>i!U0O_1Z?MaP;(2~TC)as zJ5U-9;8>arQ3GoHdVp+z_~STuUL0H$fmz^|2dII{-wZlQ1Tt-Y@C75}EMf3D2mISu zxDNhcap*V(kp+#MgPS0brRF{VKsRioYiNGJ57z)Hp~2(rXu3ef8Q2joK|Rlx;QN7y z@cb?&1_sBSpiJx23*PzR0qyueJ7C~+2|jtl19a9_H>6Dun&h7fKHj49lt*_ji1p&P zD7c#d^1eqmq?s=91KMJMuCoTqK`%!KO=^P6FT-y;Ks^A&5P3JaU-#nWTo#7ot!tnw zs*i(@lmG=aXxrC6&>n8k>=aUn_kpq@SQR`>L4n=;gNMHd+!llM$iUHggpq+^2e^U+ zpFsig#8yy$fPWj07?=aOF?c`u0MK}n)u_lh9g3sT|H4eqUWa&`BDxR)P5 z&NzXb+@KA14`c;J_g>ICiWdTm3=I20-t9bi`FSU}M*)=qEn5d$UjhzNP+!9uy!HaR zj0?%p6TsmMQrB_N19IjFXsi3b|NlF9#JXES5}+z^D>&#_rh!alndZ?A?#?$?u`uyB zL7IxKUpX!BISB1xbb=P?fb%pY1A@v74|pS-7qqkny!Q{< z2nVlBfHlHF9MGsJBdGcLV(M%b21t7pR5{|AfA)a1_rUG*7ujDSiMbUt6#!dT3o7Bk zqb%CKA39IKPxu#r+-JnUjdkjBnw*aYX-{|7e(A3L()k0tIpU?H2WTF+6Li<)N>CBm z%en?;HpBR!)8{;p_yrL89+-Rrgg*(!4}kDz!uSpl z{!$p<0Kz{A6C(fp|DXBq|NlzRYOa6(|IhsQ|NqK=|Nrm&_y7ONfB*mA1T9ti_y7OTfB*k; z{{R19^8f$;n*aa*xBUPAzvut||0DnZ|DOrEOYQ&v|DFH;|DXB)|NoW$|NjS_?s4+} z|Nl2ZE!_YA|AYEvFbq1c50u_JYyULY{^93u16}=g-1QG=X$hpL^!;=B0VwMwU+_5i zh{>b#`is69EDRptE)x8HWJG@~5-y2opESOizyvLMeY#mA4}lsK3_hUI%+9@_?OUKh zz`y_hcTT+mBB5u>fsfPhXg<2;bc-@pI=gF95o@D3H& z__WSbX`NF+hg+o`Z{76w|9?h?Zm>l2OUBC&(mD^NbxvLP7qlT8bixRz(t;crXWa^N zB7gHCa8U}916Ng`-Oc>_S-hHm{O51Ahs$*w>;}8K^HB4l|D97o9_R#Z+w9&8@^^>N zb|#3aoyXFSx0HeoZDVA3=?B-2Vl@B$tsnx zd_jhS(IsAq8>=IHW*~mnopc1@Z&rNExsM$RRKF!7T)kTNn_P z?O)I`k^lc+eggXqbSxi4D~M$cPR#txr@>JS5&=gs$P*4-C0$Go9b5ZByM(v3f~d}e zY2ACk5t!!K`6JD-`2b^@OJ@s2q;qc%I5E;D`3?*h~v?C1XPy99)59bG7AHw{2{d7%Aw~J2Qg^Xxam5Jq5Vj1l@+wS^5S%#s;zFfk!9U2GGrYEFPVp z>$yOeR@R>A_I(2yh&j^jdIVHvxgO|tJpc+U*FDX(Zx|8tO`t^bvK$msu5Z#ni6Hbw zS|=Ci?x(clPE3pp$6a@Tg0tIohqdb-{$^LO${k>ZC(@3ybTKe=yY2w>C|sd}4jm<3 zOx><;(8ZX$U3Y*Tu;b-=a8$X178^4_swphvXN_;bH@bF$X9+wyw}P8%o#6B7JUYRx zXdg&Z)dMmF?a|o_YI1@m{6U?>PROZe-C&A;yCYL4WIz@?C=5D305tE>yB9nV3poSN z13GFCZ;ygH1wN35um|KwBoD}uNYITJ9-Ui3=hXN>HZ2@)RrwDpy+Jzz!6y)Tz>hov z9mec|c9JvXDDY0mxxLT>dzugYg>D$~fSuqCK56JhH)tOZ?4We;1k8&aT##kYuyaU3 zvof%Kr!JtLZ!dUE+oKmU71li!wEN=4Bhb#XUho_fn#>7^3@AKa$nZhTo^r6^AtV2` zgC5;`!P_;h_ktF4@=t;YUsSvRUboo>TI9MHkj!R6HyhdA-Y~78IR=>7$O^iU6+mVgkj!TG=!Wc{ zZ9c@}(Ftb01T8xTR~m@pZNOU-nh*X14OpF!1#0(f={cdV`8g?b;<-+?(1 zIeeZYE9izCV+hGlaF=>?Uh`-^#^}+x6%v&%4s(MN7Bhcq9%!8to`}_g84f;65KF{n zA}fF#M~KtfmolL40Q_(tyureWB{3sM%tYAkJJ8H6ELe~gTtHR;JtmM)u&e>+WgNkR z9Ol9>!$FJJVYVYH2troS4PN7pJ6OQ070!YyN$_!K9^H^bX2A1Okaa`g{YD;;>EV}N z;9{k=0d(pysEEZ|Ub=n&E#!jSgjfomwm9y322_)P*IsnGg077Ot&@Y?fC##Ns?!y; zvSfn?xD*5}RVamALD&b{@X`rhUggmZzSiQ!ZrGS4I8{T>lmlJ+Fb6cY20d%Gx3s~d zJM_Yfcc2Yey^zD*U}CqxVzmt~4554H5Jg?>4iD?v73hT=sD}z(1lR%^y!Sv@@uHp! zno&TrbKsNlk!24e%R*-GKoJtUqk{{@IMAvxkKWRT7X~m5E}$X}yypqw_7_FSvfZs9 zHBjTc7*Wgv4cvjvd%y{G9=L#jng^Fvf|&=B?FOGY12c~a#XPwCCcw-CpO6DJ4=#HN zSr&4f4$M4ekM7VN9^euybjQnjknf<$0kooPg9ogj0u6(J2D-te)EQ8AtUbfPj}hz~ zFq=TzE--=}bhQj;X5!;kQ3w_3V96p@`K1(l9GPhYMjiAQoA+8*&a24$nG) zx9u@Q?oL37V0e;P&kpsh4k(JC5e%38h%DRP3Q~j3v*4md5nM=s7l5IxG=dZ-9?(@B zjYmN3JkSy!l=X$6_4*Z{CUP&hE9KKW71ZhT>75JeM|yP2W^4kr>ZLt8V?n3aBX#&a zI;VoVA|8+v<3TeBpw40^xJM5=cB~V296Y$2+1U%~xE=@F1M1Lwbc3Zq>tz0d&cDC! z(Rs|L^U{mdN8oijpl+5&_g)b7!U&W;L6alUQE8AM?1&mrY1a+z0YZD3uv05RT@>(8 zz$);t&sKkM60UU;r(u0QtHbeE$23!Y^QB zK~6^WsNQSRPO~4)x8%i*A0>DhRAh;z zRfq3m&5~^lp!PDhU3vW5TpvSr<{{3XF#wm@p!L5*o1X-#!HG2=G~WN=1!&M3 zTx&t5_(1zXL5uhgbRGv;kF*~Y6g-_c_k&{D?+G!#AJobMRoU?M480)pU6Ix^)PMwz zzW}WpM4pC2*>4J(-vABAA>0dEVgw4;7a;RIK)n&rIzq4rXv`S28gw#-o8j{r{M%S} zcGF~j4>Fz!3Ow+rBp{R;^XRR)It3~+;AXn3eHfDDD% z^uoeJ6{HQMqVoqvdl9<+@dwg=)0$fXC}Qzk^mJzSvU@o-_sBf2{zSZwKoK9ok8h{u)gEjc-6h zY#yC^LF0!Wom)ZUXq{6*<5~RsL@%|oFmz4@jUhGf1(A&Wt&ZTC0q`1T56FQA-C)%o zkh5bvI>C3IbWR1W133;}01sM)alCcOzyJS1EZ8|Rosd;Z(DgdZ;Dt)Cbxn}9QUd%< ztHCusY?TveV=QPDcXuzy@Wz81E6wLM9xOlgy0xQP@s4~hrgN+vUqfYZ_nuluZDq& zLwdK}TOr;CpP1a)dI#if@PK?Xc;hrh&W-9xg!^vv?5;HU%UB87YC7(!CXARyWvz zFR~Iq7J*_O>OBu+Z-Y_+R2hm7SND3#M@BQpk4%-2J$v&paK@#U@?mq zX<$=85|D8Wh$-D$L1uM>9r&UcoFI_AhvIF}E(h%11`T(2w(@|MOn?svf?Q|G*tr*U zk}=HNAZdiRL0VoogY8oR#UXg$1v2mQLI)&#{KeK7khejy-K`+9vsDJ+ZK!EbZ-Yz& zc^h<&GR)gxF^d=ZU{gR6-K`)JVoLW`kXhYe2fnBQCkQ0(A$c3>VGnEYz7PI3fj^-2 zbD#`y@Bw2txVSj@Ql9ffH+VMg;6wKAsi0(Y@RcOzMaC1|;1c8DL!R!bCI9~aKln-n zA_}tW;6owE={g5r@qt94=LvzDY2Dy;XsjF4!^9w>po`}~N$Vgp_)sCp(L&Gy2fQx< zc5f3uxW51{6444G(0o{DFK7a!6MX&@K(gJfAhHvDv>dbuf|>@69gsFq?10X^hQ$t8%;LqxD3B>2iSAYq2{9!TY?kf1 zO^2Al>oOsM09o~U@PS}AI1moLY9lY_h zb85+d&{7)Ea3&}ej<8s!oXD0Z`Qm zI-#!{yq6QT>I5B%1``MS7JPU=M5MFV2DFU;q6=Def)1GL-V4$NI?f3!4RUDrRFES* zI$bAp`u22&b~M*^Fm~=00c}cZbpmx}YI_*@TOmoScEXGQ;1mQpT>T}q2Q}fvYmnIS z7bOv(#08S-ZUvE@u5(_R!c72WG|K~8^Q0K1z`3H2NC+uj172Osu zvcNh)qTS%zO+niMO1E@_UGbte5@alryP?GO^T^MGvSNAu5tPS-u1r8}BycQAJD z{R3WP0*RkJ2>%>-5f3&UbRi}zeh$3w28kViaU~37F-WQ#d?<6L>lvtjKqf%_a{%O@ zFMt34hxz9~x5bNDV4Wb*?p6@l>3XKS^h`I{6))C;BM-^lDEQ7S8{HN!PKAO(0wmfE9VciSLmHcg> z+ef;=X%k#Mff6aSdIH5SxOxIJVAT^Sl)%*!m;tMvKz4zvColt6J%P?=06V%Hyek`8 zJ%L0)x43~S|AWlXGgP61pcDkHo>oOP-*kq)Xs&(12n+T% zh+zNlLINBJPmqHB!wVLW*zp&=A)vqmNp-h^$WGTk&|n9d01fsJpkN10n}B@)nw0If zco7WN2@>rFAA#TL`X|%%52z4khMb=YCcD8TxZVNz3#Hxx3BV56htxYDzr6&l8-cA( z010?NFYO#Yz?iNY-fH3(6pj{H-rR_jN#uBad##+3DS2<8a4-e4I4UpNz9dI%DJ%XT? zEhsWTS~|fEOBin_+(hug0#K{QqZ@oBDAa}By&$i2PX+amL8~)C4Lfi{vJ>2NYzB9$ z7(sjc!DT9_snQJIjKIhbJ&+c>Y5B#|5b&-M&}0_)6iSb7i1=Bs`0*F4L2!A{^-7>w zAjr<_m!Q48a3dk5SSzSQ?9t8O(b)=`PK1`#P*IB)I|IRHfuuaT!74l;W_fgP1+5!{ ztzDfM3|0r~t3Z7Vxds7zsG$d>y$zK>3kFads=fypUm7ukpuwVd-9|MaYfAKE>>?Dvp=t@aQFo1R=L4yHmBs3U6S|PyzTK)_R z28gJ|i%k%-KvJOb2ZUK3-CIFMf_m?usmLx!&_fdmXv70G7(g8n?7;w9*VYLhm*|`d zI-Q^yd`1B%7(jEkuwVdkNdC;k)7a&%G=6+$p05a>v4zT#~7a#n< z!2ps6?UaEy9ki_m8Vpb)p}_#s3JC_#EH5k=Afgs8mO#t`NqKZbE(q@gZ_)AS25;qo z&6?LhQVo)CQGx;Ne-CT$stx|O3%@}VzmRh0-~-Twui(sd@TI*6=LwH)$n{+ZAF_LN zPX#BhgRd+-I4^<jbZZ@qi3{ zcywjO?QAbF2&$o=rpjd+l96lx?i$$+#%l1v4nu>v_Fq}$>} zA;c_@6sXYzNq(U1ScaeppYE+7<_jN4x&n1sK&u-;F=>nzAmA`Q_(0mD8+=cd$H8~1 z9-JRM_QC5O{`JQ^_9d^|bO==cdoY6fZD2jnC3@g{YQPEa1DgVp=xzm(5L3FrV`ZHOJ$4}l%_$FK$hozh z5DI#V?ZJoQ9+2ypJr2Io1m%uh@QR;*{V9)KKP%1Wfkq0zd(B~5L1_@$A_p;H%{~wt z7M9JqkajOf46-+O7d-v*uRr90BX+qFt}*rCyny0`LmudvgAueG8q|-i2hXEG63oFD zifE=I+A{p>A!^_TffLBV7qaL&;NHfiLjg?(WMv)n=Af4ncp7@3liy+Ymv(|rnLiG0 zoI*w`K#MY=4LyiL$U<|-T?U|%1LV_A$kOs=@QOzT{w8inLl4x=fj0C&tJXb`8+xG5 zhzD9j4>Ta;pM%A3Qko;t5#%_zM{i zxICy43NBP!8(zAAhB{#8K}#o)d63d60%RJBsKtxR?qGvJQlLZppayw#gI1RA1v&D? zMo+K`a9}|^39ZUN-A|A1y^w?M!O?{3d*sm^kIuaT;HE9O`~(l@K⩔ybu8UsnrAI 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%0A0lBswVoEpIEG!#g!J!0Q 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

1Jy*JLZ-VHWG!S`1Cj!| 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 Pn>WobB7G$Z=z>cCDk!J8 From dd447bb9a73bc4cee24c545f35349d88bdb88287 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 19:00:55 -0700 Subject: [PATCH 006/318] fixed an error with number of jobs --- contrib/adaptive-compression/v2 | Bin 0 -> 467087 bytes contrib/adaptive-compression/v2.c | 32 +++++++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) create mode 100755 contrib/adaptive-compression/v2 diff --git a/contrib/adaptive-compression/v2 b/contrib/adaptive-compression/v2 new file mode 100755 index 0000000000000000000000000000000000000000..0c0e194ebae05845c80f8f24d2534a53a56c414d GIT binary patch literal 467087 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

0;n-1&!K) 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 OX?_vuD@i~XTmk^TZ&K|5 literal 0 HcmV?d00001 diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index 4a171bd09..5bbb8b9bf 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -47,6 +47,10 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) { adaptCCtx* ctx = malloc(sizeof(adaptCCtx)); + if (ctx == NULL) { + DISPLAY("Error: could not allocate space for context\n"); + return NULL; + } memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = 6; /* default */ pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); @@ -76,6 +80,8 @@ static void freeCompressionJobs(adaptCCtx* ctx) { unsigned u; for (u=0; unumJobs; u++) { + DISPLAY("freeing compression job %u\n", u); + DISPLAY("%u\n", ctx->numJobs); jobDescription job = ctx->jobs[u]; if (job.dst.start) free(job.dst.start); if (job.src.start) free(job.src.start); @@ -84,6 +90,7 @@ static void freeCompressionJobs(adaptCCtx* ctx) static int freeCCtx(adaptCCtx* ctx) { + /* TODO: wait until jobs finish */ int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); @@ -130,6 +137,8 @@ static void* outputThread(void* arg) { DISPLAY("started output thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; + DISPLAY("casted ctx\n"); + unsigned currJob = 0; for ( ; ; ) { jobDescription* job = &ctx->jobs[currJob]; @@ -225,7 +234,7 @@ int main(int argCount, const char* argv[]) BYTE* const src = malloc(FILE_CHUNK_SIZE); FILE* const srcFile = fopen(srcFilename, "rb"); size_t fileSize = getFileSize(srcFilename); - size_t const numJobsPrelim = (fileSize / FILE_CHUNK_SIZE) + 1; + size_t const numJobsPrelim = (fileSize >> 22) + 1; /* TODO: figure out why can't divide here */ size_t const numJobs = (numJobsPrelim * FILE_CHUNK_SIZE) == fileSize ? numJobsPrelim : numJobsPrelim + 1; int ret = 0; adaptCCtx* ctx = NULL; @@ -236,7 +245,7 @@ int main(int argCount, const char* argv[]) ret = 1; goto cleanup; } - if (!srcFilename || !dstFilename || !src) { + if (!srcFilename || !dstFilename || !src || !srcFile) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; goto cleanup; @@ -270,13 +279,14 @@ int main(int argCount, const char* argv[]) /* creating jobs */ for ( ; ; ) { + DISPLAY("in job creation loop\n"); size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); ret = 1; goto cleanup; } - + DISPLAY("reading was fine\n"); /* reading was fine, now create the compression job */ { int const error = createCompressionJob(ctx, src, readSize); @@ -285,19 +295,13 @@ int main(int argCount, const char* argv[]) goto cleanup; } } + if (feof(srcFile)) break; } - - /* file compression completed */ - { - int const fileCloseError = fclose(srcFile); - int const cctxReleaseError = freeCCtx(ctx); - if (fileCloseError | cctxReleaseError) { - ret = 1; - goto cleanup; - } - } + DISPLAY("cleanup\n"); cleanup: + /* file compression completed */ + ret |= (srcFile != NULL) ? fclose(srcFile) : 0; + ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; if (src != NULL) free(src); - if (ctx != NULL) freeCCtx(ctx); return ret; } From 95ea54b4cf3fd49d4ada2809ebfa41d73303761b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 19:24:22 -0700 Subject: [PATCH 007/318] added code for waiitng for all jobs to finish --- contrib/adaptive-compression/v2 | Bin 467087 -> 466752 bytes contrib/adaptive-compression/v2.c | 37 ++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/contrib/adaptive-compression/v2 b/contrib/adaptive-compression/v2 index 0c0e194ebae05845c80f8f24d2534a53a56c414d..38034cdab55dde6c55382233c34d86b0b58d2083 100755 GIT binary patch delta 40567 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 gtqk!jobCompleted_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); + pthread_mutex_init(&ctx->allJobsCompleted_mutex, NULL); + pthread_cond_init(&ctx->allJobsCompleted_cond, NULL); ctx->numJobs = numJobs; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); ctx->nextJobID = 0; ctx->threadError = 0; + ctx->allJobsCompleted = 0; if (!ctx->jobs) { DISPLAY("Error: could not allocate space for jobs during context creation\n"); return NULL; @@ -90,15 +96,21 @@ static void freeCompressionJobs(adaptCCtx* ctx) static int freeCCtx(adaptCCtx* ctx) { - /* TODO: wait until jobs finish */ - int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); - int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); - int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); - int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); - int const fileError = fclose(ctx->dstFile); - freeCompressionJobs(ctx); - free(ctx->jobs); - return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError; + pthread_mutex_lock(&ctx->allJobsCompleted_mutex); + while (ctx->allJobsCompleted == 0) { + pthread_cond_wait(&ctx->allJobsCompleted_cond, &ctx->allJobsCompleted_mutex); + } + pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); + { + int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); + int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); + int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); + int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); + int const fileError = fclose(ctx->dstFile); + freeCompressionJobs(ctx); + free(ctx->jobs); + return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError; + } } static void* compressionThread(void* arg) @@ -164,6 +176,10 @@ static void* outputThread(void* arg) currJob++; if (currJob >= ctx->numJobs || ctx->threadError) { /* finished with all jobs */ + pthread_mutex_lock(&ctx->allJobsCompleted_mutex); + ctx->allJobsCompleted = 1; + pthread_cond_signal(&ctx->allJobsCompleted_cond); + pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); break; } } @@ -267,6 +283,7 @@ int main(int argCount, const char* argv[]) goto cleanup; } } + /* create compression thread */ { pthread_t compression; @@ -297,7 +314,7 @@ int main(int argCount, const char* argv[]) } if (feof(srcFile)) break; } - DISPLAY("cleanup\n"); + cleanup: /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; From cd50382c0374dbe5109027ed126a6f22b0a75763 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 19:28:48 -0700 Subject: [PATCH 008/318] fixed some issues with segfaults --- contrib/adaptive-compression/v2 | Bin 466752 -> 0 bytes contrib/adaptive-compression/v2.c | 11 ++++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) delete mode 100755 contrib/adaptive-compression/v2 diff --git a/contrib/adaptive-compression/v2 b/contrib/adaptive-compression/v2 deleted file mode 100755 index 38034cdab55dde6c55382233c34d86b0b58d2083..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 466752 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%0DRf-mO)ukw2U-kII)`=U#Pu_N?8 z|F+Wm&HotqTR>au__x*Ge-rc^VF7zbR;b12i0m za4%?y5hz?=fXwp%^+rJJ2*DztF=Nnb(8(BXhRkIkkjAfJ{`CMG4BE*a3RC3IbWR1W133;}01sM)alCcOzyJS1 zEZ8|RosfkM(DgdZ;Dt)Cbxn}9QUd%QP@s4~hrgN+vUqfYZ_nuluZDq&LwdK}TOr;CpP1a)dI#if@PK?Xc;hrh&W- z9xg!^vv?5;HU%UB87YC7(!CXARyWvzFR~Iq7J*_O>OBu+Z-Y_+R2ur8YO{w8+6VAG^iIrrh&W-8mNH9HdxH!MH<)?kOX8L17b?|R*+fUU}-`mcpGXO)Y~A_K;8zOqYU#lSj^%@KG+nHM0YEQgqYI36=YU7 z*nuxiZ2YRSL<{|~+rfrx_aI`~isa=OmJS9~B*=y^h*W?DCR9UAM# z^e{1qDCpujP+mI73_er{a(rY1-T@a z5f(cj2}JCG6u(#sPB1b^u>%(F0|_60As!8i9gu8yD~Rj_A1wzhf}o~BV+W)S6g!|Z zuVJwR7PEM9F$!b~NTRzHL_$o-1e;~MZqp%V@VZP$AV5}q9(*9!4Gx5ZFZnr7WFF9e zeMKE2?*Uz52_AC=FSdLMUhM-Jb_Iut2Q-|TgF!7*NS(pIjU)3UD5#Oc7HY*q?(V7J zb)~OFIWK0OgcSK80Yun?&V&Qa)xHF+6oNYO1-~(pyEDOTaAg8!!4&agDgt>K;u)AC z0c1rGLCDPR%VN+x0c0KytpWvYeg|(n?VMWjAGDMPG@J>K-3lT>YwAnqbc0>-!WU$EE5zN9v(_P%189W2doN^*99SH(hqZev z#6P|(I$f7^mM&YpVD|EzeS4>lcqiZRqbD_%&0#E!pM5)SeYNUFOP zM0UDvf%*qz0@V2{K>h(;=m|R-VMVvai!88CkZ3pfc2m%HfYL49U{}1TjRYBsi=P88yg_2e zUt9?TSqzfu1|Q1Y>3Rn0ACL)9{~Q4M=gZ&!|6%?)&~5Qz7FZ`pw7V5VcDkPFEBHXawUHo==PCraM}b{PoP8!t)4*f3$C8P3|RF93MFv$ z1ZKdhCy-s>>Iuw%RZpPv8NiP22Jgy-R!<;N&@FDD%KsoU^bA#~ASeYvt0z$T!QcJ@ zQtX1nA*UU}Gy8{5-#4A1FPdv#Fv5cU4IZ_8CP0Jz11Q)*(_968S$Zsz}>qcO!6F>qU(2Kla^$ti3sonvZ(g_Z_ZZLTeRPTTU z4KzcH6YSzlFbOV+!7QjhUho=&xyXt@UWV{tiUg1q zK?EW7PB(a6kw@bZPe>La&~$*SUL0nLXS@H3L4N!4gWxu7-;PXsOmZnUV8yzfli(T z&ACC2%!8ed#|*hVv=g#@w0SRRhlK)vQzvKv6r{K0(R|iiK84bw8zO!dEPnh2YYbiER276`I4`z2^EFWg8-Db@<=5PNhpcyzXcrW2uM zHB{8%#m+#mSs*EoZmUU%(1HQf zhU)AEosS7w^wtdC2LlQQ&=eqlD3MMvVkKxTCoCAi;>W<^$6x#l06PgJ z54utk5)7c-NYG$_8VL;skXA@AfR;bQf&n6G@nRFiERYmv`~hK>NB35ck)Yl?XezP` z67JEi4#7;}(cu0O@&g9bzSD zP7@XkVDbH6@#8PP`-6i4BoCU3hBzIxk_Q?LP$Qwi0MZHx2GE=>?Dpt)aIFo4W@u>&lA{KW@9a4>-6K|5t2P6utPfd&KANN6yCv_gUbG|LMM z28gJ|izN`VKvEvvkPE^)!CQ1Zy1`p{V6)~mkW_=@Ta;h```^PFylR8L?ZR)+#4n`W zIrso{;VU>Z9eio;!Fj@?8*+Wu!H4V~-BZEI>)c&x1RpvNwxpgHAX3^}*96GA~xu|4=u+yinQv&X?#nxNdV3tsW_uRrCn z>u06;JkUr1c&|B3D<};@TjU@ntl0-*!@{yT7t-zpi9z<}?t-U({`H4EaKtV*!ZoHI zoEK2MaL5Bab1;IILxcLU_279FNP;=|LJ`e$L|cY`Jwy%MAaDXX_(B$42i)7ZbSR+d zfUK;8-W>E&0#8E^bn-jw{?bnHDf7p{jZ?^I1!z$ww4n!42w7+jxyt}la)5l=30Yd+ z3|{f5z~96TY3PBvInah4Xw|w0azhW)8Sy}C=z#{LJP?7y3|W8)zuOnA1-`-^%tLGF zfyNs_EgX=WATES7=Ak$1Le6c6oPpIj6*SP%yca}*s&>%TlKicpU2)BOK_fPZ0t$5Y z){8%0-~uQClzkwl?SltrUOWMdAAcd^0hb3gLcxWKYr{(y&`<}=JZR|zG7nNZMSx60 z5w&=6*&S>UND6d_AJia^ZqUloy&y-v*yssX0S+vPC!tjtsQc;By%%!OJvf?BeUChv zb|erojqxei=Ybk|OJ@f&RG@fXGr_kkomy1{BdCu6<@ z9R}MBHU_ft%XI?8iw+<|5JDC&KD&Wk4-)t2ZUs}|<26cWcyxoE^x`5UD3QE@>Ob(- z28hSe{I{ahcS&a`cytGJc?OdImN3H3uB=`0!T{`*Rs)dJp#EF&LK-Z1{6#RteIQAX zZm=5AX}nPXfsBEAW(CB58X!XuLKZKCA^Jh$pj#p!@drAec7sRvUJFpe^rJgC1dzOe z>OV*##uk5|V?jGZ!J|DMoqH9K{0ADWfyUo~7Xgp}lmIym>c0aotigiEUlhASYzH~4 zyA`Cyqto>S)PEpjpq@DZ@t*+55QLD$3uB0WkT|Gf1R2xm3OY&f1Ssi&{3i(UACfmv z{0H`!2lV>pHb3YUsGuARX=;JfHN2?>P6d#r7KDk^)B=Yfq^SjAA~m(ZwnLg)5GGPn z3pAnviVTpQ-CIHSz1RzhHB=dpg)bIklL1-xq7$18$g&st2pP<#7P#E%gx*^M8U01M zn+24sKrRC{53IrEDu25Qq#Xdt;AlzyMyKzU&QS1x8GkDzFM|fUplRX83m1q}I6%<| zO$#?(=z|51zeslmrv;EC=mKv@Tt0xN1&}e&h`#|z3k)Db5JDC&)FJvo;vU_tV5-yg z0i+pK`rrizB&mQmKR~w|8AJRH_dYnlA#ErKlZZAHSRo|;BPFbtpk4za>_BZOQ1!## z&Vw`T2#t4XxPaP3e?V7-Lkj8I4=+SOV#i<1b%aTEw}KSl9`E`A(heU0vv`pV)(H~r zZUvE$)jn7nM__MuLhjjx-UI__9DyZ>X&iwSLT*5UHjY3E2-Y|Pu|1#(4AwXTiD7RX zfz3jXHc;aTEOGD^*2WQ74jxmW#u3PZm(a!$SQgSa0yCkeBkF(t^;lC2xN(G{1G#a8 zEYS_ofA9sUafC&pvlV<#VmE|pJOUa$0}ZD^&-a3?F>F0bckPcC z(GiFhC{X>-^;(egyFiB#86JRaH%F{*1Fa8($a7-q-vM3=a{R@F|Ns9Jw14&g|Nk+~ zPk{IVb{@7*uj>KB1CBdEXC=FKo&v49=`8)?(|H_pB1-5EpUz95Q*M1aU01xA9?rth z?fQikG%m*q>cDimo&fP7%ro7tUz!gvbh=*XcKxCYQUkg?4t)MI=zfv&p#2V@y%*iS z8(w@4V_|SLJn7Nty1=FLgh%s{gxJI2^D8_W-+)drfOr$hi8DY>1hvGVP6Vyr@d3H; zJBSFK0C8rAPp50ciw|Ke3?7J9AhYiW4@8Fnd>qvRq*f+K8geP`2ao213?R>f*DxbG zCgAglK)XYJcyxz0fKJTpyaqn3wcEGjg(l1=;Nw|Ao@qP+@&Up>7a-%WkY>$RP<8Lo zITh5F;NQm@m;@Ss)%goLSp`Hg^0&_925;R2EAQ?FEAIv?_kir+_UP;dkCJpw1r2Z< zZ*2i@)(7i>h_@p5Vwqv%AE2%mbTkBX5;1t0KeIunfk*RRkU0Vl{F9G6G(2Z?;NSKg zG>Zyyk_BAUg@4;|7tUi)3GM=x&H@%Fr@KG{bddtYp5`9{{LP>pEKo_0<^vL-sd~`X z>_Cl9$au(BusgfK?guSLykZY>eJ{uvj-8;f6wq!17iLEdmrh3w$bJY=zr&-m71X?W znGQPc4zg>#xk7`1zvVgTa8yu+gxciMd{6>3=?t>8NTc%;s9V?#vlL{uwvz_TLft<^5 zycIOk2I?d=gU8%J1s$ki)wx#$+(d&cFajNbfSBC}4PU-EZVReKKusyg2Al4^VBrlQ z;o~nXtzoj=tspg>;O!C6E;-aRXqOzM4b&y)0oBmRViqqxTY*dgNp!b@NQf!jki#dy z4t&923yu>|xO6(gLIGN1po9<)G=$&*1R56ZY&`;MI6yXxbVE3ho9{bYLFZK*2M<+4 zLI*T<4VrjG3KozOM6iGc(qC+_0XdlkBjxeud3l;(N zU;#}cc7o@@Ji2>99LS`YM<;lSr4u||db|}hEdvP_FspMfs38F#zyalS_;NJR{OpUF z;Pe1$Gr&>{Shxu!eEbDB*h3&$XuASD$qWq^(EKcXCJ#K%3ksGmpr#2ls6a+_Tf8`G z0SXq7M0YEQ>;#+Ay%jW})D3pvi%V8WsRibBY{4Re9xR~Bwi7&S?$O-~;y|X*Jvw_q z1C*Vup!EgE!K(xy!2%k515E?}0qv22WFn9fL}~$Pd(i|=4{tzzfu*SQ3YHfjqmaccUaSL~0+Q%%1(6U_y0?OcT)X#v`1Ak& zi`|wC4EvA@rcOtg|1rZxyHEpkW&r4rm=B$YKt)z(FQg#v1!cnHj-W%N7+`~w-M!%Q zy!qe<(5~wCN07xJ5Hav#5YV6mv<&X{;OOj)0p~|>so(9v)7c9hO8^<&?IF}M|UrX16i}_(b)@{N9=3`t+YK39?^va2WYI3v2*VYq~HK4K?Da#+lzzX z#B~Ma2WW7Bg;#-ukH0Vmdjurg-3lT*!OL}_!2vZ5>UEGdP;guT8HFrn@#3usC^$e8 z-K`)JVoLW`&_dyEumfNG0*5=2x1s445*&~;gBl#5S=~>$AbW_9j8gA^Q~6?ur@0Ik}4u?g&s6Cgi8g99u)8zg-Eg$mdsAldF#5ZMWy z9)$)6)HJBqLE1pUaRg)(vY5q-d&Z#P07-PWf=Gxd-CIEuu-#w>zIY7|cO-A41P3U~ zK(jDBb%5qzJ6k~~+Ic{Z-0OyLAS-ntmm3}juYZOF2k1CK#?HM5kb(m`4+Ma8W0}Vy`Ykaziq>B(E3bh#sZZ(osJyctP4O@xQ+ssk=-ykr1oZ~qXy_?&p)6w4WJoRP?NJ8ycxH%)d$q>I0tEWVr`g%rdvEZ z!I$@Xbb^nuJPuxE4Kf6@VHGr&(+Qn}giy?oxhKfQwI1DI9=Kr+G78!-2hCZ*o8Zuf zIY_!0Tf-bA0dJUtIIxB}$P^FchB-(Q5@3+)iV+QS&@>nH{BTgi+}Z>jWFP||0R_^D zXqba0c)%X)Y+V6LKno!g3Lr(84ReqvxP{#fo}21MG|Zuvf)pS%%=a6E?FNa0wRJ-7 zrR|0|8Qd@jU2ovh4Gw3I&R)>k9+yt=4b~pr5FX?{Ymd%e&^v1_NmWWx6RKqmaccUPOUS0ZDYXf=Gxd-CIFpi``%czDPHOhYO@(4hjWm#sf29 zAp~ljf&}0J1RA=8tg`m#2H$1d4PLnI(b)@{i0Nzv9e{QmeE%^dbU=fkjGcQYAO#CZ z38H8QX?r1V2=aIj$S=@f0ShyLgpa@I)Pu=(w}R9_#&e*-0yPa91R!moVCeuEg)C}&;;1X$m0zlzd(ZpEDYN4-g*2*kuFTO zyA`Aca;yk6SfHjsg8-xr6f89$qmaccUg&^L0ZDYXf=Gxd-CIGU$=zTFzOd9s3Kp2x zu>}jLVTuwg38008t&m%2psQxV9LUYno!}MA$6Gw?1mqWJuz-axfrO90h}HoG3rM!R6+}Yj=Agj>H4PdBAZ?&vDF7LTEN1aS6l@Ae zqPrDDLQLu23Yuf<20QSDk{+UA4k}?F4RetHF~bJZFb6F~>4Z1TK|BvgMng2rp@QI5 zD6odP45VQW5d$~OA?t9!=MsY(=Aes7JRmp5gB#|MwLDOH0mzagxV%VbFKF>m_g<(j zkd@uwMHL>Mt__{NHK0j?Zr6rx2n(`S`?zZdIEujQwR6Dn16dE;1MmLTPI%!DPD~k~ z;DiorPk3Ps54SNu?8+^JL=$s626B4wP!vl0gz3Uv0Zr=snu5&=c zbe*nij=Qda_-zGa=iUS)zpX*|ZNrOVuuU-_$3gwJ;YB=1?D&fpnjpV{q`F%{WT)#6 zsNX;)K)td7?x zWM3Mb4{FkalDmgBc<&#)NeeO!(n5nSSc6`i1U`A%1Ky;atpzUK!KDl2RB8{+i%2as zNR!qA+@ysy*1ExKbs$aJZg9iaqwxr6h#I=zwDAr2)ZtF>vJL)ytn0l&Bh;Xxzj-f+ zWaMw%3))Q&-JR0u+Hu^q1r(Ri`{^Y-AmuHb+wI%Y4QpXSE=mMRHCM1O@V8`w*Wb8y zbbD|de8>nA?+)NN_)4DhB81Iz@DV$hRlsxboh0WcMyR;J!ACq0ae;&HL=fU42OkMR z#6=Fi;{%Cz_JZmS=p6wdiv$`Ef|d$F#KCGgn89jbBH(d+=<0Y-EyLgb5$sB^c(-p0 zXlX;I?-bD0w%xu{K)2j=`Yt){x&Ragpgkn8W%5f9xpc*gkKjZHs+ggR6>C?#xC;_H z{vu5klpaA+kORazT{l2;DaZt9!UgXh0WBeiEt3cB9?=2o1c^cyg1c_Wblm`2Bh3u1 zo*|WVH<%4xCJzbC2-3gde5IV_!3Kg*fM#LBBW*V zAX6Zr*9{Iy@G^OjAj&d%kN~I$2dbh#XD&mM33Qn}ND{nE9>jpSI}_|~@YE8R1@#AL znLN58ke4BvV2T8c!FrGdq4(lCpd{W4h|}OMfHNHAL^ue$+xG$@<$}(Sa=idawrSu5 z;d%j{Y%d@s+Y8+R&}4g|8#&ot=njA;+Y8+VNXhm>cK|fmUg$1>CtKew;5$4$xBtvl^!e})~-AF+iybLbfDY!j7O*IgHGQ&p!+nteeZa5x`HOP zkxE=x3PmYv{lSR|)Y^ol&<`)HL1M>W98m(LP>@tN_#i&qWi6=b3QM7&vbGDX6C~OV zZbu_+s^j*tCc1%2 zQ_yap=Di@2k-v2vWWEnH9gC}f2WsL`HrA{ko z#saq1f*Cw5n`vB&)R{^^l1K}0H3u3s|4Tl3f9ye z=mDFx15ICnPW%Kr=J*Ra6>#MPG5}KLfONVrJ9@ZuI(i&$1ub?0$69AAsC)f#8)WnZ zq^P;VgMq*0C1}AG5@Zo>Zcw9GRE}*j&G|ku9n*t6a&>$WpjKC}p zSVs>MI3P8Mzyaxd@c`tIPH_JV7C2z>6JYV8+l3$=0*%c<0|#m(G;ly# zA?uAm{SR2+KtwHGY>@*84oJ!a(nA4lRRSf`?yVprJ)ph59#DjI!vY5$BHa}dsDUE@ z4;;|i^3Gn+(cz#_2c=91r?V9_h27Z;IyC$^c;*`tIG`zP(85tjPXrP;AT@}<0qJ{j z803&naIXOtIAHPBVDaNG9?OC~1nO0Q)_Xz%2Q&x@4IHSE(7*v{g#-?$kq-+Th^WPj z=@7F(QlOnJ2(vu8w}OoHfc9AOK@kEE9C(OyS16zcjs!e#KwDNi!KeN~H?BaA00bQr z0-D9@1x-g|nFa?pv|-EsL23|z1Ddjau?*ypPH@v47C2z>KCt-l7sq74fdgu~gBFWI zJOtWB1PvUhkaX@vw1s2L0k9EhmJizG}1wnEQ3_BrZ z6)gF{f<@cI16uZWp6omaS;~tQ`&)pCXWK~#a zFKB!Baq#>%BzQn`;GhgIffPI-HHhE=&1t{51aS)JkP29u28-_kiywdSNfPWOkUXf) zhIk1y)eTM4P$QuM0n!Qy9uB17frwhXSOzf*Bn4W)i7?Bfdn?FDkM6x7*S@HOL}(&0aW;a=Psay8MvY8(dh`Pks;v?69H925G!CJ zpkf!4Bw?i}Y!VPu)_Q>2Hr-pn(%rqFWiKAxQ^7p=h)E{M+30de&DTyx4^a8}8#DrhKUqDZavFQ7C9^%bPG4pIOz!KE8KHw`)@`QQKl zE}gBA1>4}!E|+cyuM>Pru?yr7tmELxQ%KFF9_Y53u&!`0|wGs2Qxt_8r)ikoZkhW7U=8+ornc0 zyuc+4nA6z`n*QwU{R7^T4;t%(gb|qK(Ybd4Qs98pAOZ)Z?}d>V*z+?$eu0)SU~wt1 z`0*D(!f<)eq9TZgK(nXNz=0YG4F-@_NZ?EW8Hgfk@q!Iv7Dx)T(-&cuM>qJ8Dvxfk zYhT<01tL6fKxG3sL?EqoFcT6u0^PmPzyZyYcJ_*Z2AH~AL7Z*~2XZkAxYhN4$1tL6f zK*c0DL?EqoFcT6u67awQPjiA#`vJ9@L8BlLPA6nYrx!FxgE7tN(YY5?cEeihAT@}T z0~!!|@dMmkrdE!fd`H>Fb6Vr3fZ)H96UA)NztJ3YEXMC0-T~j z%jL0dTy%z{*Z@!%Ln93|r29e}EPnh&GCw%QgXBSdMsPqA+_>lgG7TDIAk#d$EnY}L z3<61kT0~HTpc@xKj(qV05~t9RgN8b&=L2TKjxq!v5(Z1k;4O>b6QX>3KpO};edio^ zodFJj+8H3johAbvdn@~Z}tUymUCdg4Vq#4-htGogMx z@xlu%c>F~Z=$56mix*B1{UCABFeFsJM|bH3 z&`w*BUlkxRkK`q2`w-+ik8bekFKDS9|Dd%;=UxFMzk@d}az^@kvC{Q>nW$QY={K0y4+05Sw2Wbq;lq8}sX1Qi^MbaCAZ=ktvH&;ZplxAD;K4*dg*?OxmFFHl<;);{cR1+gG) z;cjq?*rV|XXuv!6F!X%q#y8+wSUbTJ`271=^Yy_a-v2;5TtIWijQp)@Ap;-a#v3H?Lh^b2HArO zVIu881)B@mg9>3H?Lmbsgxd?Dz?V0_u!JN&h!9K$WZ?^WY%(D0Ua(@50a^CqHK_9f zJtG5rnH6*g=1%yj2h1K4uw{%;xo!pz=2o!%5RIS?IH;QpD!Ht|;m_Zm3<-2lW3#&# zbZ#m1&N-4BW3Yw>w8;d?%aB%BKg8{z(|lm1+RtCS#jn1jx~0XY%U za)LHAphiL~D3Df2eh1|c*svf()Z#@l#4L~$w2Xqb!XVpN!Po7*umg3GAQ!`f&MyL4 zXp9z6;9!I7WQ8z^*vSf32$`gZmLi~Fec1{!A1(~q$qHH{06%aAqy`@JWHt3*n@pe$ zGU&=jP=oA+2`6a&543U^w#fu6EDI7o{$d#$Ot!lfq^1+RXbie&3u+oRvAT^pcma03}s*h8d11u>`r3zY?x z!l3rAN4IMOqzLwH@PL=U;1(vN{Dm-)%3rV{kn$J8L@Iy5CPKKgWeOOA0!Uy>q7NIipA0kFSI~4dne=o1Ly)F zW4IqXJitX^X$LWvBNB^cS+;0UAuCuXl)N0To-I1EZl4 zRr}$EH%RRGiz|#UsqR*g0^I8zKy5ZqmmPF{ENH#MEU-?HXm=}!1g$M|{exM4`F418 zf|7x6hX=UW3hf}~WFxSS8IaP-cLubma-HD;N;b`*(D1-oLZQR~w?{YFK5X0b;lU1C zEivQeTuAQh@Bo)YP*af0AgEBcD@ci~2Q0FY3mp_C3UDPLEs!Dx)*Eg-0%|$N9!9_4 zL;<=by}?#Vm4$(UzXh~yzPt9z3($`BPS-Dx>mp!##XWjUzkn_nc`-o(e3!xt$h{8y z@{sc|J8S=R*Zw*9T%r+lU(f$;*FOgzaDcD;;5-iEyx>p;UH1XyNPw^U;5-h#=mWAp zz4;B;J)r&P4Ym_izzzc671&+-;e{3`5;|Q!>;PR4fw-}uw-j{8#E%z$#gUx^F%NVb z#L&L)1aV#gC}Loz8K9kaz`u_*RhfkWe4`GeSy%hx#cjw1H=yfqJ6*rLgk0WH`{Tu7 zkhtrYZr3j`(;7gP4(xsqPujGJO=RTbe+dvG(*a*53pN&e!MUQ z6}p|SA3z7t!t4Z}pW0dbr?d1&X6X;mDk0|5Kb@t2x=a5Ye8vk(Mel_<|8 zaqzvO2j@SqF!F(NFqN+0+i1}5FKK=wV0fStahXbY=?9O_^Di1eIUD9-W7i)Zpeu|J zXL0ebcm3h96LC)639x*(>kmlaq#^D%0l6QPe!J~K*ARii4|G}va(pRx96t!U-Wwck zAg^|Uu3f1G`R#*8cj${3&qTn-WGH~{P=Q_r1ExUny~9QWvcw9Oo_bl27$e_LgnZrx zw)6mUAHMtj2zj|NlQI zN3?=cKI|SbP+T)uohKkQ2*eOjc!11@lwmK7Qse!oqxD=1HX@MyLG-B9>~ zzXg&5Twi!}b3oPtm45ht5fr7c9Pr`AdSP(c^aFI64W!6~luw}hyim=1z~5p6&QY!p zP|P#@29IcvO|>sPxeLG1tG0lMP{w49N@#RTGj4<5}G91Q#|klqR?`+C4U_~ZXY&`HJ&pq%sK zfDj78kYj?C5MC^gbykeyTQzzAP;n& z>O9bS%;VrQCeXQQAbT0VLnnVg2jPMQI=6x*dEn-D_ktXK@Fg?jiRJ^$ovl+qYPz?A z<^Vl9w}K`{AWFd=Y61HXbvjWVAfN-+qn( zWF$xd$Tgrct$QjcYWVlFcs2j{&)*u3uAsZ67i4nt0mkmBAbp(|J0T(v)0WC zZUy;omRf$9MrE1Gf#F z@}O28VC)3@?4>klG#?zjAm_pS*?F?_RA86J~jWzqbnP2ar_9!EUg@orgTS!4cfSBL)#+ z2FJcKINf%F)j-6p!2!hICJQzL#3lunAU0I)eY7Lb|Xmt z%VzL39iYi8kTZ_Aroh7*#Dc{GXbVy|SPtwUSeooS*8J{&XDev(qjReZXzaShoPhyS z4t0ZtTMzKJ$fF8@l6EV20;7csBm+93^rapn0|PkQcZ18CgAWC|r-HIpXRi&YP`Y^e zLHAS;OY;!a8OK0J9D}4nzO!_LC+C`9Fm_J`IU00y(Oys)(8=;0Dg}xwkRM+j1BWcg zl&GNo59R?7aaJ>fQ^M=?2SmgJrtGGB50Zg3hBhwE#Qa3{=Z`SpEa8U3m{$O9akO z-L0ToVLcj;fLsuJ_{9Wn@D<1??G;G-w!8FC^D##L^`$=!zGH4a#@zX>^8=`&ZLa;p zz~2IDk$@U@9-Xc)z`GK5FfcJNfZCM4FTiD+N4M*XSm^m(p!Py1=vKSZ7apCVpnkba zr|XU8+8aFlZD50ryZ!*z5VfFVP-}0n@;AeT!8sqgx8g;2?Vaw@8y=mm7hZsNxOjB? zUhuFy$lt8OzyLluth@FD=*m2g?$8I_zIR-@U2nVqw@N@4UMewwZfpisBcN+9(UpNm zO3)e{@c05}GjL0`6U75AoxWEyl2_CEczInh&sax-RH;-2h6au4}qo zH*~tL04Y4c*y*~ZxpoaBf8TE=28QO^84Ua_lR){-bxx=2ly28Kpu#5fL0Yqo7lQ-; z)ME|}-~Ky%JI2W03cmH650sZ(=YWl!^YSdb6n34{?K`FUpoHZK{-#O>28JD=ItC)t z={p6*BvNF)(zyE&*S^exQS^+jYso7aUNY7o$ga zC}=AO6DYjEQUaZ>OJ0_tJBJzM9CeU$m_hCaxsVyvIpDBj>U3T3@&u^nbe*H^itJjr zbaU+j0sg)eaN;7)17@Uq0A$+>0gz)L@j9pTVDmx7PTx7;4GP_*5BT@9gmt=3=yqLk z@Bw4<3r20%HJz?K&9xI4_x53%j+bm8?}C}#t{vUJYcvmb`mX7AodAxv z3Ei#}tX)^|_i%z$^)Q1Jf&`JGsoQl6v+DwI8k%73+QUERV8cU3{%r@lLll%=Cy`biFx9gVX1OLH$bXFYv!Px1#;^kdX zcZKBOy$*IeN$CR-yubha|4(c}%mXDK(7;Q#?~;yF9^It}I>fqtmmGY_0p*1;cKa@9 zKFHqb+R}MqX18z4_n#nD9?3sE7(w0Y1KqB3T)JKFfEx&w2N2~IIF$GyT@FxAIN<@h zLIiT>FM5B!`AvY~0ng(HKzfkN5syya8=yU~pv}GD!i?GXhDWFG36E~y7cSkLE|8gh zP;vsDKIqczd&IH%Afs#NNte!39tR&Wd32udJm%AR@Wl~UP;a^R2B>Sd0d^Xg>x<(c zt>CNOL5!DYK#d0w3$%mG!`k%(f3q6I0~b6x4|+5oWCS^cf15z(1(1E+B8|<57&}E8 zU!Fl^%_HCv=0meh7w!V)2sn`(d3g?;DUX1B)5&<;bq~nuZr455u1EOWL9L!{*F6x^ zx>>rcT@UcLfr1lR5VSc9stKI-_q^N-HWJi819crCy~6;E@e{QA1{BObovs%=8f$Od zVP#<8ZY*0@U3{9lr_o zXnX@I>J1M#?gG{EuAP@Xjvt0b{tJ)J(gz-$p`eJq;L_=O#;4O4lzu>b(0P(OJeYki zfOfKj5{64RqYGq5J17`Dx_u9Lbowsv==NRU(Rh#nGz-(|JHw;fcLstx!K2%E0)pEC zRt!lew>&y;zL?1jPAV5bx5t3yEBISMBN&K&dbjI~?$9-$h`$6{DUk(I-tGI~IH=|X zC8ZBwmq7Nsy%b{s)k13+_*>aQ{SMbPpuUMmckPZBTVxm*I$c{pY>(#SEFPV;J3P8U z`z3uhcr+jU2s)vxnH5~fwse9fe`wpk%D4wjh?mJxvH0m)22m63py#kCzs!f^W;VfKga@BnXo4c!5^A7s`7 zBu%uizfl?*WCLVB<3QN|@)>C5Hz;K^ANT{B+XAN&Yu63%vS|mRY}x@Un--v!P0h7u z82DRef|6h>=(r0|D(nWctXmIC!f#DDObFPlFuR-Uq4%!4hDH zEdVI(ig(9Q1<8^Kn*=w|5xRoe)M zfbtF42``gDJ^@R(u7M~9`4nd}at+9ru4|yB{2EYk)ydfHx&qt>fDD>99sw1D(HP}( z;~NJ71_o%q$D^CIT?5qD0##n15jJq|rW4$|@#vfi?(%f@f_p6}Js*#5h+H>V?gjHN zP~V5w25b~kx9tRS-v`9;XgmV4J@)X6DgT(kWgmQ7mb#n#KhkWI%&}n(h)%F zWx%TiR*>zWx&zc+_2>pwy&!#C(*N>651({(%ELz(G`Qc*D+4x) zxbP|Y3kjdj+Ap1@ADU}Fpm*0mo&|?Vx9^AMmyDMmfQGF?zce3WJot!_@dA9{5j<%D zo{E5t9}7VGn9a36`1#u)b;l1-?GH8W2g0;JSWJVAlnef4W`GQWW0~IoPb`4ucW(ZH zsX`uaH|VVW1DdYu41Lq-`vNrP?9u#=vGZf+`4?6n{{Po@eF3`G(e+O!Xgs_2LwD($ z7e(L+s5{NI58&0^1JL9`x9byY*9ZK~kXHMXPFK*8CLj^eq(4Kq>zz*52hFt)SoxbF z{(J!HiZ$0h;DLGk0cfPZ+ZA-dy8-n0Zq)V!=)QN5e>x$vm>$jV89V=Vet$9H{r~@< z?CbggH1pX6<}r1;zUg-TVD0*bzZv4X51^|pA&z?k_SBP3*B9Nc&^gdhkIttc^LH|V zyax}I?%F?~K)9c9y;Y_2@w@ESFB#E6ufU;DPf7 zWPNk(8}!cnC2-I64SHJI4Q>p)@#wC-(Or6_)3xUXBdD!c+GBYPZ8Tsa6LjPYI=<}z z8uJ8|TMj$z7#J9|ecyDR>^$$$S$hJsj-vAe=t%LUi?z#qKV%l-nRiMq&t~Xq|UEjEXhPYoib{=r) zbX{`XbpuF6x9bLL*DL((<=|fU25`Cg0942x^sx55!QTq%*nv7#Ahj>ABP#+Gu8_NN zK^=Thq5UE4IExnp!%JCYW#EE*34c3yq}3H-0a)Sx|M2eE%V(g91S$!&oqs*3SqU1H z0ADW*+OZEk)Z_B2w9ad3ou!NT_q#4?{=vcDn*(YN_p&|!^&&uhdj9>Pi@HrFbbj#Q zUw^`*(|1O9>4we^pc1~@q!BFYdccE!zwaXOXb0r5x9= zNB;e_ix|JBId=X?bLn&iTg<=Tca`M{{yxx{L|V7&qO?xeRo%WDI(^rGcpw(2yY?Zi zlO+sfQ+Mqa&GVhLTfk#Wu%VF+FKhq({}1XnZ|U}30~+8i{lLGUC+y%)(Bwdm1ISz5 z;M@xu5&{+0F5RwIUSxp8J3)()YA3vi4uQq5nx+SgiApib@ z-Jxqh!=$wz`1gyHGagFoyqMO>Q{L_R23+4krpOwhr@Uj9N1*iqAJ8&O+Hu!Q|Nb9$ zJpoD^Ajfr=zUlnrff<~yI9`MKxy}{gF`lk69W9PTdj~;$-kInnJ3F#wvnxo}GyY9ha6eB~oD?y_`H2};aumk@6?{+-{cEFjJQvd(|hgJ`7E3~1eO2LWDN5gXyzT=Z{4mZx_z%e=DAwC zKy6#s9iWtQ+;t0t0(IvZVDS!~kV1+hYu7FOZJ<$2P;kr2P7LnefN*QwFy+1bh}RI_B{hiS)~uUd8T!q^59<|I>UqU z0(2=Ks8hbeky`UaRGniyVtMM#BHiJ79X`Kf_ zA$SN>Ij42T1ZV0dZ4-X4Z7=hgIw?W20XtT zdI3D)>-qq}`-eO-^a3)N-|hN>*>w-tvwJ|3lBE|wp6zzs11<~pyu5%^7Jz2HK&`kf zouE0d&@G@5&K<~RKHzUR06Sqvx9=Ws93AKcO)a_Z0Zo9xg;=g3%s+w_exSAwV*P$(c$%fN_60PU9(Z(v*&H6A;Ondf1>1wPjlT-EzI!}Oy7-8#Q zn%_8pqSFI9JppQsfycW*ODsSoHE7c0;5&8?#!nuW7x^b2P+@f8-*&>K)AtOd=M3)L zfLl}Gc<(L+Ez@`as**qsd0_!c;-Fd)GB9=$t0M6B7(}ne0Tg5~_rcDOLrej|oCtCQ zs9oU!83a4v0T}^>41{%p#)@ihF!8sew4&xRF)(=a)`C{$Le>gJKvvy@mbiO#?*&mW zKqpIqR?$Febyx|K4AR-{dc&jn0iy?4x*z1V-o4=J0K8Mlbpxnb*nIFmXb6sfTfqMp zRUp%Q_kxaig=}WRR1ywWQrqFt3#nMTF_m250*#b`hABV|9+2~21adPlK$fHQ)^@zu z4C1*#oP*D<*PsKH@w@m0ND0`RFPJJLhQkGtU6);uweS9 zkqg;Bk3l?8@&y-79-trrkMV8-dl9eoD?siCMKJE*Y(*%+<%uMO5=iu7`qQ2hBU1Ko zB6~s((9XJ<6M7u9DKp< z!Fb%G@i=Jy)C0Vz6#f1v(0K9*&|>XwR|%i)Pyxr?put#=ZdrfO+R<)lkIvW+pp{AB zafPinz|_c?AO{DiJ?VgU`xH}3@nJ0pKP zk}Sxmm(Q3P7@GHjoXNo73R=r?9PDP$P!YtjaAAwi4DA@SM` zj#toX`=9^+dv+dpVfq%dR-m;297cOV)QdwPyM8e6gBO?(Y0SGfU}HeOd1(b&*53^= z(z+GoA?WG{h;Knj@TC|?O*7a&Mig1FZ-0P`TZjoz7ax4d4j%eP516x{-ak0HJi51n z?CPEh^7e~Z(1M3v@Y*L(5d8fAzZ0C`y0?M?$OB?3H0EC{|ou+VACQXt^-jo<}flq7GQ(gm>w|G2^nfQ?uk#1-a0Sh10 z^y<^y3UVMQoLs+y4G4g^1Vp_6nFEe0M^IFeYT%b|U<1J}0xjI|XxswVCV`D_ z1<9g^A0#xpd%;DaCg|2NP*U`0e#r<*v`y%V7P4j?T~jBxFaRwi0(*{kl^|&F7Bu4e z!Nc;RM`x=As1$029O4S*cr+dX74OlphhH$hU}gYKD58!BgX#->>y;bd6j*>7k*s!L zyI=$94~~PvWa|wuHT43R>OFBBQXzn;-UG)$dvHM{4`RLMacEgq2%0=+0o{Ic(4%+i z2ax{my{9x-FgIMX7^N(X&#+ZcN_=RHHScl-+);s9Xk)Wc3$-9 zo(qwYQSmtLqQU_(|AiPRs5)C;fUSU1yoCa=fcpUnxE05dmN9`i9*svp9*;fz;^Z@C z2ITQw1;-trLe+66xC{k_A8e6%r!T054qs>vYJ9%f4j&m8)|qxlfT=FTt$kLKD3 z9EhgvXVB~m$S_z>8`N)u+7FqZ04@Ii&>0FEA_W`r;sdDA41ELct^e@ot{3p=Hu(;o zUIuSOFXQm&EWObk`lRs?LjwcD!B+yEp;x*?uN-_NpdI?8^Ac2Fr#W~-LAP0V=#|ck z9+&@lB-cIwEq>^(y}`d-q#U8F`3TPOX#>#sG;~0{8|tJFXioaz(G77DIAlQfJ?Xp- zvgqJHfzD5zp;y2?DbQX7kld9QA3^0WoQu0Dz;lb_!#i2XL!57S( z2Re^~dr06`SG|M>_y$VD7x%zc_<~lA{qSJ+lkn*D1Iu+gax_0+2k*i6{orx%Au}Y1 z>^v^N_vo~PPC6i`H_+O8@co?KcVV_60rv%svYd-~OJ_N08=yp&5yR_3$0+brM zJvf>lvUh@(v%7xqIQW3sqdWA6N4F8E$8!0dN2d|gVd(8gczA&F05}9ddymXO%lEni zBwloa3-H<>FF>nrp+=yVKh19hzzwF(W4l1rJ~Sjhyx<2lc0hZazJW3jII7Adz`aB3 zas__SffAq<1K=(z=#mHUjyUk{`fgB=dkTP-%z&njDmfVVrypu~#K^zxlSlG74@UH@ zB^dJ|p#Cm6KY-H`sNW0fw1QSvbl1LkF`a>d0oWNpMldCIJrOY z04=u%1x05$2iS<_V=T?JcNjc6OE2)ZfX2f ze8S`4Lne>z&EwZEYf`{TUaB>LX0A-42Ahrj$fk&r7H>hxM{lOf-(HX!2>VQM% zYe4%YKz4SPes}>|?E$W4A-mo|TW(xGbRO(H_ree~<_+>CIGjM+FzP{7C3trP%KkgU z10b^z;~}8=BamY|OTWATO`Cv?htzMiKe|i5yjTu088m?bG8wdktF!h+=kXVy2^7$1 zN$0T_tq_|Ou!jfI{<;^SfmpBwpqXuud7$Wf0UFPK2^yY&h`4_60FB6W9tXPzWCy6x z0cjV2X0lMz6aO~W!ne!}AWuQ&gF9<~be4YUhD^70nxoHOLzebZuwM`w7a*5|-3zh; zY5g!LEWzRjpIdl<$9x$W82)=G9tX$t!3P>1J3;A>f#E-chvEqjPMEj^RQx{!syGKk z98@79tA7Cz2MMF7e<9%k-uwtR_=ScCcmpItz`_H(2@)g#>9Jy_Z$!ccd8IoXmR4(j zcyOD0beenDlb1UcsMvu;^4xlosvlnz1Am{)iFs~JIE+mA4Tw@42rw1g*+zQdx3Oe8Ncq{09 zOVB*+@m7g{|Nn!gQ$3pZg3Lnn@<4|ug5u?+8>m;*yA`CxqjxW4Uo^z}Zm{()oUZw)l_*bDL#^sLM7sUUa1c=GW7e~;d&V8{3F1sxpO3HEvSR)|`#M_jtW;qu}+ zNHOF%RFCex9RL3Rf6)T#^*}pbNCsgwVhO|ukh@;8fn3}>6=W&s=payRbc3V8qq`S$ zdZ-7e;{|f1OE=g}pu9chBB-qkK84kzdoSn+))$O0CqO%1B$?*|F%RsXGSF5FkRg!8 z`UgM`>IGkk=+V0uaHBlfVwU%K%29W9} zP+-poC=Ee&fYzcGbzExDRs9(RDkbyY1|8v z0u`pv2Hf#h(3w2%{{R2~@-Pzv!!g&_498o+ryYSNBfu=kSx=BDzGhJM+uaH>xcLC1 zHdr3CFAJoj^H7J6=xfNmF`yX`u;j}f;EhNic~Ia&WUX64uHbLBhuZ;~2?o2Jf167e zQ|Cd4t`c6TQJn`JI=1kF&M<;#Mi{*wH2={HzOxW?@Fge;K-Y70gAd^Z^@qE+g6!!8 z?IZ-Bhy!X~p95Wj49fc%;Kfj_pksMJ2RDL*Ny0To96Z6`dsr-Jwp*ByKZnkfJ7Vfl-H5?K0!;>YHn4E!yc zAUAY_-3Qu}0FJ&64%i7nAdhx}%Z;FO58=5H#PMi60?JddhhOMjXJ$ZcZ#jm6Mzjr> zKwF>~L0g~>`1FD^zEAH|(8awzy>mga=+QeD6c9e$rs)izeNkpUpmu)eUQo5(xzz{c z5|mo~cq>RVD8L|B4TD&q>-#_oyQhLGM~}|?KAp!rI;VmPV4u!g9-Y0Q^3SL9q;Ka1 zpKb`J8!9WJ^5VWZxTu5_tl;wX#hTyH`j@k=yH)8w+Bmv$Hbrn=MgWD$Ey&&&G3JTb^T97jE z;?s5DeUYGM3dqODTS3<_gKGY6Fv}Y3DE{_XaIyiZ>I7HX5LxgZNKjyRPX%cMZCt&0 z`2jPywuN3^4N(o2?!4C7vK6cvY&0mjgBdR;f{P+h%K+pANUdxQb~%5uAJ}4$93s6! z?(psgI}Kdgfo|CDI0VrMDzUbLBDo`^j|n0Kwh3g!%LV^I3x0Njmf?4v@_^UE;O0dq z_^5Yq1cOeC?`#E~Aphdq6=nvH?y2A^qVsz9R*)d*KGVPd|G&6(g_!|zlQ_iPphgnR z-Jm4~-QcDb$U##<4uTe;Y290VK^8)mFF@E3m0)**40*W$oRdHi3lclfc`)sGi!Z1J z!oZMryp``Cc!w3(|B!Z5Gq~Brz~2TMo=ocotAJd7-gzpmvn3Ry6~^b^-V+M4pMU#S zkU@;+(mFq+b%M3VR*=yzXMrk{Zdi2>O16kczkrPKf$k#UZv{^#cZ2UD0Bu!dVql1NjEg<|;?X6@@F;3|;?ej9v@5}*o7E0% z9JKzfUEtAKI-@gmN~iAxpH9~fpH5%UDN`Mwxg>~_K+C1Uhb(o1&r3}^-WvD+|9=KX zkH*>t(8dk;GK>}A;$D~yG+j6WzQS<=XmGKyb_%4@2lt$hyF!${U}a!1{083Pi8%75 zdoRes7vh&d334xZsa$vIjPB4W9-XbANyZoNSs56*L#K3t*C|0R6aq&BNNKapDF*0r zLGVpO;P?bhY`(kziyY){1{Ew2&w;k@{f8X13%LsRC3p)Q zSO-Xsu@ig+Dnzaud_QbwD|lNfN*HvrobpHptMq_`lQqjJoYq)?T8lWW0m*f@f=I~m z%+UMYVBrFeM9{%`@Yo^{fnc>T-Qbu)GZ8F?8GA1m{R3Uw4N6n+#Cgo+6gvatsyc9j z14T*WUJwbYA^Dp?rw&7646cQLyUQsS1aF(mDfZ?Akh2#+`rtAS9VMsOA$q|{9wZ5_ z1&)JnbAuFNAVF(zw%~84mR*dH1o?6vIAwveYd3g#M|bFi7owp2eQ6))43*CB9=*OB ze0p6kfR>bY?gfpa`g9)eyynvlKBc>Js?LAdObF<7@fWG*K=~hh+p0i+597y=U!;Bvknwja2*n<25r-v(-Crgej*AvgGe>LHiV4KNP>cAwA-{M)yJ z^uuZ(5SxE{FNlKIKp+mx>i-ZsUQP!$7tp-pI)%R-bUrggG2AZ>UGVsai-G*d2I7S- zaOf!MVuENvcx@Ff^FjCSKomoC!z=U>-wp7Jt;Tl)vKmO0268OIf|a21!*v39>F)~2 z$$h+szJj6^RFy${sG!s4;ca3N$D{EGsGf>F{6gvsGXv6jNsVtD7#SE059|cx7RSyL zKE1u5lRSO8MRUJ0Bb_}1Igt^33T)?8(EYbQkd1wZUI?B6HDo~jB1i%Ov8=(jp!2u; zg9}lR2*}gmPF*)k7sxZmU%Zt99cu@=$Dq3vM1tCbXF+y@q+7tOZg5`+#0N88&IboB z$T-l#&gQ)!76X49q`3s*z|H8M3gUv=l$Sa$GDA%3gitU;|3iCPTj7R++z&BSfxp=Z z$?4rtonS*j=ct_h0xpQac7Tqn05M*IPL+c6yg>#*tbqlD6=+>O*b~SB(FyLwL;CU{ zUmkyPLz003#A-bQwg5ze{RFbF^LX=d#?C!pyF0-bv>F}&b<;tE1l?1?O0QYBd<6wz zNAK}}p!Fu8)pZc#A^m62p&2JZ>kzks;?tVtngjpz;|?8rL8`l2LF5Y9hJ8pk9%MGy z#USp)KqEPoN~u0G{WtJi!lIgv9{bPQ?g51;h0uGXrG&w($+< zYy*#O)^$(~ovuGTI%_v{magdxT>)B24_Xzufqz@barVy8CEcM*tV37uw}YBf-JnAd z_Po5#4Bg`aJ0`(*iMHASfA0X5$eo8gdVLRcyDrgoUC|9{ ziS~jUXa_I<@2p*L+;z%_4_yoyT7Uo?vDG?VWS&0Xf8V0{B#b9`ImEGH9cM zFDS4MvRjAt@Hc_ik1K-4Fp#A>LwmYICv=yt&@S!i{MPxQGo*ftvl` z<$@r4K?eP9F)zPYdd&4dei^^|IgnJn!f4=IkmB-_W;-hQx0`Z1<7@_f=GBC zg2Yk>C^5TsyaW|A;90zG-wyE99_&m6kd-eOL0i@uYqmlRYz4Q(Kz4wFqdT+%T9kFV z^d10P4HAZU3|vb&G#q1uG*>`+nrnIwFz~m6rm4V+kGnwJ`O*bBkU_}|(>QRV2FGQG zo!i+4xR$B#U4ou6a>4yCpPujK;oBPH_u zob?|;dq_=xyag3=#GRiL0CFArejm_yGiY)dG>3oi1qWz-XY+9mkH+JTpj-={G2@p< z8lQ%im&nUZI(=_ImvBFTo(S;)X^k}KgLD`pSKfdL+!Qxm|scOEDpdRg1R8o@=Q zN3uwV2jijtk33-Rab4lj?R$cMTL~k0g8juukYY?p&~Y*sz~}Q~=rn=If%1EE?Ggt5 z7VxAr#E2Jv-?kU*WN27{PO*N$2yq|OmLN3e zHNRj&kvGDSXGW0+t$%|$1>B-+e!+qw|A_%K`he~W8FXJ%g9Jdu1KbeMes55>5o-7g zaHMxaPAY+CGoJtd|AQ{tTK@0<|1MCo1vz~pC(Q294<7Vi{{-4U2bz=jfSwcDUHS#I zjl%T>|8|oJ-M(KsK?g9s;NKR~#Pq@vbfHNzXqum29x|T~TmL|CJ&+wpD|m5ADP7hB zodaD`g2g`CuLlC<2?q4_JpaMBf7E_>VFvE=f>sI9Wj#;h8_-+`cv%s6h(8om=E2Ip z8y?LEnLQ4^WP+B1AUV*1Dad<8K7e*>cz{YSW?xX*2r4-?fR9RPu6-ehR_I*-O)rBF zAA_GV2FfpxiE_}^ujT`69-XcWJV5O>@GkQEO zBQK^thL($<^QQ@Dj|MUV4 zplk$QLWQ~>r}>RSS|?~p)Q_}I*KaQ>c%W;(L2Xme>Ky*iXsR{`r(5bvH z(z<=WflkSCebFHRQgQGfheO9F(BT#x0gRC2fFbw$K<4v6c6*@gP%*si*m=+JHe@Ll z>Y+z2oxUf0IzvI}cL#WRR(I(G5Aaqs-wQ6?u4i1jL8oB39`NZ7-Eo{n1=Oqa=yXxx z@aYUu5df_l2Ccnj05L#|z8H?XsDOIe3@)9qTYNf8S9HcMc>(rZ;}MXvz>^1v@fOE$ zh>Kr<8{!r|-Ju3P-LV=T-LVQj-K7#P-L;_QoHsyg0l`Q6b^0D~>2%%W(&@XyrLz`n zfk$WT0-w&<89tq%6FOacKwITJz|#x94UqGjAe-*HeH&o=xsQXJwcsm)pR?d*D+Udq8s-&9yt=0y{vRac~m?+HwOGgsfmUcY+#b zt_wVLp*&Dwv;uUWEx6KfUEzU4S#xa<1AmJTD20IwlNQiw6Aw@y)q;t?)dVEoUEAQ% zy%jVV?*Tom08)@0Z~@(Q*6ljOqmxDDg$DR~AJ+#jKY$lnmF@xE9R{`>bPl60=zxb6 zpiMCfyFf)rD`?Xe=+f^0|Np;udl#BDLCM9V7pK;zJ3(4qAH3`ZSo3K-YWzOD)8I}1D=p%fSrR)R7l2ZqvlkQI7f%`GV&T*wx_naQ9vi_2TAjsH;K8*+4rwmRS&7{T#^5h4k>U<#^cE67D2 z%v-^Nppy(hOOZiW4}y|+^8p?YP#SC^A+P)a?Jfqz@Db1&Tu^C&VH!%}1PvmnyIiPLY z9-uwcpk?0Oz9(F|U5~hQ`+_ScpYGTV9^J7ke7Z{)bi2-h)>WWtio>TfMn%A*Ge$+i zr?W&wq0>c0!=1A-6v^v?YN8q4UGjk^m<;?mF! zpEwW7a_MY6^B%O#rx$$S6`o_WT)H7DyTK}bI-#3#K$G7-ol_6I|NsAm6sT#_&HMNc z$ZF8^26P+K4)DQR;2w2%D~RLKcm(8X&^9Kk&B*f`klGeh5yRRCu7oZU!bNIa=QRyzZHS><7)VyW{C&n8@!3f2I6{@#Dl(n0C)P4yA6s&`lg>>-~;}6 z7vBO|O=SA{wE;Q(cr?EuX+Kxz3D6PlFFt}6eu2jU(8lKop3e;$zaw$|I?8w+Y`kG~ zzXj-kHU;$k6a3p)S@(?Yw}9>2pm@IpQr|AE+w~i$N0-*g4%*HF-~SWf(G9)-1#N%@ zd;mwM>jB6?Akfi^7vM@3G^`BT1RDxEqxb@-!wVYr@CL8;cHQt2(pm!D=mI(;&-Vpn zhy+Eg{5x=y2{e$g0d&S1=rq_DRh-b~7P#F3ZkoCNcnMl=-~s7?fW=`)c7P^Jz{Aqa zt}j4m&4aq1pu>s5w)tKFtwjd!%|+1!nnplY*a__*Hy;6=6^v2dgZJA&^80aDP{V@Z zxa$*8Nen*P?+t%H*b&LDPe2wo+cYxpPlaz*0}KA(-&WJe)cl`;e+oq6m*PL}(ifej zFS=cyfM!WtpMVzxHrKvj;GcTHK!0v=Rpr`-ya?aU-Ec#`aS^P1_Zu)2*d$73T$d8=sF)z zu!2=`fVS*0gAV+MohRtg%WAlTnZcu*!=sZ&#iQGU!=uwjg~OxUK>*Z|7kB}h>w^v= zpx0mE^Eg5G`+%C&Qx7(L{0~09{37IHAB4b1!%xk>K#ToeK&HrGJ7-^lni@Mm!+fA~ zgF(kT3VL*ksCaY-a(HxxfE*Dh0AdKd(3=iUpdUP%Yd;`5)sRUI&_+4X7D>qTil9g5 z1PxHbA8g130|-;VqjQ1>15n>4859?cKfA;-6%By@MsvJv?9u#_!I6K;0mp_9{~a41 zFgo&Y`v6+3*!&Ze0xs~kfKKc4=(Rm?oSDI+*YxahW`N%>IlvFRjTp0hfSs>|C6$0~3Fyoi@a%T|;K2$>N4PSE)Kt(Ur0WNd;|(A!&>Z){qkG2!CI*HV-H>?u;BmYW#D_?O zw$tuB0rE|!>x&m0CE)l6DFKNgl+=FkXgm(K9o+dj4^9=`I5G$5i^hg3TV%qyFI0Y!Av5FQ5~iAbY{(t}Cw6 z98}daf^Gm%K(!t+z6;+!4mwZwHYjSqdx8=B-$C^WXuiAgxCf}b0!?tEw1+!uL1$lf zhQ8^ z)ILFMDc1(ih`wMz?EVS;(AhNu6u!_{ehE8J;SV@uz(){+vN!5@fk*S30#N$xEPVr+ zUi{F!e*)-;2SjH60?N#jI6$|Wyy-mH{4%A}^+~7i1OAq9*mNt{AE}+LclcY@p@^k* zy58V#0c}k~_&2@N^$LH>B-n&ASZ_wB>jnN67G~%qG*~RN)AbC0OBsq-R;TL;{+1Ug zV%eRpNBCP}SrB@4K_yS8>jC~2&CcX9Te0rI$h_y&;l`i=U5)(?*ql=3n_NcG5IgRX#{mX>JuC25ER!B z9?d&eV2QrVY+y4%jkr$75Ll`9au0Y_{|of$e<^;$K!-aw*S_F_ZY?{}T>C;Anhq~; zUIcG5gVogEL1`xpY+UV!7uhgZ)PCr6kAS0~?-# zb{c@1a3EJCz+JI{6|}|xBP?cuwc&I{6+{=ZD`fD6g%{Yk+7B<{kzEl3cLnIiXe`ky z4AzFz6<@$p8ZQ`;!a@q4D~^CBO+aCB6Lc*hEG$ysu2=#w4?SJ-4q_gaJm9iIyBe5V1c=!vG&DtW(HV6i8X<41y5hp zet2<;8JbN%Nh1gDkg1>z>Y)A$YM!eDYs2Y~c!)0KNa4U29Qt76YCpViL3Tv}+!g!~ z^I$#!t@`-T=~V)ke-GME2XZA!?NI@jKM#|i0^Sh{)n5abUk|E0K=mc|A_P<`G}pdh zf={yj0B7z>(Ci5)1^IsHY(%Ml;_;gYYJh@MIV`{8&y$OO>vI_MD8PS*)9YO+8wpoUo^xU~b))C_L;ASG#VQ`rPjYrX)N(TyOp z;4vc#wX%5!NSG1PPXasWJHovnYZ&+uy~K+i$%i0aCuk4h&2DfHVfAiihFvWnQ+s(o z>}F-T-R&L)``Lwt*TSpy@V7s9}v2;NakI zg^ZbjdP5!&kmZ~n36Nz?9vPs!x4HugIzw-~1P#S_^wu6Q1f8+m?fap-Fa#9VFV2Ik z>;+xC2O4$*t(frr&^-}cT!2({f{GrWZr2ZoTmru+BMe& z5a)nac!3VZZLGZk8tvkr2Dzi)BItH7@B#{`z(>YUU?B$v2AG;t=C{x7d*g+(6@lssWjJu*2Fl2vIl4xGRPuOE&C z-XMFC3j$E>1RAAw8@Vnk~5aCqZ+1vxPK2>}C>9=nyh=mj%d;;3gfYH3AB=<_Z=zgvJUM z8C0E!qz=~g59}7#56u-E$htVt>_gK9>Z(KR;$cLxi$@BtU6bH;2{0q+5@5%x3p~%# z4RWCf3tU%Yg$TG&fD$NZj)YZr%@qr|=fll88FU-J$PCq~c!0@ujm4Dko&*TFrO*@-S;HKS$ zjm!+YSU|1lUS8Kt%nUCkO@g+nK+f}E26b0{Fz~lR4uXM{6yW=NxgZl2UK}3Xpt8bC zpu0e#vq<8lJZPN#z)n!*LPSC+&%?B7Q4?==)2Z-P4y5}XhX6SZ>D?SRUIzXFkK-*E<89_X%M=`N6XF%>-S za|2Y!bo(B#X5?=JE!==|wHdnuSu_uI2C{&(f^teTbeYeA;|wqss6fUSM&MEt+ziEN z?-81Bhu;Op2ELnsf7=J}>3e^)OTTn}z_PvvDLsLXu8vP#|zK}CSZ4iE6Qu zl3orr=mF+nSq?P49Mb$qdO6VS*nyd{LI7h6X@m`h!b~!1-Azy=z8`Cpv8I36%ve)Jq3mbAUiN$ioiCiad<2{1#-E0A%ZGfZBBP7B`wDndZU zSwoWoSfH~Nw72#pcw<$!>x6C&$hO;VurO@rC&-MAjJv?$ajnz!4Cqdq;~l-lpi%nH zpODZv;Bnk_#~w}4kZ$P>59ZPf9?Yc&JPtl%^5`tx;Bnk_#UEED29NI23C(p9>|J2J z9lb@M-DQw%Ufr$}z;)G>gAn1aR*)npV#!5O{{C^8%z50-fIu@aT1Y05S`77AUB%avXFQ7HDGgg%+sC*b7<{ zfus2es#8i-BwlO;l@rh<38?*J(0l}_EsimH4yu%VR0O&MBtX44P!;(i`|tn%yTA$U zA~Zaq>E=SWK<9OE=Moa4pu)@YJ9^4IfR-{ZKvU)p570Rxr8{78(d~KwmPkP5Rk!N} zPypG&Hb%3oAfown%&7BS!i3z>Y&6+)(nE~wI&<`HXyFf!E46p{+!A{pJpk0^9 z0-zg}z^8Znbi2NA>Gr(>-u2KKdH_`UyMo3e4|MyUcmZDa3!Q5P1sJM-8s9)BPeX5j z=TAX*NrSp44?02PY_%6WI!jOZbh;jJ>Ga(Lx|gZB%7hWongb88xUeuV>;MfjAe~NB zdcmXd7--zU1C&%?6UC6balngrK|9!A_;!MtSiTb=m!UfD1TCZoT}R$sdcvde7%0p< zK$kCqcRRRrw#I-~-RFYzfUcbEbY0`p?fL<9!=gtw=xU_W1Kl7ug05zH;$!GjsX1>IfJITf;HA@qVr_aqKbyAynu(6%4oF_hW{kAu&dKnI;$e&U}3 zU(RySBl&_y^DhSY%7w@kppZ2cUe3(03p@_q%Nx6bnc>CNc0}X#f=71)cw26_?*)(U zPKaHh9UdT8FkbYqJmJ7U<(NamxBm_e&lnx}xBc@-K915t{<0iw$+~6C47&_KgYCV% zW*|!n;FiD^aJz!+g7_fxgh%%z2oth719T|kR?v}E9-!0Vr-E27OxnRCy(e6{9YLp- zUvTLJ(;&VI=yH3PPDc=Z!UMeP`vSPC1#R;21l7D3JUT&*ZZIEofyAd??3K4=aPc-iY2{M2#V*pneplua9Kr2pA zvqSeb$buA5vu{6`2|3=!1DqYe2lm*tF*7*s04-&4LDvge>H^jaW_Iocuj&Wug|Hx7 z5`MRWi~y~b!7u`{mIiDDn2ElW-lG$|ivGopR#0)&$^oiByIoIocL;zJq3eZC80$py z4v-iFKX}&-#$x>o9^Km{zyDi#9Ged?xPTf^t`}Z_nx)`|J4hR(#RM8aF#wg|i1BaGhA!~_VbB)f z(l2S9ANaR_cribafuXzf%fWX74jmss%O*SgnIP3X>i8t6e(J3Kam@9*@G;kKDj<~} z-S*(c>!4*1-SwdD$Oo$Imh$x)9*_&*K({l1_6z;+=w|THJnnJuKXd2LPTw=o z%RjqaPguL2;cvbT-mq{2+(di%+T$7r1KR2HXeaaUr;v^bjS(#_M*}U9Xw*)t`PQ%ZkuZ_!oY^W zgl=^3h&9(9sE1i{;Ds4jo`2iH<^w97t~*{rE!hvY`IL5-H3~I?XSAh0{K^m3_=?lkLm`pstO(k&qtMLtZ zc&V2)E)aAT9B+C60|TT!^61mV49c*0S8~;GEpqlxIOQ(Yhc;_+bZr>@O(KHwS zZJ;GiCp?osK>8mipz<5|o9lQO7(Afc9zjk#4&KZIYK4K-fE$Y7oseKI=t4}*6P<@T zr-CZyZZO$=h*5iL&j0`aJEwv+@pXeq>(C{TMl7gp-~l~^#MkY<^wDq-Mt`Jdo&+p0VlZbt)ME% z2fQuvxJTz!&@rDLouFfwL97=SV;LC0+90Q^KrSu^pR==;1G*ds(lPGs1y@JMTS3(S z2VYq|x*=P>z=c~Ws8PDZ1=3M;=>~6X{SUg34Rn|tD4;<(+Zx>7<8OmZaDnSJ4~Vtk z$c41fK&O0z6nBCXf=BaS&{-fX{LP@b0kAUAwn@zs9tVGLfG7T#!P5yRUatK6|9^As z14jO)JlKjMuySkH4gAfJg*DJX>U2HP-3v-T%?BAf!3>XP@Ge9K{+5@ZGmpS6KW*^I zft{@~pfhQ}Byzk=@aSv>o#u|5ra*j;Zm=*YmO!592G_jZy&ywiX$n;T!qOB-93xGE z1hJ8ybd)j7(V)Zx;(I_( zTknLP=?)GjP(lKkkCc!=^*bUVLC(qrB_t5*MMpF|A%W}u>{bxP?AUqGrLz}wD4It$ zIQp4Ax_d!eE*(2hf-e4Z0rvwyC-J`c_WJ*S(4HLVxx1jNqni&gc3$Xg{Qyew&3i!x zvhX*7HWGHXf{e5V%kcN9fb$yILRf-AD@Pz{3!IV>ghy{L=rA*nlHU`q;sg0F=E??COm0ICCz zdvx}K4!3>r>J>aJKzvA8uz<9Kj#~3*1|J*E!rufrP!D9JHTcXr{yxy^N0{SanG5Vc z#OCW0(9Y1y+ke1$hKauw+{y=)R^aTS1Zwkxc;Mm}Pj-0^PG_LJ1j{a<^8q2J1fpk` z6Mz5zhowAF5e(vcbhm;j$We449_TQ=UXUTM>~a7k0xg0;;uzTlB#1q`_`tHuod|Gt z0U6cVdIS_Try@Yv1;hkp7dfyuzy_gZmmNrs24xoz-=n(~Om%|K;RhEoka8DfK2ml8 zozjoUE*n4tIpA^^#Cj14auP^eC-`J}*a7+nA23034Cpj=k8W^|@qnZ;j7+lviH zK*peCngt*aeRvM{5Qq=WG*dvH!INobfV6H!Xa(^hVZjc{G@yF{Jev1{EQee6{NzWvlVp4H$>jr7twBL0NtSqG6LFe zm;vQ`bhmbZ6Jl${zyJS1$K`o+PX!(F>tP*wprg0_FX${v(2)yJDUj(N)>A=>VX}}p zKCl=0w|V>r-7g9{3Tj)xf6I&DfrXB(AY-~(K_vWuJ;+8Gur8101OK5V5L7c%1Z){J z&4WuGaPof{2Cjj@Ej;k)z0e8-6r!CcAk9&5RDdq*0av=d2SDN8*;)WjG@!Zptq>+? zkp#F|;n;Zqmbsc=FuQaf1f2viEfiErK%B@7w%P-_Rso#HK_z4RDRBIPhTy=F+YLU1 zyYnJ6W^=$X3nIZW3%&}n+ZEJSs6dJfP~ivSdvv#gsm@l=k?A1!fVB65w8K&X=p1xd zTLC1FQTTxbu@`>QV3*dOcyT`%6fsFsSeYF+qjjTX4e@Y!F)Emx1JH zP~ivSdvv#gsm@l=S@+<80hMCiy&&_E3O|r6qVP)qr#Vnt0mOO{6AWIz z)1@1H$eRo3KsazTxj@blcIgC*gARoM|NlRvd;}#2&_W5wK}L|+YJR~CipO(c4?~2Y ztwXRoJ6#XFH2U}dKRB^Kh!W5+JY-Y>a+)uwJJjjq0y%RN!tsDKqd_O}eDdg=3cm0O zdS&zyrUa8xJ9SJ-2{cR-lytCXD>8%fLL?vNF&vCGb!#X!S;SCum&- zc&BA|=m(c>rvQ)c00qy^hn}5*3Lf175}uub5+2yo}Gdo zou^zn55Ab54q6KXI!~l_$BU>q=z^-2~ptz>aT-$;)Q`mA2&djSk-ZOXkPSSJnzw2#{mk?ZqPN1z8@gtnjfG% zZAY2r9~}J6pkqcsvxMCqpuy`8ogOmabO@SJ_h1H%rGmByd4f#;&|M_c`3bbj3p89i z{kTKJbI?%jcd$~B$GRcPT)LekKqG?CaotH^g)aQtj=LxxYyQW;-vT;*6|4!_e9+xV z-~)O`!jYSW9XOmJj(yo6r(hoS04 zL@q=HXu8v*6MBN0a|~*-m;su`dZ`aimeAm1ZUnEC>4Z?AZZ9Nt{D7^X0wop5Mn2f- zp|Jf^pg}q4{hT44u3s8!zdQ#m;cEqNtOd{ic7W46c%-reGz|dSm*vsj0UA94twQXc z0Gey)4E+Ed`+}^Q1zi!?dFqAv_5c6DlfB?f-wm3Uxzc&DJM_xsN0t}(dqD{uEZymP z0lb}d2WaB6+xG_OJmU-9t`|IJKvv4a@;hw5A=+MT(9N1Jv_bVCIKR7I@UV700hyQu z4W@$6djfU9T@6oylzM=!#Ow}z0GgEq#SXNHd9m_0cmW+Gpupt}+WIe~^EyDYke#Rp zrGOTKpl<1f&Pu|U9D-KTy=cA#w&j3FckO`}pb-%81!JI~0H>}_*B38S89;}ggJw}- zC#-;+=>tCUDRhT#ca(xh=VPDFXa(QyC<&j=XbIo$C;^|&XaV1DDc|lQ4xi3q4xdix zv;~{nQ#Do z{aE80P%`LtweaZ;1&t)bN47hC?|@Ee_389I(dl}`vs(zX&)5@Gn1J>fdxCBO1|3-D z*zF_%T8jxfPv%9|O;Erbe-RW3PVkQWQx7{feEtu?k3mZ;52McvU7ZM?wE5Y?%&=<` zXjHA2cX2N>!;9Uy(3v4{lMb|zpu=}Qe39}6&|dQn*ZEzp^G*Ik_MD&aNWO?N=Tr?b zc|OSG$qk;rd&C2@xVGCv z26P-_cYsPKXbEfRhHl>-FI*mjeC_(8!0pCtX0iSLrpY9+I-_Ae|-%duuOP#JWUZiV-3y>8a-L)%TSp5TsBXoij zbWB;N>kr5hYVd~WQg9K~?R)1%fHG*71iYx?hezWPP@F2+Ozv~;?=^be%s109N4JHrPwe&IUd zMaQN8|M!9NOy>vCA?1eOJV5qb_vk$2(dpU&I>+wC!EDf^3;3)_@NsJ3=5jY^n%4C~ zcj=DKQqZLS3=i=6cE`bEj-eed+8~-HK(iXCMFd|j1FobY_o{-XPr#SJp7ZED)p_tm zC#WqC-BAit9{_1T!B@V5R@QaGjzk8fJ4mb|6>Xp*^~E{xE%2ZPeYFQ(d<;cR7ze;L z3S>hv$O!NP1<*;N4?MJe4|ss)lTN*W45ov60ktPytVc5ew7U7n%OFUd^r3mj98g1x zzXiS$7P4gmREc_YcY^CG=mBso;BxP{>kn`l4VvCSZXAG(1r57HrZ}+8_d_ZtL?ZM6 zt^e)>PfpwbEz$9S&Ln}NySWOZ)d%Uj!rFSEgSA2N0nUb?d&@y{Z67=u4}omKpA|t3 zB~M6JbTz!>(s{`Pd?@0@PS-6jHiE{R!8KFui5DdysDTgmBlyBRaDyGR{~q4TI{|JW zb%Kf#&@l?IR^Er^3KM4j7FPxahU2cF-AW9gofjgmI5Twa?SV)0dk`Dcp8U|f1LS!I{uamjf#Ud*G@b-@ea%isbQQYNbXL=F8Fpjz-{7|5sH zwGWWbG6Nm!;saX#*zLN(r#o~3qSn=b*1DkD7gXy?AZlF@m&2oz`2~0(4P@g!Y6Xk3 zUIpS`P;m?MFVgv6c>E1^DQIqOgAZtCpxZ;iv-5#xr-y<^w}*shC#a$AA>i5RApmNU zfIA%y9G;yVj>jE9g&PCtgs~f)H(feom-uv+&Uj%6I>-!scxCO17c4)pg%GsKiV;FD zQXy?7(7`w$kAnOHZb^XKmk|GfI!VyuPr)<8phG=CIkeOF0%&7|5BO}k&>fwwTe?fP zfR^QWfKK@AEN$`V1g|9FZ-EXffcoR0^(5f!E1-M|Jwg;(WI(27dTTqnOIu*4WOg2W z@fmcgT(4`#an~iFq7eO1RZtBL3PsR?sy^MZD7A(c)SwQAVo<(9*u7lK*QF(tqgzv|93S!>9`BD zCeO9=GJ5aY1+-KeR9&BU={yI%;IZ|`pa1_~d`baTXRPxJK@9-NO?2I?Ga)?iWNHn{JT{AKRqzX+K=5fE z7eKeuGJ|(Mfr@vu_9v*l4{2#b)+ktbblRwRbXRbAbk=~5tEmtGF~Ez_K??^fu7dWY z9Z+bl{lWms*pQ|VB&GO1fOUpIXZV83JW%`p1^8fz6Od5_kONS|7qq^i*YyUtB@U^h zK^>cJ-yfja&8Sn5<269LB0mjc z(*RnH14{P%t>CdyNS_lt#@SqZf`Pvkw00TX_XjQ30hPd@(D%LJ(do$XvI$bMfhM1C zfKM7BaK_3rKX75W1AMX+c;E!I_#1Si0@ys%gQy;Wx2HOR2HhazNuXn_AP0noo_JxK z1Wr?+wQ(N3wG*JV8^{;|k8W1aT9B-3L$|1VEi*%BC}^EoLpO+mWMf<_JmGz*7ussf z42GZ+o_c*JcpN_lIv?2Z5@dL+Uc#r__l9q`CkLo)=>j^D8gv?EC%Df9%H*IvlTSD3 zq~d4+pYGTRKHa4=P&-YohW}kVe|sE13^LcFb1P^@)~EAa=Ty+TJs(i@-U}K+2DdjRI*BcPN3Kl>6h zJwn^Fy{;EPow^gST;3V_rn~eFxOoL?c^`VQ|M>s^hTlATT`zd_@*V$`NtveKSj`SJOMoX8@lRn)HKAkKoKHUx+KA_^pr`tgQ#6awZ1a(Zos$5hQV9GRL z$_!pyJn{cOQqvZ+qL2aBUM&DQ0>5L0j{pC^3sf{4o&+iO=(QEdWo7_vwFQqhK{hXd zt;g<259sCv@b)tXkjt?-QU~Nn$Tkebekn-*)dQ0C;2jx{PTw0Yovv40Izej*31w@K zPS+JL{#^h?yX%SO+AGj4yUAJxKe9(UBi5E#*{{IJ$-*|L`PJnfjfM#=0(PmAJ+TL*Acpzp_5c6>U(Nz`eh+jWG(6zZ8#)8htNadXZ1{FN zNO(3L0C~!zvvz_<=XKE4%iW<9JUV?lI$c{{M1b7{>Y4g(0qq=j-2-Z1gS3L$DITCU zY3C!4PA|}6(hcD8cF+=`PA|~1&>0@xr3*Ydi#R+wxsE$QT6>_Ih&rKNN;l9JOW0z- z#v>r#fqJ9R_BZG}QBZpYF*pw{HbA>Pd2>ML6?FPO=w{8z0xj%@4j#OCaT(G}1s!II zW&bQ_{W_=>kCM4yLqm`Ofl|oL5ug&^cY{lJ0EY|G@CfLBbnvz$&}do4TjZ3}4Zh{! z2kzn2lOMpD5;mR(KGF)kes}QbWnBn5qye;bvJao6HaO9tU0D5HI z0Z;?A+xJ6bCFDea7SM_pkKWn^;BvG(^h0-12&k3(;>iI}yU}+6sPggvCw$P_XiypE zd!gHPN2e>~%!XdjyeDWdqx3^}+YBa9v#9igPd9jTz7ObBukO+Zo}G_8JBt)Ry*|&* zBGAm%1<%eR0nl8DPj~DI-_9rw&rT!5+nx77_a*y)HobI)P5@W_z8#$hUr2%)SB*zN z9qnkW<7JTk4>+eosu*~muG9AntSbwe7y=){7yvtjF#vK1BWOYbvMF!H3%?_vq@ox0U(?ImUct=pVtxeb*ys(0 z4&UYQvC#vNvC-vSuFG+bjc%<0kGeL4Og4v@90xMl6=pJc7#}ut?a>{2q1*QitUvA3 z9l8S1mj?Br@sD7G8U~CG?Pz`blXzTXzhAA*P=&`6Cpb8r4 z=xk{F-lOpicn}2=`4~kYsCSNI!T{E$0r@=W5Xk4pUsySTJ&ir?Pk@era7)T6tmoYQESR95L_b(Wb$Hi|z;vVm~xEAO@zFyOIkjb$SlaoLud%{eH z?CWWH4+`Su+6Nr`O`sEFK@)%;%~(%@JO}DOfQ}1+9xQSgRJedn$pJTD2%O{LyTG?w z5L6KRb_as*K;iK14io?#6fWS}9SIo)@&y&uAu0;K-2q@F8otm5t^sIA;tSz@paKO{ z@{@O-2OsEoo@|JBZ!qw;=z*BMwJSiy$_qEpspQ?IpuwjF9-ZdkyW~2{IUp^ma)EBw z1D)>RX;IiImPgluTvPkO<9Pc5SRw--wE)_|v=g-P-=ovD15_x&3fb<`4<4X`6?C0W zXKe#`3q~pEb`{X3oEO}opt{HR1MJ`#@W{XKhi>qZD4@Q)Pj?f@R8XDf(GA-1SEK+h z7mFky5k%n6^o!-Ry!fPULf~(zu*Kn@Zh6+prj9~JdpYW6tt%xr6}kU ziP94<-hTxxtU zZr>T84j{-gzMWj4M$bV=YwWy7XK9BIsJ#^00Gej#_HB4!@CTFw;U0sunn1lX^!8%& z8wHS2*xFAXiJ%TIsQu)Yh|+#~jJN#+>OX<%Xq3VOPr(5lYXD74ypRN?Z1CuD?S&UQ zPr#)IyvV!&9`px=D)z(#>RcXxB&GwPvFsNIgRrC|$ZP{>od#%6l25lMDDgl7+*1M) z;GP1I0O$1SjszzjaCmnfd~pkU-50pE59yhqhbO$hy8}G?1BxP0>;D6&8Q$spq`MS! z^7ISnbQ9MzP^T9>b+ZF36Z)eQ)M!97;6S4Shy-x~;y&LK5ch#@aEUztO%O2ad^h-X zp7aGR&T(DwLKEa*NGXpxztrn`!0;rfGu~PH1>`jFAQ7k?dHhA`Q@Eo*9X1%pdF8()dKL9BcV4SD@Q<6WT2HJp`aTzPJm|mJUayqzjb~B?JnyK-2qDApczG< z&XeF?Xa{Jh6Xrl{=@TBFC(wqpKoek~7L)IR7l)s~U3>yGBL!-8frpm^6d;ZakN|DX z^y~}(t=TvMDmZ*QJvcl&1yEcDTAl(5y@Ozvy#oy~!(0Y$UxNA@@CIUMD5!_^0=(A) zG(m42hIE|9e^8go3RJR!>UYptOrVJpYe#e?po?iyC;1`gX~Via;Im^9_BXx(ok@Z0 zgihZ(AYX!(#DMNU0c~Xg)kDl-9PlkJ5Wav1WUI>yh*HpKJm~JS7akC$pxrJoK1k_` z6JIj<6vCq4gOL(-4EM6)`is;6O6y1^6^{P%9l2!_y8meEQ$;h>?HWCy(S~D4RCjAcpa&X7GKf>d+%M zyE{M)!`73KJr(>dpzZ%)W!>Nn6`(t!!Q+DbEub~(C^Af)tuFun|L1R64?3}S6X-Ci zg2tgCT2byc|Fz~m6dTvk)!1jTbU0FART*2QAI=bMv z>k{yAjUxE?%{z>^bbuP*{H@43-ZO(1wRC_kQvTr4JqcoZ=?4$(X&|-GxgHPj+<)l` z4^7azL(r&qbL}0bj$UU_NWpf;9RM%y0Ucie67FgRk)VqMPIUG{mPkT(9q$FTfIK>< zg7yZzkXZm~Sb^CdmWTM8z=JiAMG=rgD4@Ou_Vhk{N2v-Tb)MWv&Ji0p}K_B|TgSiXR69Vny>jK3w zQ~`t;`oY7x3sl1Jx2b}C3_f}tq#2X~pgx8e0BW3q6A3JgS|eaOkdm-RS?BLEuP+)-<&4KQ_Z-vZByEb?* zyMpF-LANe{Xx;$|00zihA~X?$hM!&>+4BGY%V00n!Tum6Tw~ zZg3Y7tVO#K)Omz2qX#wqK|NLQdTFqN?oM#J0-cq^+yz$K4ZiRhytra2XnPrGsYy3@ zzuAk6yr4V_UJVRg|BLVmRMhu_2Q$=82nAh2ZVi^_Z$Zv@&~7p4JTj2Y@W?~y;vxbF zbix9104?79|Nl#9kV1n1)DeCOJv8)i4!B|{OJ`=-r45NPscdG37Zx7O3@^`vT1=pE zI?%3s%@d$Cy&j#8pu-D5i!gV9Rs(=ad593g3E;d5-AMh?14~MR6^4+7>&<&XQ!&sh z<)KjnPM44!P_Q-fur@&B5m2QM9k4+e4+M>0fmf$=*1qt7PE&(tX+cesAke_j0}te8 z$p>(=q>c$Rx&%6A-vKlPdjnk0z;<#<hD+fR>@*uJR4j!QrTv#>}uQ7!r;q>C6l- zKDa@XK4^@gc?YPvVc>7w06IDlR91sW*+8SdFFd-pftFi?&bWXqaNM>7WNT;X0WcTT zMeIEP;
    -
    unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
    -

    NOTE: This function is planned to be obsolete, in favor of ZSTD_getFrameContentSize(). - ZSTD_getFrameContentSize() works the same way, - returning the decompressed size of a single frame, - but distinguishes empty frames from frames with an unknown size, or errors. - - 'src' is the start of a zstd compressed frame. - @return : content size to be decompressed, as a 64-bits value _if known_, 0 otherwise. - note 1 : decompressed size is an optional field, it may not be present, typically in streaming mode. - When `return==0`, data to decompress could be any size. +

    #define ZSTD_CONTENTSIZE_UNKNOWN (0ULL - 1)
    +#define ZSTD_CONTENTSIZE_ERROR   (0ULL - 2)
    +unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize);
    +

    `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. + @return : - decompressed size of the frame in `src`, if known + - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined + - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) + note 1 : a 0 return value means the frame is valid but "empty". + note 2 : decompressed size is an optional field, it may not be present, typically in streaming mode. + When `return==ZSTD_CONTENTSIZE_UNKNOWN`, data to decompress could be any size. In which case, it's necessary to use streaming mode to decompress data. - Optionally, application can use ZSTD_decompress() while relying on implied limits. - (For example, data may be necessarily cut into blocks <= 16 KB). - note 2 : decompressed size is always present when compression is done with ZSTD_compress() - note 3 : decompressed size can be very large (64-bits value), + Optionally, application can rely on some implicit limit, + as ZSTD_decompress() only needs an upper bound of decompressed size. + (For example, data could be necessarily cut into blocks <= 16 KB). + note 3 : decompressed size is always present when compression is done with ZSTD_compress() + note 4 : decompressed size can be very large (64-bits value), potentially larger than what local system can handle as a single memory segment. In which case, it's necessary to use streaming mode to decompress data. - note 4 : If source is untrusted, decompressed size could be wrong or intentionally modified. - Always ensure result fits within application's authorized limits. + note 5 : If source is untrusted, decompressed size could be wrong or intentionally modified. + Always ensure return value fits within application's authorized limits. Each application can set its own limits. - note 5 : when `return==0`, if precise failure cause is needed, use ZSTD_getFrameHeader() to know more. + note 6 : This function replaces ZSTD_getDecompressedSize() +


    + +
    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.


    Helper functions

    int         ZSTD_maxCLevel(void);               /*!< maximum compression level available */
    @@ -298,8 +312,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
     
    size_t ZSTD_DStreamOutSize(void);   /*!< recommended size for output buffer. Guarantee to successfully flush at least one complete block in all circumstances. */
     

    START OF ADVANCED AND EXPERIMENTAL FUNCTIONS

     The definitions in this section are considered experimental.
    - They should never be used with a dynamic library, as they may change in the future.
    - They are provided for advanced usages.
    + They should never be used with a dynamic library, as prototypes may change in the future.
    + They are provided for advanced scenarios.
      Use them only in association with static linking.
      
     
    @@ -348,26 +362,15 @@ static const ZSTD_customMem ZSTD_defaultCMem = { NULL, NULL, NULL };
    size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize);
     

    `src` should point to the start of a ZSTD encoded frame or skippable frame `srcSize` must be at least as large as the frame - @return : the compressed size of the frame pointed to by `src`, + @return : the compressed size of the first frame starting at `src`, suitable to pass to `ZSTD_decompress` or similar, - or an error code if given invalid input. -


    - -
    #define ZSTD_CONTENTSIZE_UNKNOWN (0ULL - 1)
    -#define ZSTD_CONTENTSIZE_ERROR   (0ULL - 2)
    -unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize);
    -

    `src` should point to the start of a ZSTD encoded frame. - `srcSize` must be at least as large as the frame header. - A value >= `ZSTD_frameHeaderSize_max` is guaranteed to be large enough. - @return : - decompressed size of the frame pointed to be `src` if known - - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined - - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) + or an error code if input is invalid


    unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
     

    `src` should point the start of a series of ZSTD encoded and/or skippable frames `srcSize` must be the _exact_ size of this series - (i.e. there should be a frame boundary exactly `srcSize` bytes after `src`) + (i.e. there should be a frame boundary exactly at `srcSize` bytes after `src`) @return : - decompressed size of all data in all successive frames - if the decompressed size cannot be determined: ZSTD_CONTENTSIZE_UNKNOWN - if an error occurred: ZSTD_CONTENTSIZE_ERROR @@ -375,8 +378,6 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize); note 1 : decompressed size is an optional field, that may not be present, especially in streaming mode. When `return==ZSTD_CONTENTSIZE_UNKNOWN`, data to decompress could be any size. In which case, it's necessary to use streaming mode to decompress data. - Optionally, application can still use ZSTD_decompress() while relying on implied limits. - (For example, data may be necessarily cut into blocks <= 16 KB). note 2 : decompressed size is always present when compression is done with ZSTD_compress() note 3 : decompressed size can be very large (64-bits value), potentially larger than what local system can handle as a single memory segment. @@ -385,7 +386,7 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize); Always ensure result fits within application's authorized limits. Each application can set its own limits. note 5 : ZSTD_findDecompressedSize handles multiple frames, and so it must traverse the input to - read each contained frame header. This is efficient as most of the data is skipped, + read each contained frame header. This is fast as most of the data is skipped, however it does mean that all frame data must be present and valid.


    @@ -483,14 +484,15 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, unsigned byReference); 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, rawContent otherwize */
    +
    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,
                                           unsigned byReference, ZSTD_dictMode_e dictMode,
    -                                      ZSTD_compressionParameters cParams, ZSTD_customMem customMem);
    +                                      ZSTD_compressionParameters cParams,
    +                                      ZSTD_customMem customMem);
     

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


    diff --git a/tests/fullbench.c b/tests/fullbench.c index 5c105ee75..45ef2b6a3 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -114,7 +114,6 @@ size_t local_ZSTD_decodeLiteralsBlock(void* dst, size_t dstSize, void* buff2, co return ZSTD_decodeLiteralsBlock((ZSTD_DCtx*)g_zdc, buff2, g_cSize); } -extern size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* bpPtr); extern size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeq, const void* src, size_t srcSize); size_t local_ZSTD_decodeSeqHeaders(void* dst, size_t dstSize, void* buff2, const void* src, size_t srcSize) { From 8aa34a76086b32ae66119b50961e33c4777e2bbf Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 6 Jul 2017 07:30:49 -0700 Subject: [PATCH 032/318] Switch to mmapping files --- contrib/long_distance_matching/Makefile | 9 +- contrib/long_distance_matching/main-ldm.c | 411 ++++++++++++++++++++++ 2 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 contrib/long_distance_matching/main-ldm.c diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index bfe02ea2a..0efae69b5 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -10,18 +10,23 @@ # This Makefile presumes libzstd is installed, using `sudo make install` +LDFLAGS += -lzstd + .PHONY: default all clean default: all -all: main +all: main main-ldm main : ldm.c main.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ +main-ldm : ldm.c main-ldm.c + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main + main main-ldm @echo Cleaning completed diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c new file mode 100644 index 000000000..bdcffb0f7 --- /dev/null +++ b/contrib/long_distance_matching/main-ldm.c @@ -0,0 +1,411 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ldm.h" + +#define BUF_SIZE 16*1024 // Block size +#define LDM_HEADER_SIZE 8 +#define DEBUG + +#if 0 +static size_t compress_file(FILE *in, FILE *out, size_t *size_in, + size_t *size_out) { + char *src, *buf = NULL; + size_t r = 1; + size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; + + src = malloc(BUF_SIZE); + if (!src) { + printf("Not enough memory\n"); + goto cleanup; + } + + size = BUF_SIZE + LDM_HEADER_SIZE; + buf = malloc(size); + if (!buf) { + printf("Not enough memory\n"); + goto cleanup; + } + + + for (;;) { + k = fread(src, 1, BUF_SIZE, in); + if (k == 0) + break; + count_in += k; + + n = LDM_compress(src, buf, k, BUF_SIZE); + + // n = k; + // offset += n; + offset = k; + count_out += k; + +// k = fwrite(src, 1, offset, out); + + k = fwrite(buf, 1, offset, out); + if (k < offset) { + if (ferror(out)) + printf("Write failed\n"); + else + printf("Short write\n"); + goto cleanup; + } + + } + *size_in = count_in; + *size_out = count_out; + r = 0; + cleanup: + free(src); + free(buf); + return r; +} + +static size_t decompress_file(FILE *in, FILE *out) { + void *src = malloc(BUF_SIZE); + void *dst = NULL; + size_t dst_capacity = BUF_SIZE; + size_t ret = 1; + size_t bytes_written = 0; + + if (!src) { + perror("decompress_file(src)"); + goto cleanup; + } + + while (ret != 0) { + /* Load more input */ + size_t src_size = fread(src, 1, BUF_SIZE, in); + void *src_ptr = src; + void *src_end = src_ptr + src_size; + if (src_size == 0 || ferror(in)) { + printf("(TODO): Decompress: not enough input or error reading file\n"); + //TODO + ret = 0; + goto cleanup; + } + + /* Allocate destination buffer if it hasn't been allocated already */ + if (!dst) { + dst = malloc(dst_capacity); + if (!dst) { + perror("decompress_file(dst)"); + goto cleanup; + } + } + + // TODO + + /* Decompress: + * Continue while there is more input to read. + */ + while (src_ptr != src_end && ret != 0) { + // size_t dst_size = src_size; + size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); + size_t written = fwrite(dst, 1, dst_size, out); +// printf("Writing %zu bytes\n", dst_size); + bytes_written += dst_size; + if (written != dst_size) { + printf("Decompress: Failed to write to file\n"); + goto cleanup; + } + src_ptr += src_size; + src_size = src_end - src_ptr; + } + + /* Update input */ + + } + + printf("Wrote %zu bytes\n", bytes_written); + + cleanup: + free(src); + free(dst); + + return ret; +} +#endif + +static size_t compress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + + /* 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 size of input file */ + if (fstat (fdin, &statbuf) < 0) { + perror("Fstat error"); + return 1; + } + + /* go to the location corresponding to the last byte */ + if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write 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; + } + + /* mmap the output file */ + if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + + /* Copy input file to output file */ +// memcpy(dst, src, statbuf.st_size); + size_t size_out = ZSTD_compress(dst, statbuf.st_size, + src, statbuf.st_size, 1); + printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, + (unsigned)statbuf.st_size, (unsigned)size_out, oname, + (double)size_out / (statbuf.st_size) * 100); + + close(fdin); + close(fdout); + return 0; +} + +static size_t decompress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + + /* 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 size of input file */ + if (fstat (fdin, &statbuf) < 0) { + perror("Fstat error"); + return 1; + } + + /* go to the location corresponding to the last byte */ + if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write 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; + } + + /* mmap the output file */ + if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + + /* Copy input file to output file */ +// memcpy(dst, src, statbuf.st_size); + + size_t size_out = ZSTD_decompress(dst, statbuf.st_size, + src, statbuf.st_size); + + + close(fdin); + close(fdout); + return 0; +} + +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; +} + +static void verify(const char *inpFilename, const char *decFilename) { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); +} + +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"); + return 1; + } + + /* decompress */ + if (decompress(ldmFilename, decFilename)) { + printf("Decompress error"); + return 1; + } + + /* verify */ + verify(inpFilename, decFilename); +} + +#if 0 +int main2(int argc, char *argv[]) { + char inpFilename[256] = { 0 }; + char ldmFilename[256] = { 0 }; + char decFilename[256] = { 0 }; + + if (argc < 2) { + printf("Please specify input filename\n"); + return 0; + } + 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 */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *outFp = fopen(ldmFilename, "wb"); + size_t sizeIn = 0; + size_t sizeOut = 0; + size_t ret; + printf("compress : %s -> %s\n", inpFilename, ldmFilename); + ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); + if (ret) { + printf("compress : failed with code %zu\n", ret); + return ret; + } + printf("%s: %zu → %zu bytes, %.1f%%\n", + inpFilename, sizeIn, sizeOut, + (double)sizeOut / sizeIn * 100); + printf("compress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* decompress */ + { + FILE *inpFp = fopen(ldmFilename, "rb"); + FILE *outFp = fopen(decFilename, "wb"); + size_t ret; + + printf("decompress : %s -> %s\n", ldmFilename, decFilename); + ret = decompress_file(inpFp, outFp); + if (ret) { + printf("decompress : failed with code %zu\n", ret); + return ret; + } + printf("decompress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* verify */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); + } + return 0; +} +#endif + From 94fe291b831ec9d0a631b052dda2aec69b01a111 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 10:29:16 -0700 Subject: [PATCH 033/318] small changes --- contrib/adaptive-compression/multi.c | 11 +++++++---- contrib/adaptive-compression/pipetests.sh | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 208a0a790..8371a0bcb 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,7 +1,7 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define DEBUGLOG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 -#define MAX_NUM_JOBS 50; +#define MAX_NUM_JOBS 100; #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 @@ -168,6 +168,7 @@ static void* compressionThread(void* arg) DEBUGLOG(2, "signaling for job %u\n", currJob); pthread_cond_signal(&ctx->jobCompleted_cond); pthread_mutex_unlock(&ctx->jobCompleted_mutex); + DEBUGLOG(2, "finished job compression %u\n", currJob); currJob++; if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished compressing all jobs */ @@ -189,7 +190,7 @@ static void* outputThread(void* arg) // DEBUGLOG(2, "outputThread(): waiting on job completed\n"); pthread_mutex_lock(&ctx->jobCompleted_mutex); while (currJob + 1 > ctx->jobCompletedID) { - DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); + DEBUGLOG(2, "waiting on job completed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } pthread_mutex_unlock(&ctx->jobCompleted_mutex); @@ -208,6 +209,7 @@ static void* outputThread(void* arg) } } } + DEBUGLOG(2, "finished job write %u\n", currJob); currJob++; DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); @@ -237,9 +239,9 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) jobDescription* job = &ctx->jobs[nextJobIndex]; // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - DISPLAY("Creating new compression job -- nextJob: %u, jobWrittenID: %u, numJObs: %u\n", nextJob, ctx->jobWrittenID, ctx->numJobs); + // DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWrittenID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWrittenID, ctx->numJobs); while (nextJob - ctx->jobWrittenID >= ctx->numJobs) { - DEBUGLOG(2, "waiting on job writtten, nextJob: %u\n", nextJob); + DEBUGLOG(2, "waiting on job written, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); @@ -262,6 +264,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); pthread_mutex_unlock(&ctx->jobReady_mutex); + DEBUGLOG(2, "finished job creation %u\n", nextJob); ctx->nextJobID++; return 0; } diff --git a/contrib/adaptive-compression/pipetests.sh b/contrib/adaptive-compression/pipetests.sh index 743ce381c..71f123a17 100755 --- a/contrib/adaptive-compression/pipetests.sh +++ b/contrib/adaptive-compression/pipetests.sh @@ -1,2 +1,2 @@ make clean multi -pv -q -L 500m tests/test2048.pdf | ./multi -v -otmp.zst +pv -q -L 100m tests/test2048.pdf | ./multi -v -otmp.zst From 592a0d9495319e7eef455282674020b422f7254d Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 10:49:26 -0700 Subject: [PATCH 034/318] changed to work with std out --- contrib/adaptive-compression/multi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 8371a0bcb..6459e245a 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -274,20 +274,21 @@ static int compressFilename(const char* const srcFilename, const char* const dst BYTE* const src = malloc(FILE_CHUNK_SIZE); unsigned const stdinUsed = !strcmp(srcFilename, stdinmark); FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb"); + const char* const outFilename = (stdinUsed && !dstFilename) ? stdoutmark : dstFilename; size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; /* checking for errors */ - if (!srcFilename || !dstFilename || !src || !srcFile) { + if (!srcFilename || !outFilename || !src || !srcFile) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; goto cleanup; } /* creating context */ - ctx = createCCtx(numJobs, dstFilename); + ctx = createCCtx(numJobs, outFilename); if (ctx == NULL) { ret = 1; goto cleanup; From f57849b9c61da16f27347704d58c2db969b08a1e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 11:05:51 -0700 Subject: [PATCH 035/318] added ability to set initial compression level --- contrib/adaptive-compression/multi.c | 30 +++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 6459e245a..da182b177 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -6,6 +6,7 @@ #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 #define DEFAULT_DISPLAY_LEVEL 1 +#define DEFAULT_COMPRESSION_LEVEL 6 typedef unsigned char BYTE; #include /* fprintf */ @@ -15,6 +16,7 @@ typedef unsigned char BYTE; #include "zstd.h" static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; +static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; typedef struct { void* start; @@ -91,7 +93,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) return NULL; } memset(ctx, 0, sizeof(adaptCCtx)); - ctx->compressionLevel = 6; /* default */ + ctx->compressionLevel = g_compressionLevel; pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); /* TODO: add checks for errors on each mutex */ pthread_cond_init(&ctx->jobCompleted_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); @@ -362,6 +364,26 @@ static int compressFilenames(const char** filenameTable, unsigned numFiles) return ret; } +/*! 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 */ +static unsigned readU32FromChar(const char** stringPtr) +{ + unsigned result = 0; + while ((**stringPtr >='0') && (**stringPtr <='9')) + result *= 10, result += **stringPtr - '0', (*stringPtr)++ ; + if ((**stringPtr=='K') || (**stringPtr=='M')) { + result <<= 10; + if (**stringPtr=='M') result <<= 10; + (*stringPtr)++ ; + if (**stringPtr=='i') (*stringPtr)++; + if (**stringPtr=='B') (*stringPtr)++; + } + return result; +} + /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { @@ -391,6 +413,12 @@ int main(int argCount, const char* argv[]) g_displayLevel++; continue; } + else if (strlen(argument) > 1 && argument[1] == 'i') { + argument += 2; + g_compressionLevel = readU32FromChar(&argument); + DEBUGLOG(2, "g_compressionLevel: %u\n", g_compressionLevel); + continue; + } else { DISPLAY("Error: invalid argument provided\n"); ret = 1; From e6e25c950754099c762205af3090d73841f18f9d Mon Sep 17 00:00:00 2001 From: Igor Vuk Date: Thu, 6 Jul 2017 20:43:14 +0200 Subject: [PATCH 036/318] Fix typos in README.md --- contrib/linux-kernel/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/linux-kernel/README.md b/contrib/linux-kernel/README.md index 214c520bd..1b58304f2 100644 --- a/contrib/linux-kernel/README.md +++ b/contrib/linux-kernel/README.md @@ -5,7 +5,7 @@ The patches are based off of the linux kernel master branch (version 4.10). ## xxHash kernel module -* The patch is locaed in `xxhash.diff`. +* The patch is located in `xxhash.diff`. * The header is in `include/linux/xxhash.h`. * The source is in `lib/xxhash.c`. * `test/XXHashUserLandTest.cpp` contains tests for the patch in userland by mocking the kernel headers. @@ -18,7 +18,7 @@ The patches are based off of the linux kernel master branch (version 4.10). ## Zstd Kernel modules -* The (large) patch is locaed in `zstd.diff`, which depends on `xxhash.diff`. +* The (large) patch is located in `zstd.diff`, which depends on `xxhash.diff`. * The header is in `include/linux/zstd.h`. * It is split up into `zstd_compress` and `zstd_decompress`, which can be loaded independently. * Source files are in `lib/zstd/`. From a407ccc215fa826d84ea9760b8bfe426489d406e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 13:09:17 -0700 Subject: [PATCH 037/318] added ability to congregate statistics into single print statement rather than using debug --- contrib/adaptive-compression/multi.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index da182b177..6aa60315d 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -17,12 +17,19 @@ typedef unsigned char BYTE; static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; +static unsigned g_displayStats = 0; typedef struct { void* start; size_t size; } buffer_t; +typedef struct { + unsigned waitCompleted; + unsigned waitReady; + unsigned waitWritten; +} stat_t; + typedef struct { buffer_t src; buffer_t dst; @@ -50,6 +57,7 @@ typedef struct { pthread_cond_t allJobsCompleted_cond; pthread_mutex_t jobWrite_mutex; pthread_cond_t jobWrite_cond; + stat_t stats; jobDescription* jobs; FILE* dstFile; } adaptCCtx; @@ -150,6 +158,7 @@ static void* compressionThread(void* arg) // DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { + ctx->stats.waitReady++; DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } @@ -192,6 +201,7 @@ static void* outputThread(void* arg) // DEBUGLOG(2, "outputThread(): waiting on job completed\n"); pthread_mutex_lock(&ctx->jobCompleted_mutex); while (currJob + 1 > ctx->jobCompletedID) { + ctx->stats.waitCompleted++; DEBUGLOG(2, "waiting on job completed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } @@ -243,6 +253,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) pthread_mutex_lock(&ctx->jobWrite_mutex); // DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWrittenID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWrittenID, ctx->numJobs); while (nextJob - ctx->jobWrittenID >= ctx->numJobs) { + ctx->stats.waitWritten++; DEBUGLOG(2, "waiting on job written, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } @@ -271,6 +282,14 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) return 0; } +static void printStats(stat_t stats) +{ + DISPLAY("========STATISTICS========\n"); + DISPLAY("# times waited on job ready: %u\n", stats.waitReady); + DISPLAY("# times waited on job completed: %u\n", stats.waitCompleted); + DISPLAY("# times waited on job written: %u\n\n", stats.waitWritten); +} + static int compressFilename(const char* const srcFilename, const char* const dstFilename) { BYTE* const src = malloc(FILE_CHUNK_SIZE); @@ -341,6 +360,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst cleanup: waitUntilAllJobsCompleted(ctx); + if (g_displayStats) printStats(ctx->stats); /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; @@ -419,6 +439,10 @@ int main(int argCount, const char* argv[]) DEBUGLOG(2, "g_compressionLevel: %u\n", g_compressionLevel); continue; } + else if (strlen(argument) > 1 && argument[1] == 's') { + g_displayStats = 1; + continue; + } else { DISPLAY("Error: invalid argument provided\n"); ret = 1; From b96ad327a48bf4a97574e3d11d2f94741d900616 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 6 Jul 2017 15:23:15 -0700 Subject: [PATCH 038/318] Add simple compress and decompress functions --- contrib/long_distance_matching/ldm.c | 342 ++++++++++++++++++++-- contrib/long_distance_matching/ldm.h | 6 +- contrib/long_distance_matching/main-ldm.c | 44 ++- 3 files changed, 364 insertions(+), 28 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 34118c81f..c8051ea4d 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -1,9 +1,25 @@ #include #include #include +#include #include "ldm.h" +#define LDM_MEMORY_USAGE 14 +#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) +#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) + +#define WINDOW_SIZE (1 << 20) +#define HASH_SIZE 4 +#define MINMATCH 4 + +#define ML_BITS 4 +#define ML_MASK ((1U<>8); + } +} + + + +static U32 LDM_read32(const void *ptr) { + return *(const U32 *)ptr; +} + +static void LDM_copy8(void *dst, const void *src) { + memcpy(dst, src, 8); +} + +static void LDM_wild_copy(void *dstPtr, const void *srcPtr, void *dstEnd) { + BYTE *d = (BYTE *)dstPtr; + const BYTE *s = (const BYTE *)srcPtr; + BYTE * const e = (BYTE *)dstEnd; + + do { + LDM_copy8(d, s); + d += 8; + s += 8; + } while (d < e); + +} + struct hash_entry { U64 offset; tag t; }; -size_t LDM_compress(const char *source, char *dest, size_t source_size, size_t max_dest_size) { - // max_dest_size >= source_size - - - /** - * Loop: - * Find match at position k (hash next n bytes, rolling hash) - * Compute match length - * Output literal length: k (sequences of 4 + (k-4) bytes) - * Output match length - * Output literals - * Output offset - */ - - memcpy(dest, source, source_size); - return source_size; +static U32 LDM_hash(U32 sequence) { + return ((sequence * 2654435761U) >> ((MINMATCH*8)-LDM_HASHLOG)); } -size_t LDM_decompress(const char *source, char *dest, size_t compressed_size, size_t max_decompressed_size) { - memcpy(dest, source, compressed_size); +static U32 LDM_hash_position(const void * const p) { + return LDM_hash(LDM_read32(p)); +} + +static U64 find_best_match(tag t, U64 offset) { + return 0; +} + +static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, + const BYTE *srcBase) { + U32 *hashTable = (U32 *) tableBase; + hashTable[h] = (U32)(p - srcBase); +} + +static void LDM_put_position(const BYTE *p, void *tableBase, + const BYTE *srcBase) { + U32 const h = LDM_hash_position(p); + LDM_put_position_on_hash(p, h, tableBase, srcBase); +} + +static const BYTE *LDM_get_position_on_hash( + U32 h, void *tableBase, const BYTE *srcBase) { + const U32 * const hashTable = (U32*)tableBase; + return hashTable[h] + srcBase; +} + +static BYTE LDM_read_byte(const void *memPtr) { + BYTE val; + memcpy(&val, memPtr, 1); + return val; +} + +static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { + const BYTE * const pStart = pIn; + while (pIn < pInLimit - 1) { + BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); + if (!diff) { + pIn++; + pMatch++; + continue; + } + return (unsigned)(pIn - pStart); + } + return (unsigned)(pIn - pStart); +} + + +size_t LDM_compress(void const *source, void *dest, size_t source_size, + size_t max_dest_size) { + const BYTE * const istart = (const BYTE*)source; + const BYTE *ip = istart; + const BYTE * const iend = istart + source_size; + const BYTE *ilimit = iend - HASH_SIZE; + const BYTE * const matchlimit = iend - HASH_SIZE; + BYTE *op = (BYTE*) dest; + U32 hashTable[LDM_HASHTABLESIZE_U32]; + memset(hashTable, 0, sizeof(hashTable)); + + const BYTE *anchor = (const BYTE *)source; +// struct LDM_cctx cctx; + size_t output_size = 0; + + U32 forwardH; + + /* Hash first byte: put into hash table */ + + LDM_put_position(ip, hashTable, istart); + ip++; + forwardH = LDM_hash_position(ip); + + while (ip < ilimit) { + const BYTE *match; + BYTE *token; + /* Find a match */ + { + const BYTE *forwardIp = ip; + unsigned step = 1; + + do { + U32 const h = forwardH; + ip = forwardIp; + forwardIp += step; + + match = LDM_get_position_on_hash(h, hashTable, istart); + + forwardH = LDM_hash_position(forwardIp); + LDM_put_position_on_hash(ip, h, hashTable, istart); + } while (ip - match > WINDOW_SIZE || + LDM_read32(match) != LDM_read32(ip)); + } + + /* Encode literals */ + { + unsigned const litLength = (unsigned)(ip - anchor); + token = op++; + + printf("Cur position: %zu\n", anchor - istart); + printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match); + /* + fwrite(match, 4, 1, stdout); + printf("\n"); + */ + + if (litLength >= RUN_MASK) { + int len = (int)litLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) { + *op++ = (BYTE)len; + } + } else { + *token = (BYTE)(litLength << ML_BITS); + } + + printf("Literals "); + fwrite(anchor, litLength, 1, stdout); + printf("\n"); + + LDM_wild_copy(op, anchor, op + litLength); + op += litLength; + } +_next_match: + /* Encode offset */ + { + LDM_writeLE16(op, (U16)(ip - match)); + op += 2; + } + + /* Encode Match Length */ + { + unsigned matchCode; + matchCode = LDM_count(ip + MINMATCH, match + MINMATCH, + matchlimit); + + printf("Match length %zu\n", matchCode + MINMATCH); + fwrite(ip, MINMATCH + matchCode, 1, stdout); + printf("\n"); + ip += MINMATCH + matchCode; + if (matchCode >= ML_MASK) { + *token += ML_MASK; + matchCode -= ML_MASK; + LDM_write32(op, 0xFFFFFFFF); + while (matchCode >= 4*0xFF) { + op += 4; + LDM_write32(op, 0xffffffff); + matchCode -= 4*0xFF; + } + op += matchCode / 255; + *op++ = (BYTE)(matchCode % 255); + } else { + *token += (BYTE)(matchCode); + } + printf("\n"); + } + + anchor = ip; + + LDM_put_position(ip, hashTable, istart); + forwardH = LDM_hash_position(++ip); + } + /* Encode last literals */ + { + /* + size_t const lastRun = (size_t)(iend - anchor); + printf("last run length: %zu, %zu %zu %zu %zu\n", lastRun, iend-istart, + anchor-istart, ip-istart, ilimit-istart); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255; accumulator -= 255) { + *op++ = 255; + } + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRun << ML_BITS); + } + fwrite(anchor, lastRun, 1, stdout); + printf("^last run\n"); + memcpy(op, anchor, lastRun); + op += lastRun; + +// memcpy(dest + (ip - istart), ip, 1); +// */ + } + return (op - (BYTE *)dest); +} + +size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, + size_t max_decompressed_size) { + const BYTE *ip = (const BYTE *)source; + const BYTE * const iend = ip + compressed_size; + BYTE *op = (BYTE *)dest; + BYTE * const oend = op + max_decompressed_size; + BYTE *cpy; + + while (ip < iend) { + size_t length; + const BYTE *match; + size_t offset; + + /* get literal length */ + unsigned const token = *ip++; + if ((length=(token >> ML_BITS)) == RUN_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while (s == 255); + } + printf("Literal length: %zu\n", length); + + /* copy literals */ + cpy = op + length; + LDM_wild_copy(op, ip, cpy); + ip += length; + op = cpy; + + /* get offset */ + offset = LDM_readLE16(ip); + printf("Offset: %zu\n", offset); + ip += 2; + match = op - offset; + // LDM_write32(op, (U32)offset); + + /* get matchlength */ + length = token & ML_MASK; + printf("Match length: %zu\n", length); + if (length == ML_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while (s == 255); + } + length += MINMATCH; + + /* copy match */ + cpy = op + length; + + + + } + +// memcpy(dest, source, compressed_size); return compressed_size; } diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index d0151373c..0aab6aa3b 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -3,8 +3,10 @@ #include /* size_t */ -size_t LDM_compress(const char *source, char *dest, size_t source_size, size_t max_dest_size); +size_t LDM_compress(void const *source, void *dest, size_t source_size, + size_t max_dest_size); -size_t LDM_decompress(const char *source, char *dest, size_t compressed_size, size_t max_decompressed_size); +size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, + size_t max_decompressed_size); #endif /* LDM_H */ diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index bdcffb0f7..8b97ce92f 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #define BUF_SIZE 16*1024 // Block size #define LDM_HEADER_SIZE 8 #define DEBUG +// #define ZSTD #if 0 static size_t compress_file(FILE *in, FILE *out, size_t *size_in, @@ -163,7 +165,7 @@ static size_t compress(const char *fname, const char *oname) { perror("lseek error"); return 1; } - + /* write a dummy byte at the last location */ if (write(fdout, "", 1) != 1) { perror("write error"); @@ -186,9 +188,15 @@ static size_t compress(const char *fname, const char *oname) { /* Copy input file to output file */ // memcpy(dst, src, statbuf.st_size); - size_t size_out = ZSTD_compress(dst, statbuf.st_size, - src, statbuf.st_size, 1); - printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, + #ifdef ZSTD + size_t size_out = ZSTD_compress(dst, statbuf.st_size, + src, statbuf.st_size, 1); + #else + size_t size_out = LDM_compress(src, dst, statbuf.st_size, + statbuf.st_size); + #endif + ftruncate(fdout, size_out); + printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, (unsigned)statbuf.st_size, (unsigned)size_out, oname, (double)size_out / (statbuf.st_size) * 100); @@ -225,7 +233,7 @@ static size_t decompress(const char *fname, const char *oname) { perror("lseek error"); return 1; } - + /* write a dummy byte at the last location */ if (write(fdout, "", 1) != 1) { perror("write error"); @@ -249,9 +257,14 @@ static size_t decompress(const char *fname, const char *oname) { /* Copy input file to output file */ // memcpy(dst, src, statbuf.st_size); - size_t size_out = ZSTD_decompress(dst, statbuf.st_size, - src, statbuf.st_size); - + #ifdef ZSTD + size_t size_out = ZSTD_decompress(dst, statbuf.st_size, + src, statbuf.st_size); + #else + size_t size_out = LDM_decompress(src, dst, statbuf.st_size, + statbuf.st_size); + #endif + ftruncate(fdout, size_out); close(fdin); close(fdout); @@ -315,20 +328,35 @@ int main(int argc, const char *argv[]) { printf("ldm = [%s]\n", ldmFilename); printf("dec = [%s]\n", decFilename); + struct timeval tv1, tv2; /* compress */ + { + gettimeofday(&tv1, NULL); if (compress(inpFilename, ldmFilename)) { printf("Compress error"); return 1; } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + } /* decompress */ + + gettimeofday(&tv1, NULL); if (decompress(ldmFilename, decFilename)) { printf("Decompress error"); return 1; } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); /* verify */ verify(inpFilename, decFilename); + return 0; } #if 0 From ff9f2cd057041543851489c1ce8cc179efc2dacc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 16:06:53 -0700 Subject: [PATCH 039/318] added some basic logic for altering compression level --- contrib/adaptive-compression/multi.c | 71 +++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 6aa60315d..2b2aacabf 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,12 +1,13 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define DEBUGLOG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 -#define MAX_NUM_JOBS 100; +#define MAX_NUM_JOBS 2; #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 +#define DEFAULT_ADAPT_PARAM 2 typedef unsigned char BYTE; #include /* fprintf */ @@ -27,7 +28,10 @@ typedef struct { typedef struct { unsigned waitCompleted; unsigned waitReady; - unsigned waitWritten; + unsigned waitWrite; + unsigned readyCounter; + unsigned completedCounter; + unsigned writeCounter; } stat_t; typedef struct { @@ -47,8 +51,9 @@ typedef struct { unsigned threadError; unsigned jobReadyID; unsigned jobCompletedID; - unsigned jobWrittenID; + unsigned jobWriteID; unsigned allJobsCompleted; + unsigned adaptParam; pthread_mutex_t jobCompleted_mutex; pthread_cond_t jobCompleted_cond; pthread_mutex_t jobReady_mutex; @@ -113,12 +118,13 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->numJobs = numJobs; ctx->jobReadyID = 0; ctx->jobCompletedID = 0; - ctx->jobWrittenID = 0; + ctx->jobWriteID = 0; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; + ctx->adaptParam = DEFAULT_ADAPT_PARAM; if (!ctx->jobs) { DISPLAY("Error: could not allocate space for jobs during context creation\n"); freeCCtx(ctx); @@ -148,6 +154,41 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); } +static unsigned adaptCompressionLevel(adaptCCtx* ctx) +{ + unsigned reset = 0; + unsigned const allSlow = ctx->adaptParam < ctx->stats.completedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; + unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; + unsigned const writeWaiting = ctx->adaptParam < ctx->stats.completedCounter ? 1 : 0; + unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter ? 1 : 0; + unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; + unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; + unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; + // unsigned const writeSlow = ((compressWaiting && createWaiting)) ? 1 : 0; + // unsigned const compressSlow = ((writeWaiting && createWaiting)) ? 1 : 0; + // unsigned const createSlow = ((compressWaiting && writeWaiting)) ? 1 : 0; + DEBUGLOG(2, "ready: %u completed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.completedCounter, ctx->stats.writeCounter); + if (allSlow) { + reset = 1; + } + else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { + DEBUGLOG(2, "increasing compression level %u\n", ctx->compressionLevel); + ctx->compressionLevel++; + reset = 1; + } + else if (compressSlow && ctx->compressionLevel > 1) { + DEBUGLOG(2, "decreasing compression level %u\n", ctx->compressionLevel); + ctx->compressionLevel--; + reset = 1; + } + if (reset) { + ctx->stats.readyCounter = 0; + ctx->stats.writeCounter = 0; + ctx->stats.completedCounter = 0; + } + return ctx->compressionLevel; +} + static void* compressionThread(void* arg) { adaptCCtx* ctx = (adaptCCtx*)arg; @@ -159,6 +200,7 @@ static void* compressionThread(void* arg) pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { ctx->stats.waitReady++; + ctx->stats.readyCounter++; DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } @@ -166,7 +208,10 @@ static void* compressionThread(void* arg) // DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); /* compress the data */ { - size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, job->compressionLevel); + unsigned const cLevel = adaptCompressionLevel(ctx); + // unsigned const cLevel = job->compressionLevel; + DEBUGLOG(2, "cLevel used: %u\n", cLevel); + size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, cLevel); if (ZSTD_isError(compressedSize)) { ctx->threadError = 1; DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(compressedSize)); @@ -202,6 +247,7 @@ static void* outputThread(void* arg) pthread_mutex_lock(&ctx->jobCompleted_mutex); while (currJob + 1 > ctx->jobCompletedID) { ctx->stats.waitCompleted++; + ctx->stats.completedCounter++; DEBUGLOG(2, "waiting on job completed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } @@ -225,7 +271,7 @@ static void* outputThread(void* arg) currJob++; DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - ctx->jobWrittenID++; + ctx->jobWriteID++; pthread_cond_signal(&ctx->jobWrite_cond); pthread_mutex_unlock(&ctx->jobWrite_mutex); DEBUGLOG(2, "unlocking job write mutex\n"); @@ -251,14 +297,17 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) jobDescription* job = &ctx->jobs[nextJobIndex]; // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - // DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWrittenID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWrittenID, ctx->numJobs); - while (nextJob - ctx->jobWrittenID >= ctx->numJobs) { - ctx->stats.waitWritten++; - DEBUGLOG(2, "waiting on job written, nextJob: %u\n", nextJob); + // DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWriteID, ctx->numJobs); + while (nextJob - ctx->jobWriteID >= ctx->numJobs) { + ctx->stats.waitWrite++; + ctx->stats.writeCounter++; + DEBUGLOG(2, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); // DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); + + job->compressionLevel = ctx->compressionLevel; job->src.start = malloc(srcSize); job->src.size = srcSize; @@ -287,7 +336,7 @@ static void printStats(stat_t stats) DISPLAY("========STATISTICS========\n"); DISPLAY("# times waited on job ready: %u\n", stats.waitReady); DISPLAY("# times waited on job completed: %u\n", stats.waitCompleted); - DISPLAY("# times waited on job written: %u\n\n", stats.waitWritten); + DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite); } static int compressFilename(const char* const srcFilename, const char* const dstFilename) From 3bbfa1249e8ef2f2b966f6b71a75d8cfbb53089a Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 6 Jul 2017 16:47:08 -0700 Subject: [PATCH 040/318] Update compressor and decompressor --- contrib/long_distance_matching/ldm.c | 40 ++++++++++++++++++----- contrib/long_distance_matching/main-ldm.c | 4 +-- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index c8051ea4d..908ac2ac7 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -20,6 +20,8 @@ #define RUN_BITS (8-ML_BITS) #define RUN_MASK ((1U<= 255; len -= 255) { - *op++ = (BYTE)len; + *op++ = 255; } + *op++ = (BYTE)len; } else { *token = (BYTE)(litLength << ML_BITS); } - +#ifdef LDM_DEBUG printf("Literals "); fwrite(anchor, litLength, 1, stdout); printf("\n"); - +#endif LDM_wild_copy(op, anchor, op + litLength); op += litLength; } @@ -232,10 +238,11 @@ _next_match: unsigned matchCode; matchCode = LDM_count(ip + MINMATCH, match + MINMATCH, matchlimit); - +#ifdef LDM_DEBUG printf("Match length %zu\n", matchCode + MINMATCH); fwrite(ip, MINMATCH + matchCode, 1, stdout); printf("\n"); +#endif ip += MINMATCH + matchCode; if (matchCode >= ML_MASK) { *token += ML_MASK; @@ -251,7 +258,9 @@ _next_match: } else { *token += (BYTE)(matchCode); } +#ifdef LDM_DEBUG printf("\n"); +#endif } anchor = ip; @@ -308,24 +317,33 @@ size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, length += s; } while (s == 255); } +#ifdef LDM_DEBUG printf("Literal length: %zu\n", length); +#endif /* copy literals */ cpy = op + length; +#ifdef LDM_DEBUG + printf("Literals "); + fwrite(ip, length, 1, stdout); + printf("\n"); +#endif LDM_wild_copy(op, ip, cpy); ip += length; op = cpy; /* get offset */ offset = LDM_readLE16(ip); + +#ifdef LDM_DEBUG printf("Offset: %zu\n", offset); +#endif ip += 2; match = op - offset; // LDM_write32(op, (U32)offset); /* get matchlength */ length = token & ML_MASK; - printf("Match length: %zu\n", length); if (length == ML_MASK) { unsigned s; do { @@ -334,16 +352,20 @@ size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, } while (s == 255); } length += MINMATCH; - +#ifdef LDM_DEBUG + printf("Match length: %zu\n", length); +#endif /* copy match */ cpy = op + length; - - + // Inefficient for now + while (match < cpy - offset) { + *op++ = *match++; + } } // memcpy(dest, source, compressed_size); - return compressed_size; + return op - (BYTE *)dest; } diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 8b97ce92f..7f1abdabf 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -229,7 +229,7 @@ static size_t decompress(const char *fname, const char *oname) { } /* go to the location corresponding to the last byte */ - if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1) { + if (lseek(fdout, 2*statbuf.st_size - 1, SEEK_SET) == -1) { perror("lseek error"); return 1; } @@ -264,7 +264,7 @@ static size_t decompress(const char *fname, const char *oname) { size_t size_out = LDM_decompress(src, dst, statbuf.st_size, statbuf.st_size); #endif - ftruncate(fdout, size_out); + //ftruncate(fdout, size_out); close(fdin); close(fdout); From b6cc0847162ff6eb7e7398f92736ecf4941b614c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 17:48:18 -0700 Subject: [PATCH 041/318] added really simple progress update in the corner --- contrib/adaptive-compression/multi.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 2b2aacabf..324a570ba 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -14,11 +14,14 @@ typedef unsigned char BYTE; #include /* malloc, free */ #include /* pthread functions */ #include /* memset */ +#include /* clock(), CLOCKS_PER_SEC */ #include "zstd.h" static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; static unsigned g_displayStats = 0; +static clock_t g_time = 0; +static clock_t const refreshRate = CLOCKS_PER_SEC / 60; /* 60 Hz */ typedef struct { void* start; @@ -235,6 +238,16 @@ static void* compressionThread(void* arg) return arg; } +static void displayProgress(unsigned jobDoneID) +{ + clock_t currTime = clock(); + unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; + if (refresh) { + fprintf(stdout, "%u jobs completed\r", jobDoneID+1); + fflush(stdout); + } +} + static void* outputThread(void* arg) { adaptCCtx* ctx = (adaptCCtx*)arg; @@ -268,6 +281,7 @@ static void* outputThread(void* arg) } } DEBUGLOG(2, "finished job write %u\n", currJob); + displayProgress(currJob); currJob++; DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); @@ -348,6 +362,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; + g_time = clock(); /* checking for errors */ From 57ec0232a832ad93c1b9b57145e6a21c6c41da84 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 18:09:10 -0700 Subject: [PATCH 042/318] added help menu --- contrib/adaptive-compression/multi.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 324a570ba..4e33a0514 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,4 +1,5 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define PRINT(...) fprintf(stdout, __VA_ARGS__) #define DEBUGLOG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 #define MAX_NUM_JOBS 2; @@ -243,7 +244,7 @@ static void displayProgress(unsigned jobDoneID) clock_t currTime = clock(); unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; if (refresh) { - fprintf(stdout, "%u jobs completed\r", jobDoneID+1); + fprintf(stdout, "\r%u jobs completed", jobDoneID+1); fflush(stdout); } } @@ -468,6 +469,18 @@ static unsigned readU32FromChar(const char** stringPtr) return result; } +static void help() +{ + PRINT("Usage:\n"); + PRINT(" ./multi [options] [file(s)]\n"); + PRINT("\n"); + PRINT("Options:\n"); + PRINT(" -oFILE : specify the output file name\n"); + PRINT(" -v : display debug information\n"); + PRINT(" -i# : provide initial compression level\n"); + PRINT(" -s : display information stats\n"); + PRINT(" -h : display help/information\n"); +} /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { @@ -507,6 +520,10 @@ int main(int argCount, const char* argv[]) g_displayStats = 1; continue; } + else if (strlen(argument) > 1 && argument[1] == 'h') { + help(); + return 0; + } else { DISPLAY("Error: invalid argument provided\n"); ret = 1; From 2939301023cdda52bd6e7883206a58722f861463 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 20:30:20 -0700 Subject: [PATCH 043/318] fixed problem with progress bar not persisting, added time elapsed --- contrib/adaptive-compression/multi.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 4e33a0514..814018d68 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -22,6 +22,7 @@ static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; static unsigned g_displayStats = 0; static clock_t g_time = 0; +static clock_t g_startTime = 0; static clock_t const refreshRate = CLOCKS_PER_SEC / 60; /* 60 Hz */ typedef struct { @@ -239,13 +240,19 @@ static void* compressionThread(void* arg) return arg; } -static void displayProgress(unsigned jobDoneID) +static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) { clock_t currTime = clock(); unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; + double timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC); if (refresh) { - fprintf(stdout, "\r%u jobs completed", jobDoneID+1); - fflush(stdout); + fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0fms |", jobDoneID, cLevel, timeElapsed); + if (last) { + fprintf(stdout, "\n"); + } + else { + fflush(stdout); + } } } @@ -282,8 +289,8 @@ static void* outputThread(void* arg) } } DEBUGLOG(2, "finished job write %u\n", currJob); - displayProgress(currJob); currJob++; + displayProgress(currJob, ctx->compressionLevel, currJob >= ctx->lastJobID); DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); ctx->jobWriteID++; @@ -364,6 +371,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst int ret = 0; adaptCCtx* ctx = NULL; g_time = clock(); + g_startTime = clock(); /* checking for errors */ From f351848b76d6c3f780f1679556eb269dec81d4d8 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 20:40:00 -0700 Subject: [PATCH 044/318] added data amount --- contrib/adaptive-compression/multi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 814018d68..7c769e499 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -24,6 +24,7 @@ static unsigned g_displayStats = 0; static clock_t g_time = 0; static clock_t g_startTime = 0; static clock_t const refreshRate = CLOCKS_PER_SEC / 60; /* 60 Hz */ +static size_t g_streamedSize = 0; typedef struct { void* start; @@ -244,9 +245,10 @@ static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) { clock_t currTime = clock(); unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; - double timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC); + double const timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC); + double const sizeMB = (double)g_streamedSize / (1 << 20); if (refresh) { - fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0fms |", jobDoneID, cLevel, timeElapsed); + fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB |", jobDoneID, cLevel, timeElapsed, sizeMB); if (last) { fprintf(stdout, "\n"); } @@ -372,6 +374,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst adaptCCtx* ctx = NULL; g_time = clock(); g_startTime = clock(); + g_streamedSize = 0; /* checking for errors */ @@ -416,6 +419,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst ret = 1; goto cleanup; } + g_streamedSize += readSize; /* reading was fine, now create the compression job */ { int const error = createCompressionJob(ctx, src, readSize); From 00bc5df4e0de9811a3579052b0fbeaa949aecb93 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 09:35:39 -0700 Subject: [PATCH 045/318] added compression rate to status bar --- contrib/adaptive-compression/multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 7c769e499..f8568c42f 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -247,8 +247,9 @@ static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; double const timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC); double const sizeMB = (double)g_streamedSize / (1 << 20); + double const avgCompRate = sizeMB / timeElapsed; if (refresh) { - fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB |", jobDoneID, cLevel, timeElapsed, sizeMB); + fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); if (last) { fprintf(stdout, "\n"); } From 4679132f59f02aa2c8fc3a3a3bf7a561a22e58f4 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 10:25:38 -0700 Subject: [PATCH 046/318] updated avg compression rate, also hiding progress bar behind a flag now --- contrib/adaptive-compression/multi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index f8568c42f..5223d7068 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -25,6 +25,7 @@ static clock_t g_time = 0; static clock_t g_startTime = 0; static clock_t const refreshRate = CLOCKS_PER_SEC / 60; /* 60 Hz */ static size_t g_streamedSize = 0; +static unsigned g_useProgressBar = 0; typedef struct { void* start; @@ -243,11 +244,12 @@ static void* compressionThread(void* arg) static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) { + if (!g_useProgressBar) return; clock_t currTime = clock(); unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; double const timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC); double const sizeMB = (double)g_streamedSize / (1 << 20); - double const avgCompRate = sizeMB / timeElapsed; + double const avgCompRate = sizeMB * 1000 / timeElapsed; if (refresh) { fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); if (last) { @@ -537,6 +539,10 @@ int main(int argCount, const char* argv[]) help(); return 0; } + else if (strlen(argument) > 1 && argument[1] == 'p') { + g_useProgressBar = 1; + continue; + } else { DISPLAY("Error: invalid argument provided\n"); ret = 1; From f7e6b358d04d8b1da2599ed039c1c12d301db6f2 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 10:29:06 -0700 Subject: [PATCH 047/318] added tests that check to ensure stdout is working --- contrib/adaptive-compression/run.sh | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index b906ae7d1..f030be555 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -76,6 +76,42 @@ diff tmp tests/test.pdf echo "diff test complete: test.pdf" rm tmp* +cat tests/test2048.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test2048.pdf +echo "diff test complete: test2048.pdf" +rm tmp* + +cat tests/test512.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test512.pdf +echo "diff test complete: test512.pdf" +rm tmp* + +cat tests/test64.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test64.pdf +echo "diff test complete: test64.pdf" +rm tmp* + +cat tests/test16.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test16.pdf +echo "diff test complete: test16.pdf" +rm tmp* + +cat tests/test4.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test4.pdf +echo "diff test complete: test4.pdf" +rm tmp* + +cat tests/test.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test.pdf +echo "diff test complete: test.pdf" +rm tmp* + echo "Running multi-file tests" ./multi tests/* zstd -d tests/test.pdf.zst -o tests/tmp From 532f439961747c60d13d8dde0dee4aafb1342426 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 10:58:43 -0700 Subject: [PATCH 048/318] cleaned up code for arguments a bit --- contrib/adaptive-compression/multi.c | 60 +++++++++++++--------------- contrib/adaptive-compression/run.sh | 3 ++ 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 5223d7068..d88d37878 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -515,39 +515,35 @@ int main(int argCount, const char* argv[]) const char* argument = argv[argNum]; /* output filename designated with "-o" */ - if (argument[0]=='-') { - if (strlen(argument) > 1 && argument[1] == 'o') { - argument += 2; - outFilename = argument; - continue; - } - else if (strlen(argument) > 1 && argument[1] == 'v') { - g_displayLevel++; - continue; - } - else if (strlen(argument) > 1 && argument[1] == 'i') { - argument += 2; - g_compressionLevel = readU32FromChar(&argument); - DEBUGLOG(2, "g_compressionLevel: %u\n", g_compressionLevel); - continue; - } - else if (strlen(argument) > 1 && argument[1] == 's') { - g_displayStats = 1; - continue; - } - else if (strlen(argument) > 1 && argument[1] == 'h') { - help(); - return 0; - } - else if (strlen(argument) > 1 && argument[1] == 'p') { - g_useProgressBar = 1; - continue; - } - else { - DISPLAY("Error: invalid argument provided\n"); - ret = 1; - goto _main_exit; + if (argument[0]=='-' && strlen(argument) > 1) { + switch (argument[1]) { + case 'o': + argument += 2; + outFilename = argument; + break; + case 'v': + g_displayLevel++; + break; + case 'i': + argument += 2; + g_compressionLevel = readU32FromChar(&argument); + DEBUGLOG(2, "g_compressionLevel: %u\n", g_compressionLevel); + break; + case 's': + g_displayStats = 1; + break; + case 'h': + help(); + goto _main_exit; + case 'p': + g_useProgressBar = 1; + break; + default: + DISPLAY("Error: invalid argument provided\n"); + ret = 1; + goto _main_exit; } + continue; } /* regular files to be compressed */ diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index f030be555..bcd8c7188 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -140,6 +140,9 @@ diff tests/test512.pdf tests/tmp512 diff tests/test1024.pdf tests/tmp1024 diff tests/test2048.pdf tests/tmp2048 +echo "Running Args Tests" +./multi -h +./multi -i22 -p -s -otmp.zst tests/test2048.pdf echo "finished with tests" make clean From 70a4153bd38ddf2f44067193c5fcdf7b1273073b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 11:32:14 -0700 Subject: [PATCH 049/318] added ability to force output to stdout, wrote an additional test for this functionality --- contrib/adaptive-compression/multi.c | 20 +++++++++++++++----- contrib/adaptive-compression/run.sh | 8 ++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index d88d37878..7802c429d 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -95,7 +95,7 @@ static int freeCCtx(adaptCCtx* ctx) int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex); int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); - int const fileCloseError = ctx->dstFile != NULL ? fclose(ctx->dstFile) : 0; + int const fileCloseError = (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; if (ctx->jobs){ freeCompressionJobs(ctx); free(ctx->jobs); @@ -448,7 +448,7 @@ cleanup: return ret; } -static int compressFilenames(const char** filenameTable, unsigned numFiles) +static int compressFilenames(const char** filenameTable, unsigned numFiles, unsigned forceStdout) { int ret = 0; unsigned fileNum; @@ -459,7 +459,13 @@ static int compressFilenames(const char** filenameTable, unsigned numFiles) DISPLAY("Error: output filename is too long\n"); return 1; } - ret |= compressFilename(filename, outFile); + if (!forceStdout) { + ret |= compressFilename(filename, outFile); + } + else { + ret |= compressFilename(filename, stdoutmark); + } + } return ret; } @@ -503,6 +509,7 @@ int main(int argCount, const char* argv[]) const char** filenameTable = (const char**)malloc(argCount*sizeof(const char*)); unsigned filenameIdx = 0; filenameTable[0] = stdinmark; + unsigned forceStdout = 0; int ret = 0; int argNum; @@ -538,6 +545,9 @@ int main(int argCount, const char* argv[]) case 'p': g_useProgressBar = 1; break; + case 'c': + forceStdout = 1; + break; default: DISPLAY("Error: invalid argument provided\n"); ret = 1; @@ -551,7 +561,7 @@ int main(int argCount, const char* argv[]) } /* error checking with number of files */ - if (filenameIdx > 1 && outFilename != NULL) { + if (filenameIdx > 1 && (outFilename != NULL && strcmp(outFilename, stdoutmark))) { DISPLAY("Error: multiple input files provided, cannot use specified output file\n"); ret = 1; goto _main_exit; @@ -562,7 +572,7 @@ int main(int argCount, const char* argv[]) ret |= compressFilename(filenameTable[0], outFilename); } else { - ret |= compressFilenames(filenameTable, filenameIdx); + ret |= compressFilenames(filenameTable, filenameIdx, forceStdout); } _main_exit: free(filenameTable); diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index bcd8c7188..67814ec1b 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -140,9 +140,17 @@ diff tests/test512.pdf tests/tmp512 diff tests/test1024.pdf tests/tmp1024 diff tests/test2048.pdf tests/tmp2048 +rm -f tests/*.zst tests/tmp* echo "Running Args Tests" ./multi -h ./multi -i22 -p -s -otmp.zst tests/test2048.pdf +rm tmp* + +echo "Running Tests With Multiple Files > stdout" +./multi tests/* -c > tmp.zst +zstd -d tmp.zst +rm tmp* + echo "finished with tests" make clean From 8c0eb62920f0badddabedd4804b79806c1bf2a73 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 11:47:16 -0700 Subject: [PATCH 050/318] removed unnecessary comments, uncommented DEBUGLOG for later use --- contrib/adaptive-compression/multi.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 7802c429d..acb06f074 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -171,9 +171,6 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; - // unsigned const writeSlow = ((compressWaiting && createWaiting)) ? 1 : 0; - // unsigned const compressSlow = ((writeWaiting && createWaiting)) ? 1 : 0; - // unsigned const createSlow = ((compressWaiting && writeWaiting)) ? 1 : 0; DEBUGLOG(2, "ready: %u completed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.completedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; @@ -203,7 +200,7 @@ static void* compressionThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - // DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); + DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { ctx->stats.waitReady++; @@ -212,11 +209,10 @@ static void* compressionThread(void* arg) pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } pthread_mutex_unlock(&ctx->jobReady_mutex); - // DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); + DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); - // unsigned const cLevel = job->compressionLevel; DEBUGLOG(2, "cLevel used: %u\n", cLevel); size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, cLevel); if (ZSTD_isError(compressedSize)) { @@ -269,7 +265,7 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - // DEBUGLOG(2, "outputThread(): waiting on job completed\n"); + DEBUGLOG(2, "outputThread(): waiting on job completed\n"); pthread_mutex_lock(&ctx->jobCompleted_mutex); while (currJob + 1 > ctx->jobCompletedID) { ctx->stats.waitCompleted++; @@ -278,7 +274,7 @@ static void* outputThread(void* arg) pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } pthread_mutex_unlock(&ctx->jobCompleted_mutex); - // DEBUGLOG(2, "outputThread(): continuing after job completed\n"); + DEBUGLOG(2, "outputThread(): continuing after job completed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { @@ -322,9 +318,9 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; - // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); + DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - // DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWriteID, ctx->numJobs); + DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; @@ -332,7 +328,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); - // DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); + DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); job->compressionLevel = ctx->compressionLevel; From f791fc27e3faae15eef3dfa7ce768a05cd2773cb Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Fri, 7 Jul 2017 12:44:29 -0700 Subject: [PATCH 051/318] Add header with compress and decompress size --- contrib/long_distance_matching/Makefile | 6 +-- contrib/long_distance_matching/ldm.c | 16 +++--- contrib/long_distance_matching/ldm.h | 7 +++ contrib/long_distance_matching/main-ldm.c | 61 +++++++++++++++-------- 4 files changed, 57 insertions(+), 33 deletions(-) diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 0efae69b5..4e04fd6a2 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -16,11 +16,11 @@ LDFLAGS += -lzstd default: all -all: main main-ldm +all: main-ldm -main : ldm.c main.c - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ +#main : ldm.c main.c +# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ main-ldm : ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 908ac2ac7..cb90efecb 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -69,8 +69,6 @@ static void LDM_writeLE16(void *memPtr, U16 value) { } } - - static U32 LDM_read32(const void *ptr) { return *(const U32 *)ptr; } @@ -98,17 +96,13 @@ struct hash_entry { }; static U32 LDM_hash(U32 sequence) { - return ((sequence * 2654435761U) >> ((MINMATCH*8)-LDM_HASHLOG)); + return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); } static U32 LDM_hash_position(const void * const p) { return LDM_hash(LDM_read32(p)); } -static U64 find_best_match(tag t, U64 offset) { - return 0; -} - static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, const BYTE *srcBase) { U32 *hashTable = (U32 *) tableBase; @@ -148,6 +142,12 @@ static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, return (unsigned)(pIn - pStart); } +void LDM_read_header(void const *source, size_t *compressed_size, + size_t *decompressed_size) { + U32 *ip = (U32 *)source; + *compressed_size = *ip++; + *decompressed_size = *ip; +} size_t LDM_compress(void const *source, void *dest, size_t source_size, size_t max_dest_size) { @@ -359,7 +359,7 @@ size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, cpy = op + length; // Inefficient for now - while (match < cpy - offset) { + while (match < cpy - offset && op < oend) { *op++ = *match++; } } diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 0aab6aa3b..f4ca25a38 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -3,10 +3,17 @@ #include /* size_t */ +#define LDM_COMPRESS_SIZE 4 +#define LDM_DECOMPRESS_SIZE 4 +#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) + size_t LDM_compress(void const *source, void *dest, size_t source_size, size_t max_dest_size); size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, size_t max_decompressed_size); +void LDM_read_header(void const *source, size_t *compressed_size, + size_t *decompressed_size); + #endif /* LDM_H */ diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 7f1abdabf..4d54ef6d2 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -11,10 +11,10 @@ #include #include "ldm.h" -#define BUF_SIZE 16*1024 // Block size -#define LDM_HEADER_SIZE 8 +// #define BUF_SIZE 16*1024 // Block size #define DEBUG -// #define ZSTD + +//#define ZSTD #if 0 static size_t compress_file(FILE *in, FILE *out, size_t *size_in, @@ -159,9 +159,10 @@ static size_t compress(const char *fname, const char *oname) { perror("Fstat error"); return 1; } + size_t size_in = statbuf.st_size; /* go to the location corresponding to the last byte */ - if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1) { + if (lseek(fdout, size_in + LDM_HEADER_SIZE - 1, SEEK_SET) == -1) { perror("lseek error"); return 1; } @@ -178,24 +179,31 @@ static size_t compress(const char *fname, const char *oname) { perror("mmap error for input"); return 1; } + size_t out_size = statbuf.st_size + LDM_HEADER_SIZE; /* mmap the output file */ - if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, + if ((dst = mmap(0, out_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { perror("mmap error for output"); return 1; } - /* Copy input file to output file */ -// memcpy(dst, src, statbuf.st_size); #ifdef ZSTD size_t size_out = ZSTD_compress(dst, statbuf.st_size, src, statbuf.st_size, 1); #else - size_t size_out = LDM_compress(src, dst, statbuf.st_size, + size_t size_out = LDM_compress(src, dst + LDM_HEADER_SIZE, statbuf.st_size, statbuf.st_size); + size_out += LDM_HEADER_SIZE; + + // TODO: should depend on LDM_DECOMPRESS_SIZE write32 + memcpy(dst, &size_out, 4); + memcpy(dst + 4, &(statbuf.st_size), 4); + printf("Compressed size: %zu\n", size_out); + printf("Decompressed size: %zu\n", statbuf.st_size); #endif ftruncate(fdout, size_out); + printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, (unsigned)statbuf.st_size, (unsigned)size_out, oname, (double)size_out / (statbuf.st_size) * 100); @@ -228,8 +236,22 @@ static size_t decompress(const char *fname, const char *oname) { 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 header */ + size_t compressed_size, decompressed_size; + LDM_read_header(src, &compressed_size, &decompressed_size); + + printf("Size, compressed_size, decompressed_size: %zu %zu %zu\n", + statbuf.st_size, compressed_size, decompressed_size); + /* go to the location corresponding to the last byte */ - if (lseek(fdout, 2*statbuf.st_size - 1, SEEK_SET) == -1) { + if (lseek(fdout, decompressed_size - 1, SEEK_SET) == -1) { perror("lseek error"); return 1; } @@ -240,15 +262,8 @@ static size_t decompress(const char *fname, const char *oname) { 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; - } - /* mmap the output file */ - if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, + if ((dst = mmap(0, decompressed_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { perror("mmap error for output"); return 1; @@ -258,13 +273,15 @@ static size_t decompress(const char *fname, const char *oname) { // memcpy(dst, src, statbuf.st_size); #ifdef ZSTD - size_t size_out = ZSTD_decompress(dst, statbuf.st_size, - src, statbuf.st_size); + size_t size_out = ZSTD_decompress(dst, decomrpessed_size, + src + LDM_HEADER_SIZE, + statbuf.st_size - LDM_HEADER_SIZE); #else - size_t size_out = LDM_decompress(src, dst, statbuf.st_size, - statbuf.st_size); + size_t size_out = LDM_decompress(src + LDM_HEADER_SIZE, dst, + statbuf.st_size - LDM_HEADER_SIZE, + decompressed_size); #endif - //ftruncate(fdout, size_out); + ftruncate(fdout, size_out); close(fdin); close(fdout); From 09d7c6a994d5f7a67a7442ad92d4dfc2e0a06bda Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 13:18:55 -0700 Subject: [PATCH 052/318] changed completed variables to compressed for clarity --- contrib/adaptive-compression/multi.c | 54 ++++++++++++++-------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index acb06f074..03667d48f 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -33,11 +33,11 @@ typedef struct { } buffer_t; typedef struct { - unsigned waitCompleted; + unsigned waitCompressed; unsigned waitReady; unsigned waitWrite; unsigned readyCounter; - unsigned completedCounter; + unsigned compressedCounter; unsigned writeCounter; } stat_t; @@ -57,12 +57,12 @@ typedef struct { unsigned nextJobID; unsigned threadError; unsigned jobReadyID; - unsigned jobCompletedID; + unsigned jobCompressedID; unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - pthread_mutex_t jobCompleted_mutex; - pthread_cond_t jobCompleted_cond; + pthread_mutex_t jobCompressed_mutex; + pthread_cond_t jobCompressed_cond; pthread_mutex_t jobReady_mutex; pthread_cond_t jobReady_cond; pthread_mutex_t allJobsCompleted_mutex; @@ -87,8 +87,8 @@ static void freeCompressionJobs(adaptCCtx* ctx) static int freeCCtx(adaptCCtx* ctx) { { - int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); - int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); + int const compressedMutexError = pthread_mutex_destroy(&ctx->jobCompressed_mutex); + int const compressedCondError = pthread_cond_destroy(&ctx->jobCompressed_cond); int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); @@ -100,7 +100,7 @@ static int freeCCtx(adaptCCtx* ctx) freeCompressionJobs(ctx); free(ctx->jobs); } - return completedMutexError | completedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError; + return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError; } } @@ -114,8 +114,8 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) } memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = g_compressionLevel; - pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); /* TODO: add checks for errors on each mutex */ - pthread_cond_init(&ctx->jobCompleted_cond, NULL); + pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); /* TODO: add checks for errors on each mutex */ + pthread_cond_init(&ctx->jobCompressed_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); pthread_mutex_init(&ctx->allJobsCompleted_mutex, NULL); @@ -124,7 +124,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) pthread_cond_init(&ctx->jobWrite_cond, NULL); ctx->numJobs = numJobs; ctx->jobReadyID = 0; - ctx->jobCompletedID = 0; + ctx->jobCompressedID = 0; ctx->jobWriteID = 0; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -164,14 +164,14 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) static unsigned adaptCompressionLevel(adaptCCtx* ctx) { unsigned reset = 0; - unsigned const allSlow = ctx->adaptParam < ctx->stats.completedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; + unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; - unsigned const writeWaiting = ctx->adaptParam < ctx->stats.completedCounter ? 1 : 0; + unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter ? 1 : 0; unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter ? 1 : 0; unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; - DEBUGLOG(2, "ready: %u completed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.completedCounter, ctx->stats.writeCounter); + DEBUGLOG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; } @@ -188,7 +188,7 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) if (reset) { ctx->stats.readyCounter = 0; ctx->stats.writeCounter = 0; - ctx->stats.completedCounter = 0; + ctx->stats.compressedCounter = 0; } return ctx->compressionLevel; } @@ -222,11 +222,11 @@ static void* compressionThread(void* arg) } job->compressedSize = compressedSize; } - pthread_mutex_lock(&ctx->jobCompleted_mutex); - ctx->jobCompletedID++; + pthread_mutex_lock(&ctx->jobCompressed_mutex); + ctx->jobCompressedID++; DEBUGLOG(2, "signaling for job %u\n", currJob); - pthread_cond_signal(&ctx->jobCompleted_cond); - pthread_mutex_unlock(&ctx->jobCompleted_mutex); + pthread_cond_signal(&ctx->jobCompressed_cond); + pthread_mutex_unlock(&ctx->jobCompressed_mutex); DEBUGLOG(2, "finished job compression %u\n", currJob); currJob++; if (currJob >= ctx->lastJobID || ctx->threadError) { @@ -266,14 +266,14 @@ static void* outputThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUGLOG(2, "outputThread(): waiting on job completed\n"); - pthread_mutex_lock(&ctx->jobCompleted_mutex); - while (currJob + 1 > ctx->jobCompletedID) { - ctx->stats.waitCompleted++; - ctx->stats.completedCounter++; + pthread_mutex_lock(&ctx->jobCompressed_mutex); + while (currJob + 1 > ctx->jobCompressedID) { + ctx->stats.waitCompressed++; + ctx->stats.compressedCounter++; DEBUGLOG(2, "waiting on job completed, nextJob: %u\n", currJob); - pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); + pthread_cond_wait(&ctx->jobCompressed_cond, &ctx->jobCompressed_mutex); } - pthread_mutex_unlock(&ctx->jobCompleted_mutex); + pthread_mutex_unlock(&ctx->jobCompressed_mutex); DEBUGLOG(2, "outputThread(): continuing after job completed\n"); { size_t const compressedSize = job->compressedSize; @@ -320,7 +320,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) jobDescription* job = &ctx->jobs[nextJobIndex]; DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWriteID, ctx->numJobs); + DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; @@ -358,7 +358,7 @@ static void printStats(stat_t stats) { DISPLAY("========STATISTICS========\n"); DISPLAY("# times waited on job ready: %u\n", stats.waitReady); - DISPLAY("# times waited on job completed: %u\n", stats.waitCompleted); + DISPLAY("# times waited on job compressed: %u\n", stats.waitCompressed); DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite); } From 11fc0f41197f885def5744e50dfe5418364ca724 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 13:55:38 -0700 Subject: [PATCH 053/318] changed completed -> compressed --- contrib/adaptive-compression/multi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 03667d48f..647d54ba4 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -265,16 +265,16 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUGLOG(2, "outputThread(): waiting on job completed\n"); + DEBUGLOG(2, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex); while (currJob + 1 > ctx->jobCompressedID) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; - DEBUGLOG(2, "waiting on job completed, nextJob: %u\n", currJob); + DEBUGLOG(2, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond, &ctx->jobCompressed_mutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex); - DEBUGLOG(2, "outputThread(): continuing after job completed\n"); + DEBUGLOG(2, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { From 7945f9ee4757f045d4006813cd1f3e1ddfafa85c Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Fri, 7 Jul 2017 14:14:01 -0700 Subject: [PATCH 054/318] Fix offset overflow bug --- contrib/long_distance_matching/ldm.c | 36 ++++++++++++++--------- contrib/long_distance_matching/main-ldm.c | 1 + 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index cb90efecb..b02869fef 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -11,7 +11,7 @@ #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) #define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) -#define WINDOW_SIZE (1 << 20) +#define WINDOW_SIZE (1 << 15) #define HASH_SIZE 4 #define MINMATCH 4 @@ -144,7 +144,7 @@ static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, void LDM_read_header(void const *source, size_t *compressed_size, size_t *decompressed_size) { - U32 *ip = (U32 *)source; + const U32 *ip = (const U32 *)source; *compressed_size = *ip++; *decompressed_size = *ip; } @@ -156,6 +156,7 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size, const BYTE * const iend = istart + source_size; const BYTE *ilimit = iend - HASH_SIZE; const BYTE * const matchlimit = iend - HASH_SIZE; + const BYTE * const mflimit = iend - MINMATCH; BYTE *op = (BYTE*) dest; U32 hashTable[LDM_HASHTABLESIZE_U32]; memset(hashTable, 0, sizeof(hashTable)); @@ -172,6 +173,7 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size, ip++; forwardH = LDM_hash_position(ip); + //TODO Loop terminates before ip>=ilimit. while (ip < ilimit) { const BYTE *match; BYTE *token; @@ -186,6 +188,10 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size, ip = forwardIp; forwardIp += step; + if (forwardIp > mflimit) { + goto _last_literals; + } + match = LDM_get_position_on_hash(h, hashTable, istart); forwardH = LDM_hash_position(forwardIp); @@ -194,6 +200,12 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size, LDM_read32(match) != LDM_read32(ip)); } + // TODO catchup + while (ip > anchor && match > istart && ip[-1] == match[-1]) { + ip--; + match--; + } + /* Encode literals */ { unsigned const litLength = (unsigned)(ip - anchor); @@ -223,7 +235,8 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size, fwrite(anchor, litLength, 1, stdout); printf("\n"); #endif - LDM_wild_copy(op, anchor, op + litLength); + memcpy(op, anchor, litLength); + //LDM_wild_copy(op, anchor, op + litLength); op += litLength; } _next_match: @@ -268,29 +281,22 @@ _next_match: LDM_put_position(ip, hashTable, istart); forwardH = LDM_hash_position(++ip); } +_last_literals: /* Encode last literals */ { - /* size_t const lastRun = (size_t)(iend - anchor); - printf("last run length: %zu, %zu %zu %zu %zu\n", lastRun, iend-istart, - anchor-istart, ip-istart, ilimit-istart); if (lastRun >= RUN_MASK) { size_t accumulator = lastRun - RUN_MASK; *op++ = RUN_MASK << ML_BITS; for(; accumulator >= 255; accumulator -= 255) { *op++ = 255; } - *op++ = (BYTE) accumulator; + *op++ = (BYTE)accumulator; } else { *op++ = (BYTE)(lastRun << ML_BITS); } - fwrite(anchor, lastRun, 1, stdout); - printf("^last run\n"); memcpy(op, anchor, lastRun); op += lastRun; - -// memcpy(dest + (ip - istart), ip, 1); -// */ } return (op - (BYTE *)dest); } @@ -328,7 +334,8 @@ size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, fwrite(ip, length, 1, stdout); printf("\n"); #endif - LDM_wild_copy(op, ip, cpy); + memcpy(op, ip, length); +// LDM_wild_copy(op, ip, cpy); ip += length; op = cpy; @@ -358,12 +365,13 @@ size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, /* copy match */ cpy = op + length; +// printf("TMP_PREV: %zu\n", op - (BYTE *)dest); // Inefficient for now while (match < cpy - offset && op < oend) { *op++ = *match++; } +// printf("TMP: %zu\n", op - (BYTE *)dest); } - // memcpy(dest, source, compressed_size); return op - (BYTE *)dest; } diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 4d54ef6d2..26db1e946 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -280,6 +280,7 @@ static size_t decompress(const char *fname, const char *oname) { size_t size_out = LDM_decompress(src + LDM_HEADER_SIZE, dst, statbuf.st_size - LDM_HEADER_SIZE, decompressed_size); + printf("Ret size out: %zu\n", size_out); #endif ftruncate(fdout, size_out); From e622330a3bdf0ad8b35315b40f84fbf2bda4ad52 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 7 Jul 2017 14:19:01 -0700 Subject: [PATCH 055/318] extended frameHeader.windowSize to unsigned long long --- doc/zstd_manual.html | 2 +- lib/decompress/zstd_decompress.c | 6 ++++-- lib/zstd.h | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 0c82115b3..6ac5573f0 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -346,7 +346,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB

    typedef struct {
         unsigned long long frameContentSize;
    -    size_t windowSize;
    +    unsigned long long windowSize;   /* can be == frameContentSize */
         unsigned dictID;
         unsigned checksumFlag;
     } ZSTD_frameHeader;
    diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
    index 6eca1c471..5e2776579 100644
    --- a/lib/decompress/zstd_decompress.c
    +++ b/lib/decompress/zstd_decompress.c
    @@ -375,7 +375,8 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize)
         }
     #endif
         {   ZSTD_frameHeader fParams;
    -        if (ZSTD_getFrameHeader(&fParams, src, srcSize) != 0) return ZSTD_CONTENTSIZE_ERROR;
    +        if (ZSTD_getFrameHeader(&fParams, src, srcSize) != 0)
    +            return ZSTD_CONTENTSIZE_ERROR;
             if (fParams.windowSize == 0) {
                 /* Either skippable or empty frame, size == 0 either way */
                 return 0;
    @@ -442,7 +443,8 @@ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize)
     *   compatible with legacy mode
     *   @return : decompressed size if known, 0 otherwise
                   note : 0 can mean any of the following :
    -                   - decompressed size is not present within frame header
    +                   - frame content is empty
    +                   - decompressed size field is not present in frame header
                        - frame header unknown / not supported
                        - frame header not complete (`srcSize` too small) */
     unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize)
    diff --git a/lib/zstd.h b/lib/zstd.h
    index 58e9a5606..1314b8296 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -427,7 +427,7 @@ typedef struct {
     
     typedef struct {
         unsigned long long frameContentSize;
    -    size_t windowSize;
    +    unsigned long long windowSize;   /* can be == frameContentSize */
         unsigned dictID;
         unsigned checksumFlag;
     } ZSTD_frameHeader;
    
    From 4076be09ec17c53680927396ffd85e02e10160ad Mon Sep 17 00:00:00 2001
    From: Stella Lau 
    Date: Fri, 7 Jul 2017 14:52:40 -0700
    Subject: [PATCH 056/318] [ldm] Update to hash every position
    
    ---
     contrib/long_distance_matching/ldm.c | 48 ++++++++++++++++++++--------
     1 file changed, 34 insertions(+), 14 deletions(-)
    
    diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c
    index b02869fef..b03e43686 100644
    --- a/contrib/long_distance_matching/ldm.c
    +++ b/contrib/long_distance_matching/ldm.c
    @@ -11,7 +11,8 @@
     #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2)
     #define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG))
     
    -#define WINDOW_SIZE (1 << 15)
    +#define WINDOW_SIZE (1 << 20)
    +#define MAX_WINDOW_SIZE 31
     #define HASH_SIZE 4
     #define MINMATCH 4
     
    @@ -73,6 +74,11 @@ static U32 LDM_read32(const void *ptr) {
       return *(const U32 *)ptr;
     }
     
    +static U64 LDM_read64(const void *ptr) {
    +  return *(const U64 *)ptr;
    +}
    +
    +
     static void LDM_copy8(void *dst, const void *src) {
       memcpy(dst, src, 8);
     }
    @@ -87,7 +93,6 @@ static void LDM_wild_copy(void *dstPtr, const void *srcPtr, void *dstEnd) {
         d += 8;
         s += 8;
       } while (d < e);
    -
     }
     
     struct hash_entry {
    @@ -99,12 +104,23 @@ static U32 LDM_hash(U32 sequence) {
       return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG));
     }
     
    +static U32 LDM_hash5(U64 sequence) {
    +  static const U64 prime5bytes = 889523592379ULL;
    +  static const U64 prime8bytes = 11400714785074694791ULL;
    +  const U32 hashLog = LDM_HASHLOG;
    +  if (LDM_isLittleEndian())
    +    return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
    +  else
    +    return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
    +}
    +
     static U32 LDM_hash_position(const void * const p) {
       return LDM_hash(LDM_read32(p));
     }
     
     static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase,
                                          const BYTE *srcBase) {
    +//  printf("Hashing: %zu\n", p - srcBase);
       U32 *hashTable = (U32 *) tableBase;
       hashTable[h] = (U32)(p - srcBase);
     }
    @@ -170,6 +186,7 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size,
       /* Hash first byte: put into hash table */
     
       LDM_put_position(ip, hashTable, istart);
    +  const BYTE *lastHash = ip;
       ip++;
       forwardH = LDM_hash_position(ip);
     
    @@ -196,8 +213,9 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size,
     
             forwardH = LDM_hash_position(forwardIp);
             LDM_put_position_on_hash(ip, h, hashTable, istart);
    +        lastHash = ip;
           } while (ip - match > WINDOW_SIZE ||
    -               LDM_read32(match) != LDM_read32(ip));
    +               LDM_read64(match) != LDM_read64(ip));
         }
     
         // TODO catchup
    @@ -215,10 +233,6 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size,
           printf("Cur position: %zu\n", anchor - istart);
           printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match);
     #endif
    -      /*
    -      fwrite(match, 4, 1, stdout);
    -      printf("\n");
    -      */
     
           if (litLength >= RUN_MASK) {
             int len = (int)litLength - RUN_MASK;
    @@ -242,8 +256,8 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size,
     _next_match:
         /* Encode offset */
         {
    -      LDM_writeLE16(op, (U16)(ip - match));
    -      op += 2;
    +      LDM_write32(op, ip - match);
    +      op += 4;
         }
     
         /* Encode Match Length */
    @@ -256,7 +270,13 @@ _next_match:
           fwrite(ip, MINMATCH + matchCode, 1, stdout);
           printf("\n");
     #endif
    -      ip += MINMATCH + matchCode;
    +
    +      unsigned ctr = 1;
    +      ip++;
    +      for (; ctr < MINMATCH + matchCode; ip++, ctr++) {
    +        LDM_put_position(ip, hashTable, istart);
    +      }
    +//      ip += MINMATCH + matchCode;
           if (matchCode >= ML_MASK) {
             *token += ML_MASK;
             matchCode -= ML_MASK;
    @@ -280,6 +300,7 @@ _next_match:
     
         LDM_put_position(ip, hashTable, istart);
         forwardH = LDM_hash_position(++ip);
    +    lastHash = ip;
       }
     _last_literals:
         /* Encode last literals */
    @@ -340,12 +361,12 @@ size_t LDM_decompress(void const *source, void *dest, size_t compressed_size,
         op = cpy;
     
         /* get offset */
    -    offset = LDM_readLE16(ip);
    +    offset = LDM_read32(ip);
     
     #ifdef LDM_DEBUG
         printf("Offset: %zu\n", offset);
     #endif
    -    ip += 2;
    +    ip += 4;
         match = op - offset;
      //   LDM_write32(op, (U32)offset);
     
    @@ -365,12 +386,11 @@ size_t LDM_decompress(void const *source, void *dest, size_t compressed_size,
         /* copy match */
         cpy = op + length;
     
    -//    printf("TMP_PREV: %zu\n", op - (BYTE *)dest);
         // Inefficient for now
    +
         while (match < cpy - offset && op < oend) {
           *op++ = *match++;
         }
    -//    printf("TMP: %zu\n", op - (BYTE *)dest);
       }
     //  memcpy(dest, source, compressed_size);
       return op - (BYTE *)dest;
    
    From c0c236a28b13ccbc71465eba4c7a52bae96a8e8a Mon Sep 17 00:00:00 2001
    From: Paul Cruz 
    Date: Fri, 7 Jul 2017 15:13:40 -0700
    Subject: [PATCH 057/318] changed to using compressCCtx
    
    ---
     contrib/adaptive-compression/multi.c | 12 ++++++++++--
     1 file changed, 10 insertions(+), 2 deletions(-)
    
    diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c
    index 647d54ba4..943bd8564 100644
    --- a/contrib/adaptive-compression/multi.c
    +++ b/contrib/adaptive-compression/multi.c
    @@ -72,6 +72,7 @@ typedef struct {
         stat_t stats;
         jobDescription* jobs;
         FILE* dstFile;
    +    ZSTD_CCtx* cctx;
     } adaptCCtx;
     
     static void freeCompressionJobs(adaptCCtx* ctx)
    @@ -96,11 +97,12 @@ static int freeCCtx(adaptCCtx* ctx)
             int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex);
             int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond);
             int const fileCloseError =  (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0;
    +        int const cctxError = ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)) ? 1 : 0;
             if (ctx->jobs){
                 freeCompressionJobs(ctx);
                 free(ctx->jobs);
             }
    -        return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError;
    +        return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError | cctxError;
         }
     }
     
    @@ -132,6 +134,12 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename)
         ctx->threadError = 0;
         ctx->allJobsCompleted = 0;
         ctx->adaptParam = DEFAULT_ADAPT_PARAM;
    +    ctx->cctx = ZSTD_createCCtx();
    +    if (!ctx->cctx) {
    +        DISPLAY("Error: could not allocate ZSTD_CCtx\n");
    +        freeCCtx(ctx);
    +        return NULL;
    +    }
         if (!ctx->jobs) {
             DISPLAY("Error: could not allocate space for jobs during context creation\n");
             freeCCtx(ctx);
    @@ -214,7 +222,7 @@ static void* compressionThread(void* arg)
             {
                 unsigned const cLevel = adaptCompressionLevel(ctx);
                 DEBUGLOG(2, "cLevel used: %u\n", cLevel);
    -            size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, cLevel);
    +            size_t const compressedSize = ZSTD_compressCCtx(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size, cLevel);
                 if (ZSTD_isError(compressedSize)) {
                     ctx->threadError = 1;
                     DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(compressedSize));
    
    From 990449b89d3747821d185b672eeba95f046eaae2 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 7 Jul 2017 15:21:35 -0700
    Subject: [PATCH 058/318] new field : ZSTD_frameHeader.frameType
    
    Makes frame type (zstd,skippable) detection more straighforward.
    ZSTD_getFrameHeader set frameContentSize=ZSTD_CONTENTSIZE_UNKNOWN to mean "field not present"
    ---
     doc/zstd_manual.html             |  7 ++-
     lib/decompress/zstd_decompress.c | 76 ++++++++++++++++----------------
     lib/zstd.h                       |  7 ++-
     tests/fuzzer.c                   | 18 ++++----
     4 files changed, 57 insertions(+), 51 deletions(-)
    
    diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
    index 6ac5573f0..ccb0fe85b 100644
    --- a/doc/zstd_manual.html
    +++ b/doc/zstd_manual.html
    @@ -344,9 +344,12 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
         ZSTD_frameParameters fParams;
     } ZSTD_parameters;
     

    +
    typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
    +

    typedef struct {
    -    unsigned long long frameContentSize;
    -    unsigned long long windowSize;   /* can be == frameContentSize */
    +    unsigned long long frameContentSize; /* ZSTD_CONTENTSIZE_UNKNOWN means this field is not available. 0 means "empty" */
    +    unsigned long long windowSize;       /* can be very large, up to <= frameContentSize */
    +    ZSTD_frameType_e frameType;          /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
         unsigned dictID;
         unsigned checksumFlag;
     } ZSTD_frameHeader;
    diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
    index 5e2776579..b1048da0e 100644
    --- a/lib/decompress/zstd_decompress.c
    +++ b/lib/decompress/zstd_decompress.c
    @@ -304,7 +304,8 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
                     return ZSTD_skippableHeaderSize; /* magic number + frame length */
                 memset(zfhPtr, 0, sizeof(*zfhPtr));
                 zfhPtr->frameContentSize = MEM_readLE32((const char *)src + 4);
    -            zfhPtr->windowSize = 0; /* windowSize==0 means a frame is skippable */
    +            zfhPtr->frameType = ZSTD_skippableFrame;
    +            zfhPtr->windowSize = 0;
                 return 0;
             }
             return ERROR(prefix_unknown);
    @@ -321,11 +322,12 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
             U32 const singleSegment = (fhdByte>>5)&1;
             U32 const fcsID = fhdByte>>6;
             U32 const windowSizeMax = 1U << ZSTD_WINDOWLOG_MAX;
    -        U32 windowSize = 0;
    +        U64 windowSize = 0;
             U32 dictID = 0;
    -        U64 frameContentSize = 0;
    +        U64 frameContentSize = ZSTD_CONTENTSIZE_UNKNOWN;
             if ((fhdByte & 0x08) != 0)
    -            return ERROR(frameParameter_unsupported);   /* reserved bits, must be zero */
    +            return ERROR(frameParameter_unsupported); /* reserved bits, must be zero */
    +
             if (!singleSegment) {
                 BYTE const wlByte = ip[pos++];
                 U32 const windowLog = (wlByte >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN;
    @@ -334,10 +336,9 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
                 windowSize = (1U << windowLog);
                 windowSize += (windowSize >> 3) * (wlByte&7);
             }
    -
             switch(dictIDSizeCode)
             {
    -            default:   /* impossible */
    +            default: assert(0);  /* impossible */
                 case 0 : break;
                 case 1 : dictID = ip[pos]; pos++; break;
                 case 2 : dictID = MEM_readLE16(ip+pos); pos+=2; break;
    @@ -345,27 +346,30 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
             }
             switch(fcsID)
             {
    -            default:   /* impossible */
    +            default: assert(0);  /* impossible */
                 case 0 : if (singleSegment) frameContentSize = ip[pos]; break;
                 case 1 : frameContentSize = MEM_readLE16(ip+pos)+256; break;
                 case 2 : frameContentSize = MEM_readLE32(ip+pos); break;
                 case 3 : frameContentSize = MEM_readLE64(ip+pos); break;
             }
    -        if (!windowSize) windowSize = (U32)frameContentSize;
    -        if (windowSize > windowSizeMax) return ERROR(frameParameter_windowTooLarge);
    +        if (singleSegment) windowSize = frameContentSize;
    +
    +        zfhPtr->frameType = ZSTD_frame;
             zfhPtr->frameContentSize = frameContentSize;
             zfhPtr->windowSize = windowSize;
             zfhPtr->dictID = dictID;
             zfhPtr->checksumFlag = checksumFlag;
    +        if (windowSize > windowSizeMax)
    +            return ERROR(frameParameter_windowTooLarge);   /* should windowSizeMax control be delegated to caller ? */
         }
         return 0;
     }
     
     /** ZSTD_getFrameContentSize() :
    -*   compatible with legacy mode
    -*   @return : decompressed size of the single frame pointed to be `src` if known, otherwise
    -*             - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined
    -*             - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) */
    + *  compatible with legacy mode
    + * @return : decompressed size of the single frame pointed to be `src` if known, otherwise
    + *         - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined
    + *         - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) */
     unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize)
     {
     #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
    @@ -374,18 +378,14 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize)
             return ret == 0 ? ZSTD_CONTENTSIZE_UNKNOWN : ret;
         }
     #endif
    -    {   ZSTD_frameHeader fParams;
    -        if (ZSTD_getFrameHeader(&fParams, src, srcSize) != 0)
    +    {   ZSTD_frameHeader zfh;
    +        if (ZSTD_getFrameHeader(&zfh, src, srcSize) != 0)
                 return ZSTD_CONTENTSIZE_ERROR;
    -        if (fParams.windowSize == 0) {
    -            /* Either skippable or empty frame, size == 0 either way */
    +        if (zfh.frameType == ZSTD_skippableFrame) {
                 return 0;
    -        } else if (fParams.frameContentSize != 0) {
    -            return fParams.frameContentSize;
             } else {
    -            return ZSTD_CONTENTSIZE_UNKNOWN;
    -        }
    -    }
    +            return zfh.frameContentSize;
    +    }   }
     }
     
     /** ZSTD_findDecompressedSize() :
    @@ -462,7 +462,8 @@ static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t he
         size_t const result = ZSTD_getFrameHeader(&(dctx->fParams), src, headerSize);
         if (ZSTD_isError(result)) return result;  /* invalid header */
         if (result>0) return ERROR(srcSize_wrong);   /* headerSize too small */
    -    if (dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID)) return ERROR(dictionary_wrong);
    +    if (dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID))
    +        return ERROR(dictionary_wrong);
         if (dctx->fParams.checksumFlag) XXH64_reset(&dctx->xxhState, 0);
         return 0;
     }
    @@ -1445,13 +1446,13 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
             const BYTE* ip = (const BYTE*)src;
             const BYTE* const ipstart = ip;
             size_t remainingSize = srcSize;
    -        ZSTD_frameHeader fParams;
    +        ZSTD_frameHeader zfh;
     
    -        size_t const headerSize = ZSTD_frameHeaderSize(ip, remainingSize);
    +        size_t const headerSize = ZSTD_frameHeaderSize(src, srcSize);
             if (ZSTD_isError(headerSize)) return headerSize;
     
             /* Frame Header */
    -        {   size_t const ret = ZSTD_getFrameHeader(&fParams, ip, remainingSize);
    +        {   size_t const ret = ZSTD_getFrameHeader(&zfh, src, srcSize);
                 if (ZSTD_isError(ret)) return ret;
                 if (ret > 0) return ERROR(srcSize_wrong);
             }
    @@ -1474,7 +1475,7 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
                 if (blockProperties.lastBlock) break;
             }
     
    -        if (fParams.checksumFlag) {   /* Frame content checksum */
    +        if (zfh.checksumFlag) {   /* Final frame content checksum */
                 if (remainingSize < 4) return ERROR(srcSize_wrong);
                 ip += 4;
                 remainingSize -= 4;
    @@ -2135,7 +2136,8 @@ unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict)
      *  ZSTD_getFrameHeader(), which will provide a more precise error code. */
     unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize)
     {
    -    ZSTD_frameHeader zfp = { 0 , 0 , 0 , 0 };
    +    ZSTD_frameHeader zfp;
    +    zfp.dictID = 0;
         size_t const hError = ZSTD_getFrameHeader(&zfp, src, srcSize);
         if (ZSTD_isError(hError)) return 0;
         return zfp.dictID;
    @@ -2252,11 +2254,11 @@ size_t ZSTD_estimateDStreamSize(size_t windowSize)
     
     ZSTDLIB_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize)
     {
    -    ZSTD_frameHeader fh;
    -    size_t const err = ZSTD_getFrameHeader(&fh, src, srcSize);
    +    ZSTD_frameHeader zfh;
    +    size_t const err = ZSTD_getFrameHeader(&zfh, src, srcSize);
         if (ZSTD_isError(err)) return err;
         if (err>0) return ERROR(srcSize_wrong);
    -    return ZSTD_estimateDStreamSize(fh.windowSize);
    +    return ZSTD_estimateDStreamSize(zfh.windowSize);
     }
     
     
    @@ -2307,16 +2309,14 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
                             size_t const dictSize = zds->ddict ? zds->ddict->dictSize : 0;
                             /* 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));
    +                        CHECK_F(ZSTD_initLegacyStream(&zds->legacyContext,
    +                                    zds->previousLegacyVersion, legacyVersion,
    +                                    dict, dictSize));
                             zds->legacyVersion = zds->previousLegacyVersion = legacyVersion;
    -                        return ZSTD_decompressLegacyStream(zds->legacyContext, zds->legacyVersion, output, input);
    -                    } else {
    -                        return hSize; /* error */
    +                        return ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input);
                         }
    -#else
    -                    return hSize;
     #endif
    +                    return hSize; /* error */
                     }
                     if (hSize != 0) {   /* need more input */
                         size_t const toLoad = hSize - zds->lhSize;   /* if hSize!=0, hSize > zds->lhSize */
    diff --git a/lib/zstd.h b/lib/zstd.h
    index 1314b8296..4a232d38e 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -425,9 +425,12 @@ typedef struct {
         ZSTD_frameParameters fParams;
     } ZSTD_parameters;
     
    +typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
    +
     typedef struct {
    -    unsigned long long frameContentSize;
    -    unsigned long long windowSize;   /* can be == frameContentSize */
    +    unsigned long long frameContentSize; /* ZSTD_CONTENTSIZE_UNKNOWN means this field is not available. 0 means "empty" */
    +    unsigned long long windowSize;       /* can be very large, up to <= frameContentSize */
    +    ZSTD_frameType_e frameType;          /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
         unsigned dictID;
         unsigned checksumFlag;
     } ZSTD_frameHeader;
    diff --git a/tests/fuzzer.c b/tests/fuzzer.c
    index b8f514785..4d88893a1 100644
    --- a/tests/fuzzer.c
    +++ b/tests/fuzzer.c
    @@ -434,9 +434,9 @@ static int basicUnitTests(U32 seed, double compressibility)
                 CHECKPLUS(r, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, ZSTD_compressBound(testSize),
                                               (const char*)CNBuffer + dictSize, testSize),
                           cSize = r);
    -            {   ZSTD_frameHeader fp;
    -                if (ZSTD_getFrameHeader(&fp, compressedBuffer, cSize)) goto _output_error;
    -                if ((fp.frameContentSize != testSize) && (fp.frameContentSize != 0)) goto _output_error;
    +            {   ZSTD_frameHeader zfh;
    +                if (ZSTD_getFrameHeader(&zfh, compressedBuffer, cSize)) goto _output_error;
    +                if ((zfh.frameContentSize != testSize) && (zfh.frameContentSize != 0)) goto _output_error;
             }   }
             DISPLAYLEVEL(4, "OK \n");
     
    @@ -1006,17 +1006,17 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD
                       CHECK(endCheck != endMark, "ZSTD_compressCCtx : dst buffer overflow"); }
             }   }
     
    +        /* frame header decompression test */
    +        {   ZSTD_frameHeader zfh;
    +            CHECK_Z( ZSTD_getFrameHeader(&zfh, cBuffer, cSize) );
    +            CHECK(zfh.frameContentSize != sampleSize, "Frame content size incorrect");
    +        }
    +
             /* Decompressed size test */
             {   unsigned long long const rSize = ZSTD_findDecompressedSize(cBuffer, cSize);
                 CHECK(rSize != sampleSize, "decompressed size incorrect");
             }
     
    -        /* frame header decompression test */
    -        {   ZSTD_frameHeader dParams;
    -            CHECK_Z( ZSTD_getFrameHeader(&dParams, cBuffer, cSize) );
    -            CHECK(dParams.frameContentSize != sampleSize, "Frame content size incorrect");
    -        }
    -
             /* successful decompression test */
             {   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);
    
    From 46396523c0d50cf57f05237281cf1bb19049788f Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 7 Jul 2017 15:32:12 -0700
    Subject: [PATCH 059/318] ZSTD_getFrameHeader : control of windowSize limits is
     delegated to caller
    
    Extracting frame header is a separate operation.
    It's now possible to get frame header, whatever the window size set in it.
    ---
     lib/decompress/zstd_decompress.c | 6 +++---
     1 file changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
    index b1048da0e..87e60170e 100644
    --- a/lib/decompress/zstd_decompress.c
    +++ b/lib/decompress/zstd_decompress.c
    @@ -321,7 +321,6 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
             U32 const checksumFlag = (fhdByte>>2)&1;
             U32 const singleSegment = (fhdByte>>5)&1;
             U32 const fcsID = fhdByte>>6;
    -        U32 const windowSizeMax = 1U << ZSTD_WINDOWLOG_MAX;
             U64 windowSize = 0;
             U32 dictID = 0;
             U64 frameContentSize = ZSTD_CONTENTSIZE_UNKNOWN;
    @@ -359,8 +358,6 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
             zfhPtr->windowSize = windowSize;
             zfhPtr->dictID = dictID;
             zfhPtr->checksumFlag = checksumFlag;
    -        if (windowSize > windowSizeMax)
    -            return ERROR(frameParameter_windowTooLarge);   /* should windowSizeMax control be delegated to caller ? */
         }
         return 0;
     }
    @@ -2254,10 +2251,13 @@ size_t ZSTD_estimateDStreamSize(size_t windowSize)
     
     ZSTDLIB_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize)
     {
    +    U32 const windowSizeMax = 1U << ZSTD_WINDOWLOG_MAX;
         ZSTD_frameHeader zfh;
         size_t const err = ZSTD_getFrameHeader(&zfh, src, srcSize);
         if (ZSTD_isError(err)) return err;
         if (err>0) return ERROR(srcSize_wrong);
    +    if (zfh.windowSize > windowSizeMax)
    +        return ERROR(frameParameter_windowTooLarge);
         return ZSTD_estimateDStreamSize(zfh.windowSize);
     }
     
    
    From 1c9d6b2c6b26f0c916218d5ecfad8a155273c403 Mon Sep 17 00:00:00 2001
    From: Paul Cruz 
    Date: Fri, 7 Jul 2017 15:42:20 -0700
    Subject: [PATCH 060/318] rewrote time elapsed with UTIL
    
    ---
     contrib/adaptive-compression/Makefile |  1 +
     contrib/adaptive-compression/multi.c  | 38 +++++++++++++--------------
     2 files changed, 19 insertions(+), 20 deletions(-)
    
    diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile
    index 2ebec9ba9..2d3de33ea 100644
    --- a/contrib/adaptive-compression/Makefile
    +++ b/contrib/adaptive-compression/Makefile
    @@ -1,5 +1,6 @@
     
     ZSTDDIR = ../../lib
    +PRGDIR  = ../../programs
     ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c
     ZSTDCOMP_FILES   := $(ZSTDDIR)/compress/*.c
     ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/*.c
    diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c
    index 943bd8564..9f981f812 100644
    --- a/contrib/adaptive-compression/multi.c
    +++ b/contrib/adaptive-compression/multi.c
    @@ -15,17 +15,16 @@ typedef unsigned char BYTE;
     #include      /* malloc, free */
     #include     /* pthread functions */
     #include      /* memset */
    -#include        /* clock(), CLOCKS_PER_SEC */
     #include "zstd.h"
    +#include "util.h"
     
     static int g_displayLevel = DEFAULT_DISPLAY_LEVEL;
     static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL;
     static unsigned g_displayStats = 0;
    -static clock_t g_time = 0;
    -static clock_t g_startTime = 0;
    -static clock_t const refreshRate = CLOCKS_PER_SEC / 60; /* 60 Hz */
    +static UTIL_time_t g_startTime;
     static size_t g_streamedSize = 0;
     static unsigned g_useProgressBar = 0;
    +static UTIL_freq_t g_ticksPerSecond;
     
     typedef struct {
         void* start;
    @@ -39,7 +38,7 @@ typedef struct {
         unsigned readyCounter;
         unsigned compressedCounter;
         unsigned writeCounter;
    -} stat_t;
    +} cStat_t;
     
     typedef struct {
         buffer_t src;
    @@ -69,7 +68,7 @@ typedef struct {
         pthread_cond_t allJobsCompleted_cond;
         pthread_mutex_t jobWrite_mutex;
         pthread_cond_t jobWrite_cond;
    -    stat_t stats;
    +    cStat_t stats;
         jobDescription* jobs;
         FILE* dstFile;
         ZSTD_CCtx* cctx;
    @@ -249,19 +248,17 @@ static void* compressionThread(void* arg)
     static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last)
     {
         if (!g_useProgressBar) return;
    -    clock_t currTime = clock();
    -    unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0;
    -    double const timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC);
    +    UTIL_time_t currTime;
    +    UTIL_getTime(&currTime);
    +    double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0);
         double const sizeMB = (double)g_streamedSize / (1 << 20);
         double const avgCompRate = sizeMB * 1000 / timeElapsed;
    -    if (refresh) {
    -        fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate);
    -        if (last) {
    -            fprintf(stdout, "\n");
    -        }
    -        else {
    -            fflush(stdout);
    -        }
    +    fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate);
    +    if (last) {
    +        fprintf(stdout, "\n");
    +    }
    +    else {
    +        fflush(stdout);
         }
     }
     
    @@ -362,7 +359,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize)
         return 0;
     }
     
    -static void printStats(stat_t stats)
    +static void printStats(cStat_t stats)
     {
         DISPLAY("========STATISTICS========\n");
         DISPLAY("# times waited on job ready: %u\n", stats.waitReady);
    @@ -379,8 +376,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst
         size_t const numJobs = MAX_NUM_JOBS;
         int ret = 0;
         adaptCCtx* ctx = NULL;
    -    g_time = clock();
    -    g_startTime = clock();
    +    UTIL_getTime(&g_startTime);
         g_streamedSize = 0;
     
     
    @@ -517,6 +513,8 @@ int main(int argCount, const char* argv[])
         int ret = 0;
         int argNum;
     
    +    UTIL_initTimer(&g_ticksPerSecond);
    +
         if (filenameTable == NULL) {
             DISPLAY("Error: could not allocate sapce for filename table.\n");
             return 1;
    
    From ead4dd48f66d87af395cd852cd11e285a8b07510 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 7 Jul 2017 15:51:24 -0700
    Subject: [PATCH 061/318] new field frameHeader.headerSize
    
    ---
     doc/zstd_manual.html             | 21 ++++++++++-----------
     lib/decompress/zstd_decompress.c | 15 +++++++--------
     lib/zstd.h                       | 19 +++++++++----------
     3 files changed, 26 insertions(+), 29 deletions(-)
    
    diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
    index ccb0fe85b..e17f24a38 100644
    --- a/doc/zstd_manual.html
    +++ b/doc/zstd_manual.html
    @@ -344,16 +344,6 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
         ZSTD_frameParameters fParams;
     } ZSTD_parameters;
     

    -
    typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
    -

    -
    typedef struct {
    -    unsigned long long frameContentSize; /* ZSTD_CONTENTSIZE_UNKNOWN means this field is not available. 0 means "empty" */
    -    unsigned long long windowSize;       /* can be very large, up to <= frameContentSize */
    -    ZSTD_frameType_e frameType;          /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
    -    unsigned dictID;
    -    unsigned checksumFlag;
    -} ZSTD_frameHeader;
    -

    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;
    @@ -765,7 +755,16 @@ size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned lo
       It also returns Frame Size as fparamsPtr->frameContentSize.
     
    -

    Buffer-less streaming decompression functions

    size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize);   /**< doesn't consume input */
    +

    Buffer-less streaming decompression functions

    typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
    +typedef struct {
    +    unsigned long long frameContentSize; /* ZSTD_CONTENTSIZE_UNKNOWN means this field is not available. 0 means "empty" */
    +    unsigned long long windowSize;       /* can be very large, up to <= frameContentSize */
    +    ZSTD_frameType_e frameType;          /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
    +    unsigned headerSize;
    +    unsigned dictID;
    +    unsigned checksumFlag;
    +} ZSTD_frameHeader;
    +size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize);   /**< doesn't consume input */
     size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx);
     size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
     size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
    diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
    index 87e60170e..959a4fb60 100644
    --- a/lib/decompress/zstd_decompress.c
    +++ b/lib/decompress/zstd_decompress.c
    @@ -312,8 +312,10 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
         }
     
         /* ensure there is enough `srcSize` to fully read/decode frame header */
    -    { size_t const fhsize = ZSTD_frameHeaderSize(src, srcSize);
    -      if (srcSize < fhsize) return fhsize; }
    +    {   size_t const fhsize = ZSTD_frameHeaderSize(src, srcSize);
    +        if (srcSize < fhsize) return fhsize;
    +        zfhPtr->headerSize = (U32)fhsize;
    +    }
     
         {   BYTE const fhdByte = ip[4];
             size_t pos = 5;
    @@ -1445,17 +1447,14 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
             size_t remainingSize = srcSize;
             ZSTD_frameHeader zfh;
     
    -        size_t const headerSize = ZSTD_frameHeaderSize(src, srcSize);
    -        if (ZSTD_isError(headerSize)) return headerSize;
    -
    -        /* Frame Header */
    +        /* Extract Frame Header */
             {   size_t const ret = ZSTD_getFrameHeader(&zfh, src, srcSize);
                 if (ZSTD_isError(ret)) return ret;
                 if (ret > 0) return ERROR(srcSize_wrong);
             }
     
    -        ip += headerSize;
    -        remainingSize -= headerSize;
    +        ip += zfh.headerSize;
    +        remainingSize -= zfh.headerSize;
     
             /* Loop on each block */
             while (1) {
    diff --git a/lib/zstd.h b/lib/zstd.h
    index 4a232d38e..abb1b7c73 100644
    --- a/lib/zstd.h
    +++ b/lib/zstd.h
    @@ -425,16 +425,6 @@ typedef struct {
         ZSTD_frameParameters fParams;
     } ZSTD_parameters;
     
    -typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
    -
    -typedef struct {
    -    unsigned long long frameContentSize; /* ZSTD_CONTENTSIZE_UNKNOWN means this field is not available. 0 means "empty" */
    -    unsigned long long windowSize;       /* can be very large, up to <= frameContentSize */
    -    ZSTD_frameType_e frameType;          /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
    -    unsigned dictID;
    -    unsigned checksumFlag;
    -} ZSTD_frameHeader;
    -
     /*= Custom memory allocation functions */
     typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
     typedef void  (*ZSTD_freeFunction) (void* opaque, void* address);
    @@ -877,6 +867,15 @@ ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapaci
     */
     
     /*=====   Buffer-less streaming decompression functions  =====*/
    +typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
    +typedef struct {
    +    unsigned long long frameContentSize; /* ZSTD_CONTENTSIZE_UNKNOWN means this field is not available. 0 means "empty" */
    +    unsigned long long windowSize;       /* can be very large, up to <= frameContentSize */
    +    ZSTD_frameType_e frameType;          /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
    +    unsigned headerSize;
    +    unsigned dictID;
    +    unsigned checksumFlag;
    +} ZSTD_frameHeader;
     ZSTDLIB_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize);   /**< doesn't consume input */
     ZSTDLIB_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx);
     ZSTDLIB_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
    
    From 842644e42ee196715f5ac890378a4819b50e84eb Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 7 Jul 2017 15:55:41 -0700
    Subject: [PATCH 062/318] target gpptest uses CXX environment variable
    
    ---
     Makefile | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/Makefile b/Makefile
    index ac3034c9b..5e887364a 100644
    --- a/Makefile
    +++ b/Makefile
    @@ -180,7 +180,7 @@ ppc64fuzz: clean
     	CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc64-static MOREFLAGS="-m64 -static" FUZZER_FLAGS=--no-big-tests $(MAKE) -C $(TESTDIR) fuzztest
     
     gpptest: clean
    -	CC=g++ $(MAKE) -C $(PRGDIR) all CFLAGS="-O3 -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror"
    +	CC=$(CXX) $(MAKE) -C $(PRGDIR) all CFLAGS="-O3 -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror"
     
     gcc5test: clean
     	gcc-5 -v
    
    From 7163ffafde9fdc2eac50ae12056b2cf66daa2945 Mon Sep 17 00:00:00 2001
    From: Paul Cruz 
    Date: Fri, 7 Jul 2017 15:56:00 -0700
    Subject: [PATCH 063/318] playing around with adapt param
    
    ---
     contrib/adaptive-compression/multi.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c
    index 9f981f812..41fd9af49 100644
    --- a/contrib/adaptive-compression/multi.c
    +++ b/contrib/adaptive-compression/multi.c
    @@ -8,7 +8,7 @@
     #define MAX_PATH 256
     #define DEFAULT_DISPLAY_LEVEL 1
     #define DEFAULT_COMPRESSION_LEVEL 6
    -#define DEFAULT_ADAPT_PARAM 2
    +#define DEFAULT_ADAPT_PARAM 1
     typedef unsigned char BYTE;
     
     #include       /* fprintf */
    
    From 593d517ebfe9656181f7af84a5d3ecce7795aca9 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 7 Jul 2017 16:09:47 -0700
    Subject: [PATCH 064/318] fixed minor cast warning
    
    ---
     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 959a4fb60..c8ab5c5f7 100644
    --- a/lib/decompress/zstd_decompress.c
    +++ b/lib/decompress/zstd_decompress.c
    @@ -334,7 +334,7 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
                 U32 const windowLog = (wlByte >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN;
                 if (windowLog > ZSTD_WINDOWLOG_MAX)
                     return ERROR(frameParameter_windowTooLarge);
    -            windowSize = (1U << windowLog);
    +            windowSize = (1ULL << windowLog);
                 windowSize += (windowSize >> 3) * (wlByte&7);
             }
             switch(dictIDSizeCode)
    
    From 9bde061a0b9f2760135b6a4fa034586403b123ab Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 7 Jul 2017 16:14:17 -0700
    Subject: [PATCH 065/318] fixed minor Visual compilation limitation
    
    ---
     lib/decompress/zstd_decompress.c | 3 +--
     1 file changed, 1 insertion(+), 2 deletions(-)
    
    diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
    index c8ab5c5f7..e7b4db04c 100644
    --- a/lib/decompress/zstd_decompress.c
    +++ b/lib/decompress/zstd_decompress.c
    @@ -2132,8 +2132,7 @@ unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict)
      *  ZSTD_getFrameHeader(), which will provide a more precise error code. */
     unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize)
     {
    -    ZSTD_frameHeader zfp;
    -    zfp.dictID = 0;
    +    ZSTD_frameHeader zfp = { 0, 0, ZSTD_frame, 0, 0, 0 };
         size_t const hError = ZSTD_getFrameHeader(&zfp, src, srcSize);
         if (ZSTD_isError(hError)) return 0;
         return zfp.dictID;
    
    From ed0243a63c0057383ed71b3a91aacfd918ffaa77 Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 7 Jul 2017 16:16:14 -0700
    Subject: [PATCH 066/318] removed zbufftest from list of `all` tests
    
    ---
     tests/Makefile | 8 ++++----
     1 file changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/tests/Makefile b/tests/Makefile
    index 82f12887b..345e0e8eb 100644
    --- a/tests/Makefile
    +++ b/tests/Makefile
    @@ -73,13 +73,13 @@ DECODECORPUS_TESTTIME ?= -T30
     
     default: fullbench
     
    -all: fullbench fuzzer zstreamtest paramgrill datagen zbufftest decodecorpus
    +all: fullbench fuzzer zstreamtest paramgrill datagen decodecorpus
     
    -all32: fullbench32 fuzzer32 zstreamtest32 zbufftest32
    +all32: fullbench32 fuzzer32 zstreamtest32
     
    -allnothread: fullbench fuzzer paramgrill datagen zbufftest decodecorpus
    +allnothread: fullbench fuzzer paramgrill datagen  decodecorpus
     
    -dll: fuzzer-dll zstreamtest-dll zbufftest-dll
    +dll: fuzzer-dll zstreamtest-dll 
     
     zstd:
     	$(MAKE) -C $(PRGDIR) $@
    
    From c3ae23d459611438ede914036a6ace1b37849ad6 Mon Sep 17 00:00:00 2001
    From: Paul Cruz 
    Date: Fri, 7 Jul 2017 17:07:05 -0700
    Subject: [PATCH 067/318] added ability to compress without specifying out
     filename
    
    ---
     contrib/adaptive-compression/multi.c | 21 +++++++++++++--------
     contrib/adaptive-compression/run.sh  |  6 ++++++
     2 files changed, 19 insertions(+), 8 deletions(-)
    
    diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c
    index 41fd9af49..78d9ca154 100644
    --- a/contrib/adaptive-compression/multi.c
    +++ b/contrib/adaptive-compression/multi.c
    @@ -367,18 +367,28 @@ static void printStats(cStat_t stats)
         DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite);
     }
     
    -static int compressFilename(const char* const srcFilename, const char* const dstFilename)
    +static int compressFilename(const char* const srcFilename, const char* const dstFilenameOrNull)
     {
         BYTE* const src = malloc(FILE_CHUNK_SIZE);
         unsigned const stdinUsed = !strcmp(srcFilename, stdinmark);
         FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb");
    -    const char* const outFilename = (stdinUsed && !dstFilename) ? stdoutmark : dstFilename;
    +    const char* const outFilenameIntermediate = (stdinUsed && !dstFilenameOrNull) ? stdoutmark : dstFilenameOrNull;
    +    const char* outFilename = outFilenameIntermediate;
    +    char fileAndSuffix[MAX_PATH];
         size_t const numJobs = MAX_NUM_JOBS;
         int ret = 0;
         adaptCCtx* ctx = NULL;
         UTIL_getTime(&g_startTime);
         g_streamedSize = 0;
     
    +    if (!outFilenameIntermediate) {
    +        if (snprintf(fileAndSuffix, MAX_PATH, "%s.zst", srcFilename) + 1 > MAX_PATH) {
    +            DISPLAY("Error: output filename is too long\n");
    +            ret = 1;
    +            goto cleanup;
    +        }
    +        outFilename = fileAndSuffix;
    +    }
     
         /* checking for errors */
         if (!srcFilename || !outFilename || !src || !srcFile) {
    @@ -452,15 +462,10 @@ static int compressFilenames(const char** filenameTable, unsigned numFiles, unsi
     {
         int ret = 0;
         unsigned fileNum;
    -    char outFile[MAX_PATH];
         for (fileNum=0; fileNum MAX_PATH) {
    -            DISPLAY("Error: output filename is too long\n");
    -            return 1;
    -        }
             if (!forceStdout) {
    -            ret |= compressFilename(filename, outFile);
    +            ret |= compressFilename(filename, NULL);
             }
             else {
                 ret |= compressFilename(filename, stdoutmark);
    diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh
    index 67814ec1b..3c40969e6 100755
    --- a/contrib/adaptive-compression/run.sh
    +++ b/contrib/adaptive-compression/run.sh
    @@ -151,6 +151,12 @@ echo "Running Tests With Multiple Files > stdout"
     zstd -d tmp.zst
     rm tmp*
     
    +echo "Running single test without output filename"
    +./multi tests/test2048.pdf -p
    +zstd -d tests/test2048.pdf.zst -o tmp
    +diff tmp tests/test2048.pdf
    +rm tmp*
    +
     echo "finished with tests"
     
     make clean
    
    From acdeb9f30211b018460c8f9e71595b49d553e555 Mon Sep 17 00:00:00 2001
    From: Stella Lau 
    Date: Fri, 7 Jul 2017 17:09:28 -0700
    Subject: [PATCH 068/318] Add compression statistics
    
    ---
     contrib/long_distance_matching/ldm.c      | 57 ++++++++++++++++++++---
     contrib/long_distance_matching/main-ldm.c |  2 +
     2 files changed, 53 insertions(+), 6 deletions(-)
    
    diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c
    index b03e43686..f8061f533 100644
    --- a/contrib/long_distance_matching/ldm.c
    +++ b/contrib/long_distance_matching/ldm.c
    @@ -5,6 +5,8 @@
     
     #include "ldm.h"
     
    +#define HASH_EVERY 7
    +
     #define LDM_MEMORY_USAGE 14
     #define LDM_HASHLOG (LDM_MEMORY_USAGE-2)
     #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE))
    @@ -13,8 +15,8 @@
     
     #define WINDOW_SIZE (1 << 20)
     #define MAX_WINDOW_SIZE 31
    -#define HASH_SIZE 4
    -#define MINMATCH 4
    +#define HASH_SIZE 8
    +#define MINMATCH 8
     
     #define ML_BITS 4
     #define ML_MASK ((1U<num_matches);
    +  printf("Average match length: %.1f\n", ((double)stats->total_match_length) /
    +                                         (double)stats->num_matches);
    +  printf("Average literal length: %.1f\n",
    +         ((double)stats->total_literal_length) / (double)stats->num_matches);
    +  printf("Average offset length: %.1f\n",
    +         ((double)stats->total_offset) / (double)stats->num_matches);
    +  printf("=====================\n");
    +}
    +
     struct hash_entry {
       U64 offset;
       tag t;
    @@ -121,12 +143,19 @@ static U32 LDM_hash_position(const void * const p) {
     static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase,
                                          const BYTE *srcBase) {
     //  printf("Hashing: %zu\n", p - srcBase);
    +  if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) {
    +    return;
    +  }
    +
       U32 *hashTable = (U32 *) tableBase;
       hashTable[h] = (U32)(p - srcBase);
     }
     
     static void LDM_put_position(const BYTE *p, void *tableBase,
                                  const BYTE *srcBase) {
    +  if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) {
    +    return;
    +  }
       U32 const h = LDM_hash_position(p);
       LDM_put_position_on_hash(p, h, tableBase, srcBase);
     }
    @@ -174,6 +203,9 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size,
       const BYTE * const matchlimit = iend - HASH_SIZE;
       const BYTE * const mflimit = iend - MINMATCH;
       BYTE *op = (BYTE*) dest;
    +
    +  compress_stats compressStats = { 0 };
    +
       U32 hashTable[LDM_HASHTABLESIZE_U32];
       memset(hashTable, 0, sizeof(hashTable));
     
    @@ -217,8 +249,9 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size,
           } while (ip - match > WINDOW_SIZE ||
                    LDM_read64(match) != LDM_read64(ip));
         }
    +    compressStats.num_matches++;
     
    -    // TODO catchup
    +    /* Catchup: look back to extend match from found match */
         while (ip > anchor && match > istart && ip[-1] == match[-1]) {
           ip--;
           match--;
    @@ -229,6 +262,8 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size,
           unsigned const litLength = (unsigned)(ip - anchor);
           token = op++;
     
    +      compressStats.total_literal_length += litLength;
    +
     #ifdef LDM_DEBUG
           printf("Cur position: %zu\n", anchor - istart);
           printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match);
    @@ -256,8 +291,13 @@ size_t LDM_compress(void const *source, void *dest, size_t source_size,
     _next_match:
         /* Encode offset */
         {
    +      /*
    +      LDM_writeLE16(op, ip-match);
    +      op += 2;
    +      */
           LDM_write32(op, ip - match);
           op += 4;
    +      compressStats.total_offset += (ip - match);
         }
     
         /* Encode Match Length */
    @@ -270,7 +310,7 @@ _next_match:
           fwrite(ip, MINMATCH + matchCode, 1, stdout);
           printf("\n");
     #endif
    -
    +      compressStats.total_match_length += matchCode + MINMATCH;
           unsigned ctr = 1;
           ip++;
           for (; ctr < MINMATCH + matchCode; ip++, ctr++) {
    @@ -293,6 +333,7 @@ _next_match:
           }
     #ifdef LDM_DEBUG
           printf("\n");
    +
     #endif
         }
     
    @@ -319,6 +360,7 @@ _last_literals:
         memcpy(op, anchor, lastRun);
         op += lastRun;
       }
    +  print_compress_stats(&compressStats);
       return (op - (BYTE *)dest);
     }
     
    @@ -361,12 +403,15 @@ size_t LDM_decompress(void const *source, void *dest, size_t compressed_size,
         op = cpy;
     
         /* get offset */
    +    /*
    +    offset = LDM_readLE16(ip);
    +    ip += 2;
    +    */
         offset = LDM_read32(ip);
    -
    +    ip += 4;
     #ifdef LDM_DEBUG
         printf("Offset: %zu\n", offset);
     #endif
    -    ip += 4;
         match = op - offset;
      //   LDM_write32(op, (U32)offset);
     
    diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c
    index 26db1e946..10869cce3 100644
    --- a/contrib/long_distance_matching/main-ldm.c
    +++ b/contrib/long_distance_matching/main-ldm.c
    @@ -1,3 +1,5 @@
    +// TODO: file size must fit into a U32
    +
     #include 
     #include 
     #include 
    
    From 0f4fc6c20a45341e534e1ca8b1365a81b76dcfee Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Fri, 7 Jul 2017 17:13:12 -0700
    Subject: [PATCH 069/318] fixed several conversion warnings
    
    ---
     lib/common/mem.h                 | 4 ++--
     lib/decompress/zstd_decompress.c | 6 +++---
     2 files changed, 5 insertions(+), 5 deletions(-)
    
    diff --git a/lib/common/mem.h b/lib/common/mem.h
    index b0e5bf60b..528286eff 100644
    --- a/lib/common/mem.h
    +++ b/lib/common/mem.h
    @@ -110,7 +110,7 @@ Only use if no other choice to achieve best performance on target platform */
     MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; }
     MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; }
     MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; }
    -MEM_STATIC U64 MEM_readST(const void* memPtr) { return *(const size_t*) memPtr; }
    +MEM_STATIC size_t MEM_readST(const void* memPtr) { return *(const size_t*) memPtr; }
     
     MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }
     MEM_STATIC void MEM_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
    @@ -131,7 +131,7 @@ MEM_STATIC void MEM_write64(void* memPtr, U64 value) { *(U64*)memPtr = value; }
     MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign*)ptr)->u16; }
     MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
     MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign*)ptr)->u64; }
    -MEM_STATIC U64 MEM_readST(const void* ptr) { return ((const unalign*)ptr)->st; }
    +MEM_STATIC size_t MEM_readST(const void* ptr) { return ((const unalign*)ptr)->st; }
     
     MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; }
     MEM_STATIC void MEM_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; }
    diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
    index e7b4db04c..a145dbf86 100644
    --- a/lib/decompress/zstd_decompress.c
    +++ b/lib/decompress/zstd_decompress.c
    @@ -2256,7 +2256,7 @@ ZSTDLIB_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t sr
         if (err>0) return ERROR(srcSize_wrong);
         if (zfh.windowSize > windowSizeMax)
             return ERROR(frameParameter_windowTooLarge);
    -    return ZSTD_estimateDStreamSize(zfh.windowSize);
    +    return ZSTD_estimateDStreamSize((size_t)zfh.windowSize);
     }
     
     
    @@ -2365,8 +2365,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
                 if (zds->fParams.windowSize > zds->maxWindowSize) return ERROR(frameParameter_windowTooLarge);
     
                 /* Adapt buffer sizes to frame header instructions */
    -            {   size_t const blockSize = MIN(zds->fParams.windowSize, ZSTD_BLOCKSIZE_MAX);
    -                size_t const neededOutSize = zds->fParams.windowSize + blockSize + WILDCOPY_OVERLENGTH * 2;
    +            {   size_t const blockSize = (size_t)(MIN(zds->fParams.windowSize, ZSTD_BLOCKSIZE_MAX));
    +                size_t const neededOutSize = (size_t)(zds->fParams.windowSize + blockSize + WILDCOPY_OVERLENGTH * 2);
                     zds->blockSize = blockSize;
                     if ((zds->inBuffSize < blockSize) || (zds->outBuffSize < neededOutSize)) {
                         size_t const bufferSize = blockSize + neededOutSize;
    
    From 40156a49672c277303462502ee05c249c274224a Mon Sep 17 00:00:00 2001
    From: Yann Collet 
    Date: Sat, 8 Jul 2017 04:55:09 -0700
    Subject: [PATCH 070/318] bumped version nb to v1.3.1
    
    ---
     NEWS                 | 4 ++++
     doc/zstd_manual.html | 4 ++--
     lib/zstd.h           | 2 +-
     3 files changed, 7 insertions(+), 3 deletions(-)
    
    diff --git a/NEWS b/NEWS
    index d23a58f02..5bf184e8d 100644
    --- a/NEWS
    +++ b/NEWS
    @@ -1,3 +1,7 @@
    +v1.3.1
    +build: fix Visual compilation for non x86/x64 targets, reported by Greg Slazinski (#718)
    +API exp : breaking change : ZSTD_getframeHeader() 
    +
     v1.3.0
     cli : new : `--list` command, by Paul Cruz
     cli : changed : xz/lzma support enabled by default
    diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
    index e17f24a38..1058503f8 100644
    --- a/doc/zstd_manual.html
    +++ b/doc/zstd_manual.html
    @@ -1,10 +1,10 @@
     
     
     
    -zstd 1.3.0 Manual
    +zstd 1.3.1 Manual
     
     
    -

    zstd 1.3.0 Manual

    +

    zstd 1.3.1 Manual


    Contents

      diff --git a/lib/zstd.h b/lib/zstd.h index abb1b7c73..291c6df25 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 0 +#define ZSTD_VERSION_RELEASE 1 #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 */ From 719ccdc5a58efc9531a9b0d35ee358da7650cef3 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Sun, 9 Jul 2017 22:45:54 -0700 Subject: [PATCH 071/318] Update mainfile --- contrib/long_distance_matching/main.c | 29 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/contrib/long_distance_matching/main.c b/contrib/long_distance_matching/main.c index ddf5145f7..67144166b 100644 --- a/contrib/long_distance_matching/main.c +++ b/contrib/long_distance_matching/main.c @@ -1,12 +1,31 @@ +#include #include #include #include +#include +#include #include "ldm.h" #define BUF_SIZE 16*1024 // Block size #define LDM_HEADER_SIZE 8 +/* +static size_t compress_file_mmap(FILE *in, FILE *out, size_t *size_in, + size_t *size_out) { + char *src, *dst; + struct stat statbuf; + + if (fstat(in, &statbuf) < 0) { + printf("fstat error\n"); + return 1; + } + + + return 0; +} +*/ + static size_t compress_file(FILE *in, FILE *out, size_t *size_in, size_t *size_out) { char *src, *buf = NULL; @@ -26,7 +45,6 @@ static size_t compress_file(FILE *in, FILE *out, size_t *size_in, goto cleanup; } - for (;;) { k = fread(src, 1, BUF_SIZE, in); if (k == 0) @@ -37,10 +55,8 @@ static size_t compress_file(FILE *in, FILE *out, size_t *size_in, // n = k; // offset += n; - offset = k; - count_out += k; - -// k = fwrite(src, 1, offset, out); + offset = n; + count_out += n; k = fwrite(buf, 1, offset, out); if (k < offset) { @@ -94,8 +110,6 @@ static size_t decompress_file(FILE *in, FILE *out) { } } - // TODO - /* Decompress: * Continue while there is more input to read. */ @@ -220,7 +234,6 @@ int main(int argc, char *argv[]) { fclose(inpFp); } - return 0; } From eb280cd5685ae960ff75ff3c5381507ebaaef403 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 10 Jul 2017 06:32:05 -0700 Subject: [PATCH 072/318] Add folder for old versions --- .../long_distance_matching/versions/v1/ldm.c | 394 +++++++++++++++ .../long_distance_matching/versions/v1/ldm.h | 19 + .../versions/v1/main-ldm.c | 459 ++++++++++++++++++ 3 files changed, 872 insertions(+) create mode 100644 contrib/long_distance_matching/versions/v1/ldm.c create mode 100644 contrib/long_distance_matching/versions/v1/ldm.h create mode 100644 contrib/long_distance_matching/versions/v1/main-ldm.c diff --git a/contrib/long_distance_matching/versions/v1/ldm.c b/contrib/long_distance_matching/versions/v1/ldm.c new file mode 100644 index 000000000..266425f8a --- /dev/null +++ b/contrib/long_distance_matching/versions/v1/ldm.c @@ -0,0 +1,394 @@ +#include +#include +#include +#include + +#include "ldm.h" + +#define LDM_MEMORY_USAGE 14 +#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) +#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) + +#define WINDOW_SIZE (1 << 20) +#define MAX_WINDOW_SIZE 31 +#define HASH_SIZE 4 +#define MINMATCH 4 + +#define ML_BITS 4 +#define ML_MASK ((1U<>8); + } +} + +static U32 LDM_read32(const void *ptr) { + return *(const U32 *)ptr; +} + +static U64 LDM_read64(const void *ptr) { + return *(const U64 *)ptr; +} + + +static void LDM_copy8(void *dst, const void *src) { + memcpy(dst, src, 8); +} + +static void LDM_wild_copy(void *dstPtr, const void *srcPtr, void *dstEnd) { + BYTE *d = (BYTE *)dstPtr; + const BYTE *s = (const BYTE *)srcPtr; + BYTE * const e = (BYTE *)dstEnd; + + do { + LDM_copy8(d, s); + d += 8; + s += 8; + } while (d < e); + +} + +struct hash_entry { + U64 offset; + tag t; +}; + +static U32 LDM_hash(U32 sequence) { + return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); +} + +static U32 LDM_hash5(U64 sequence) { + static const U64 prime5bytes = 889523592379ULL; + static const U64 prime8bytes = 11400714785074694791ULL; + const U32 hashLog = LDM_HASHLOG; + if (LDM_isLittleEndian()) + return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); + else + return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); +} + +static U32 LDM_hash_position(const void * const p) { + return LDM_hash(LDM_read32(p)); +} + +static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, + const BYTE *srcBase) { + U32 *hashTable = (U32 *) tableBase; + hashTable[h] = (U32)(p - srcBase); +} + +static void LDM_put_position(const BYTE *p, void *tableBase, + const BYTE *srcBase) { + U32 const h = LDM_hash_position(p); + LDM_put_position_on_hash(p, h, tableBase, srcBase); +} + +static const BYTE *LDM_get_position_on_hash( + U32 h, void *tableBase, const BYTE *srcBase) { + const U32 * const hashTable = (U32*)tableBase; + return hashTable[h] + srcBase; +} + +static BYTE LDM_read_byte(const void *memPtr) { + BYTE val; + memcpy(&val, memPtr, 1); + return val; +} + +static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { + const BYTE * const pStart = pIn; + while (pIn < pInLimit - 1) { + BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); + if (!diff) { + pIn++; + pMatch++; + continue; + } + return (unsigned)(pIn - pStart); + } + return (unsigned)(pIn - pStart); +} + +void LDM_read_header(void const *source, size_t *compressed_size, + size_t *decompressed_size) { + const U32 *ip = (const U32 *)source; + *compressed_size = *ip++; + *decompressed_size = *ip; +} + +size_t LDM_compress(void const *source, void *dest, size_t source_size, + size_t max_dest_size) { + const BYTE * const istart = (const BYTE*)source; + const BYTE *ip = istart; + const BYTE * const iend = istart + source_size; + const BYTE *ilimit = iend - HASH_SIZE; + const BYTE * const matchlimit = iend - HASH_SIZE; + const BYTE * const mflimit = iend - MINMATCH; + BYTE *op = (BYTE*) dest; + U32 hashTable[LDM_HASHTABLESIZE_U32]; + memset(hashTable, 0, sizeof(hashTable)); + + const BYTE *anchor = (const BYTE *)source; +// struct LDM_cctx cctx; + size_t output_size = 0; + + U32 forwardH; + + /* Hash first byte: put into hash table */ + + LDM_put_position(ip, hashTable, istart); + ip++; + forwardH = LDM_hash_position(ip); + + //TODO Loop terminates before ip>=ilimit. + while (ip < ilimit) { + const BYTE *match; + BYTE *token; + + /* Find a match */ + { + const BYTE *forwardIp = ip; + unsigned step = 1; + + do { + U32 const h = forwardH; + ip = forwardIp; + forwardIp += step; + + if (forwardIp > mflimit) { + goto _last_literals; + } + + match = LDM_get_position_on_hash(h, hashTable, istart); + + forwardH = LDM_hash_position(forwardIp); + LDM_put_position_on_hash(ip, h, hashTable, istart); + } while (ip - match > WINDOW_SIZE || + LDM_read64(match) != LDM_read64(ip)); + } + + // TODO catchup + while (ip > anchor && match > istart && ip[-1] == match[-1]) { + ip--; + match--; + } + + /* Encode literals */ + { + unsigned const litLength = (unsigned)(ip - anchor); + token = op++; + +#ifdef LDM_DEBUG + printf("Cur position: %zu\n", anchor - istart); + printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match); +#endif + /* + fwrite(match, 4, 1, stdout); + printf("\n"); + */ + + if (litLength >= RUN_MASK) { + int len = (int)litLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) { + *op++ = 255; + } + *op++ = (BYTE)len; + } else { + *token = (BYTE)(litLength << ML_BITS); + } +#ifdef LDM_DEBUG + printf("Literals "); + fwrite(anchor, litLength, 1, stdout); + printf("\n"); +#endif + memcpy(op, anchor, litLength); + //LDM_wild_copy(op, anchor, op + litLength); + op += litLength; + } +_next_match: + /* Encode offset */ + { + LDM_write32(op, ip - match); + op += 4; + } + + /* Encode Match Length */ + { + unsigned matchCode; + matchCode = LDM_count(ip + MINMATCH, match + MINMATCH, + matchlimit); +#ifdef LDM_DEBUG + printf("Match length %zu\n", matchCode + MINMATCH); + fwrite(ip, MINMATCH + matchCode, 1, stdout); + printf("\n"); +#endif + ip += MINMATCH + matchCode; + if (matchCode >= ML_MASK) { + *token += ML_MASK; + matchCode -= ML_MASK; + LDM_write32(op, 0xFFFFFFFF); + while (matchCode >= 4*0xFF) { + op += 4; + LDM_write32(op, 0xffffffff); + matchCode -= 4*0xFF; + } + op += matchCode / 255; + *op++ = (BYTE)(matchCode % 255); + } else { + *token += (BYTE)(matchCode); + } +#ifdef LDM_DEBUG + printf("\n"); +#endif + } + + anchor = ip; + + LDM_put_position(ip, hashTable, istart); + forwardH = LDM_hash_position(++ip); + } +_last_literals: + /* Encode last literals */ + { + size_t const lastRun = (size_t)(iend - anchor); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255; accumulator -= 255) { + *op++ = 255; + } + *op++ = (BYTE)accumulator; + } else { + *op++ = (BYTE)(lastRun << ML_BITS); + } + memcpy(op, anchor, lastRun); + op += lastRun; + } + return (op - (BYTE *)dest); +} + +size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, + size_t max_decompressed_size) { + const BYTE *ip = (const BYTE *)source; + const BYTE * const iend = ip + compressed_size; + BYTE *op = (BYTE *)dest; + BYTE * const oend = op + max_decompressed_size; + BYTE *cpy; + + while (ip < iend) { + size_t length; + const BYTE *match; + size_t offset; + + /* get literal length */ + unsigned const token = *ip++; + if ((length=(token >> ML_BITS)) == RUN_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while (s == 255); + } +#ifdef LDM_DEBUG + printf("Literal length: %zu\n", length); +#endif + + /* copy literals */ + cpy = op + length; +#ifdef LDM_DEBUG + printf("Literals "); + fwrite(ip, length, 1, stdout); + printf("\n"); +#endif + memcpy(op, ip, length); +// LDM_wild_copy(op, ip, cpy); + ip += length; + op = cpy; + + /* get offset */ + offset = LDM_read32(ip); + +#ifdef LDM_DEBUG + printf("Offset: %zu\n", offset); +#endif + ip += 4; + match = op - offset; + // LDM_write32(op, (U32)offset); + + /* get matchlength */ + length = token & ML_MASK; + if (length == ML_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while (s == 255); + } + length += MINMATCH; +#ifdef LDM_DEBUG + printf("Match length: %zu\n", length); +#endif + /* copy match */ + cpy = op + length; + + // Inefficient for now + + while (match < cpy - offset && op < oend) { + *op++ = *match++; + } + } +// memcpy(dest, source, compressed_size); + return op - (BYTE *)dest; +} + + diff --git a/contrib/long_distance_matching/versions/v1/ldm.h b/contrib/long_distance_matching/versions/v1/ldm.h new file mode 100644 index 000000000..f4ca25a38 --- /dev/null +++ b/contrib/long_distance_matching/versions/v1/ldm.h @@ -0,0 +1,19 @@ +#ifndef LDM_H +#define LDM_H + +#include /* size_t */ + +#define LDM_COMPRESS_SIZE 4 +#define LDM_DECOMPRESS_SIZE 4 +#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) + +size_t LDM_compress(void const *source, void *dest, size_t source_size, + size_t max_dest_size); + +size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, + size_t max_decompressed_size); + +void LDM_read_header(void const *source, size_t *compressed_size, + size_t *decompressed_size); + +#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v1/main-ldm.c b/contrib/long_distance_matching/versions/v1/main-ldm.c new file mode 100644 index 000000000..10869cce3 --- /dev/null +++ b/contrib/long_distance_matching/versions/v1/main-ldm.c @@ -0,0 +1,459 @@ +// TODO: file size must fit into a U32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ldm.h" + +// #define BUF_SIZE 16*1024 // Block size +#define DEBUG + +//#define ZSTD + +#if 0 +static size_t compress_file(FILE *in, FILE *out, size_t *size_in, + size_t *size_out) { + char *src, *buf = NULL; + size_t r = 1; + size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; + + src = malloc(BUF_SIZE); + if (!src) { + printf("Not enough memory\n"); + goto cleanup; + } + + size = BUF_SIZE + LDM_HEADER_SIZE; + buf = malloc(size); + if (!buf) { + printf("Not enough memory\n"); + goto cleanup; + } + + + for (;;) { + k = fread(src, 1, BUF_SIZE, in); + if (k == 0) + break; + count_in += k; + + n = LDM_compress(src, buf, k, BUF_SIZE); + + // n = k; + // offset += n; + offset = k; + count_out += k; + +// k = fwrite(src, 1, offset, out); + + k = fwrite(buf, 1, offset, out); + if (k < offset) { + if (ferror(out)) + printf("Write failed\n"); + else + printf("Short write\n"); + goto cleanup; + } + + } + *size_in = count_in; + *size_out = count_out; + r = 0; + cleanup: + free(src); + free(buf); + return r; +} + +static size_t decompress_file(FILE *in, FILE *out) { + void *src = malloc(BUF_SIZE); + void *dst = NULL; + size_t dst_capacity = BUF_SIZE; + size_t ret = 1; + size_t bytes_written = 0; + + if (!src) { + perror("decompress_file(src)"); + goto cleanup; + } + + while (ret != 0) { + /* Load more input */ + size_t src_size = fread(src, 1, BUF_SIZE, in); + void *src_ptr = src; + void *src_end = src_ptr + src_size; + if (src_size == 0 || ferror(in)) { + printf("(TODO): Decompress: not enough input or error reading file\n"); + //TODO + ret = 0; + goto cleanup; + } + + /* Allocate destination buffer if it hasn't been allocated already */ + if (!dst) { + dst = malloc(dst_capacity); + if (!dst) { + perror("decompress_file(dst)"); + goto cleanup; + } + } + + // TODO + + /* Decompress: + * Continue while there is more input to read. + */ + while (src_ptr != src_end && ret != 0) { + // size_t dst_size = src_size; + size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); + size_t written = fwrite(dst, 1, dst_size, out); +// printf("Writing %zu bytes\n", dst_size); + bytes_written += dst_size; + if (written != dst_size) { + printf("Decompress: Failed to write to file\n"); + goto cleanup; + } + src_ptr += src_size; + src_size = src_end - src_ptr; + } + + /* Update input */ + + } + + printf("Wrote %zu bytes\n", bytes_written); + + cleanup: + free(src); + free(dst); + + return ret; +} +#endif + +static size_t compress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + + /* 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 size of input file */ + if (fstat (fdin, &statbuf) < 0) { + perror("Fstat error"); + return 1; + } + size_t size_in = statbuf.st_size; + + /* go to the location corresponding to the last byte */ + if (lseek(fdout, size_in + LDM_HEADER_SIZE - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write 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; + } + size_t out_size = statbuf.st_size + LDM_HEADER_SIZE; + + /* mmap the output file */ + if ((dst = mmap(0, out_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + + #ifdef ZSTD + size_t size_out = ZSTD_compress(dst, statbuf.st_size, + src, statbuf.st_size, 1); + #else + size_t size_out = LDM_compress(src, dst + LDM_HEADER_SIZE, statbuf.st_size, + statbuf.st_size); + size_out += LDM_HEADER_SIZE; + + // TODO: should depend on LDM_DECOMPRESS_SIZE write32 + memcpy(dst, &size_out, 4); + memcpy(dst + 4, &(statbuf.st_size), 4); + printf("Compressed size: %zu\n", size_out); + printf("Decompressed size: %zu\n", statbuf.st_size); + #endif + ftruncate(fdout, size_out); + + printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, + (unsigned)statbuf.st_size, (unsigned)size_out, oname, + (double)size_out / (statbuf.st_size) * 100); + + close(fdin); + close(fdout); + return 0; +} + +static size_t decompress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + + /* 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 size of 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 header */ + size_t compressed_size, decompressed_size; + LDM_read_header(src, &compressed_size, &decompressed_size); + + printf("Size, compressed_size, decompressed_size: %zu %zu %zu\n", + statbuf.st_size, compressed_size, decompressed_size); + + /* go to the location corresponding to the last byte */ + if (lseek(fdout, decompressed_size - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write error"); + return 1; + } + + /* mmap the output file */ + if ((dst = mmap(0, decompressed_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + + /* Copy input file to output file */ +// memcpy(dst, src, statbuf.st_size); + + #ifdef ZSTD + size_t size_out = ZSTD_decompress(dst, decomrpessed_size, + src + LDM_HEADER_SIZE, + statbuf.st_size - LDM_HEADER_SIZE); + #else + size_t size_out = LDM_decompress(src + LDM_HEADER_SIZE, dst, + statbuf.st_size - LDM_HEADER_SIZE, + decompressed_size); + printf("Ret size out: %zu\n", size_out); + #endif + ftruncate(fdout, size_out); + + close(fdin); + close(fdout); + return 0; +} + +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; +} + +static void verify(const char *inpFilename, const char *decFilename) { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); +} + +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); + + struct timeval tv1, tv2; + /* compress */ + { + gettimeofday(&tv1, NULL); + if (compress(inpFilename, ldmFilename)) { + printf("Compress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + } + + /* decompress */ + + gettimeofday(&tv1, NULL); + if (decompress(ldmFilename, decFilename)) { + printf("Decompress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + + /* verify */ + verify(inpFilename, decFilename); + return 0; +} + +#if 0 +int main2(int argc, char *argv[]) { + char inpFilename[256] = { 0 }; + char ldmFilename[256] = { 0 }; + char decFilename[256] = { 0 }; + + if (argc < 2) { + printf("Please specify input filename\n"); + return 0; + } + 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 */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *outFp = fopen(ldmFilename, "wb"); + size_t sizeIn = 0; + size_t sizeOut = 0; + size_t ret; + printf("compress : %s -> %s\n", inpFilename, ldmFilename); + ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); + if (ret) { + printf("compress : failed with code %zu\n", ret); + return ret; + } + printf("%s: %zu → %zu bytes, %.1f%%\n", + inpFilename, sizeIn, sizeOut, + (double)sizeOut / sizeIn * 100); + printf("compress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* decompress */ + { + FILE *inpFp = fopen(ldmFilename, "rb"); + FILE *outFp = fopen(decFilename, "wb"); + size_t ret; + + printf("decompress : %s -> %s\n", ldmFilename, decFilename); + ret = decompress_file(inpFp, outFp); + if (ret) { + printf("decompress : failed with code %zu\n", ret); + return ret; + } + printf("decompress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* verify */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); + } + return 0; +} +#endif + From 474e06ac5bd166b4771dd8dc2640ce8f0440aa77 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 10 Jul 2017 06:32:29 -0700 Subject: [PATCH 073/318] Minor refactoring --- contrib/long_distance_matching/ldm.c | 31 +- contrib/long_distance_matching/ldm.h | 12 +- contrib/long_distance_matching/main-ldm.c | 499 +++++++++++----------- 3 files changed, 279 insertions(+), 263 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index f8061f533..aeef4a330 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -187,29 +187,30 @@ static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, return (unsigned)(pIn - pStart); } -void LDM_read_header(void const *source, size_t *compressed_size, +void LDM_read_header(const void *src, size_t *compressed_size, size_t *decompressed_size) { - const U32 *ip = (const U32 *)source; + const U32 *ip = (const U32 *)src; *compressed_size = *ip++; *decompressed_size = *ip; } -size_t LDM_compress(void const *source, void *dest, size_t source_size, - size_t max_dest_size) { - const BYTE * const istart = (const BYTE*)source; +// TODO: maxDstSize is unused +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { + const BYTE * const istart = (const BYTE*)src; const BYTE *ip = istart; - const BYTE * const iend = istart + source_size; + const BYTE * const iend = istart + srcSize; const BYTE *ilimit = iend - HASH_SIZE; const BYTE * const matchlimit = iend - HASH_SIZE; const BYTE * const mflimit = iend - MINMATCH; - BYTE *op = (BYTE*) dest; + BYTE *op = (BYTE*) dst; compress_stats compressStats = { 0 }; U32 hashTable[LDM_HASHTABLESIZE_U32]; memset(hashTable, 0, sizeof(hashTable)); - const BYTE *anchor = (const BYTE *)source; + const BYTE *anchor = (const BYTE *)src; // struct LDM_cctx cctx; size_t output_size = 0; @@ -361,14 +362,14 @@ _last_literals: op += lastRun; } print_compress_stats(&compressStats); - return (op - (BYTE *)dest); + return (op - (BYTE *)dst); } -size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, - size_t max_decompressed_size) { - const BYTE *ip = (const BYTE *)source; +size_t LDM_decompress(const void *src, size_t compressed_size, + void *dst, size_t max_decompressed_size) { + const BYTE *ip = (const BYTE *)src; const BYTE * const iend = ip + compressed_size; - BYTE *op = (BYTE *)dest; + BYTE *op = (BYTE *)dst; BYTE * const oend = op + max_decompressed_size; BYTE *cpy; @@ -437,8 +438,8 @@ size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, *op++ = *match++; } } -// memcpy(dest, source, compressed_size); - return op - (BYTE *)dest; +// memcpy(dst, src, compressed_size); + return op - (BYTE *)dst; } diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index f4ca25a38..0ac7b2ece 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -7,13 +7,13 @@ #define LDM_DECOMPRESS_SIZE 4 #define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) -size_t LDM_compress(void const *source, void *dest, size_t source_size, - size_t max_dest_size); +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); -size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, - size_t max_decompressed_size); +size_t LDM_decompress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); -void LDM_read_header(void const *source, size_t *compressed_size, - size_t *decompressed_size); +void LDM_read_header(const void *src, size_t *compressSize, + size_t *decompressSize); #endif /* LDM_H */ diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 10869cce3..0017335b8 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -18,6 +18,263 @@ //#define ZSTD +/* Compress file given by fname and output to oname. + * Returns 0 if successful, error code otherwise. + */ +static int compress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + + /* 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; + } + + size_t maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; + + /* Go to the location corresponding to the last byte. */ + /* TODO: fallocate? */ + if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* Write a dummy byte at the last location. */ + if (write(fdout, "", 1) != 1) { + perror("write 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; + } + + /* mmap the output file */ + if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + +#ifdef ZSTD + size_t compressSize = ZSTD_compress(dst, statbuf.st_size, + src, statbuf.st_size, 1); +#else + size_t compressSize = LDM_HEADER_SIZE + + LDM_compress(src, statbuf.st_size, + dst + LDM_HEADER_SIZE, statbuf.st_size); + + // Write compress and decompress size to header + // TODO: should depend on LDM_DECOMPRESS_SIZE write32 + memcpy(dst, &compressSize, 4); + memcpy(dst + 4, &(statbuf.st_size), 4); + +#ifdef DEBUG + printf("Compressed size: %zu\n", compressSize); + printf("Decompressed size: %zu\n", statbuf.st_size); +#endif +#endif + + // Truncate file to compressSize. + ftruncate(fdout, compressSize); + + printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, + (unsigned)statbuf.st_size, (unsigned)compressSize, oname, + (double)compressSize / (statbuf.st_size) * 100); + + // Close files. + close(fdin); + close(fdout); + return 0; +} + +/* 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; + + /* 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. */ + size_t compressSize, decompressSize; + LDM_read_header(src, &compressSize, &decompressSize); + +#ifdef DEBUG + printf("Size, compressSize, decompressSize: %zu %zu %zu\n", + statbuf.st_size, compressSize, decompressSize); +#endif + + /* Go to the location corresponding to the last byte. */ + if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write error"); + return 1; + } + + /* mmap the output file */ + if ((dst = mmap(0, decompressSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + +#ifdef ZSTD + size_t outSize = ZSTD_decompress(dst, decomrpessed_size, + src + LDM_HEADER_SIZE, + statbuf.st_size - LDM_HEADER_SIZE); +#else + size_t outSize = LDM_decompress( + src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, + dst, decompressSize); + + printf("Ret size out: %zu\n", outSize); + #endif + ftruncate(fdout, 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 void verify(const char *inpFilename, const char *decFilename) { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); +} + +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); + + struct timeval tv1, tv2; + + /* Compress */ + + gettimeofday(&tv1, NULL); + if (compress(inpFilename, ldmFilename)) { + printf("Compress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + + /* Decompress */ + + gettimeofday(&tv1, NULL); + if (decompress(ldmFilename, decFilename)) { + printf("Decompress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + + /* verify */ + verify(inpFilename, decFilename); + return 0; +} + + #if 0 static size_t compress_file(FILE *in, FILE *out, size_t *size_in, size_t *size_out) { @@ -137,249 +394,7 @@ static size_t decompress_file(FILE *in, FILE *out) { return ret; } -#endif -static size_t compress(const char *fname, const char *oname) { - int fdin, fdout; - struct stat statbuf; - char *src, *dst; - - /* 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 size of input file */ - if (fstat (fdin, &statbuf) < 0) { - perror("Fstat error"); - return 1; - } - size_t size_in = statbuf.st_size; - - /* go to the location corresponding to the last byte */ - if (lseek(fdout, size_in + LDM_HEADER_SIZE - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write 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; - } - size_t out_size = statbuf.st_size + LDM_HEADER_SIZE; - - /* mmap the output file */ - if ((dst = mmap(0, out_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - - #ifdef ZSTD - size_t size_out = ZSTD_compress(dst, statbuf.st_size, - src, statbuf.st_size, 1); - #else - size_t size_out = LDM_compress(src, dst + LDM_HEADER_SIZE, statbuf.st_size, - statbuf.st_size); - size_out += LDM_HEADER_SIZE; - - // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &size_out, 4); - memcpy(dst + 4, &(statbuf.st_size), 4); - printf("Compressed size: %zu\n", size_out); - printf("Decompressed size: %zu\n", statbuf.st_size); - #endif - ftruncate(fdout, size_out); - - printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, - (unsigned)statbuf.st_size, (unsigned)size_out, oname, - (double)size_out / (statbuf.st_size) * 100); - - close(fdin); - close(fdout); - return 0; -} - -static size_t decompress(const char *fname, const char *oname) { - int fdin, fdout; - struct stat statbuf; - char *src, *dst; - - /* 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 size of 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 header */ - size_t compressed_size, decompressed_size; - LDM_read_header(src, &compressed_size, &decompressed_size); - - printf("Size, compressed_size, decompressed_size: %zu %zu %zu\n", - statbuf.st_size, compressed_size, decompressed_size); - - /* go to the location corresponding to the last byte */ - if (lseek(fdout, decompressed_size - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write error"); - return 1; - } - - /* mmap the output file */ - if ((dst = mmap(0, decompressed_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - - /* Copy input file to output file */ -// memcpy(dst, src, statbuf.st_size); - - #ifdef ZSTD - size_t size_out = ZSTD_decompress(dst, decomrpessed_size, - src + LDM_HEADER_SIZE, - statbuf.st_size - LDM_HEADER_SIZE); - #else - size_t size_out = LDM_decompress(src + LDM_HEADER_SIZE, dst, - statbuf.st_size - LDM_HEADER_SIZE, - decompressed_size); - printf("Ret size out: %zu\n", size_out); - #endif - ftruncate(fdout, size_out); - - close(fdin); - close(fdout); - return 0; -} - -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; -} - -static void verify(const char *inpFilename, const char *decFilename) { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); -} - -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); - - struct timeval tv1, tv2; - /* compress */ - { - gettimeofday(&tv1, NULL); - if (compress(inpFilename, ldmFilename)) { - printf("Compress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - } - - /* decompress */ - - gettimeofday(&tv1, NULL); - if (decompress(ldmFilename, decFilename)) { - printf("Decompress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - - /* verify */ - verify(inpFilename, decFilename); - return 0; -} - -#if 0 int main2(int argc, char *argv[]) { char inpFilename[256] = { 0 }; char ldmFilename[256] = { 0 }; From 5432214ee31b0dc56b4311e786e2420548afb7c7 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 10 Jul 2017 06:50:49 -0700 Subject: [PATCH 074/318] Minor refactoring --- contrib/long_distance_matching/ldm.c | 47 +- .../versions/v2/Makefile | 32 ++ .../long_distance_matching/versions/v2/ldm.c | 436 ++++++++++++++++ .../long_distance_matching/versions/v2/ldm.h | 19 + .../versions/v2/main-ldm.c | 474 ++++++++++++++++++ 5 files changed, 980 insertions(+), 28 deletions(-) create mode 100644 contrib/long_distance_matching/versions/v2/Makefile create mode 100644 contrib/long_distance_matching/versions/v2/ldm.c create mode 100644 contrib/long_distance_matching/versions/v2/ldm.h create mode 100644 contrib/long_distance_matching/versions/v2/main-ldm.c diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index aeef4a330..9081d1362 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -33,8 +33,7 @@ typedef uint64_t U64; typedef uint64_t tag; -static unsigned LDM_isLittleEndian(void) -{ +static unsigned LDM_isLittleEndian(void) { const union { U32 u; BYTE c[4]; } one = { 1 }; return one.c[0]; } @@ -54,11 +53,11 @@ static U16 LDM_readLE16(const void *memPtr) { } } -static void LDM_write32(void *memPtr, U32 value) { +static void LDM_write16(void *memPtr, U16 value){ memcpy(memPtr, &value, sizeof(value)); } -static void LDM_write16(void *memPtr, U16 value) { +static void LDM_write32(void *memPtr, U32 value) { memcpy(memPtr, &value, sizeof(value)); } @@ -80,23 +79,10 @@ static U64 LDM_read64(const void *ptr) { return *(const U64 *)ptr; } - static void LDM_copy8(void *dst, const void *src) { memcpy(dst, src, 8); } -static void LDM_wild_copy(void *dstPtr, const void *srcPtr, void *dstEnd) { - BYTE *d = (BYTE *)dstPtr; - const BYTE *s = (const BYTE *)srcPtr; - BYTE * const e = (BYTE *)dstEnd; - - do { - LDM_copy8(d, s); - d += 8; - s += 8; - } while (d < e); -} - typedef struct compress_stats { U32 num_matches; U32 total_match_length; @@ -104,7 +90,7 @@ typedef struct compress_stats { U64 total_offset; } compress_stats; -static void print_compress_stats(const compress_stats *stats) { +static void LDM_printCompressStats(const compress_stats *stats) { printf("=====================\n"); printf("Compression statistics\n"); printf("Total number of matches: %u\n", stats->num_matches); @@ -117,6 +103,7 @@ static void print_compress_stats(const compress_stats *stats) { printf("=====================\n"); } +// TODO: unused. struct hash_entry { U64 offset; tag t; @@ -142,7 +129,6 @@ static U32 LDM_hash_position(const void * const p) { static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, const BYTE *srcBase) { -// printf("Hashing: %zu\n", p - srcBase); if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { return; } @@ -187,11 +173,11 @@ static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, return (unsigned)(pIn - pStart); } -void LDM_read_header(const void *src, size_t *compressed_size, - size_t *decompressed_size) { +void LDM_read_header(const void *src, size_t *compressSize, + size_t *decompressSize) { const U32 *ip = (const U32 *)src; - *compressed_size = *ip++; - *decompressed_size = *ip; + *compressSize = *ip++; + *decompressSize = *ip; } // TODO: maxDstSize is unused @@ -286,7 +272,6 @@ size_t LDM_compress(const void *src, size_t srcSize, printf("\n"); #endif memcpy(op, anchor, litLength); - //LDM_wild_copy(op, anchor, op + litLength); op += litLength; } _next_match: @@ -361,10 +346,19 @@ _last_literals: memcpy(op, anchor, lastRun); op += lastRun; } - print_compress_stats(&compressStats); + LDM_printCompressStats(&compressStats); return (op - (BYTE *)dst); } +typedef struct LDM_DCtx { + const BYTE * const ibase; /* Pointer to base of input */ + const BYTE *ip; /* Pointer to current input position */ + const BYTE *iend; /* End of source */ + BYTE *op; /* Pointer to output */ + const BYTE * const oend; /* Pointer to end of output */ + +} LDM_DCtx; + size_t LDM_decompress(const void *src, size_t compressed_size, void *dst, size_t max_decompressed_size) { const BYTE *ip = (const BYTE *)src; @@ -399,7 +393,6 @@ size_t LDM_decompress(const void *src, size_t compressed_size, printf("\n"); #endif memcpy(op, ip, length); -// LDM_wild_copy(op, ip, cpy); ip += length; op = cpy; @@ -433,12 +426,10 @@ size_t LDM_decompress(const void *src, size_t compressed_size, cpy = op + length; // Inefficient for now - while (match < cpy - offset && op < oend) { *op++ = *match++; } } -// memcpy(dst, src, compressed_size); return op - (BYTE *)dst; } diff --git a/contrib/long_distance_matching/versions/v2/Makefile b/contrib/long_distance_matching/versions/v2/Makefile new file mode 100644 index 000000000..4e04fd6a2 --- /dev/null +++ b/contrib/long_distance_matching/versions/v2/Makefile @@ -0,0 +1,32 @@ +# ################################################################ +# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. +# ################################################################ + +# This Makefile presumes libzstd is installed, using `sudo make install` + + +LDFLAGS += -lzstd + +.PHONY: default all clean + +default: all + +all: main-ldm + + +#main : ldm.c main.c +# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +main-ldm : ldm.c main-ldm.c + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +clean: + @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ + main main-ldm + @echo Cleaning completed + diff --git a/contrib/long_distance_matching/versions/v2/ldm.c b/contrib/long_distance_matching/versions/v2/ldm.c new file mode 100644 index 000000000..9081d1362 --- /dev/null +++ b/contrib/long_distance_matching/versions/v2/ldm.c @@ -0,0 +1,436 @@ +#include +#include +#include +#include + +#include "ldm.h" + +#define HASH_EVERY 7 + +#define LDM_MEMORY_USAGE 14 +#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) +#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) + +#define WINDOW_SIZE (1 << 20) +#define MAX_WINDOW_SIZE 31 +#define HASH_SIZE 8 +#define MINMATCH 8 + +#define ML_BITS 4 +#define ML_MASK ((1U<>8); + } +} + +static U32 LDM_read32(const void *ptr) { + return *(const U32 *)ptr; +} + +static U64 LDM_read64(const void *ptr) { + return *(const U64 *)ptr; +} + +static void LDM_copy8(void *dst, const void *src) { + memcpy(dst, src, 8); +} + +typedef struct compress_stats { + U32 num_matches; + U32 total_match_length; + U32 total_literal_length; + U64 total_offset; +} compress_stats; + +static void LDM_printCompressStats(const compress_stats *stats) { + printf("=====================\n"); + printf("Compression statistics\n"); + printf("Total number of matches: %u\n", stats->num_matches); + printf("Average match length: %.1f\n", ((double)stats->total_match_length) / + (double)stats->num_matches); + printf("Average literal length: %.1f\n", + ((double)stats->total_literal_length) / (double)stats->num_matches); + printf("Average offset length: %.1f\n", + ((double)stats->total_offset) / (double)stats->num_matches); + printf("=====================\n"); +} + +// TODO: unused. +struct hash_entry { + U64 offset; + tag t; +}; + +static U32 LDM_hash(U32 sequence) { + return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); +} + +static U32 LDM_hash5(U64 sequence) { + static const U64 prime5bytes = 889523592379ULL; + static const U64 prime8bytes = 11400714785074694791ULL; + const U32 hashLog = LDM_HASHLOG; + if (LDM_isLittleEndian()) + return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); + else + return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); +} + +static U32 LDM_hash_position(const void * const p) { + return LDM_hash(LDM_read32(p)); +} + +static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, + const BYTE *srcBase) { + if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { + return; + } + + U32 *hashTable = (U32 *) tableBase; + hashTable[h] = (U32)(p - srcBase); +} + +static void LDM_put_position(const BYTE *p, void *tableBase, + const BYTE *srcBase) { + if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { + return; + } + U32 const h = LDM_hash_position(p); + LDM_put_position_on_hash(p, h, tableBase, srcBase); +} + +static const BYTE *LDM_get_position_on_hash( + U32 h, void *tableBase, const BYTE *srcBase) { + const U32 * const hashTable = (U32*)tableBase; + return hashTable[h] + srcBase; +} + +static BYTE LDM_read_byte(const void *memPtr) { + BYTE val; + memcpy(&val, memPtr, 1); + return val; +} + +static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { + const BYTE * const pStart = pIn; + while (pIn < pInLimit - 1) { + BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); + if (!diff) { + pIn++; + pMatch++; + continue; + } + return (unsigned)(pIn - pStart); + } + return (unsigned)(pIn - pStart); +} + +void LDM_read_header(const void *src, size_t *compressSize, + size_t *decompressSize) { + const U32 *ip = (const U32 *)src; + *compressSize = *ip++; + *decompressSize = *ip; +} + +// TODO: maxDstSize is unused +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { + const BYTE * const istart = (const BYTE*)src; + const BYTE *ip = istart; + const BYTE * const iend = istart + srcSize; + const BYTE *ilimit = iend - HASH_SIZE; + const BYTE * const matchlimit = iend - HASH_SIZE; + const BYTE * const mflimit = iend - MINMATCH; + BYTE *op = (BYTE*) dst; + + compress_stats compressStats = { 0 }; + + U32 hashTable[LDM_HASHTABLESIZE_U32]; + memset(hashTable, 0, sizeof(hashTable)); + + const BYTE *anchor = (const BYTE *)src; +// struct LDM_cctx cctx; + size_t output_size = 0; + + U32 forwardH; + + /* Hash first byte: put into hash table */ + + LDM_put_position(ip, hashTable, istart); + const BYTE *lastHash = ip; + ip++; + forwardH = LDM_hash_position(ip); + + //TODO Loop terminates before ip>=ilimit. + while (ip < ilimit) { + const BYTE *match; + BYTE *token; + + /* Find a match */ + { + const BYTE *forwardIp = ip; + unsigned step = 1; + + do { + U32 const h = forwardH; + ip = forwardIp; + forwardIp += step; + + if (forwardIp > mflimit) { + goto _last_literals; + } + + match = LDM_get_position_on_hash(h, hashTable, istart); + + forwardH = LDM_hash_position(forwardIp); + LDM_put_position_on_hash(ip, h, hashTable, istart); + lastHash = ip; + } while (ip - match > WINDOW_SIZE || + LDM_read64(match) != LDM_read64(ip)); + } + compressStats.num_matches++; + + /* Catchup: look back to extend match from found match */ + while (ip > anchor && match > istart && ip[-1] == match[-1]) { + ip--; + match--; + } + + /* Encode literals */ + { + unsigned const litLength = (unsigned)(ip - anchor); + token = op++; + + compressStats.total_literal_length += litLength; + +#ifdef LDM_DEBUG + printf("Cur position: %zu\n", anchor - istart); + printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match); +#endif + + if (litLength >= RUN_MASK) { + int len = (int)litLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) { + *op++ = 255; + } + *op++ = (BYTE)len; + } else { + *token = (BYTE)(litLength << ML_BITS); + } +#ifdef LDM_DEBUG + printf("Literals "); + fwrite(anchor, litLength, 1, stdout); + printf("\n"); +#endif + memcpy(op, anchor, litLength); + op += litLength; + } +_next_match: + /* Encode offset */ + { + /* + LDM_writeLE16(op, ip-match); + op += 2; + */ + LDM_write32(op, ip - match); + op += 4; + compressStats.total_offset += (ip - match); + } + + /* Encode Match Length */ + { + unsigned matchCode; + matchCode = LDM_count(ip + MINMATCH, match + MINMATCH, + matchlimit); +#ifdef LDM_DEBUG + printf("Match length %zu\n", matchCode + MINMATCH); + fwrite(ip, MINMATCH + matchCode, 1, stdout); + printf("\n"); +#endif + compressStats.total_match_length += matchCode + MINMATCH; + unsigned ctr = 1; + ip++; + for (; ctr < MINMATCH + matchCode; ip++, ctr++) { + LDM_put_position(ip, hashTable, istart); + } +// ip += MINMATCH + matchCode; + if (matchCode >= ML_MASK) { + *token += ML_MASK; + matchCode -= ML_MASK; + LDM_write32(op, 0xFFFFFFFF); + while (matchCode >= 4*0xFF) { + op += 4; + LDM_write32(op, 0xffffffff); + matchCode -= 4*0xFF; + } + op += matchCode / 255; + *op++ = (BYTE)(matchCode % 255); + } else { + *token += (BYTE)(matchCode); + } +#ifdef LDM_DEBUG + printf("\n"); + +#endif + } + + anchor = ip; + + LDM_put_position(ip, hashTable, istart); + forwardH = LDM_hash_position(++ip); + lastHash = ip; + } +_last_literals: + /* Encode last literals */ + { + size_t const lastRun = (size_t)(iend - anchor); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255; accumulator -= 255) { + *op++ = 255; + } + *op++ = (BYTE)accumulator; + } else { + *op++ = (BYTE)(lastRun << ML_BITS); + } + memcpy(op, anchor, lastRun); + op += lastRun; + } + LDM_printCompressStats(&compressStats); + return (op - (BYTE *)dst); +} + +typedef struct LDM_DCtx { + const BYTE * const ibase; /* Pointer to base of input */ + const BYTE *ip; /* Pointer to current input position */ + const BYTE *iend; /* End of source */ + BYTE *op; /* Pointer to output */ + const BYTE * const oend; /* Pointer to end of output */ + +} LDM_DCtx; + +size_t LDM_decompress(const void *src, size_t compressed_size, + void *dst, size_t max_decompressed_size) { + const BYTE *ip = (const BYTE *)src; + const BYTE * const iend = ip + compressed_size; + BYTE *op = (BYTE *)dst; + BYTE * const oend = op + max_decompressed_size; + BYTE *cpy; + + while (ip < iend) { + size_t length; + const BYTE *match; + size_t offset; + + /* get literal length */ + unsigned const token = *ip++; + if ((length=(token >> ML_BITS)) == RUN_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while (s == 255); + } +#ifdef LDM_DEBUG + printf("Literal length: %zu\n", length); +#endif + + /* copy literals */ + cpy = op + length; +#ifdef LDM_DEBUG + printf("Literals "); + fwrite(ip, length, 1, stdout); + printf("\n"); +#endif + memcpy(op, ip, length); + ip += length; + op = cpy; + + /* get offset */ + /* + offset = LDM_readLE16(ip); + ip += 2; + */ + offset = LDM_read32(ip); + ip += 4; +#ifdef LDM_DEBUG + printf("Offset: %zu\n", offset); +#endif + match = op - offset; + // LDM_write32(op, (U32)offset); + + /* get matchlength */ + length = token & ML_MASK; + if (length == ML_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while (s == 255); + } + length += MINMATCH; +#ifdef LDM_DEBUG + printf("Match length: %zu\n", length); +#endif + /* copy match */ + cpy = op + length; + + // Inefficient for now + while (match < cpy - offset && op < oend) { + *op++ = *match++; + } + } + return op - (BYTE *)dst; +} + + diff --git a/contrib/long_distance_matching/versions/v2/ldm.h b/contrib/long_distance_matching/versions/v2/ldm.h new file mode 100644 index 000000000..0ac7b2ece --- /dev/null +++ b/contrib/long_distance_matching/versions/v2/ldm.h @@ -0,0 +1,19 @@ +#ifndef LDM_H +#define LDM_H + +#include /* size_t */ + +#define LDM_COMPRESS_SIZE 4 +#define LDM_DECOMPRESS_SIZE 4 +#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) + +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +size_t LDM_decompress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +void LDM_read_header(const void *src, size_t *compressSize, + size_t *decompressSize); + +#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v2/main-ldm.c b/contrib/long_distance_matching/versions/v2/main-ldm.c new file mode 100644 index 000000000..0017335b8 --- /dev/null +++ b/contrib/long_distance_matching/versions/v2/main-ldm.c @@ -0,0 +1,474 @@ +// TODO: file size must fit into a U32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ldm.h" + +// #define BUF_SIZE 16*1024 // Block size +#define DEBUG + +//#define ZSTD + +/* Compress file given by fname and output to oname. + * Returns 0 if successful, error code otherwise. + */ +static int compress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + + /* 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; + } + + size_t maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; + + /* Go to the location corresponding to the last byte. */ + /* TODO: fallocate? */ + if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* Write a dummy byte at the last location. */ + if (write(fdout, "", 1) != 1) { + perror("write 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; + } + + /* mmap the output file */ + if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + +#ifdef ZSTD + size_t compressSize = ZSTD_compress(dst, statbuf.st_size, + src, statbuf.st_size, 1); +#else + size_t compressSize = LDM_HEADER_SIZE + + LDM_compress(src, statbuf.st_size, + dst + LDM_HEADER_SIZE, statbuf.st_size); + + // Write compress and decompress size to header + // TODO: should depend on LDM_DECOMPRESS_SIZE write32 + memcpy(dst, &compressSize, 4); + memcpy(dst + 4, &(statbuf.st_size), 4); + +#ifdef DEBUG + printf("Compressed size: %zu\n", compressSize); + printf("Decompressed size: %zu\n", statbuf.st_size); +#endif +#endif + + // Truncate file to compressSize. + ftruncate(fdout, compressSize); + + printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, + (unsigned)statbuf.st_size, (unsigned)compressSize, oname, + (double)compressSize / (statbuf.st_size) * 100); + + // Close files. + close(fdin); + close(fdout); + return 0; +} + +/* 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; + + /* 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. */ + size_t compressSize, decompressSize; + LDM_read_header(src, &compressSize, &decompressSize); + +#ifdef DEBUG + printf("Size, compressSize, decompressSize: %zu %zu %zu\n", + statbuf.st_size, compressSize, decompressSize); +#endif + + /* Go to the location corresponding to the last byte. */ + if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write error"); + return 1; + } + + /* mmap the output file */ + if ((dst = mmap(0, decompressSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + +#ifdef ZSTD + size_t outSize = ZSTD_decompress(dst, decomrpessed_size, + src + LDM_HEADER_SIZE, + statbuf.st_size - LDM_HEADER_SIZE); +#else + size_t outSize = LDM_decompress( + src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, + dst, decompressSize); + + printf("Ret size out: %zu\n", outSize); + #endif + ftruncate(fdout, 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 void verify(const char *inpFilename, const char *decFilename) { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); +} + +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); + + struct timeval tv1, tv2; + + /* Compress */ + + gettimeofday(&tv1, NULL); + if (compress(inpFilename, ldmFilename)) { + printf("Compress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + + /* Decompress */ + + gettimeofday(&tv1, NULL); + if (decompress(ldmFilename, decFilename)) { + printf("Decompress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + + /* verify */ + verify(inpFilename, decFilename); + return 0; +} + + +#if 0 +static size_t compress_file(FILE *in, FILE *out, size_t *size_in, + size_t *size_out) { + char *src, *buf = NULL; + size_t r = 1; + size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; + + src = malloc(BUF_SIZE); + if (!src) { + printf("Not enough memory\n"); + goto cleanup; + } + + size = BUF_SIZE + LDM_HEADER_SIZE; + buf = malloc(size); + if (!buf) { + printf("Not enough memory\n"); + goto cleanup; + } + + + for (;;) { + k = fread(src, 1, BUF_SIZE, in); + if (k == 0) + break; + count_in += k; + + n = LDM_compress(src, buf, k, BUF_SIZE); + + // n = k; + // offset += n; + offset = k; + count_out += k; + +// k = fwrite(src, 1, offset, out); + + k = fwrite(buf, 1, offset, out); + if (k < offset) { + if (ferror(out)) + printf("Write failed\n"); + else + printf("Short write\n"); + goto cleanup; + } + + } + *size_in = count_in; + *size_out = count_out; + r = 0; + cleanup: + free(src); + free(buf); + return r; +} + +static size_t decompress_file(FILE *in, FILE *out) { + void *src = malloc(BUF_SIZE); + void *dst = NULL; + size_t dst_capacity = BUF_SIZE; + size_t ret = 1; + size_t bytes_written = 0; + + if (!src) { + perror("decompress_file(src)"); + goto cleanup; + } + + while (ret != 0) { + /* Load more input */ + size_t src_size = fread(src, 1, BUF_SIZE, in); + void *src_ptr = src; + void *src_end = src_ptr + src_size; + if (src_size == 0 || ferror(in)) { + printf("(TODO): Decompress: not enough input or error reading file\n"); + //TODO + ret = 0; + goto cleanup; + } + + /* Allocate destination buffer if it hasn't been allocated already */ + if (!dst) { + dst = malloc(dst_capacity); + if (!dst) { + perror("decompress_file(dst)"); + goto cleanup; + } + } + + // TODO + + /* Decompress: + * Continue while there is more input to read. + */ + while (src_ptr != src_end && ret != 0) { + // size_t dst_size = src_size; + size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); + size_t written = fwrite(dst, 1, dst_size, out); +// printf("Writing %zu bytes\n", dst_size); + bytes_written += dst_size; + if (written != dst_size) { + printf("Decompress: Failed to write to file\n"); + goto cleanup; + } + src_ptr += src_size; + src_size = src_end - src_ptr; + } + + /* Update input */ + + } + + printf("Wrote %zu bytes\n", bytes_written); + + cleanup: + free(src); + free(dst); + + return ret; +} + +int main2(int argc, char *argv[]) { + char inpFilename[256] = { 0 }; + char ldmFilename[256] = { 0 }; + char decFilename[256] = { 0 }; + + if (argc < 2) { + printf("Please specify input filename\n"); + return 0; + } + 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 */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *outFp = fopen(ldmFilename, "wb"); + size_t sizeIn = 0; + size_t sizeOut = 0; + size_t ret; + printf("compress : %s -> %s\n", inpFilename, ldmFilename); + ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); + if (ret) { + printf("compress : failed with code %zu\n", ret); + return ret; + } + printf("%s: %zu → %zu bytes, %.1f%%\n", + inpFilename, sizeIn, sizeOut, + (double)sizeOut / sizeIn * 100); + printf("compress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* decompress */ + { + FILE *inpFp = fopen(ldmFilename, "rb"); + FILE *outFp = fopen(decFilename, "wb"); + size_t ret; + + printf("decompress : %s -> %s\n", ldmFilename, decFilename); + ret = decompress_file(inpFp, outFp); + if (ret) { + printf("decompress : failed with code %zu\n", ret); + return ret; + } + printf("decompress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* verify */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); + } + return 0; +} +#endif + From ae9cf235d62bb7482496309294852f51bdfd1f1d Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 10 Jul 2017 07:38:09 -0700 Subject: [PATCH 075/318] Add LDM_DCtx --- contrib/long_distance_matching/ldm.c | 74 ++++++++++++++--------- contrib/long_distance_matching/main-ldm.c | 4 +- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 9081d1362..b18ed3d45 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -351,33 +351,49 @@ _last_literals: } typedef struct LDM_DCtx { - const BYTE * const ibase; /* Pointer to base of input */ - const BYTE *ip; /* Pointer to current input position */ - const BYTE *iend; /* End of source */ - BYTE *op; /* Pointer to output */ - const BYTE * const oend; /* Pointer to end of output */ + const BYTE *ibase; /* Pointer to base of input */ + const BYTE *ip; /* Pointer to current input position */ + const BYTE *iend; /* End of source */ + const BYTE *obase; /* Pointer to base of output */ + BYTE *op; /* Pointer to output */ + const BYTE *oend; /* Pointer to end of output */ + + size_t compressSize; + size_t maxDecompressSize; } LDM_DCtx; -size_t LDM_decompress(const void *src, size_t compressed_size, - void *dst, size_t max_decompressed_size) { - const BYTE *ip = (const BYTE *)src; - const BYTE * const iend = ip + compressed_size; - BYTE *op = (BYTE *)dst; - BYTE * const oend = op + max_decompressed_size; +static void LDM_initializeDCtx(LDM_DCtx *dctx, + const void *src, size_t compressSize, + void *dst, size_t maxDecompressSize) { + dctx->ibase = src; + dctx->ip = (const BYTE *)src; + dctx->iend = dctx->ip + compressSize; + dctx->op = dst; + dctx->oend = dctx->op + maxDecompressSize; + + dctx->compressSize = compressSize; + dctx->maxDecompressSize = maxDecompressSize; +} + +size_t LDM_decompress(const void *src, size_t compressSize, + void *dst, size_t maxDecompressSize) { + LDM_DCtx dctx; + LDM_initializeDCtx(&dctx, src, compressSize, dst, maxDecompressSize); + BYTE *cpy; - while (ip < iend) { + while (dctx.ip < dctx.iend) { size_t length; const BYTE *match; size_t offset; /* get literal length */ - unsigned const token = *ip++; + unsigned const token = *(dctx.ip)++; if ((length=(token >> ML_BITS)) == RUN_MASK) { unsigned s; do { - s = *ip++; + s = *(dctx.ip)++; length += s; } while (s == 255); } @@ -386,27 +402,27 @@ size_t LDM_decompress(const void *src, size_t compressed_size, #endif /* copy literals */ - cpy = op + length; + cpy = dctx.op + length; #ifdef LDM_DEBUG printf("Literals "); - fwrite(ip, length, 1, stdout); + fwrite(dctx.ip, length, 1, stdout); printf("\n"); #endif - memcpy(op, ip, length); - ip += length; - op = cpy; + memcpy(dctx.op, dctx.ip, length); + dctx.ip += length; + dctx.op = cpy; /* get offset */ /* - offset = LDM_readLE16(ip); - ip += 2; + offset = LDM_readLE16(dctx.ip); + dctx.ip += 2; */ - offset = LDM_read32(ip); - ip += 4; + offset = LDM_read32(dctx.ip); + dctx.ip += 4; #ifdef LDM_DEBUG printf("Offset: %zu\n", offset); #endif - match = op - offset; + match = dctx.op - offset; // LDM_write32(op, (U32)offset); /* get matchlength */ @@ -414,7 +430,7 @@ size_t LDM_decompress(const void *src, size_t compressed_size, if (length == ML_MASK) { unsigned s; do { - s = *ip++; + s = *(dctx.ip)++; length += s; } while (s == 255); } @@ -423,14 +439,14 @@ size_t LDM_decompress(const void *src, size_t compressed_size, printf("Match length: %zu\n", length); #endif /* copy match */ - cpy = op + length; + cpy = dctx.op + length; // Inefficient for now - while (match < cpy - offset && op < oend) { - *op++ = *match++; + while (match < cpy - offset && dctx.op < dctx.oend) { + *(dctx.op)++ = *match++; } } - return op - (BYTE *)dst; + return dctx.op - (BYTE *)dst; } diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 0017335b8..b529201fd 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -88,7 +88,7 @@ static int compress(const char *fname, const char *oname) { #ifdef DEBUG printf("Compressed size: %zu\n", compressSize); - printf("Decompressed size: %zu\n", statbuf.st_size); + printf("Decompressed size: %zu\n", (size_t)statbuf.st_size); #endif #endif @@ -145,7 +145,7 @@ static int decompress(const char *fname, const char *oname) { #ifdef DEBUG printf("Size, compressSize, decompressSize: %zu %zu %zu\n", - statbuf.st_size, compressSize, decompressSize); + (size_t)statbuf.st_size, compressSize, decompressSize); #endif /* Go to the location corresponding to the last byte. */ From 62ebbabd32dfccb5363cb245c06f7affb7535727 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 09:36:22 -0700 Subject: [PATCH 076/318] updated error checking in each thread --- contrib/adaptive-compression/multi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 78d9ca154..4c3f584e1 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -284,12 +284,14 @@ static void* outputThread(void* arg) size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { DISPLAY("Error: an error occurred during compression\n"); + ctx->threadError = 1; return arg; } { size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, ctx->dstFile); if (writeSize != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); + ctx->threadError = 1; return arg; } } @@ -429,6 +431,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); + ctx->threadError = 1; ret = 1; goto cleanup; } @@ -438,6 +441,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst int const error = createCompressionJob(ctx, src, readSize); if (error != 0) { ret = error; + ctx->threadError = 1; goto cleanup; } } From 82f0d64beefb4d6c7041ab131602b22c23e320ae Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 10:51:50 -0700 Subject: [PATCH 077/318] removed single.c --- contrib/adaptive-compression/single.c | 73 --------------------------- 1 file changed, 73 deletions(-) delete mode 100644 contrib/adaptive-compression/single.c diff --git a/contrib/adaptive-compression/single.c b/contrib/adaptive-compression/single.c deleted file mode 100644 index ef8062d8f..000000000 --- a/contrib/adaptive-compression/single.c +++ /dev/null @@ -1,73 +0,0 @@ -#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) -#define FILE_CHUNK_SIZE 4 << 20 -typedef unsigned char BYTE; - -#include -#include -#include "zstd.h" - - - -/* return 0 if successful, else return error */ -int main(int argCount, const char* argv[]) -{ - const char* const srcFilename = argv[1]; - const char* const dstFilename = argv[2]; - FILE* const srcFile = fopen(srcFilename, "rb"); - FILE* const dstFile = fopen(dstFilename, "wb"); - BYTE* const src = malloc(FILE_CHUNK_SIZE); - size_t const dstSize = ZSTD_compressBound(FILE_CHUNK_SIZE); - BYTE* const dst = malloc(dstSize); - int ret = 0; - - /* checking for errors */ - if (!srcFilename || !dstFilename || !src || !dst) { - DISPLAY("Error: initial variables could not be allocated\n"); - ret = 1; - goto cleanup; - } - - /* compressing in blocks */ - for ( ; ; ) { - size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); - if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { - DISPLAY("Error: could not read %d bytes\n", FILE_CHUNK_SIZE); - ret = 1; - goto cleanup; - } - { - size_t const compressedSize = ZSTD_compress(dst, dstSize, src, readSize, 6); - if (ZSTD_isError(compressedSize)) { - DISPLAY("Error: something went wrong during compression\n"); - ret = 1; - goto cleanup; - } - { - size_t const writeSize = fwrite(dst, 1, compressedSize, dstFile); - if (writeSize != compressedSize) { - DISPLAY("Error: could not write compressed data to file\n"); - ret = 1; - goto cleanup; - } - } - } - if (feof(srcFile)) { - /* reached end of file */ - break; - } - } - - /* file compression completed */ - { - int const error = fclose(srcFile); - if (ret != 0) { - DISPLAY("Error: could not close the file\n"); - ret = error; - goto cleanup; - } - } -cleanup: - if (src != NULL) free(src); - if (dst != NULL) free(dst); - return ret; -} From ced3ec5714073fd17a4711d132a6da876617833a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 10:53:02 -0700 Subject: [PATCH 078/318] removed scripts --- contrib/adaptive-compression/pipetests.sh | 2 - contrib/adaptive-compression/run.sh | 162 ---------------------- 2 files changed, 164 deletions(-) delete mode 100755 contrib/adaptive-compression/pipetests.sh delete mode 100755 contrib/adaptive-compression/run.sh diff --git a/contrib/adaptive-compression/pipetests.sh b/contrib/adaptive-compression/pipetests.sh deleted file mode 100755 index 71f123a17..000000000 --- a/contrib/adaptive-compression/pipetests.sh +++ /dev/null @@ -1,2 +0,0 @@ -make clean multi -pv -q -L 100m tests/test2048.pdf | ./multi -v -otmp.zst diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh deleted file mode 100755 index 3c40969e6..000000000 --- a/contrib/adaptive-compression/run.sh +++ /dev/null @@ -1,162 +0,0 @@ -make clean multi - -echo "running file tests" - -./multi tests/test2048.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test2048.pdf -echo "diff test complete: test2048.pdf" -rm tmp* - -./multi tests/test512.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test512.pdf -echo "diff test complete: test512.pdf" -rm tmp* - -./multi tests/test64.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test64.pdf -echo "diff test complete: test64.pdf" -rm tmp* - -./multi tests/test16.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test16.pdf -echo "diff test complete: test16.pdf" -rm tmp* - -./multi tests/test4.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test4.pdf -echo "diff test complete: test4.pdf" -rm tmp* - -./multi tests/test.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test.pdf -echo "diff test complete: test.pdf" -rm tmp* - -echo "Running std input/output tests" - -cat tests/test2048.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test2048.pdf -echo "diff test complete: test2048.pdf" -rm tmp* - -cat tests/test512.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test512.pdf -echo "diff test complete: test512.pdf" -rm tmp* - -cat tests/test64.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test64.pdf -echo "diff test complete: test64.pdf" -rm tmp* - -cat tests/test16.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test16.pdf -echo "diff test complete: test16.pdf" -rm tmp* - -cat tests/test4.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test4.pdf -echo "diff test complete: test4.pdf" -rm tmp* - -cat tests/test.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test.pdf -echo "diff test complete: test.pdf" -rm tmp* - -cat tests/test2048.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test2048.pdf -echo "diff test complete: test2048.pdf" -rm tmp* - -cat tests/test512.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test512.pdf -echo "diff test complete: test512.pdf" -rm tmp* - -cat tests/test64.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test64.pdf -echo "diff test complete: test64.pdf" -rm tmp* - -cat tests/test16.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test16.pdf -echo "diff test complete: test16.pdf" -rm tmp* - -cat tests/test4.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test4.pdf -echo "diff test complete: test4.pdf" -rm tmp* - -cat tests/test.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test.pdf -echo "diff test complete: test.pdf" -rm tmp* - -echo "Running multi-file tests" -./multi tests/* -zstd -d tests/test.pdf.zst -o tests/tmp -zstd -d tests/test2.pdf.zst -o tests/tmp2 -zstd -d tests/test4.pdf.zst -o tests/tmp4 -zstd -d tests/test8.pdf.zst -o tests/tmp8 -zstd -d tests/test16.pdf.zst -o tests/tmp16 -zstd -d tests/test32.pdf.zst -o tests/tmp32 -zstd -d tests/test64.pdf.zst -o tests/tmp64 -zstd -d tests/test128.pdf.zst -o tests/tmp128 -zstd -d tests/test256.pdf.zst -o tests/tmp256 -zstd -d tests/test512.pdf.zst -o tests/tmp512 -zstd -d tests/test1024.pdf.zst -o tests/tmp1024 -zstd -d tests/test2048.pdf.zst -o tests/tmp2048 - -diff tests/test.pdf tests/tmp -diff tests/test2.pdf tests/tmp2 -diff tests/test4.pdf tests/tmp4 -diff tests/test8.pdf tests/tmp8 -diff tests/test16.pdf tests/tmp16 -diff tests/test32.pdf tests/tmp32 -diff tests/test64.pdf tests/tmp64 -diff tests/test128.pdf tests/tmp128 -diff tests/test256.pdf tests/tmp256 -diff tests/test512.pdf tests/tmp512 -diff tests/test1024.pdf tests/tmp1024 -diff tests/test2048.pdf tests/tmp2048 - -rm -f tests/*.zst tests/tmp* -echo "Running Args Tests" -./multi -h -./multi -i22 -p -s -otmp.zst tests/test2048.pdf -rm tmp* - -echo "Running Tests With Multiple Files > stdout" -./multi tests/* -c > tmp.zst -zstd -d tmp.zst -rm tmp* - -echo "Running single test without output filename" -./multi tests/test2048.pdf -p -zstd -d tests/test2048.pdf.zst -o tmp -diff tmp tests/test2048.pdf -rm tmp* - -echo "finished with tests" - -make clean From ed72ea54380dc732fe9c648f9cf5ee683fad17d0 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 10:58:03 -0700 Subject: [PATCH 079/318] removed single from Makefile --- contrib/adaptive-compression/Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 2d3de33ea..3027b88c3 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -19,15 +19,13 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -all: clean single multi -single: $(ZSTD_FILES) single.c - $(CC) $(FLAGS) $^ -o $@ +all: clean multi multi: $(ZSTD_FILES) multi.c $(CC) $(FLAGS) $^ -o $@ clean: - @$(RM) -f single multi + @$(RM) -f multi @$(RM) -rf *.dSYM @$(RM) -f tmp* @$(RM) -f tests/*.zst From 7e09b508ff5e58fb1c4272b1616d808d10110392 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 11:05:37 -0700 Subject: [PATCH 080/318] changed name --- contrib/adaptive-compression/Makefile | 6 +++--- contrib/adaptive-compression/{multi.c => adapt.c} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename contrib/adaptive-compression/{multi.c => adapt.c} (100%) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 3027b88c3..38e3a7787 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -19,13 +19,13 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -all: clean multi +all: clean adapt -multi: $(ZSTD_FILES) multi.c +adapt: $(ZSTD_FILES) adapt.c $(CC) $(FLAGS) $^ -o $@ clean: - @$(RM) -f multi + @$(RM) -f adapt @$(RM) -rf *.dSYM @$(RM) -f tmp* @$(RM) -f tests/*.zst diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/adapt.c similarity index 100% rename from contrib/adaptive-compression/multi.c rename to contrib/adaptive-compression/adapt.c From cc7f8e4d71bb2bb355f0c0a89de42ba176d83d64 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 11:10:11 -0700 Subject: [PATCH 081/318] small changes --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 4c3f584e1..61fb31421 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -115,7 +115,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) } memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = g_compressionLevel; - pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); /* TODO: add checks for errors on each mutex */ + pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); pthread_cond_init(&ctx->jobCompressed_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); From 89190ef07dc87d3c292ad98ff1f5d20f0238c19e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 11:32:30 -0700 Subject: [PATCH 082/318] renamed pool.c to poolTests.c --- tests/Makefile | 10 +++++----- tests/{pool.c => poolTests.c} | 0 2 files changed, 5 insertions(+), 5 deletions(-) rename tests/{pool.c => poolTests.c} (100%) diff --git a/tests/Makefile b/tests/Makefile index 345e0e8eb..cd89a9405 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -79,7 +79,7 @@ all32: fullbench32 fuzzer32 zstreamtest32 allnothread: fullbench fuzzer paramgrill datagen decodecorpus -dll: fuzzer-dll zstreamtest-dll +dll: fuzzer-dll zstreamtest-dll zstd: $(MAKE) -C $(PRGDIR) $@ @@ -192,7 +192,7 @@ else $(CC) $(FLAGS) $^ -o $@$(EXT) -Wl,-rpath=$(ZSTDDIR) $(ZSTDDIR)/libzstd.so endif -pool : pool.c $(ZSTDDIR)/common/pool.c $(ZSTDDIR)/common/threading.c +poolTests : poolTests.c $(ZSTDDIR)/common/pool.c $(ZSTDDIR)/common/threading.c $(CC) $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT) namespaceTest: @@ -213,7 +213,7 @@ clean: fuzzer-dll$(EXT) zstreamtest-dll$(EXT) zbufftest-dll$(EXT)\ zstreamtest$(EXT) zstreamtest32$(EXT) \ datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) \ - symbols$(EXT) invalidDictionaries$(EXT) legacy$(EXT) pool$(EXT) \ + symbols$(EXT) invalidDictionaries$(EXT) legacy$(EXT) poolTests$(EXT) \ decodecorpus$(EXT) @echo Cleaning completed @@ -375,7 +375,7 @@ test-decodecorpus-cli: decodecorpus cd .. @rm -rf testdir -test-pool: pool - $(QEMU_SYS) ./pool +test-pool: poolTests + $(QEMU_SYS) ./poolTests endif diff --git a/tests/pool.c b/tests/poolTests.c similarity index 100% rename from tests/pool.c rename to tests/poolTests.c From e32fb0c1fe92cc225ed9585efd2a3bcee3aab053 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 10 Jul 2017 12:29:57 -0700 Subject: [PATCH 083/318] added ZSTD_sizeof_CCtx() test --- lib/compress/zstdmt_compress.c | 4 ++-- tests/fuzzer.c | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 0cee01eac..d5607adfd 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -763,10 +763,10 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi zcs->inBuff.filled = 0; zcs->dictSize = 0; zcs->frameEnded = 1; - if (zcs->nextJobID == 0) + if (zcs->nextJobID == 0) { /* single chunk exception : checksum is calculated directly within worker thread */ zcs->params.fParams.checksumFlag = 0; - } + } } DEBUGLOG(4, "posting job %u : %u bytes (end:%u) (note : doneJob = %u=>%u)", zcs->nextJobID, diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 4d88893a1..c9461ee90 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -136,10 +136,20 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "test%3i : compress %u bytes : ", testNb++, (U32)CNBuffSize); - CHECKPLUS(r, ZSTD_compress(compressedBuffer, ZSTD_compressBound(CNBuffSize), - CNBuffer, CNBuffSize, 1), - cSize=r ); - DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); + { ZSTD_CCtx* cctx = ZSTD_createCCtx(); + if (cctx==NULL) goto _output_error; + CHECKPLUS(r, ZSTD_compressCCtx(cctx, + compressedBuffer, ZSTD_compressBound(CNBuffSize), + CNBuffer, CNBuffSize, 1), + cSize=r ); + DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); + + DISPLAYLEVEL(4, "test%3i : size of cctx for level 1 : ", testNb++); + { size_t const cctxSize = ZSTD_sizeof_CCtx(cctx); + DISPLAYLEVEL(4, "%u bytes \n", (U32)cctxSize); + } + ZSTD_freeCCtx(cctx); + } DISPLAYLEVEL(4, "test%3i : ZSTD_getFrameContentSize test : ", testNb++); From 10a71d9f1c31539b79c66c90750025e26094621c Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 10 Jul 2017 12:38:27 -0700 Subject: [PATCH 084/318] Add compression context --- contrib/long_distance_matching/ldm.c | 287 ++++++++++++++++----------- 1 file changed, 167 insertions(+), 120 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index b18ed3d45..7bf267815 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -31,7 +31,10 @@ typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; -typedef uint64_t tag; +typedef uint32_t offset_t; +typedef uint32_t hash_t; + +// typedef uint64_t tag; static unsigned LDM_isLittleEndian(void) { const union { U32 u; BYTE c[4]; } one = { 1 }; @@ -65,7 +68,7 @@ static void LDM_writeLE16(void *memPtr, U16 value) { if (LDM_isLittleEndian()) { LDM_write16(memPtr, value); } else { - BYTE* p = (BYTE*)memPtr; + BYTE* p = (BYTE *)memPtr; p[0] = (BYTE) value; p[1] = (BYTE)(value>>8); } @@ -82,15 +85,18 @@ static U64 LDM_read64(const void *ptr) { static void LDM_copy8(void *dst, const void *src) { memcpy(dst, src, 8); } +typedef struct LDM_hashEntry { + offset_t offset; +} LDM_hashEntry; -typedef struct compress_stats { +typedef struct LDM_compressStats { U32 num_matches; U32 total_match_length; U32 total_literal_length; U64 total_offset; -} compress_stats; +} LDM_compressStats; -static void LDM_printCompressStats(const compress_stats *stats) { +static void LDM_printCompressStats(const LDM_compressStats *stats) { printf("=====================\n"); printf("Compression statistics\n"); printf("Total number of matches: %u\n", stats->num_matches); @@ -103,53 +109,83 @@ static void LDM_printCompressStats(const compress_stats *stats) { printf("=====================\n"); } -// TODO: unused. -struct hash_entry { - U64 offset; - tag t; -}; +typedef struct LDM_CCtx { + size_t isize; /* Input size */ + size_t maxOSize; /* Maximum output size */ -static U32 LDM_hash(U32 sequence) { + 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_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; + + const BYTE *lastPosHashed; /* Last position hashed */ + hash_t lastHash; /* Hash corresponding to lastPosHashed */ + +} LDM_CCtx; + + +static hash_t LDM_hash(U32 sequence) { return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); } -static U32 LDM_hash5(U64 sequence) { +static hash_t LDM_hash5(U64 sequence) { static const U64 prime5bytes = 889523592379ULL; static const U64 prime8bytes = 11400714785074694791ULL; const U32 hashLog = LDM_HASHLOG; if (LDM_isLittleEndian()) - return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); + return (((sequence << 24) * prime5bytes) >> (64 - hashLog)); else - return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); + return (((sequence >> 24) * prime8bytes) >> (64 - hashLog)); } -static U32 LDM_hash_position(const void * const p) { +static hash_t LDM_hash_position(const void * const p) { return LDM_hash(LDM_read32(p)); } -static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, - const BYTE *srcBase) { +static void LDM_put_position_on_hash(const BYTE *p, hash_t h, + void *tableBase, const BYTE *srcBase) { if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { return; } - U32 *hashTable = (U32 *) tableBase; - hashTable[h] = (U32)(p - srcBase); + LDM_hashEntry *hashTable = (LDM_hashEntry *) tableBase; + hashTable[h] = (LDM_hashEntry) { (hash_t )(p - srcBase) }; } -static void LDM_put_position(const BYTE *p, void *tableBase, +static void LDM_putPosition(const BYTE *p, void *tableBase, const BYTE *srcBase) { if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { return; } - U32 const h = LDM_hash_position(p); + hash_t const h = LDM_hash_position(p); LDM_put_position_on_hash(p, h, tableBase, srcBase); } +static void LDM_putHashOfCurrentPosition(LDM_CCtx *const cctx) { + LDM_putPosition(cctx->ip, cctx->hashTable, cctx->ibase); +} + + static const BYTE *LDM_get_position_on_hash( - U32 h, void *tableBase, const BYTE *srcBase) { - const U32 * const hashTable = (U32*)tableBase; - return hashTable[h] + srcBase; + hash_t h, void *tableBase, const BYTE *srcBase) { + const LDM_hashEntry * const hashTable = (LDM_hashEntry *)tableBase; + return hashTable[h].offset + srcBase; } static BYTE LDM_read_byte(const void *memPtr) { @@ -180,140 +216,149 @@ void LDM_read_header(const void *src, size_t *compressSize, *decompressSize = *ip; } -// TODO: maxDstSize is unused +static void 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 - HASH_SIZE; + cctx->imatchLimit = cctx->iend - MINMATCH; + + cctx->obase = (BYTE *)dst; + cctx->op = (BYTE *)cctx->obase; + + cctx->anchor = cctx->ibase; + + memset(&(cctx->stats), 0, sizeof(cctx->stats)); + memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); + + cctx->lastPosHashed = NULL; +} + +// TODO: srcSize and maxDstSize is unused size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { - const BYTE * const istart = (const BYTE*)src; - const BYTE *ip = istart; - const BYTE * const iend = istart + srcSize; - const BYTE *ilimit = iend - HASH_SIZE; - const BYTE * const matchlimit = iend - HASH_SIZE; - const BYTE * const mflimit = iend - MINMATCH; - BYTE *op = (BYTE*) dst; - - compress_stats compressStats = { 0 }; - - U32 hashTable[LDM_HASHTABLESIZE_U32]; - memset(hashTable, 0, sizeof(hashTable)); - - const BYTE *anchor = (const BYTE *)src; -// struct LDM_cctx cctx; - size_t output_size = 0; + LDM_CCtx cctx; + LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); U32 forwardH; - /* Hash first byte: put into hash table */ + /* Hash the first position and put it into the hash table. */ + LDM_putHashOfCurrentPosition(&cctx); + const BYTE *lastHash = cctx.ip; + cctx.ip++; + forwardH = LDM_hash_position(cctx.ip); - LDM_put_position(ip, hashTable, istart); - const BYTE *lastHash = ip; - ip++; - forwardH = LDM_hash_position(ip); - - //TODO Loop terminates before ip>=ilimit. - while (ip < ilimit) { + // TODO: loop condition is not accurate. + while (1) { const BYTE *match; BYTE *token; /* Find a match */ { - const BYTE *forwardIp = ip; + const BYTE *forwardIp = cctx.ip; unsigned step = 1; do { U32 const h = forwardH; - ip = forwardIp; + cctx.ip = forwardIp; forwardIp += step; - if (forwardIp > mflimit) { + if (forwardIp > cctx.imatchLimit) { goto _last_literals; } - match = LDM_get_position_on_hash(h, hashTable, istart); + match = LDM_get_position_on_hash(h, cctx.hashTable, cctx.ibase); forwardH = LDM_hash_position(forwardIp); - LDM_put_position_on_hash(ip, h, hashTable, istart); - lastHash = ip; - } while (ip - match > WINDOW_SIZE || - LDM_read64(match) != LDM_read64(ip)); + LDM_put_position_on_hash(cctx.ip, h, cctx.hashTable, cctx.ibase); + lastHash = cctx.ip; + } while (cctx.ip - match > WINDOW_SIZE || + LDM_read64(match) != LDM_read64(cctx.ip)); } - compressStats.num_matches++; + cctx.stats.num_matches++; /* Catchup: look back to extend match from found match */ - while (ip > anchor && match > istart && ip[-1] == match[-1]) { - ip--; + while (cctx.ip > cctx.anchor && match > cctx.ibase && cctx.ip[-1] == match[-1]) { + cctx.ip--; match--; } /* Encode literals */ { - unsigned const litLength = (unsigned)(ip - anchor); - token = op++; + unsigned const litLength = (unsigned)(cctx.ip - cctx.anchor); + token = cctx.op++; - compressStats.total_literal_length += litLength; + cctx.stats.total_literal_length += litLength; #ifdef LDM_DEBUG - printf("Cur position: %zu\n", anchor - istart); - printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match); + printf("Cur position: %zu\n", cctx.anchor - cctx.ibase); + printf("LitLength %zu. (Match offset). %zu\n", litLength, cctx.ip - match); #endif if (litLength >= RUN_MASK) { int len = (int)litLength - RUN_MASK; *token = (RUN_MASK << ML_BITS); for (; len >= 255; len -= 255) { - *op++ = 255; + *(cctx.op)++ = 255; } - *op++ = (BYTE)len; + *(cctx.op)++ = (BYTE)len; } else { *token = (BYTE)(litLength << ML_BITS); } #ifdef LDM_DEBUG printf("Literals "); - fwrite(anchor, litLength, 1, stdout); + fwrite(cctx.anchor, litLength, 1, stdout); printf("\n"); #endif - memcpy(op, anchor, litLength); - op += litLength; + memcpy(cctx.op, cctx.anchor, litLength); + cctx.op += litLength; } _next_match: /* Encode offset */ { /* - LDM_writeLE16(op, ip-match); - op += 2; + LDM_writeLE16(cctx.op, cctx.ip-match); + cctx.op += 2; */ - LDM_write32(op, ip - match); - op += 4; - compressStats.total_offset += (ip - match); + LDM_write32(cctx.op, cctx.ip - match); + cctx.op += 4; + cctx.stats.total_offset += (cctx.ip - match); } /* Encode Match Length */ { unsigned matchCode; - matchCode = LDM_count(ip + MINMATCH, match + MINMATCH, - matchlimit); + matchCode = LDM_count(cctx.ip + MINMATCH, match + MINMATCH, + cctx.ihashLimit); #ifdef LDM_DEBUG printf("Match length %zu\n", matchCode + MINMATCH); - fwrite(ip, MINMATCH + matchCode, 1, stdout); + fwrite(cctx.ip, MINMATCH + matchCode, 1, stdout); printf("\n"); #endif - compressStats.total_match_length += matchCode + MINMATCH; + cctx.stats.total_match_length += matchCode + MINMATCH; unsigned ctr = 1; - ip++; - for (; ctr < MINMATCH + matchCode; ip++, ctr++) { - LDM_put_position(ip, hashTable, istart); + cctx.ip++; + for (; ctr < MINMATCH + matchCode; cctx.ip++, ctr++) { + LDM_putHashOfCurrentPosition(&cctx); } -// ip += MINMATCH + matchCode; +// cctx.ip += MINMATCH + matchCode; if (matchCode >= ML_MASK) { *token += ML_MASK; matchCode -= ML_MASK; - LDM_write32(op, 0xFFFFFFFF); + LDM_write32(cctx.op, 0xFFFFFFFF); while (matchCode >= 4*0xFF) { - op += 4; - LDM_write32(op, 0xffffffff); + cctx.op += 4; + LDM_write32(cctx.op, 0xffffffff); matchCode -= 4*0xFF; } - op += matchCode / 255; - *op++ = (BYTE)(matchCode % 255); + cctx.op += matchCode / 255; + *(cctx.op)++ = (BYTE)(matchCode % 255); } else { *token += (BYTE)(matchCode); } @@ -323,57 +368,58 @@ _next_match: #endif } - anchor = ip; + cctx.anchor = cctx.ip; - LDM_put_position(ip, hashTable, istart); - forwardH = LDM_hash_position(++ip); - lastHash = ip; + LDM_putPosition(cctx.ip, cctx.hashTable, cctx.ibase); + forwardH = LDM_hash_position(++cctx.ip); + lastHash = cctx.ip; } _last_literals: /* Encode last literals */ { - size_t const lastRun = (size_t)(iend - anchor); + size_t const lastRun = (size_t)(cctx.iend - cctx.anchor); if (lastRun >= RUN_MASK) { size_t accumulator = lastRun - RUN_MASK; - *op++ = RUN_MASK << ML_BITS; + *(cctx.op)++ = RUN_MASK << ML_BITS; for(; accumulator >= 255; accumulator -= 255) { - *op++ = 255; + *(cctx.op)++ = 255; } - *op++ = (BYTE)accumulator; + *(cctx.op)++ = (BYTE)accumulator; } else { - *op++ = (BYTE)(lastRun << ML_BITS); + *(cctx.op)++ = (BYTE)(lastRun << ML_BITS); } - memcpy(op, anchor, lastRun); - op += lastRun; + memcpy(cctx.op, cctx.anchor, lastRun); + cctx.op += lastRun; } - LDM_printCompressStats(&compressStats); - return (op - (BYTE *)dst); + LDM_printCompressStats(&cctx.stats); + return (cctx.op - (BYTE *)cctx.obase); } typedef struct LDM_DCtx { - const BYTE *ibase; /* Pointer to base of input */ - const BYTE *ip; /* Pointer to current input position */ - const BYTE *iend; /* End of source */ - - const BYTE *obase; /* Pointer to base of output */ - BYTE *op; /* Pointer to output */ - const BYTE *oend; /* Pointer to end of output */ - size_t compressSize; size_t maxDecompressSize; + + 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 */ } LDM_DCtx; static void LDM_initializeDCtx(LDM_DCtx *dctx, const void *src, size_t compressSize, void *dst, size_t maxDecompressSize) { - dctx->ibase = src; - dctx->ip = (const BYTE *)src; - dctx->iend = dctx->ip + compressSize; - dctx->op = dst; - dctx->oend = dctx->op + maxDecompressSize; - dctx->compressSize = compressSize; dctx->maxDecompressSize = maxDecompressSize; + + dctx->ibase = src; + dctx->ip = (const BYTE *)src; + dctx->iend = dctx->ip + dctx->compressSize; + dctx->op = dst; + dctx->oend = dctx->op + dctx->maxDecompressSize; + } size_t LDM_decompress(const void *src, size_t compressSize, @@ -382,15 +428,14 @@ size_t LDM_decompress(const void *src, size_t compressSize, LDM_initializeDCtx(&dctx, src, compressSize, dst, maxDecompressSize); BYTE *cpy; + size_t length; + const BYTE *match; + size_t offset; while (dctx.ip < dctx.iend) { - size_t length; - const BYTE *match; - size_t offset; - /* get literal length */ unsigned const token = *(dctx.ip)++; - if ((length=(token >> ML_BITS)) == RUN_MASK) { + if ((length = (token >> ML_BITS)) == RUN_MASK) { unsigned s; do { s = *(dctx.ip)++; @@ -417,6 +462,8 @@ size_t LDM_decompress(const void *src, size_t compressSize, offset = LDM_readLE16(dctx.ip); dctx.ip += 2; */ + + //TODO : dynamic offset size offset = LDM_read32(dctx.ip); dctx.ip += 4; #ifdef LDM_DEBUG From e4155b11d749c2c5f6faac9cabbc6e0dc1262e14 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 10 Jul 2017 13:08:19 -0700 Subject: [PATCH 085/318] Add warning flags to makefile and clean up code to remove warnings --- contrib/long_distance_matching/Makefile | 10 ++- contrib/long_distance_matching/ldm.c | 57 +++++++++-------- contrib/long_distance_matching/ldm.h | 4 +- contrib/long_distance_matching/main-ldm.c | 77 ++++++++++++----------- contrib/long_distance_matching/util.c | 64 +++++++++++++++++++ contrib/long_distance_matching/util.h | 23 +++++++ 6 files changed, 171 insertions(+), 64 deletions(-) create mode 100644 contrib/long_distance_matching/util.c create mode 100644 contrib/long_distance_matching/util.h diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 4e04fd6a2..5ffd4eafe 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -9,6 +9,14 @@ # This Makefile presumes libzstd is installed, using `sudo make install` +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 @@ -22,7 +30,7 @@ all: main-ldm #main : ldm.c main.c # $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ -main-ldm : ldm.c main-ldm.c +main-ldm : util.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 7bf267815..12cffc407 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -4,6 +4,7 @@ #include #include "ldm.h" +#include "util.h" #define HASH_EVERY 7 @@ -36,6 +37,7 @@ typedef uint32_t hash_t; // typedef uint64_t tag; +/* static unsigned LDM_isLittleEndian(void) { const union { U32 u; BYTE c[4]; } one = { 1 }; return one.c[0]; @@ -85,6 +87,8 @@ static U64 LDM_read64(const void *ptr) { static void LDM_copy8(void *dst, const void *src) { memcpy(dst, src, 8); } + +*/ typedef struct LDM_hashEntry { offset_t offset; } LDM_hashEntry; @@ -144,6 +148,7 @@ static hash_t LDM_hash(U32 sequence) { return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); } +/* static hash_t LDM_hash5(U64 sequence) { static const U64 prime5bytes = 889523592379ULL; static const U64 prime8bytes = 11400714785074694791ULL; @@ -153,35 +158,40 @@ static hash_t LDM_hash5(U64 sequence) { else return (((sequence >> 24) * prime8bytes) >> (64 - hashLog)); } +*/ static hash_t LDM_hash_position(const void * const p) { return LDM_hash(LDM_read32(p)); } -static void LDM_put_position_on_hash(const BYTE *p, hash_t h, - void *tableBase, const BYTE *srcBase) { +static void LDM_putHashOfPosition(const BYTE *p, hash_t h, + void *tableBase, const BYTE *srcBase) { + LDM_hashEntry *hashTable; if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { return; } - LDM_hashEntry *hashTable = (LDM_hashEntry *) tableBase; - hashTable[h] = (LDM_hashEntry) { (hash_t )(p - srcBase) }; + hashTable = (LDM_hashEntry *) tableBase; + hashTable[h] = (LDM_hashEntry) { (hash_t)(p - srcBase) }; } static void LDM_putPosition(const BYTE *p, void *tableBase, - const BYTE *srcBase) { + const BYTE *srcBase) { + hash_t hash; if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { return; } - hash_t const h = LDM_hash_position(p); - LDM_put_position_on_hash(p, h, tableBase, srcBase); + hash = LDM_hash_position(p); + LDM_putHashOfPosition(p, hash, tableBase, srcBase); } static void LDM_putHashOfCurrentPosition(LDM_CCtx *const cctx) { - LDM_putPosition(cctx->ip, cctx->hashTable, cctx->ibase); + hash_t hash = LDM_hash_position(cctx->ip); + LDM_putHashOfPosition(cctx->ip, hash, cctx->hashTable, cctx->ibase); + cctx->lastPosHashed = cctx->ip; + cctx->lastHash = hash; } - static const BYTE *LDM_get_position_on_hash( hash_t h, void *tableBase, const BYTE *srcBase) { const LDM_hashEntry * const hashTable = (LDM_hashEntry *)tableBase; @@ -209,8 +219,8 @@ static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, return (unsigned)(pIn - pStart); } -void LDM_read_header(const void *src, size_t *compressSize, - size_t *decompressSize) { +void LDM_readHeader(const void *src, size_t *compressSize, + size_t *decompressSize) { const U32 *ip = (const U32 *)src; *compressSize = *ip++; *decompressSize = *ip; @@ -230,7 +240,7 @@ static void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->imatchLimit = cctx->iend - MINMATCH; cctx->obase = (BYTE *)dst; - cctx->op = (BYTE *)cctx->obase; + cctx->op = (BYTE *)dst; cctx->anchor = cctx->ibase; @@ -244,13 +254,12 @@ static void LDM_initializeCCtx(LDM_CCtx *cctx, size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; + U32 forwardH; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); - U32 forwardH; /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); - const BYTE *lastHash = cctx.ip; cctx.ip++; forwardH = LDM_hash_position(cctx.ip); @@ -276,8 +285,7 @@ size_t LDM_compress(const void *src, size_t srcSize, match = LDM_get_position_on_hash(h, cctx.hashTable, cctx.ibase); forwardH = LDM_hash_position(forwardIp); - LDM_put_position_on_hash(cctx.ip, h, cctx.hashTable, cctx.ibase); - lastHash = cctx.ip; + LDM_putHashOfPosition(cctx.ip, h, cctx.hashTable, cctx.ibase); } while (cctx.ip - match > WINDOW_SIZE || LDM_read64(match) != LDM_read64(cctx.ip)); } @@ -319,7 +327,7 @@ size_t LDM_compress(const void *src, size_t srcSize, memcpy(cctx.op, cctx.anchor, litLength); cctx.op += litLength; } -_next_match: + /* Encode offset */ { /* @@ -334,6 +342,7 @@ _next_match: /* Encode Match Length */ { unsigned matchCode; + unsigned ctr = 1; matchCode = LDM_count(cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); #ifdef LDM_DEBUG @@ -342,7 +351,6 @@ _next_match: printf("\n"); #endif cctx.stats.total_match_length += matchCode + MINMATCH; - unsigned ctr = 1; cctx.ip++; for (; ctr < MINMATCH + matchCode; cctx.ip++, ctr++) { LDM_putHashOfCurrentPosition(&cctx); @@ -372,7 +380,6 @@ _next_match: LDM_putPosition(cctx.ip, cctx.hashTable, cctx.ibase); forwardH = LDM_hash_position(++cctx.ip); - lastHash = cctx.ip; } _last_literals: /* Encode last literals */ @@ -392,7 +399,7 @@ _last_literals: cctx.op += lastRun; } LDM_printCompressStats(&cctx.stats); - return (cctx.op - (BYTE *)cctx.obase); + return (cctx.op - (const BYTE *)cctx.obase); } typedef struct LDM_DCtx { @@ -427,12 +434,12 @@ size_t LDM_decompress(const void *src, size_t compressSize, LDM_DCtx dctx; LDM_initializeDCtx(&dctx, src, compressSize, dst, maxDecompressSize); - BYTE *cpy; - size_t length; - const BYTE *match; - size_t offset; - while (dctx.ip < dctx.iend) { + BYTE *cpy; + size_t length; + const BYTE *match; + size_t offset; + /* get literal length */ unsigned const token = *(dctx.ip)++; if ((length = (token >> ML_BITS)) == RUN_MASK) { diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 0ac7b2ece..287d444dd 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -13,7 +13,7 @@ size_t LDM_compress(const void *src, size_t srcSize, size_t LDM_decompress(const void *src, size_t srcSize, void *dst, size_t maxDstSize); -void LDM_read_header(const void *src, size_t *compressSize, - size_t *decompressSize); +void LDM_readHeader(const void *src, size_t *compressSize, + size_t *decompressSize); #endif /* LDM_H */ diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index b529201fd..724d735dd 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -25,6 +25,7 @@ static int compress(const char *fname, const char *oname) { int fdin, fdout; struct stat statbuf; char *src, *dst; + size_t maxCompressSize, compressSize; /* Open the input file. */ if ((fdin = open(fname, O_RDONLY)) < 0) { @@ -44,10 +45,10 @@ static int compress(const char *fname, const char *oname) { return 1; } - size_t maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; + maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; /* Go to the location corresponding to the last byte. */ - /* TODO: fallocate? */ + /* TODO: fallocate? */ if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { perror("lseek error"); return 1; @@ -74,14 +75,14 @@ static int compress(const char *fname, const char *oname) { } #ifdef ZSTD - size_t compressSize = ZSTD_compress(dst, statbuf.st_size, + compressSize = ZSTD_compress(dst, statbuf.st_size, src, statbuf.st_size, 1); #else - size_t compressSize = LDM_HEADER_SIZE + + compressSize = LDM_HEADER_SIZE + LDM_compress(src, statbuf.st_size, dst + LDM_HEADER_SIZE, statbuf.st_size); - // Write compress and decompress size to header + // Write compress and decompress size to header // TODO: should depend on LDM_DECOMPRESS_SIZE write32 memcpy(dst, &compressSize, 4); memcpy(dst + 4, &(statbuf.st_size), 4); @@ -107,12 +108,13 @@ static int compress(const char *fname, const char *oname) { /* 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. + * 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; + size_t compressSize, decompressSize, outSize; /* Open the input file. */ if ((fdin = open(fname, O_RDONLY)) < 0) { @@ -140,8 +142,7 @@ static int decompress(const char *fname, const char *oname) { } /* Read the header. */ - size_t compressSize, decompressSize; - LDM_read_header(src, &compressSize, &decompressSize); + LDM_readHeader(src, &compressSize, &decompressSize); #ifdef DEBUG printf("Size, compressSize, decompressSize: %zu %zu %zu\n", @@ -168,11 +169,11 @@ static int decompress(const char *fname, const char *oname) { } #ifdef ZSTD - size_t outSize = ZSTD_decompress(dst, decomrpessed_size, + outSize = ZSTD_decompress(dst, decomrpessed_size, src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE); #else - size_t outSize = LDM_decompress( + outSize = LDM_decompress( src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, dst, decompressSize); @@ -211,12 +212,14 @@ static void verify(const char *inpFilename, const char *decFilename) { FILE *decFp = fopen(decFilename, "rb"); 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"); - } + { + const int cmp = compare(inpFp, decFp); + if(0 == cmp) { + printf("verify : OK\n"); + } else { + printf("verify : NG\n"); + } + } fclose(decFp); fclose(inpFp); @@ -243,32 +246,34 @@ int main(int argc, const char *argv[]) { printf("ldm = [%s]\n", ldmFilename); printf("dec = [%s]\n", decFilename); - struct timeval tv1, tv2; /* Compress */ - - gettimeofday(&tv1, NULL); - if (compress(inpFilename, ldmFilename)) { - printf("Compress error"); - return 1; + { + struct timeval tv1, tv2; + gettimeofday(&tv1, NULL); + if (compress(inpFilename, ldmFilename)) { + printf("Compress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); /* Decompress */ - - gettimeofday(&tv1, NULL); - if (decompress(ldmFilename, decFilename)) { - printf("Decompress error"); - return 1; + { + struct timeval tv1, tv2; + gettimeofday(&tv1, NULL); + if (decompress(ldmFilename, decFilename)) { + printf("Decompress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - /* verify */ verify(inpFilename, decFilename); return 0; diff --git a/contrib/long_distance_matching/util.c b/contrib/long_distance_matching/util.c new file mode 100644 index 000000000..9ea4ca1e5 --- /dev/null +++ b/contrib/long_distance_matching/util.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +#include "util.h" + +typedef uint8_t BYTE; +typedef uint16_t U16; +typedef uint32_t U32; +typedef int32_t S32; +typedef uint64_t U64; + +unsigned LDM_isLittleEndian(void) { + const union { U32 u; BYTE c[4]; } one = { 1 }; + return one.c[0]; +} + +U16 LDM_read16(const void *memPtr) { + U16 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +U16 LDM_readLE16(const void *memPtr) { + if (LDM_isLittleEndian()) { + return LDM_read16(memPtr); + } else { + const BYTE *p = (const BYTE *)memPtr; + return (U16)((U16)p[0] + (p[1] << 8)); + } +} + +void LDM_write16(void *memPtr, U16 value){ + memcpy(memPtr, &value, sizeof(value)); +} + +void LDM_write32(void *memPtr, U32 value) { + memcpy(memPtr, &value, sizeof(value)); +} + +void LDM_writeLE16(void *memPtr, U16 value) { + if (LDM_isLittleEndian()) { + LDM_write16(memPtr, value); + } else { + BYTE* p = (BYTE *)memPtr; + p[0] = (BYTE) value; + p[1] = (BYTE)(value>>8); + } +} + +U32 LDM_read32(const void *ptr) { + return *(const U32 *)ptr; +} + +U64 LDM_read64(const void *ptr) { + return *(const U64 *)ptr; +} + +void LDM_copy8(void *dst, const void *src) { + memcpy(dst, src, 8); +} + + diff --git a/contrib/long_distance_matching/util.h b/contrib/long_distance_matching/util.h new file mode 100644 index 000000000..90726412e --- /dev/null +++ b/contrib/long_distance_matching/util.h @@ -0,0 +1,23 @@ +#ifndef LDM_UTIL_H +#define LDM_UTIL_H + +unsigned LDM_isLittleEndian(void); + +uint16_t LDM_read16(const void *memPtr); + +uint16_t LDM_readLE16(const void *memPtr); + +void LDM_write16(void *memPtr, uint16_t value); + +void LDM_write32(void *memPtr, uint32_t value); + +void LDM_writeLE16(void *memPtr, uint16_t value); + +uint32_t LDM_read32(const void *ptr); + +uint64_t LDM_read64(const void *ptr); + +void LDM_copy8(void *dst, const void *src); + + +#endif /* LDM_UTIL_H */ From f9524cf366af289d2c24e673360c8b05f70b6a83 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 10 Jul 2017 13:48:41 -0700 Subject: [PATCH 086/318] added --memtest to fuzzer --- tests/fuzzer.c | 193 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 179 insertions(+), 14 deletions(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index c9461ee90..6586fc12e 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -51,14 +51,14 @@ static const U32 nbTestsDefault = 30000; /*-************************************ * Display Macros **************************************/ -#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define DISPLAY(...) fprintf(stdout, __VA_ARGS__) #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } static U32 g_displayLevel = 2; #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ if ((FUZ_clockSpan(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ { g_displayClock = clock(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stderr); } } + if (g_displayLevel>=4) fflush(stdout); } } static const clock_t g_refreshRate = CLOCKS_PER_SEC / 6; static clock_t g_displayClock = 0; @@ -97,7 +97,161 @@ static unsigned FUZ_highbit32(U32 v32) /*============================================= -* Basic Unit tests +* Memory Tests +=============================================*/ +#if defined(__APPLE__) && defined(__MACH__) + +#include /* malloc_size */ + +typedef struct { + unsigned long long totalMalloc; + size_t peakMalloc; + unsigned nbMalloc; + unsigned nbFree; +} mallocCounter_t; + +static const mallocCounter_t INIT_MALLOC_COUNTER = { 0, 0, 0, 0 }; + +static void* FUZ_mallocDebug(void* counter, size_t size) +{ + mallocCounter_t* const mcPtr = (mallocCounter_t*)counter; + void* const ptr = malloc(size); + if (ptr==NULL) return NULL; + mcPtr->totalMalloc += size; + mcPtr->peakMalloc += size; + mcPtr->nbMalloc += 1; + return ptr; +} + +static void FUZ_freeDebug(void* counter, void* address) +{ + mallocCounter_t* const mcPtr = (mallocCounter_t*)counter; + free(address); + mcPtr->nbFree += 1; + mcPtr->peakMalloc -= malloc_size(address); /* OS-X specific */ +} + +static void FUZ_displayMallocStats(mallocCounter_t count) +{ + DISPLAYLEVEL(3, "peak:%u KB, nbMallocs:%u, total:%u KB \n", + (U32)(count.peakMalloc >> 10), + count.nbMalloc, + (U32)(count.totalMalloc >> 10)); +} + +static int FUZ_mallocTests(unsigned seed, double compressibility) +{ + size_t const inSize = 64 MB + 16 MB + 4 MB + 1 MB + 256 KB + 64 KB; /* 85.3 MB */ + size_t const outSize = ZSTD_compressBound(inSize); + void* const inBuffer = malloc(inSize); + void* const outBuffer = malloc(outSize); + + /* test only played in verbose mode, as they are long */ + if (g_displayLevel<3) return 0; + + /* Create compressible noise */ + if (!inBuffer || !outBuffer) { + DISPLAY("Not enough memory, aborting\n"); + exit(1); + } + RDG_genBuffer(inBuffer, inSize, compressibility, 0. /*auto*/, seed); + + /* simple compression tests */ + { int compressionLevel; + mallocCounter_t malcount = INIT_MALLOC_COUNTER; + ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; + for (compressionLevel=1; compressionLevel<=5; compressionLevel++) { + ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); + ZSTD_compressCCtx(cctx, outBuffer, outSize, inBuffer, inSize, compressionLevel); + ZSTD_freeCCtx(cctx); + DISPLAYLEVEL(3, "compressCCtx level %i : ", compressionLevel); + FUZ_displayMallocStats(malcount); + malcount = INIT_MALLOC_COUNTER; + } } + + /* streaming compression tests */ + { int compressionLevel; + mallocCounter_t malcount = INIT_MALLOC_COUNTER; + ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; + for (compressionLevel=1; compressionLevel<=5; compressionLevel++) { + ZSTD_CCtx* const cstream = ZSTD_createCStream_advanced(cMem); + ZSTD_outBuffer out = { outBuffer, outSize, 0 }; + ZSTD_inBuffer in = { inBuffer, inSize, 0 }; + ZSTD_initCStream(cstream, compressionLevel); + ZSTD_compressStream(cstream, &out, &in); + ZSTD_endStream(cstream, &out); + ZSTD_freeCStream(cstream); + DISPLAYLEVEL(3, "compressStream level %i : ", compressionLevel); + FUZ_displayMallocStats(malcount); + malcount = INIT_MALLOC_COUNTER; + } } + + /* advanced API test */ + { int compressionLevel; + mallocCounter_t malcount = INIT_MALLOC_COUNTER; + ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; + for (compressionLevel=1; compressionLevel<=5; compressionLevel++) { + ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); + ZSTD_outBuffer out = { outBuffer, outSize, 0 }; + ZSTD_inBuffer in = { inBuffer, inSize, 0 }; + ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel); + ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); + ZSTD_freeCCtx(cctx); + DISPLAYLEVEL(3, "compress_generic,end level %i : ", compressionLevel); + FUZ_displayMallocStats(malcount); + malcount = INIT_MALLOC_COUNTER; + } } + + /* advanced MT API test */ + { int compressionLevel; + mallocCounter_t malcount = INIT_MALLOC_COUNTER; + ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; + for (compressionLevel=1; compressionLevel<=5; compressionLevel++) { + ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); + ZSTD_outBuffer out = { outBuffer, outSize, 0 }; + ZSTD_inBuffer in = { inBuffer, inSize, 0 }; + ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel); + ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, 2); + ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); + ZSTD_freeCCtx(cctx); + DISPLAYLEVEL(3, "compress_generic,-T2,end level %i : ", compressionLevel); + FUZ_displayMallocStats(malcount); + malcount = INIT_MALLOC_COUNTER; + } } + + /* advanced MT streaming API test */ + { int compressionLevel; + mallocCounter_t malcount = INIT_MALLOC_COUNTER; + ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; + for (compressionLevel=1; compressionLevel<=5; compressionLevel++) { + ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); + ZSTD_outBuffer out = { outBuffer, outSize, 0 }; + ZSTD_inBuffer in = { inBuffer, inSize, 0 }; + ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel); + ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, 2); + ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_continue); + ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); + ZSTD_freeCCtx(cctx); + DISPLAYLEVEL(3, "compress_generic,-T2,continue level %i : ", compressionLevel); + FUZ_displayMallocStats(malcount); + malcount = INIT_MALLOC_COUNTER; + } } + + return 0; +} + +#else + +static int FUZ_mallocTests(unsigned seed, double compressibility) +{ + (void)seed; (void)compressibility; + return 0; +} + +#endif + +/*============================================= +* Unit tests =============================================*/ #define CHECK_V(var, fn) size_t const var = fn; if (ZSTD_isError(var)) goto _output_error @@ -108,7 +262,8 @@ static int basicUnitTests(U32 seed, double compressibility) { size_t const CNBuffSize = 5 MB; void* const CNBuffer = malloc(CNBuffSize); - void* const compressedBuffer = malloc(ZSTD_compressBound(CNBuffSize)); + size_t const compressedBufferSize = ZSTD_compressBound(CNBuffSize); + void* const compressedBuffer = malloc(compressedBufferSize); void* const decodedBuffer = malloc(CNBuffSize); ZSTD_DCtx* dctx = ZSTD_createDCtx(); int testResult = 0; @@ -123,6 +278,9 @@ static int basicUnitTests(U32 seed, double compressibility) } RDG_genBuffer(CNBuffer, CNBuffSize, compressibility, 0., seed); + /* memory tests */ + FUZ_mallocTests(seed, compressibility); + /* Basic tests */ DISPLAYLEVEL(4, "test%3i : ZSTD_getErrorName : ", testNb++); { const char* errorString = ZSTD_getErrorName(0); @@ -139,7 +297,7 @@ static int basicUnitTests(U32 seed, double compressibility) { ZSTD_CCtx* cctx = ZSTD_createCCtx(); if (cctx==NULL) goto _output_error; CHECKPLUS(r, ZSTD_compressCCtx(cctx, - compressedBuffer, ZSTD_compressBound(CNBuffSize), + compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize, 1), cSize=r ); DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); @@ -226,7 +384,7 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "test%3i : simple compression test with static CCtx : ", testNb++); CHECKPLUS(r, ZSTD_compressCCtx(staticCCtx, - compressedBuffer, ZSTD_compressBound(CNBuffSize), + compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize, STATIC_CCTX_LEVEL), cSize=r ); DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", @@ -295,7 +453,7 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "test%3i : compress %u bytes with 2 threads : ", testNb++, (U32)CNBuffSize); CHECKPLUS(r, ZSTDMT_compressCCtx(mtctx, - compressedBuffer, ZSTD_compressBound(CNBuffSize), + compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize, 1), cSize=r ); @@ -382,7 +540,7 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "test%3i : compress with flat dictionary : ", testNb++); cSize = 0; - CHECKPLUS(r, ZSTD_compressEnd(ctxOrig, compressedBuffer, ZSTD_compressBound(CNBuffSize), + CHECKPLUS(r, ZSTD_compressEnd(ctxOrig, compressedBuffer, compressedBufferSize, (const char*)CNBuffer + dictSize, CNBuffSize - dictSize), cSize += r); DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); @@ -398,7 +556,7 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "test%3i : compress with duplicated context : ", testNb++); { size_t const cSizeOrig = cSize; cSize = 0; - CHECKPLUS(r, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, ZSTD_compressBound(CNBuffSize), + CHECKPLUS(r, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, compressedBufferSize, (const char*)CNBuffer + dictSize, CNBuffSize - dictSize), cSize += r); if (cSize != cSizeOrig) goto _output_error; /* should be identical ==> same size */ @@ -483,7 +641,7 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "OK : %u \n", dictID); DISPLAYLEVEL(4, "test%3i : compress with dictionary : ", testNb++); - cSize = ZSTD_compress_usingDict(cctx, compressedBuffer, ZSTD_compressBound(CNBuffSize), + cSize = ZSTD_compress_usingDict(cctx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize, dictBuffer, dictSize, 4); if (ZSTD_isError(cSize)) goto _output_error; @@ -521,7 +679,7 @@ static int basicUnitTests(U32 seed, double compressibility) 1 /* byReference */, ZSTD_dm_auto, cParams, ZSTD_defaultCMem); DISPLAYLEVEL(4, "(size : %u) : ", (U32)ZSTD_sizeof_CDict(cdict)); - cSize = ZSTD_compress_usingCDict(cctx, compressedBuffer, ZSTD_compressBound(CNBuffSize), + cSize = ZSTD_compress_usingCDict(cctx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize, cdict); ZSTD_freeCDict(cdict); if (ZSTD_isError(cSize)) goto _output_error; @@ -556,7 +714,7 @@ static int basicUnitTests(U32 seed, double compressibility) goto _output_error; } cSize = ZSTD_compress_usingCDict(cctx, - compressedBuffer, ZSTD_compressBound(CNBuffSize), + compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize, cdict); if (ZSTD_isError(cSize)) { DISPLAY("ZSTD_compress_usingCDict failed "); @@ -570,7 +728,7 @@ static int basicUnitTests(U32 seed, double compressibility) { 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, 1 /*byRef*/, ZSTD_dm_auto, cParams, ZSTD_defaultCMem); - cSize = ZSTD_compress_usingCDict_advanced(cctx, compressedBuffer, ZSTD_compressBound(CNBuffSize), + cSize = ZSTD_compress_usingCDict_advanced(cctx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize, cdict, fParams); ZSTD_freeCDict(cdict); if (ZSTD_isError(cSize)) goto _output_error; @@ -594,7 +752,7 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "test%3i : ZSTD_compress_advanced, no dictID : ", testNb++); { ZSTD_parameters p = ZSTD_getParams(3, CNBuffSize, dictSize); p.fParams.noDictIDFlag = 1; - cSize = ZSTD_compress_advanced(cctx, compressedBuffer, ZSTD_compressBound(CNBuffSize), + cSize = ZSTD_compress_advanced(cctx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize, dictBuffer, dictSize, p); if (ZSTD_isError(cSize)) goto _output_error; @@ -1245,6 +1403,7 @@ int main(int argc, const char** argv) U32 mainPause = 0; U32 maxDuration = 0; int bigTests = 1; + U32 memTestsOnly = 0; const char* const programName = argv[0]; /* Check command line */ @@ -1255,6 +1414,7 @@ int main(int argc, const char** argv) /* Handle commands. Aggregated commands are allowed */ if (argument[0]=='-') { + if (!strcmp(argument, "--memtest")) { memTestsOnly=1; continue; } if (!strcmp(argument, "--no-big-tests")) { bigTests=0; continue; } argument++; @@ -1326,6 +1486,11 @@ int main(int argc, const char** argv) DISPLAY("Seed = %u\n", seed); if (proba!=FUZ_compressibility_default) DISPLAY("Compressibility : %u%%\n", proba); + if (memTestsOnly) { + g_displayLevel=3; + return FUZ_mallocTests(seed, ((double)proba) / 100); + } + if (nbTests < testNb) nbTests = testNb; if (testNb==0) From 88da8f181660890c369bbee4aafd6eaa8f6bb830 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 10 Jul 2017 14:02:33 -0700 Subject: [PATCH 087/318] fix : propagate custom allocator to ZSTDMT though ZSTD_CCtx_setParameter() also : compile fuzzer with MT enabled --- lib/compress/zstd_compress.c | 2 +- tests/Makefile | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 9300357f2..c17645de7 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -377,7 +377,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v return ERROR(compressionParameter_unsupported); ZSTDMT_freeCCtx(cctx->mtctx); cctx->nbThreads = 1; - cctx->mtctx = ZSTDMT_createCCtx(value); + cctx->mtctx = ZSTDMT_createCCtx_advanced(value, cctx->customMem); if (cctx->mtctx == NULL) return ERROR(memory_allocation); } cctx->nbThreads = value; diff --git a/tests/Makefile b/tests/Makefile index 345e0e8eb..4784136e1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -79,7 +79,7 @@ all32: fullbench32 fuzzer32 zstreamtest32 allnothread: fullbench fuzzer paramgrill datagen decodecorpus -dll: fuzzer-dll zstreamtest-dll +dll: fuzzer-dll zstreamtest-dll zstd: $(MAKE) -C $(PRGDIR) $@ @@ -108,11 +108,11 @@ fullbench-dll: $(PRGDIR)/datagen.c fullbench.c $(MAKE) -C $(ZSTDDIR) libzstd $(CC) $(FLAGS) $^ -o $@$(EXT) -DZSTD_DLL_IMPORT=1 $(ZSTDDIR)/dll/libzstd.dll -fuzzer : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c fuzzer.c - $(CC) $(FLAGS) $^ -o $@$(EXT) - -fuzzer32 : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c fuzzer.c - $(CC) -m32 $(FLAGS) $^ -o $@$(EXT) +fuzzer : CPPFLAGS += $(MULTITHREAD_CPP) +fuzzer : LDFLAGS += $(MULTITHREAD_LD) +fuzzer32: CFLAGS += -m32 +fuzzer fuzzer32 : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c fuzzer.c + $(CC) $(FLAGS) $^ -o $@$(EXT) fuzzer-dll : LDFLAGS+= -L$(ZSTDDIR) -lzstd fuzzer-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c fuzzer.c From ee3423d70950f2ec41ff884a6cdacb463118f935 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 10 Jul 2017 14:09:16 -0700 Subject: [PATCH 088/318] extended fuzzer MT memory tests --- tests/fuzzer.c | 88 ++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 49 deletions(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 6586fc12e..c05ed5aea 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -160,7 +160,7 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) { int compressionLevel; mallocCounter_t malcount = INIT_MALLOC_COUNTER; ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; - for (compressionLevel=1; compressionLevel<=5; compressionLevel++) { + for (compressionLevel=1; compressionLevel<=6; compressionLevel++) { ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); ZSTD_compressCCtx(cctx, outBuffer, outSize, inBuffer, inSize, compressionLevel); ZSTD_freeCCtx(cctx); @@ -173,7 +173,7 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) { int compressionLevel; mallocCounter_t malcount = INIT_MALLOC_COUNTER; ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; - for (compressionLevel=1; compressionLevel<=5; compressionLevel++) { + for (compressionLevel=1; compressionLevel<=6; compressionLevel++) { ZSTD_CCtx* const cstream = ZSTD_createCStream_advanced(cMem); ZSTD_outBuffer out = { outBuffer, outSize, 0 }; ZSTD_inBuffer in = { inBuffer, inSize, 0 }; @@ -186,56 +186,46 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) malcount = INIT_MALLOC_COUNTER; } } - /* advanced API test */ - { int compressionLevel; - mallocCounter_t malcount = INIT_MALLOC_COUNTER; - ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; - for (compressionLevel=1; compressionLevel<=5; compressionLevel++) { - ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); - ZSTD_outBuffer out = { outBuffer, outSize, 0 }; - ZSTD_inBuffer in = { inBuffer, inSize, 0 }; - ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel); - ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); - ZSTD_freeCCtx(cctx); - DISPLAYLEVEL(3, "compress_generic,end level %i : ", compressionLevel); - FUZ_displayMallocStats(malcount); - malcount = INIT_MALLOC_COUNTER; - } } - /* advanced MT API test */ - { int compressionLevel; - mallocCounter_t malcount = INIT_MALLOC_COUNTER; - ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; - for (compressionLevel=1; compressionLevel<=5; compressionLevel++) { - ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); - ZSTD_outBuffer out = { outBuffer, outSize, 0 }; - ZSTD_inBuffer in = { inBuffer, inSize, 0 }; - ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel); - ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, 2); - ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); - ZSTD_freeCCtx(cctx); - DISPLAYLEVEL(3, "compress_generic,-T2,end level %i : ", compressionLevel); - FUZ_displayMallocStats(malcount); - malcount = INIT_MALLOC_COUNTER; - } } + { U32 nbThreads; + for (nbThreads=1; nbThreads<=4; nbThreads++) { + int compressionLevel; + mallocCounter_t malcount = INIT_MALLOC_COUNTER; + ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; + for (compressionLevel=1; compressionLevel<=6; compressionLevel++) { + ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); + ZSTD_outBuffer out = { outBuffer, outSize, 0 }; + ZSTD_inBuffer in = { inBuffer, inSize, 0 }; + ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel); + ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, nbThreads); + ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); + ZSTD_freeCCtx(cctx); + DISPLAYLEVEL(3, "compress_generic,-T%u,end level %i : ", + nbThreads, compressionLevel); + FUZ_displayMallocStats(malcount); + malcount = INIT_MALLOC_COUNTER; + } } } /* advanced MT streaming API test */ - { int compressionLevel; - mallocCounter_t malcount = INIT_MALLOC_COUNTER; - ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; - for (compressionLevel=1; compressionLevel<=5; compressionLevel++) { - ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); - ZSTD_outBuffer out = { outBuffer, outSize, 0 }; - ZSTD_inBuffer in = { inBuffer, inSize, 0 }; - ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel); - ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, 2); - ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_continue); - ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); - ZSTD_freeCCtx(cctx); - DISPLAYLEVEL(3, "compress_generic,-T2,continue level %i : ", compressionLevel); - FUZ_displayMallocStats(malcount); - malcount = INIT_MALLOC_COUNTER; - } } + { U32 nbThreads; + for (nbThreads=1; nbThreads<=4; nbThreads++) { + int compressionLevel; + mallocCounter_t malcount = INIT_MALLOC_COUNTER; + ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; + for (compressionLevel=1; compressionLevel<=6; compressionLevel++) { + ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); + ZSTD_outBuffer out = { outBuffer, outSize, 0 }; + ZSTD_inBuffer in = { inBuffer, inSize, 0 }; + ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel); + ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, nbThreads); + ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_continue); + ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); + ZSTD_freeCCtx(cctx); + DISPLAYLEVEL(3, "compress_generic,-T%u,continue level %i : ", + nbThreads, compressionLevel); + FUZ_displayMallocStats(malcount); + malcount = INIT_MALLOC_COUNTER; + } } } return 0; } From 3510efb02dc0f5a3d90b31546d425963811b4c90 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 10 Jul 2017 14:21:40 -0700 Subject: [PATCH 089/318] fix : custom allocator correctly propagated to child contexts --- lib/compress/zstdmt_compress.c | 2 +- tests/fuzzer.c | 20 ++++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index d5607adfd..bad2db9c2 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -215,7 +215,7 @@ static ZSTD_CCtx* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* pool) pool->availCCtx--; return pool->cctx[pool->availCCtx]; } - return ZSTD_createCCtx(); /* note : can be NULL, when creation fails ! */ + return ZSTD_createCCtx_advanced(pool->cMem); /* note : can be NULL, when creation fails ! */ } static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index c05ed5aea..aa1ebd48f 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -158,22 +158,21 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) /* simple compression tests */ { int compressionLevel; - mallocCounter_t malcount = INIT_MALLOC_COUNTER; - ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; for (compressionLevel=1; compressionLevel<=6; compressionLevel++) { + mallocCounter_t malcount = INIT_MALLOC_COUNTER; + ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); ZSTD_compressCCtx(cctx, outBuffer, outSize, inBuffer, inSize, compressionLevel); ZSTD_freeCCtx(cctx); DISPLAYLEVEL(3, "compressCCtx level %i : ", compressionLevel); FUZ_displayMallocStats(malcount); - malcount = INIT_MALLOC_COUNTER; } } /* streaming compression tests */ { int compressionLevel; - mallocCounter_t malcount = INIT_MALLOC_COUNTER; - ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; for (compressionLevel=1; compressionLevel<=6; compressionLevel++) { + mallocCounter_t malcount = INIT_MALLOC_COUNTER; + ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; ZSTD_CCtx* const cstream = ZSTD_createCStream_advanced(cMem); ZSTD_outBuffer out = { outBuffer, outSize, 0 }; ZSTD_inBuffer in = { inBuffer, inSize, 0 }; @@ -183,16 +182,15 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) ZSTD_freeCStream(cstream); DISPLAYLEVEL(3, "compressStream level %i : ", compressionLevel); FUZ_displayMallocStats(malcount); - malcount = INIT_MALLOC_COUNTER; } } /* advanced MT API test */ { U32 nbThreads; for (nbThreads=1; nbThreads<=4; nbThreads++) { int compressionLevel; - mallocCounter_t malcount = INIT_MALLOC_COUNTER; - ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; for (compressionLevel=1; compressionLevel<=6; compressionLevel++) { + mallocCounter_t malcount = INIT_MALLOC_COUNTER; + ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); ZSTD_outBuffer out = { outBuffer, outSize, 0 }; ZSTD_inBuffer in = { inBuffer, inSize, 0 }; @@ -203,16 +201,15 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) DISPLAYLEVEL(3, "compress_generic,-T%u,end level %i : ", nbThreads, compressionLevel); FUZ_displayMallocStats(malcount); - malcount = INIT_MALLOC_COUNTER; } } } /* advanced MT streaming API test */ { U32 nbThreads; for (nbThreads=1; nbThreads<=4; nbThreads++) { int compressionLevel; - mallocCounter_t malcount = INIT_MALLOC_COUNTER; - ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; for (compressionLevel=1; compressionLevel<=6; compressionLevel++) { + mallocCounter_t malcount = INIT_MALLOC_COUNTER; + ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); ZSTD_outBuffer out = { outBuffer, outSize, 0 }; ZSTD_inBuffer in = { inBuffer, inSize, 0 }; @@ -224,7 +221,6 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) DISPLAYLEVEL(3, "compress_generic,-T%u,continue level %i : ", nbThreads, compressionLevel); FUZ_displayMallocStats(malcount); - malcount = INIT_MALLOC_COUNTER; } } } return 0; From e410d63d458142c3930341637f05bcc3c9397e78 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 15:37:14 -0700 Subject: [PATCH 090/318] made input buffer an internal part of the compression context --- contrib/adaptive-compression/adapt.c | 29 ++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 61fb31421..f87bdf8c8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -31,6 +31,11 @@ typedef struct { size_t size; } buffer_t; +typedef struct { + size_t filled; + buffer_t buffer; +} inBuff_t; + typedef struct { unsigned waitCompressed; unsigned waitReady; @@ -68,6 +73,7 @@ typedef struct { pthread_cond_t allJobsCompleted_cond; pthread_mutex_t jobWrite_mutex; pthread_cond_t jobWrite_cond; + inBuff_t input; cStat_t stats; jobDescription* jobs; FILE* dstFile; @@ -97,6 +103,7 @@ static int freeCCtx(adaptCCtx* ctx) int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); int const fileCloseError = (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; int const cctxError = ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)) ? 1 : 0; + free(ctx->input.buffer.start); if (ctx->jobs){ freeCompressionJobs(ctx); free(ctx->jobs); @@ -115,7 +122,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) } memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = g_compressionLevel; - pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); + pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); pthread_cond_init(&ctx->jobCompressed_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); @@ -134,6 +141,14 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->allJobsCompleted = 0; ctx->adaptParam = DEFAULT_ADAPT_PARAM; ctx->cctx = ZSTD_createCCtx(); + ctx->input.filled = 0; + ctx->input.buffer.size = 2 * FILE_CHUNK_SIZE; + ctx->input.buffer.start = malloc(ctx->input.buffer.size); + if (!ctx->input.buffer.start) { + DISPLAY("Error: could not allocate input buffer\n"); + freeCCtx(ctx); + return NULL; + } if (!ctx->cctx) { DISPLAY("Error: could not allocate ZSTD_CCtx\n"); freeCCtx(ctx); @@ -320,7 +335,7 @@ static void* outputThread(void* arg) return arg; } -static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) +static int createCompressionJob(adaptCCtx* ctx, size_t srcSize) { unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; @@ -351,7 +366,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) free(job->dst.start); return 1; } - memcpy(job->src.start, data, srcSize); + memcpy(job->src.start, ctx->input.buffer.start, srcSize); pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); @@ -371,7 +386,6 @@ static void printStats(cStat_t stats) static int compressFilename(const char* const srcFilename, const char* const dstFilenameOrNull) { - BYTE* const src = malloc(FILE_CHUNK_SIZE); unsigned const stdinUsed = !strcmp(srcFilename, stdinmark); FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb"); const char* const outFilenameIntermediate = (stdinUsed && !dstFilenameOrNull) ? stdoutmark : dstFilenameOrNull; @@ -393,7 +407,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst } /* checking for errors */ - if (!srcFilename || !outFilename || !src || !srcFile) { + if (!srcFilename || !outFilename || !srcFile) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; goto cleanup; @@ -428,7 +442,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst /* creating jobs */ for ( ; ; ) { - size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); + size_t const readSize = fread(ctx->input.buffer.start, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); ctx->threadError = 1; @@ -438,7 +452,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst g_streamedSize += readSize; /* reading was fine, now create the compression job */ { - int const error = createCompressionJob(ctx, src, readSize); + int const error = createCompressionJob(ctx, readSize); if (error != 0) { ret = error; ctx->threadError = 1; @@ -458,7 +472,6 @@ cleanup: /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; - free(src); return ret; } From ef2b72831636d56d4e18168f5abaa16965e4a1a8 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 10 Jul 2017 15:48:47 -0700 Subject: [PATCH 091/318] Clean up and refactor compress function --- contrib/long_distance_matching/ldm.c | 317 ++++++++++----------------- 1 file changed, 115 insertions(+), 202 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 12cffc407..a1d4449eb 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -14,6 +14,8 @@ #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) #define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) +#define LDM_OFFSET_SIZE 4 + #define WINDOW_SIZE (1 << 20) #define MAX_WINDOW_SIZE 31 #define HASH_SIZE 8 @@ -35,81 +37,27 @@ typedef uint64_t U64; typedef uint32_t offset_t; typedef uint32_t hash_t; -// typedef uint64_t tag; - -/* -static unsigned LDM_isLittleEndian(void) { - const union { U32 u; BYTE c[4]; } one = { 1 }; - return one.c[0]; -} - -static U16 LDM_read16(const void *memPtr) { - U16 val; - memcpy(&val, memPtr, sizeof(val)); - return val; -} - -static U16 LDM_readLE16(const void *memPtr) { - if (LDM_isLittleEndian()) { - return LDM_read16(memPtr); - } else { - const BYTE *p = (const BYTE *)memPtr; - return (U16)((U16)p[0] + (p[1] << 8)); - } -} - -static void LDM_write16(void *memPtr, U16 value){ - memcpy(memPtr, &value, sizeof(value)); -} - -static void LDM_write32(void *memPtr, U32 value) { - memcpy(memPtr, &value, sizeof(value)); -} - -static void LDM_writeLE16(void *memPtr, U16 value) { - if (LDM_isLittleEndian()) { - LDM_write16(memPtr, value); - } else { - BYTE* p = (BYTE *)memPtr; - p[0] = (BYTE) value; - p[1] = (BYTE)(value>>8); - } -} - -static U32 LDM_read32(const void *ptr) { - return *(const U32 *)ptr; -} - -static U64 LDM_read64(const void *ptr) { - return *(const U64 *)ptr; -} - -static void LDM_copy8(void *dst, const void *src) { - memcpy(dst, src, 8); -} - -*/ typedef struct LDM_hashEntry { offset_t offset; } LDM_hashEntry; typedef struct LDM_compressStats { - U32 num_matches; - U32 total_match_length; - U32 total_literal_length; - U64 total_offset; + U32 numMatches; + U32 totalMatchLength; + U32 totalLiteralLength; + U64 totalOffset; } LDM_compressStats; static void LDM_printCompressStats(const LDM_compressStats *stats) { printf("=====================\n"); printf("Compression statistics\n"); - printf("Total number of matches: %u\n", stats->num_matches); - printf("Average match length: %.1f\n", ((double)stats->total_match_length) / - (double)stats->num_matches); + printf("Total number of matches: %u\n", stats->numMatches); + printf("Average match length: %.1f\n", ((double)stats->totalMatchLength) / + (double)stats->numMatches); printf("Average literal length: %.1f\n", - ((double)stats->total_literal_length) / (double)stats->num_matches); + ((double)stats->totalLiteralLength) / (double)stats->numMatches); printf("Average offset length: %.1f\n", - ((double)stats->total_offset) / (double)stats->num_matches); + ((double)stats->totalOffset) / (double)stats->numMatches); printf("=====================\n"); } @@ -140,6 +88,10 @@ typedef struct LDM_CCtx { const BYTE *lastPosHashed; /* Last position hashed */ hash_t lastHash; /* Hash corresponding to lastPosHashed */ + const BYTE *forwardIp; + hash_t forwardHash; + + unsigned step; } LDM_CCtx; @@ -160,38 +112,25 @@ static hash_t LDM_hash5(U64 sequence) { } */ -static hash_t LDM_hash_position(const void * const p) { +static hash_t LDM_hashPosition(const void * const p) { return LDM_hash(LDM_read32(p)); } -static void LDM_putHashOfPosition(const BYTE *p, hash_t h, - void *tableBase, const BYTE *srcBase) { - LDM_hashEntry *hashTable; - if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { +static void LDM_putHashOfCurrentPositionFromHash( + LDM_CCtx *cctx, hash_t hash) { + if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { return; } - - hashTable = (LDM_hashEntry *) tableBase; - hashTable[h] = (LDM_hashEntry) { (hash_t)(p - srcBase) }; -} - -static void LDM_putPosition(const BYTE *p, void *tableBase, - const BYTE *srcBase) { - hash_t hash; - if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { - return; - } - hash = LDM_hash_position(p); - LDM_putHashOfPosition(p, hash, tableBase, srcBase); -} - -static void LDM_putHashOfCurrentPosition(LDM_CCtx *const cctx) { - hash_t hash = LDM_hash_position(cctx->ip); - LDM_putHashOfPosition(cctx->ip, hash, cctx->hashTable, cctx->ibase); + (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; cctx->lastPosHashed = cctx->ip; cctx->lastHash = hash; } +static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { + hash_t hash = LDM_hashPosition(cctx->ip); + LDM_putHashOfCurrentPositionFromHash(cctx, hash); +} + static const BYTE *LDM_get_position_on_hash( hash_t h, void *tableBase, const BYTE *srcBase) { const LDM_hashEntry * const hashTable = (LDM_hashEntry *)tableBase; @@ -248,141 +187,136 @@ static void LDM_initializeCCtx(LDM_CCtx *cctx, memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); cctx->lastPosHashed = NULL; + cctx->forwardIp = NULL; + + cctx->step = 1; +} + +static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { + cctx->forwardIp = cctx->ip; + + do { + hash_t const h = cctx->forwardHash; + cctx->ip = cctx->forwardIp; + cctx->forwardIp += cctx->step; + + if (cctx->forwardIp > cctx->imatchLimit) { + return 1; + } + + *match = LDM_get_position_on_hash(h, cctx->hashTable, cctx->ibase); + + cctx->forwardHash = LDM_hashPosition(cctx->forwardIp); + LDM_putHashOfCurrentPositionFromHash(cctx, h); + } while (cctx->ip - *match > WINDOW_SIZE || + LDM_read64(*match) != LDM_read64(cctx->ip)); + return 0; } // TODO: srcSize and maxDstSize is unused size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; - U32 forwardH; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); - /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); cctx.ip++; - forwardH = LDM_hash_position(cctx.ip); + cctx.forwardHash = LDM_hashPosition(cctx.ip); // TODO: loop condition is not accurate. while (1) { const BYTE *match; - BYTE *token; - /* Find a match */ - { - const BYTE *forwardIp = cctx.ip; - unsigned step = 1; - - do { - U32 const h = forwardH; - cctx.ip = forwardIp; - forwardIp += step; - - if (forwardIp > cctx.imatchLimit) { - goto _last_literals; - } - - match = LDM_get_position_on_hash(h, cctx.hashTable, cctx.ibase); - - forwardH = LDM_hash_position(forwardIp); - LDM_putHashOfPosition(cctx.ip, h, cctx.hashTable, cctx.ibase); - } while (cctx.ip - match > WINDOW_SIZE || - LDM_read64(match) != LDM_read64(cctx.ip)); + /** + * 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. + */ + if (LDM_findBestMatch(&cctx, &match) != 0) { + goto _last_literals; } - cctx.stats.num_matches++; - /* Catchup: look back to extend match from found match */ - while (cctx.ip > cctx.anchor && match > cctx.ibase && cctx.ip[-1] == match[-1]) { + cctx.stats.numMatches++; + + /** + * Catchup: look back to extend the match backwards from the found match. + */ + while (cctx.ip > cctx.anchor && match > cctx.ibase && + cctx.ip[-1] == match[-1]) { cctx.ip--; match--; } - /* Encode literals */ + /** + * Write current block (literals, literal length, match offset, match + * length) and update pointers and hashes. + */ { - unsigned const litLength = (unsigned)(cctx.ip - cctx.anchor); - token = cctx.op++; + unsigned const literalLength = (unsigned)(cctx.ip - cctx.anchor); + unsigned const offset = cctx.ip - match; + unsigned const matchLength = LDM_count( + cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); + BYTE *token = cctx.op++; - cctx.stats.total_literal_length += litLength; + cctx.stats.totalLiteralLength += literalLength; + cctx.stats.totalOffset += offset; + cctx.stats.totalMatchLength += matchLength + MINMATCH; -#ifdef LDM_DEBUG - printf("Cur position: %zu\n", cctx.anchor - cctx.ibase); - printf("LitLength %zu. (Match offset). %zu\n", litLength, cctx.ip - match); -#endif - - if (litLength >= RUN_MASK) { - int len = (int)litLength - RUN_MASK; + /* Encode the literal length. */ + if (literalLength >= RUN_MASK) { + int len = (int)literalLength - RUN_MASK; *token = (RUN_MASK << ML_BITS); for (; len >= 255; len -= 255) { *(cctx.op)++ = 255; } *(cctx.op)++ = (BYTE)len; } else { - *token = (BYTE)(litLength << ML_BITS); + *token = (BYTE)(literalLength << ML_BITS); } -#ifdef LDM_DEBUG - printf("Literals "); - fwrite(cctx.anchor, litLength, 1, stdout); - printf("\n"); -#endif - memcpy(cctx.op, cctx.anchor, litLength); - cctx.op += litLength; - } - /* Encode offset */ - { - /* - LDM_writeLE16(cctx.op, cctx.ip-match); - cctx.op += 2; - */ - LDM_write32(cctx.op, cctx.ip - match); - cctx.op += 4; - cctx.stats.total_offset += (cctx.ip - match); - } + /* Encode the literals. */ + memcpy(cctx.op, cctx.anchor, literalLength); + cctx.op += literalLength; - /* Encode Match Length */ - { - unsigned matchCode; - unsigned ctr = 1; - matchCode = LDM_count(cctx.ip + MINMATCH, match + MINMATCH, - cctx.ihashLimit); -#ifdef LDM_DEBUG - printf("Match length %zu\n", matchCode + MINMATCH); - fwrite(cctx.ip, MINMATCH + matchCode, 1, stdout); - printf("\n"); -#endif - cctx.stats.total_match_length += matchCode + MINMATCH; - cctx.ip++; - for (; ctr < MINMATCH + matchCode; cctx.ip++, ctr++) { - LDM_putHashOfCurrentPosition(&cctx); - } -// cctx.ip += MINMATCH + matchCode; - if (matchCode >= ML_MASK) { + /* Encode the offset. */ + LDM_write32(cctx.op, offset); + cctx.op += LDM_OFFSET_SIZE; + + /* Encode match length */ + if (matchLength >= ML_MASK) { + unsigned matchLengthRemaining = matchLength; *token += ML_MASK; - matchCode -= ML_MASK; + matchLengthRemaining -= ML_MASK; LDM_write32(cctx.op, 0xFFFFFFFF); - while (matchCode >= 4*0xFF) { + while (matchLengthRemaining >= 4*0xFF) { cctx.op += 4; LDM_write32(cctx.op, 0xffffffff); - matchCode -= 4*0xFF; + matchLengthRemaining -= 4*0xFF; } - cctx.op += matchCode / 255; - *(cctx.op)++ = (BYTE)(matchCode % 255); + cctx.op += matchLengthRemaining / 255; + *(cctx.op)++ = (BYTE)(matchLengthRemaining % 255); } else { - *token += (BYTE)(matchCode); + *token += (BYTE)(matchLength); } -#ifdef LDM_DEBUG - printf("\n"); -#endif + /* Update input pointer, inserting hashes into hash table along the + * way. + */ + while (cctx.ip < cctx.anchor + MINMATCH + matchLength + literalLength) { + LDM_putHashOfCurrentPosition(&cctx); + cctx.ip++; + } } + // Set start of next block to current input pointer. cctx.anchor = cctx.ip; - - LDM_putPosition(cctx.ip, cctx.hashTable, cctx.ibase); - forwardH = LDM_hash_position(++cctx.ip); + LDM_putHashOfCurrentPosition(&cctx); + cctx.forwardHash = LDM_hashPosition(++cctx.ip); } _last_literals: - /* Encode last literals */ + /* Encode the last literals (no more matches). */ { size_t const lastRun = (size_t)(cctx.iend - cctx.anchor); if (lastRun >= RUN_MASK) { @@ -436,11 +370,10 @@ size_t LDM_decompress(const void *src, size_t compressSize, while (dctx.ip < dctx.iend) { BYTE *cpy; - size_t length; const BYTE *match; - size_t offset; + size_t length, offset; - /* get literal length */ + /* Get the literal length. */ unsigned const token = *(dctx.ip)++; if ((length = (token >> ML_BITS)) == RUN_MASK) { unsigned s; @@ -449,37 +382,19 @@ size_t LDM_decompress(const void *src, size_t compressSize, length += s; } while (s == 255); } -#ifdef LDM_DEBUG - printf("Literal length: %zu\n", length); -#endif - /* copy literals */ + /* Copy literals. */ cpy = dctx.op + length; -#ifdef LDM_DEBUG - printf("Literals "); - fwrite(dctx.ip, length, 1, stdout); - printf("\n"); -#endif memcpy(dctx.op, dctx.ip, length); dctx.ip += length; dctx.op = cpy; - /* get offset */ - /* - offset = LDM_readLE16(dctx.ip); - dctx.ip += 2; - */ - //TODO : dynamic offset size offset = LDM_read32(dctx.ip); - dctx.ip += 4; -#ifdef LDM_DEBUG - printf("Offset: %zu\n", offset); -#endif + dctx.ip += LDM_OFFSET_SIZE; match = dctx.op - offset; - // LDM_write32(op, (U32)offset); - /* get matchlength */ + /* Get the match length. */ length = token & ML_MASK; if (length == ML_MASK) { unsigned s; @@ -489,10 +404,8 @@ size_t LDM_decompress(const void *src, size_t compressSize, } while (s == 255); } length += MINMATCH; -#ifdef LDM_DEBUG - printf("Match length: %zu\n", length); -#endif - /* copy match */ + + /* Copy match. */ cpy = dctx.op + length; // Inefficient for now From 7aa36df6df018bf7611b5c469d6cbfb8f3f05a6e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 16:03:09 -0700 Subject: [PATCH 092/318] fixed memory leak that was happening when creating jobs --- contrib/adaptive-compression/adapt.c | 57 +++++++++++++++------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index f87bdf8c8..1299264d8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -92,24 +92,24 @@ static void freeCompressionJobs(adaptCCtx* ctx) static int freeCCtx(adaptCCtx* ctx) { - { - int const compressedMutexError = pthread_mutex_destroy(&ctx->jobCompressed_mutex); - int const compressedCondError = pthread_cond_destroy(&ctx->jobCompressed_cond); - int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); - int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); - int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); - int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); - int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex); - int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); - int const fileCloseError = (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; - int const cctxError = ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)) ? 1 : 0; - free(ctx->input.buffer.start); - if (ctx->jobs){ - freeCompressionJobs(ctx); - free(ctx->jobs); - } - return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError | cctxError; + if (!ctx) return 0; + int const compressedMutexError = pthread_mutex_destroy(&ctx->jobCompressed_mutex); + int const compressedCondError = pthread_cond_destroy(&ctx->jobCompressed_cond); + int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); + int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); + int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); + int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); + int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex); + int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); + int const fileCloseError = (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; + int const cctxError = ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)) ? 1 : 0; + free(ctx->input.buffer.start); + if (ctx->jobs){ + freeCompressionJobs(ctx); + free(ctx->jobs); } + free(ctx); + return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError | cctxError; } static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) @@ -136,6 +136,20 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobWriteID = 0; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); + /* allocating buffers for jobs */ + { + unsigned jobNum; + for (jobNum=0; jobNumjobs[jobNum]; + job->src.start = malloc(FILE_CHUNK_SIZE); + job->dst.start = malloc(FILE_CHUNK_SIZE); + if (!job->src.start || !job->dst.start) { + DISPLAY("Could not allocate buffers for jobs\n"); + freeCCtx(ctx); + return NULL; + } + } + } ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; @@ -354,18 +368,9 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize) job->compressionLevel = ctx->compressionLevel; - job->src.start = malloc(srcSize); job->src.size = srcSize; job->dst.size = ZSTD_compressBound(srcSize); - job->dst.start = malloc(job->dst.size); job->jobID = nextJob; - if (!job->src.start || !job->dst.start) { - /* problem occurred, free things then return */ - DISPLAY("Error: problem occurred during job creation\n"); - free(job->src.start); - free(job->dst.start); - return 1; - } memcpy(job->src.start, ctx->input.buffer.start, srcSize); pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; From c36552ef8a8aef2c097cba517346e8a7d5f0582c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 16:10:19 -0700 Subject: [PATCH 093/318] dst buffer should use ZSTD_compressBound to determine how much space it needs --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 1299264d8..e1ac0aaf3 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -142,7 +142,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) for (jobNum=0; jobNumjobs[jobNum]; job->src.start = malloc(FILE_CHUNK_SIZE); - job->dst.start = malloc(FILE_CHUNK_SIZE); + job->dst.start = malloc(ZSTD_compressBound(FILE_CHUNK_SIZE)); if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); freeCCtx(ctx); From 01fc7c42441ef75ead51f4289bdf13a799993ad9 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 16:27:58 -0700 Subject: [PATCH 094/318] changed how the detection of the last job works --- contrib/adaptive-compression/adapt.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index e1ac0aaf3..8f3acd5c9 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -50,6 +50,7 @@ typedef struct { buffer_t dst; unsigned compressionLevel; unsigned jobID; + unsigned lastJob; size_t compressedSize; } jobDescription; @@ -57,7 +58,6 @@ typedef struct { unsigned compressionLevel; unsigned numActiveThreads; unsigned numJobs; - unsigned lastJobID; unsigned nextJobID; unsigned threadError; unsigned jobReadyID; @@ -134,15 +134,15 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobReadyID = 0; ctx->jobCompressedID = 0; ctx->jobWriteID = 0; - ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); - /* allocating buffers for jobs */ + /* initializing jobs */ { unsigned jobNum; for (jobNum=0; jobNumjobs[jobNum]; job->src.start = malloc(FILE_CHUNK_SIZE); job->dst.start = malloc(ZSTD_compressBound(FILE_CHUNK_SIZE)); + job->lastJob = 0; if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); freeCCtx(ctx); @@ -265,7 +265,7 @@ static void* compressionThread(void* arg) pthread_mutex_unlock(&ctx->jobCompressed_mutex); DEBUGLOG(2, "finished job compression %u\n", currJob); currJob++; - if (currJob >= ctx->lastJobID || ctx->threadError) { + if (job->lastJob || ctx->threadError) { /* finished compressing all jobs */ DEBUGLOG(2, "all jobs finished compressing\n"); break; @@ -327,7 +327,7 @@ static void* outputThread(void* arg) } DEBUGLOG(2, "finished job write %u\n", currJob); currJob++; - displayProgress(currJob, ctx->compressionLevel, currJob >= ctx->lastJobID); + displayProgress(currJob, ctx->compressionLevel, job->lastJob); DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); ctx->jobWriteID++; @@ -335,8 +335,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->jobWrite_mutex); DEBUGLOG(2, "unlocking job write mutex\n"); - DEBUGLOG(2, "checking if done: %u/%u\n", currJob, ctx->lastJobID); - if (currJob >= ctx->lastJobID || ctx->threadError) { + if (job->lastJob || ctx->threadError) { /* finished with all jobs */ DEBUGLOG(2, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex); @@ -349,7 +348,7 @@ static void* outputThread(void* arg) return arg; } -static int createCompressionJob(adaptCCtx* ctx, size_t srcSize) +static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) { unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; @@ -371,6 +370,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize) job->src.size = srcSize; job->dst.size = ZSTD_compressBound(srcSize); job->jobID = nextJob; + job->lastJob = last; memcpy(job->src.start, ctx->input.buffer.start, srcSize); pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; @@ -457,7 +457,8 @@ static int compressFilename(const char* const srcFilename, const char* const dst g_streamedSize += readSize; /* reading was fine, now create the compression job */ { - int const error = createCompressionJob(ctx, readSize); + int const last = feof(srcFile); + int const error = createCompressionJob(ctx, readSize, last); if (error != 0) { ret = error; ctx->threadError = 1; @@ -466,7 +467,6 @@ static int compressFilename(const char* const srcFilename, const char* const dst } if (feof(srcFile)) { DEBUGLOG(2, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); - ctx->lastJobID = ctx->nextJobID; break; } } From 670b1fc547424897aa324736d47a1c79bf61e355 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 10 Jul 2017 16:30:55 -0700 Subject: [PATCH 095/318] optimized memory usage for ZSTDMT_compress() Previously, each job would reserve a CCtx right before being posted. The CCtx would be "part of the job description", and only released when the job is completed (aka flushed). For ZSTDMT_compress(), which creates all jobs first and only join at the end, that meant one CCtx per job. The nb of jobs used to be == nb of threads, but since latest modification, which reduces the size of jobs in order to spread the load of difficult areas, it also increases the nb of jobs for large sources / small compression level. This resulted in many more CCtx being created. In this new version, CCtx are reserved within the worker thread. It guaranteea there cannot be more CCtx reserved than workers (<= nb threads). To do that, it required to make the CCtx Pool multi-threading-safe : it can now be called from multiple threads in parallel. --- lib/compress/zstdmt_compress.c | 97 ++++++++++++++++++++-------------- tests/fuzzer.c | 45 ++++++++++------ 2 files changed, 86 insertions(+), 56 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index bad2db9c2..a176c3eea 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -126,6 +126,7 @@ static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool) * assumption : invocation from main thread only ! */ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) { + DEBUGLOG(2, "ZSTDMT_getBuffer"); if (pool->nbBuffers) { /* try to use an existing buffer */ buffer_t const buf = pool->bTable[--(pool->nbBuffers)]; size_t const availBufferSize = buf.size; @@ -160,21 +161,23 @@ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf) /* ===== CCtx Pool ===== */ +/* a single cctxPool can be called from multiple threads in parallel */ + typedef struct { + pthread_mutex_t poolMutex; unsigned totalCCtx; unsigned availCCtx; ZSTD_customMem cMem; ZSTD_CCtx* cctx[1]; /* variable size */ } ZSTDMT_CCtxPool; -/* assumption : CCtxPool invocation only from main thread */ - /* note : all CCtx borrowed from the pool should be released back to the pool _before_ freeing the pool */ static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool) { unsigned u; for (u=0; utotalCCtx; u++) ZSTD_freeCCtx(pool->cctx[u]); /* note : compatible with free on NULL */ + pthread_mutex_destroy(&pool->poolMutex); ZSTD_free(pool, pool->cMem); } @@ -186,6 +189,7 @@ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads, ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) ZSTD_calloc( sizeof(ZSTDMT_CCtxPool) + (nbThreads-1)*sizeof(ZSTD_CCtx*), cMem); if (!cctxPool) return NULL; + pthread_mutex_init(&cctxPool->poolMutex, NULL); cctxPool->cMem = cMem; cctxPool->totalCCtx = nbThreads; cctxPool->availCCtx = 1; /* at least one cctx for single-thread mode */ @@ -198,34 +202,47 @@ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads, /* only works during initialization phase, not during compression */ static size_t ZSTDMT_sizeof_CCtxPool(ZSTDMT_CCtxPool* cctxPool) { - unsigned const nbThreads = cctxPool->totalCCtx; - size_t const poolSize = sizeof(*cctxPool) - + (nbThreads-1)*sizeof(ZSTD_CCtx*); - unsigned u; - size_t totalCCtxSize = 0; - for (u=0; ucctx[u]); - - return poolSize + totalCCtxSize; + pthread_mutex_lock(&cctxPool->poolMutex); + { unsigned const nbThreads = cctxPool->totalCCtx; + size_t const poolSize = sizeof(*cctxPool) + + (nbThreads-1)*sizeof(ZSTD_CCtx*); + unsigned u; + size_t totalCCtxSize = 0; + for (u=0; ucctx[u]); + } + pthread_mutex_unlock(&cctxPool->poolMutex); + return poolSize + totalCCtxSize; + } } -static ZSTD_CCtx* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* pool) +static ZSTD_CCtx* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* cctxPool) { - if (pool->availCCtx) { - pool->availCCtx--; - return pool->cctx[pool->availCCtx]; - } - return ZSTD_createCCtx_advanced(pool->cMem); /* note : can be NULL, when creation fails ! */ + DEBUGLOG(5, "ZSTDMT_getCCtx"); + pthread_mutex_lock(&cctxPool->poolMutex); + if (cctxPool->availCCtx) { + cctxPool->availCCtx--; + { ZSTD_CCtx* const cctx = cctxPool->cctx[cctxPool->availCCtx]; + pthread_mutex_unlock(&cctxPool->poolMutex); + return cctx; + } } + pthread_mutex_unlock(&cctxPool->poolMutex); + DEBUGLOG(5, "create one more CCtx"); + return ZSTD_createCCtx_advanced(cctxPool->cMem); /* note : can be NULL, when creation fails ! */ } static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) { if (cctx==NULL) return; /* compatibility with release on NULL */ + pthread_mutex_lock(&pool->poolMutex); if (pool->availCCtx < pool->totalCCtx) pool->cctx[pool->availCCtx++] = cctx; - else + else { /* pool overflow : should not happen, since totalCCtx==nbThreads */ + DEBUGLOG(5, "CCtx pool overflow : free cctx"); ZSTD_freeCCtx(cctx); + } + pthread_mutex_unlock(&pool->poolMutex); } @@ -237,7 +254,6 @@ typedef struct { } inBuff_t; typedef struct { - ZSTD_CCtx* cctx; buffer_t src; const void* srcStart; size_t srcSize; @@ -253,6 +269,7 @@ typedef struct { pthread_cond_t* jobCompleted_cond; ZSTD_parameters params; const ZSTD_CDict* cdict; + ZSTDMT_CCtxPool* cctxPool; unsigned long long fullFrameSize; } ZSTDMT_jobDescription; @@ -260,37 +277,45 @@ typedef struct { void ZSTDMT_compressChunk(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; + ZSTD_CCtx* cctx = ZSTDMT_getCCtx(job->cctxPool); const void* const src = (const char*)job->srcStart + job->dictSize; buffer_t const dstBuff = job->dstBuff; DEBUGLOG(5, "job (first:%u) (last:%u) : dictSize %u, srcSize %u", job->firstChunk, job->lastChunk, (U32)job->dictSize, (U32)job->srcSize); + + if (cctx==NULL) { + job->cSize = ERROR(memory_allocation); + goto _endJob; + } + if (job->cdict) { /* should only happen for first segment */ - size_t const initError = ZSTD_compressBegin_usingCDict_advanced(job->cctx, job->cdict, job->params.fParams, job->fullFrameSize); + size_t const initError = ZSTD_compressBegin_usingCDict_advanced(cctx, job->cdict, job->params.fParams, job->fullFrameSize); DEBUGLOG(5, "using CDict"); if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ if (!job->firstChunk) job->params.fParams.contentSizeFlag = 0; /* ensure no srcSize control */ - { size_t const dictModeError = ZSTD_setCCtxParameter(job->cctx, ZSTD_p_forceRawDict, 1); /* Force loading dictionary in "content-only" mode (no header analysis) */ - size_t const initError = ZSTD_compressBegin_advanced(job->cctx, job->srcStart, job->dictSize, job->params, job->fullFrameSize); + { size_t const dictModeError = ZSTD_setCCtxParameter(cctx, ZSTD_p_forceRawDict, 1); /* Force loading dictionary in "content-only" mode (no header analysis) */ + size_t const initError = ZSTD_compressBegin_advanced(cctx, job->srcStart, job->dictSize, job->params, job->fullFrameSize); if (ZSTD_isError(initError) || ZSTD_isError(dictModeError)) { job->cSize = initError; goto _endJob; } - ZSTD_setCCtxParameter(job->cctx, ZSTD_p_forceWindow, 1); + ZSTD_setCCtxParameter(cctx, ZSTD_p_forceWindow, 1); } } if (!job->firstChunk) { /* flush and overwrite frame header when it's not first segment */ - size_t const hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, src, 0); + size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, 0); if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } - ZSTD_invalidateRepCodes(job->cctx); + ZSTD_invalidateRepCodes(cctx); } DEBUGLOG(5, "Compressing : "); DEBUG_PRINTHEX(4, job->srcStart, 12); job->cSize = (job->lastChunk) ? - ZSTD_compressEnd (job->cctx, dstBuff.start, dstBuff.size, src, job->srcSize) : - ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, src, job->srcSize); + 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)); _endJob: + ZSTDMT_releaseCCtx(job->cctxPool, cctx); PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); job->jobCompleted = 1; job->jobScanned = 0; @@ -390,8 +415,6 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx) mtctx->jobs[jobID].dstBuff = g_nullBuffer; ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[jobID].src); mtctx->jobs[jobID].src = g_nullBuffer; - ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[jobID].cctx); - mtctx->jobs[jobID].cctx = NULL; } memset(mtctx->jobs, 0, (mtctx->jobIDMask+1)*sizeof(ZSTDMT_jobDescription)); ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->inBuff.buffer); @@ -497,10 +520,9 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, size_t const dstBufferCapacity = ZSTD_compressBound(chunkSize); buffer_t const dstAsBuffer = { (char*)dst + dstBufferPos, dstBufferCapacity }; buffer_t const dstBuffer = u < compressWithinDst ? dstAsBuffer : ZSTDMT_getBuffer(mtctx->buffPool, dstBufferCapacity); - ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(mtctx->cctxPool); size_t dictSize = u ? overlapSize : 0; - if ((cctx==NULL) || (dstBuffer.start==NULL)) { + if (dstBuffer.start==NULL) { mtctx->jobs[u].cSize = ERROR(memory_allocation); /* job result */ mtctx->jobs[u].jobCompleted = 1; nbChunks = u+1; /* only wait and free u jobs, instead of initially expected nbChunks ones */ @@ -516,7 +538,7 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, /* 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].cctx = cctx; + mtctx->jobs[u].cctxPool = mtctx->cctxPool; mtctx->jobs[u].firstChunk = (u==0); mtctx->jobs[u].lastChunk = (u==nbChunks-1); mtctx->jobs[u].jobCompleted = 0; @@ -545,8 +567,6 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, pthread_mutex_unlock(&mtctx->jobCompleted_mutex); DEBUGLOG(5, "ready to write chunk %u ", chunkID); - ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[chunkID].cctx); - mtctx->jobs[chunkID].cctx = NULL; mtctx->jobs[chunkID].srcStart = NULL; { size_t const cSize = mtctx->jobs[chunkID].cSize; if (ZSTD_isError(cSize)) error = cSize; @@ -703,10 +723,9 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi { size_t const dstBufferCapacity = ZSTD_compressBound(srcSize); buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); - ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; - if ((cctx==NULL) || (dstBuffer.start==NULL)) { + if (dstBuffer.start==NULL) { zcs->jobs[jobID].jobCompleted = 1; zcs->nextJobID++; ZSTDMT_waitForAllJobsCompleted(zcs); @@ -727,7 +746,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL; zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize; zcs->jobs[jobID].dstBuff = dstBuffer; - zcs->jobs[jobID].cctx = cctx; + zcs->jobs[jobID].cctxPool = zcs->cctxPool; zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0); zcs->jobs[jobID].lastChunk = endFrame; zcs->jobs[jobID].jobCompleted = 0; @@ -804,8 +823,6 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi ZSTDMT_releaseAllJobResources(zcs); return job.cSize; } - ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); - zcs->jobs[wJobID].cctx = NULL; DEBUGLOG(5, "zcs->params.fParams.checksumFlag : %u ", zcs->params.fParams.checksumFlag); if (zcs->params.fParams.checksumFlag) { XXH64_update(&zcs->xxhState, (const char*)job.srcStart + job.dictSize, job.srcSize); @@ -884,7 +901,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, /* fill input buffer */ if ((input->src) && (mtctx->inBuff.buffer.start)) { /* support NULL input */ size_t const toLoad = MIN(input->size - input->pos, mtctx->inBuffSize - mtctx->inBuff.filled); - DEBUGLOG(2, "inBuff:%08X; inBuffSize=%u; ToCopy=%u", (U32)(size_t)mtctx->inBuff.buffer.start, (U32)mtctx->inBuffSize, (U32)toLoad); + 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); input->pos += toLoad; mtctx->inBuff.filled += toLoad; diff --git a/tests/fuzzer.c b/tests/fuzzer.c index aa1ebd48f..667de08ce 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -105,12 +105,13 @@ static unsigned FUZ_highbit32(U32 v32) typedef struct { unsigned long long totalMalloc; + size_t currentMalloc; size_t peakMalloc; unsigned nbMalloc; unsigned nbFree; } mallocCounter_t; -static const mallocCounter_t INIT_MALLOC_COUNTER = { 0, 0, 0, 0 }; +static const mallocCounter_t INIT_MALLOC_COUNTER = { 0, 0, 0, 0, 0 }; static void* FUZ_mallocDebug(void* counter, size_t size) { @@ -118,7 +119,9 @@ static void* FUZ_mallocDebug(void* counter, size_t size) void* const ptr = malloc(size); if (ptr==NULL) return NULL; mcPtr->totalMalloc += size; - mcPtr->peakMalloc += size; + mcPtr->currentMalloc += size; + if (mcPtr->currentMalloc > mcPtr->peakMalloc) + mcPtr->peakMalloc = mcPtr->currentMalloc; mcPtr->nbMalloc += 1; return ptr; } @@ -126,9 +129,10 @@ static void* FUZ_mallocDebug(void* counter, size_t size) static void FUZ_freeDebug(void* counter, void* address) { mallocCounter_t* const mcPtr = (mallocCounter_t*)counter; - free(address); + DISPLAYLEVEL(4, "releasing %u KB \n", (U32)(malloc_size(address) >> 10)); mcPtr->nbFree += 1; - mcPtr->peakMalloc -= malloc_size(address); /* OS-X specific */ + mcPtr->currentMalloc -= malloc_size(address); /* OS-X specific */ + free(address); } static void FUZ_displayMallocStats(mallocCounter_t count) @@ -139,6 +143,14 @@ static void FUZ_displayMallocStats(mallocCounter_t count) (U32)(count.totalMalloc >> 10)); } +#define CHECK_Z(f) { \ + size_t const err = f; \ + if (ZSTD_isError(err)) { \ + DISPLAY("Error => %s : %s ", \ + #f, ZSTD_getErrorName(err)); \ + exit(1); \ +} } + static int FUZ_mallocTests(unsigned seed, double compressibility) { size_t const inSize = 64 MB + 16 MB + 4 MB + 1 MB + 256 KB + 64 KB; /* 85.3 MB */ @@ -162,7 +174,7 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) mallocCounter_t malcount = INIT_MALLOC_COUNTER; ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount }; ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); - ZSTD_compressCCtx(cctx, outBuffer, outSize, inBuffer, inSize, compressionLevel); + CHECK_Z( ZSTD_compressCCtx(cctx, outBuffer, outSize, inBuffer, inSize, compressionLevel) ); ZSTD_freeCCtx(cctx); DISPLAYLEVEL(3, "compressCCtx level %i : ", compressionLevel); FUZ_displayMallocStats(malcount); @@ -176,9 +188,9 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) ZSTD_CCtx* const cstream = ZSTD_createCStream_advanced(cMem); ZSTD_outBuffer out = { outBuffer, outSize, 0 }; ZSTD_inBuffer in = { inBuffer, inSize, 0 }; - ZSTD_initCStream(cstream, compressionLevel); - ZSTD_compressStream(cstream, &out, &in); - ZSTD_endStream(cstream, &out); + CHECK_Z( ZSTD_initCStream(cstream, compressionLevel) ); + CHECK_Z( ZSTD_compressStream(cstream, &out, &in) ); + CHECK_Z( ZSTD_endStream(cstream, &out) ); ZSTD_freeCStream(cstream); DISPLAYLEVEL(3, "compressStream level %i : ", compressionLevel); FUZ_displayMallocStats(malcount); @@ -194,9 +206,9 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); ZSTD_outBuffer out = { outBuffer, outSize, 0 }; ZSTD_inBuffer in = { inBuffer, inSize, 0 }; - ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel); - ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, nbThreads); - ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); + CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel) ); + CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, nbThreads) ); + while ( ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end) ) {} ZSTD_freeCCtx(cctx); DISPLAYLEVEL(3, "compress_generic,-T%u,end level %i : ", nbThreads, compressionLevel); @@ -213,10 +225,10 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem); ZSTD_outBuffer out = { outBuffer, outSize, 0 }; ZSTD_inBuffer in = { inBuffer, inSize, 0 }; - ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel); - ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, nbThreads); - ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_continue); - ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end); + CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel) ); + CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, 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); DISPLAYLEVEL(3, "compress_generic,-T%u,continue level %i : ", nbThreads, compressionLevel); @@ -1046,6 +1058,7 @@ static size_t FUZ_randomLength(U32* seed, U32 maxLog) goto _output_error; \ } } +#undef CHECK_Z #define CHECK_Z(f) { \ size_t const err = f; \ if (ZSTD_isError(err)) { \ @@ -1473,7 +1486,7 @@ int main(int argc, const char** argv) if (proba!=FUZ_compressibility_default) DISPLAY("Compressibility : %u%%\n", proba); if (memTestsOnly) { - g_displayLevel=3; + g_displayLevel = MAX(3, g_displayLevel); return FUZ_mallocTests(seed, ((double)proba) / 100); } From 4616fad18b2f8950144cdbcffc081b74cc115fb1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 10 Jul 2017 17:16:41 -0700 Subject: [PATCH 096/318] improved ZSTDMT_compress() memory usage does not need the input buffer for streaming operations also : reduced a few tests time length --- lib/compress/zstdmt_compress.c | 31 +++++++++++++++++++------------ tests/fuzzer.c | 2 +- tests/playTests.sh | 31 +++++++++++++++---------------- tests/zstreamtest.c | 19 +------------------ 4 files changed, 36 insertions(+), 47 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index a176c3eea..fbb86b008 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -134,9 +134,11 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) /* large enough, but not too much */ return buf; /* size conditions not respected : scratch this buffer, create new one */ + DEBUGLOG(2, "existing buffer does not meet size conditions => freeing"); ZSTD_free(buf.start, pool->cMem); } /* create new buffer */ + DEBUGLOG(2, "create a new buffer"); { buffer_t buffer; void* const start = ZSTD_malloc(bSize, pool->cMem); if (start==NULL) bSize = 0; @@ -149,12 +151,14 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) /* store buffer for later re-use, up to pool capacity */ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf) { + DEBUGLOG(2, "ZSTDMT_releaseBuffer"); if (buf.start == NULL) return; /* release on NULL */ if (pool->nbBuffers < pool->totalBuffers) { pool->bTable[pool->nbBuffers++] = buf; /* store for later re-use */ return; } /* Reached bufferPool capacity (should not happen) */ + DEBUGLOG(2, "buffer pool capacity reached => freeing "); ZSTD_free(buf.start, pool->cMem); } @@ -635,8 +639,8 @@ size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, if (zcs->nbThreads==1) { DEBUGLOG(4, "single thread mode"); return ZSTD_initCStream_internal(zcs->cctxPool->cctx[0], - dict, dictSize, cdict, - params, pledgedSrcSize); + dict, dictSize, cdict, + params, pledgedSrcSize); } if (zcs->allJobsCompleted == 0) { /* previous compression not correctly finished */ @@ -671,9 +675,7 @@ size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, DEBUGLOG(4, "Section Size : %u KB", (U32)(zcs->targetSectionSize>>10)); zcs->marginSize = zcs->targetSectionSize >> 2; zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize + zcs->marginSize; - zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); - if (zcs->inBuff.buffer.start == NULL) return ERROR(memory_allocation); - zcs->inBuff.filled = 0; + zcs->inBuff.buffer = g_nullBuffer; zcs->dictSize = 0; zcs->doneJobID = 0; zcs->nextJobID = 0; @@ -899,13 +901,18 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, } /* fill input buffer */ - if ((input->src) && (mtctx->inBuff.buffer.start)) { /* support NULL input */ - 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); - input->pos += toLoad; - mtctx->inBuff.filled += toLoad; - } + if (input->src) { /* support NULL input */ + if (mtctx->inBuff.buffer.start == NULL) { + mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->buffPool, mtctx->inBuffSize); + if (mtctx->inBuff.buffer.start == NULL) return ERROR(memory_allocation); + mtctx->inBuff.filled = 0; + } + { 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); + input->pos += toLoad; + mtctx->inBuff.filled += toLoad; + } } if ( (mtctx->inBuff.filled >= newJobThreshold) /* filled enough : let's compress */ && (mtctx->nextJobID <= mtctx->doneJobID + mtctx->jobIDMask) ) { /* avoid overwriting job round buffer */ diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 667de08ce..1e25383f5 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -129,7 +129,7 @@ static void* FUZ_mallocDebug(void* counter, size_t size) static void FUZ_freeDebug(void* counter, void* address) { mallocCounter_t* const mcPtr = (mallocCounter_t*)counter; - DISPLAYLEVEL(4, "releasing %u KB \n", (U32)(malloc_size(address) >> 10)); + DISPLAYLEVEL(4, "freeing %u KB \n", (U32)(malloc_size(address) >> 10)); mcPtr->nbFree += 1; mcPtr->currentMalloc -= malloc_size(address); /* OS-X specific */ free(address); diff --git a/tests/playTests.sh b/tests/playTests.sh index 2e1cc6826..88a1c2ab4 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -7,17 +7,17 @@ die() { roundTripTest() { if [ -n "$3" ]; then - local_c="$3" - local_p="$2" + cLevel="$3" + proba="$2" else - local_c="$2" - local_p="" + cLevel="$2" + proba="" fi rm -f tmp1 tmp2 - $ECHO "roundTripTest: ./datagen $1 $local_p | $ZSTD -v$local_c | $ZSTD -d" - ./datagen $1 $local_p | $MD5SUM > tmp1 - ./datagen $1 $local_p | $ZSTD --ultra -v$local_c | $ZSTD -d | $MD5SUM > tmp2 + $ECHO "roundTripTest: ./datagen $1 $proba | $ZSTD -v$cLevel | $ZSTD -d" + ./datagen $1 $proba | $MD5SUM > tmp1 + ./datagen $1 $proba | $ZSTD --ultra -v$cLevel | $ZSTD -d | $MD5SUM > tmp2 $DIFF -q tmp1 tmp2 } @@ -625,16 +625,15 @@ roundTripTest -g35000000 -P75 10 roundTripTest -g35000000 -P75 11 roundTripTest -g35000000 -P75 12 -roundTripTest -g18000000 -P80 13 -roundTripTest -g18000000 -P80 14 -roundTripTest -g18000000 -P80 15 -roundTripTest -g18000000 -P80 16 -roundTripTest -g18000000 -P80 17 +roundTripTest -g18000013 -P80 13 +roundTripTest -g18000014 -P80 14 +roundTripTest -g18000015 -P80 15 +roundTripTest -g18000016 -P80 16 +roundTripTest -g18000017 -P80 17 +roundTripTest -g18000018 -P94 18 +roundTripTest -g18000019 -P94 19 -roundTripTest -g50000000 -P94 18 -roundTripTest -g50000000 -P94 19 - -roundTripTest -g99000000 -P99 20 +roundTripTest -g68000020 -P99 20 roundTripTest -g6000000000 -P99 1 fileRoundTripTest -g4193M -P99 1 diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 9b2b8eaf8..8b84400db 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -95,19 +95,6 @@ unsigned int FUZ_rand(unsigned int* seedPtr) return rand32 >> 5; } -static void* allocFunction(void* opaque, size_t size) -{ - void* address = malloc(size); - (void)opaque; - return address; -} - -static void freeFunction(void* opaque, void* address) -{ - (void)opaque; - free(address); -} - /*====================================================== * Basic Unit tests @@ -1543,7 +1530,6 @@ int main(int argc, const char** argv) int bigTests = (sizeof(size_t) == 8); e_api selected_api = simple_api; const char* const programName = argv[0]; - ZSTD_customMem const customMem = { allocFunction, freeFunction, NULL }; ZSTD_customMem const customNULL = ZSTD_defaultCMem; /* Check command line */ @@ -1657,10 +1643,7 @@ int main(int argc, const char** argv) if (testNb==0) { result = basicUnitTests(0, ((double)proba) / 100, customNULL); /* constant seed for predictability */ - if (!result) { - DISPLAYLEVEL(3, "Unit tests using customMem :\n") - result = basicUnitTests(0, ((double)proba) / 100, customMem); /* use custom memory allocation functions */ - } } + } if (!result) { switch(selected_api) From f91854549111eb90ba5f7f15d142b8687e4ab74f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 18:16:42 -0700 Subject: [PATCH 097/318] made some progress on improving compression ratio, but problems exist with speed limits, and for some reason higher compression levels are really slow --- contrib/adaptive-compression/adapt.c | 102 ++++++++++++++++++--------- 1 file changed, 69 insertions(+), 33 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 8f3acd5c9..2bf8bb81e 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -1,6 +1,6 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define PRINT(...) fprintf(stdout, __VA_ARGS__) -#define DEBUGLOG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } +#define DEBUG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 #define MAX_NUM_JOBS 2; #define stdinmark "/*stdin*\\" @@ -15,7 +15,7 @@ typedef unsigned char BYTE; #include /* malloc, free */ #include /* pthread functions */ #include /* memset */ -#include "zstd.h" +#include "zstd_internal.h" #include "util.h" static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; @@ -48,6 +48,7 @@ typedef struct { typedef struct { buffer_t src; buffer_t dst; + buffer_t dict; unsigned compressionLevel; unsigned jobID; unsigned lastJob; @@ -87,6 +88,7 @@ static void freeCompressionJobs(adaptCCtx* ctx) jobDescription job = ctx->jobs[u]; free(job.dst.start); free(job.src.start); + free(job.dict.start); } } @@ -142,8 +144,9 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) jobDescription* job = &ctx->jobs[jobNum]; job->src.start = malloc(FILE_CHUNK_SIZE); job->dst.start = malloc(ZSTD_compressBound(FILE_CHUNK_SIZE)); + job->dict.start = malloc(FILE_CHUNK_SIZE); job->lastJob = 0; - if (!job->src.start || !job->dst.start) { + if (!job->src.start || !job->dst.start || !job->dict.start) { DISPLAY("Could not allocate buffers for jobs\n"); freeCCtx(ctx); return NULL; @@ -207,17 +210,17 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; - DEBUGLOG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); + DEBUG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; } else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUGLOG(2, "increasing compression level %u\n", ctx->compressionLevel); + DEBUG(2, "increasing compression level %u\n", ctx->compressionLevel); ctx->compressionLevel++; reset = 1; } else if (compressSlow && ctx->compressionLevel > 1) { - DEBUGLOG(2, "decreasing compression level %u\n", ctx->compressionLevel); + DEBUG(2, "decreasing compression level %u\n", ctx->compressionLevel); ctx->compressionLevel--; reset = 1; } @@ -236,38 +239,63 @@ static void* compressionThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); + DEBUG(2, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { ctx->stats.waitReady++; ctx->stats.readyCounter++; - DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); + DEBUG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } pthread_mutex_unlock(&ctx->jobReady_mutex); - DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); + DEBUG(2, "compressionThread(): continuing after job ready\n"); /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); - DEBUGLOG(2, "cLevel used: %u\n", cLevel); - size_t const compressedSize = ZSTD_compressCCtx(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size, cLevel); - if (ZSTD_isError(compressedSize)) { + ZSTD_parameters params = ZSTD_getParams(cLevel, job->src.size, 0); + DEBUG(2, "cLevel used: %u\n", cLevel); + + /* begin compression */ + { + size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); + size_t const initError = ZSTD_compressBegin_advanced(ctx->cctx, job->dict.start, job->dict.size, params, 0); + size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); + if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { + DISPLAY("Error: something went wrong while starting compression\n"); + ctx->threadError = 1; + return arg; + } + } + + /* continue compression */ + if (currJob != 0) { /* not first job */ + size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, 0); + if (ZSTD_isError(hSize)) { + job->compressedSize = hSize; + ctx->threadError = 1; + return arg; + } + ZSTD_invalidateRepCodes(ctx->cctx); + } + job->compressedSize = (job->lastJob) ? + ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size) : + ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size); + if (ZSTD_isError(job->compressedSize)) { + DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(job->compressedSize)); ctx->threadError = 1; - DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(compressedSize)); return arg; } - job->compressedSize = compressedSize; } pthread_mutex_lock(&ctx->jobCompressed_mutex); ctx->jobCompressedID++; - DEBUGLOG(2, "signaling for job %u\n", currJob); + DEBUG(2, "signaling for job %u\n", currJob); pthread_cond_signal(&ctx->jobCompressed_cond); pthread_mutex_unlock(&ctx->jobCompressed_mutex); - DEBUGLOG(2, "finished job compression %u\n", currJob); + DEBUG(2, "finished job compression %u\n", currJob); currJob++; if (job->lastJob || ctx->threadError) { /* finished compressing all jobs */ - DEBUGLOG(2, "all jobs finished compressing\n"); + DEBUG(2, "all jobs finished compressing\n"); break; } } @@ -299,16 +327,16 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUGLOG(2, "outputThread(): waiting on job compressed\n"); + DEBUG(2, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex); while (currJob + 1 > ctx->jobCompressedID) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; - DEBUGLOG(2, "waiting on job compressed, nextJob: %u\n", currJob); + DEBUG(2, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond, &ctx->jobCompressed_mutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex); - DEBUGLOG(2, "outputThread(): continuing after job compressed\n"); + DEBUG(2, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { @@ -325,19 +353,19 @@ static void* outputThread(void* arg) } } } - DEBUGLOG(2, "finished job write %u\n", currJob); + DEBUG(2, "finished job write %u\n", currJob); currJob++; displayProgress(currJob, ctx->compressionLevel, job->lastJob); - DEBUGLOG(2, "locking job write mutex\n"); + DEBUG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); ctx->jobWriteID++; pthread_cond_signal(&ctx->jobWrite_cond); pthread_mutex_unlock(&ctx->jobWrite_mutex); - DEBUGLOG(2, "unlocking job write mutex\n"); + DEBUG(2, "unlocking job write mutex\n"); if (job->lastJob || ctx->threadError) { /* finished with all jobs */ - DEBUGLOG(2, "all jobs finished writing\n"); + DEBUG(2, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex); ctx->allJobsCompleted = 1; pthread_cond_signal(&ctx->allJobsCompleted_cond); @@ -353,17 +381,17 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; - DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); + DEBUG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); + DEBUG(2, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; - DEBUGLOG(2, "waiting on job Write, nextJob: %u\n", nextJob); + DEBUG(2, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); - DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); + DEBUG(2, "createCompressionJob(): continuing after job write\n"); job->compressionLevel = ctx->compressionLevel; @@ -371,13 +399,21 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) job->dst.size = ZSTD_compressBound(srcSize); job->jobID = nextJob; job->lastJob = last; - memcpy(job->src.start, ctx->input.buffer.start, srcSize); + memcpy(job->src.start, ctx->input.buffer.start + ctx->input.filled, srcSize); + job->dict.size = ctx->input.filled; + memcpy(job->dict.start, ctx->input.buffer.start, ctx->input.filled); pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); pthread_mutex_unlock(&ctx->jobReady_mutex); - DEBUGLOG(2, "finished job creation %u\n", nextJob); + DEBUG(2, "finished job creation %u\n", nextJob); ctx->nextJobID++; + + /* if not on the last job, reuse data as dictionary in next job */ + if (!last) { + ctx->input.filled = srcSize; + memmove(ctx->input.buffer.start, ctx->input.buffer.start + ctx->input.filled, srcSize); + } return 0; } @@ -447,7 +483,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst /* creating jobs */ for ( ; ; ) { - size_t const readSize = fread(ctx->input.buffer.start, 1, FILE_CHUNK_SIZE, srcFile); + size_t const readSize = fread(ctx->input.buffer.start + ctx->input.filled, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); ctx->threadError = 1; @@ -466,7 +502,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst } } if (feof(srcFile)) { - DEBUGLOG(2, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); + DEBUG(2, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); break; } } @@ -563,7 +599,7 @@ int main(int argCount, const char* argv[]) case 'i': argument += 2; g_compressionLevel = readU32FromChar(&argument); - DEBUGLOG(2, "g_compressionLevel: %u\n", g_compressionLevel); + DEBUG(2, "g_compressionLevel: %u\n", g_compressionLevel); break; case 's': g_displayStats = 1; From 6c3673f4c388d2a3b0d8af696e1e69c9c8f68b5b Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 10 Jul 2017 22:27:43 -0700 Subject: [PATCH 098/318] Add rolling hash --- contrib/long_distance_matching/ldm.c | 87 +++++++++++++++++++++------- 1 file changed, 66 insertions(+), 21 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index a1d4449eb..42a4affd7 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -3,12 +3,13 @@ #include #include + #include "ldm.h" #include "util.h" #define HASH_EVERY 7 -#define LDM_MEMORY_USAGE 14 +#define LDM_MEMORY_USAGE 20 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) @@ -16,16 +17,18 @@ #define LDM_OFFSET_SIZE 4 -#define WINDOW_SIZE (1 << 20) +#define WINDOW_SIZE (1 << 24) #define MAX_WINDOW_SIZE 31 -#define HASH_SIZE 8 -#define MINMATCH 8 +#define HASH_SIZE 4 +#define LDM_HASH_LENGTH 4 +#define MINMATCH 4 #define ML_BITS 4 #define ML_MASK ((1U<> 2); +// return sum & (LDM_HASHTABLESIZE - 1); +} +static U32 LDM_getRollingHash(const char *data, U32 len) { + U32 i; + U32 s1, s2; + const schar *buf = (const schar *)data; + + s1 = s2 = 0; + for (i = 0; i < (len - 4); i += 4) { + s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + + (2 * buf[i + 2]) + (buf[i + 3]); + s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; + } + for(; i < len; i++) { + s1 += buf[i]; + s2 += s1; + } + return (s1 & 0xffff) + (s2 << 16); +} + +static hash_t LDM_hashPosition(const void * const p) { + return LDM_sumToHash(LDM_getRollingHash((const char *)p, LDM_HASH_LENGTH)); +} + +typedef struct LDM_sumStruct { + U16 s1, s2; +} LDM_sumStruct; + +static void LDM_getRollingHashParts(U32 sum, LDM_sumStruct *sumStruct) { + sumStruct->s1 = sum & 0xffff; + sumStruct->s2 = sum >> 16; +} + +#else static hash_t LDM_hash(U32 sequence) { return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); } +static hash_t LDM_hashPosition(const void * const p) { + return LDM_hash(LDM_read32(p)); +} +#endif + /* static hash_t LDM_hash5(U64 sequence) { static const U64 prime5bytes = 889523592379ULL; @@ -112,10 +161,6 @@ static hash_t LDM_hash5(U64 sequence) { } */ -static hash_t LDM_hashPosition(const void * const p) { - return LDM_hash(LDM_read32(p)); -} - static void LDM_putHashOfCurrentPositionFromHash( LDM_CCtx *cctx, hash_t hash) { if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { @@ -187,26 +232,26 @@ static void LDM_initializeCCtx(LDM_CCtx *cctx, memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); cctx->lastPosHashed = NULL; - cctx->forwardIp = NULL; + cctx->nextIp = NULL; cctx->step = 1; } static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { - cctx->forwardIp = cctx->ip; + cctx->nextIp = cctx->ip; do { - hash_t const h = cctx->forwardHash; - cctx->ip = cctx->forwardIp; - cctx->forwardIp += cctx->step; + hash_t const h = cctx->nextHash; + cctx->ip = cctx->nextIp; + cctx->nextIp += cctx->step; - if (cctx->forwardIp > cctx->imatchLimit) { + if (cctx->nextIp > cctx->imatchLimit) { return 1; } *match = LDM_get_position_on_hash(h, cctx->hashTable, cctx->ibase); - cctx->forwardHash = LDM_hashPosition(cctx->forwardIp); + cctx->nextHash = LDM_hashPosition(cctx->nextIp); LDM_putHashOfCurrentPositionFromHash(cctx, h); } while (cctx->ip - *match > WINDOW_SIZE || LDM_read64(*match) != LDM_read64(cctx->ip)); @@ -222,7 +267,7 @@ size_t LDM_compress(const void *src, size_t srcSize, /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); cctx.ip++; - cctx.forwardHash = LDM_hashPosition(cctx.ip); + cctx.nextHash = LDM_hashPosition(cctx.ip); // TODO: loop condition is not accurate. while (1) { @@ -241,7 +286,7 @@ size_t LDM_compress(const void *src, size_t srcSize, cctx.stats.numMatches++; /** - * Catchup: look back to extend the match backwards from the found match. + * Catch up: look back to extend the match backwards from the found match. */ while (cctx.ip > cctx.anchor && match > cctx.ibase && cctx.ip[-1] == match[-1]) { @@ -313,7 +358,7 @@ size_t LDM_compress(const void *src, size_t srcSize, // Set start of next block to current input pointer. cctx.anchor = cctx.ip; LDM_putHashOfCurrentPosition(&cctx); - cctx.forwardHash = LDM_hashPosition(++cctx.ip); + cctx.nextHash = LDM_hashPosition(++cctx.ip); } _last_literals: /* Encode the last literals (no more matches). */ From ef0ff7fe7fd90a94721c2626d1588b053ffc76ce Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 11 Jul 2017 08:54:29 -0700 Subject: [PATCH 099/318] zstdmt: removed margin for improved memory usage --- lib/compress/zstdmt_compress.c | 12 +++++------- tests/fuzzer.c | 2 ++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index fbb86b008..255b9c7ed 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -340,7 +340,6 @@ struct ZSTDMT_CCtx_s { pthread_mutex_t jobCompleted_mutex; pthread_cond_t jobCompleted_cond; size_t targetSectionSize; - size_t marginSize; size_t inBuffSize; size_t dictSize; size_t targetDictSize; @@ -673,8 +672,7 @@ size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, zcs->targetSectionSize = MAX(ZSTDMT_SECTION_SIZE_MIN, zcs->targetSectionSize); zcs->targetSectionSize = MAX(zcs->targetDictSize, zcs->targetSectionSize); DEBUGLOG(4, "Section Size : %u KB", (U32)(zcs->targetSectionSize>>10)); - zcs->marginSize = zcs->targetSectionSize >> 2; - zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize + zcs->marginSize; + zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize; zcs->inBuff.buffer = g_nullBuffer; zcs->dictSize = 0; zcs->doneJobID = 0; @@ -871,18 +869,18 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, ZSTD_inBuffer* input, ZSTD_EndDirective endOp) { - size_t const newJobThreshold = mtctx->dictSize + mtctx->targetSectionSize + mtctx->marginSize; + size_t const newJobThreshold = mtctx->dictSize + mtctx->targetSectionSize; assert(output->pos <= output->size); assert(input->pos <= input->size); if ((mtctx->frameEnded) && (endOp==ZSTD_e_continue)) { /* current frame being ended. Only flush/end are allowed. Or start new frame with init */ return ERROR(stage_wrong); } - if (mtctx->nbThreads==1) { + if (mtctx->nbThreads==1) { /* delegate to single-thread (synchronous) */ return ZSTD_compressStream_generic(mtctx->cctxPool->cctx[0], output, input, endOp); } - /* single-pass shortcut (note : this is blocking-mode) */ + /* single-pass shortcut (note : this is synchronous-mode) */ if ( (mtctx->nextJobID==0) /* just started */ && (mtctx->inBuff.filled==0) /* nothing buffered */ && (endOp==ZSTD_e_end) /* end order */ @@ -901,7 +899,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, } /* fill input buffer */ - if (input->src) { /* support NULL input */ + if (input->size > input->pos) { /* support NULL input */ if (mtctx->inBuff.buffer.start == NULL) { mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->buffPool, mtctx->inBuffSize); if (mtctx->inBuff.buffer.start == NULL) return ERROR(memory_allocation); diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 1e25383f5..6bed9ec51 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -118,6 +118,8 @@ static void* FUZ_mallocDebug(void* counter, size_t size) mallocCounter_t* const mcPtr = (mallocCounter_t*)counter; void* const ptr = malloc(size); if (ptr==NULL) return NULL; + DISPLAYLEVEL(4, "allocating %u KB => effectively %u KB \n", + (U32)(size >> 10), (U32)(malloc_size(ptr) >> 10)); /* OS-X specific */ mcPtr->totalMalloc += size; mcPtr->currentMalloc += size; if (mcPtr->currentMalloc > mcPtr->peakMalloc) From f6c5d07fe295997d9f01461d2c40714e10aa5827 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Tue, 11 Jul 2017 09:23:44 -0700 Subject: [PATCH 100/318] Save v3 --- contrib/long_distance_matching/ldm.c | 8 +- .../versions/v3/Makefile | 40 ++ .../long_distance_matching/versions/v3/ldm.c | 464 +++++++++++++++++ .../long_distance_matching/versions/v3/ldm.h | 19 + .../versions/v3/main-ldm.c | 479 ++++++++++++++++++ .../long_distance_matching/versions/v3/util.c | 64 +++ .../long_distance_matching/versions/v3/util.h | 23 + 7 files changed, 1093 insertions(+), 4 deletions(-) create mode 100644 contrib/long_distance_matching/versions/v3/Makefile create mode 100644 contrib/long_distance_matching/versions/v3/ldm.c create mode 100644 contrib/long_distance_matching/versions/v3/ldm.h create mode 100644 contrib/long_distance_matching/versions/v3/main-ldm.c create mode 100644 contrib/long_distance_matching/versions/v3/util.c create mode 100644 contrib/long_distance_matching/versions/v3/util.h diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 42a4affd7..1dedf5c37 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -7,9 +7,9 @@ #include "ldm.h" #include "util.h" -#define HASH_EVERY 7 +#define HASH_EVERY 1 -#define LDM_MEMORY_USAGE 20 +#define LDM_MEMORY_USAGE 16 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) @@ -17,7 +17,7 @@ #define LDM_OFFSET_SIZE 4 -#define WINDOW_SIZE (1 << 24) +#define WINDOW_SIZE (1 << 20) #define MAX_WINDOW_SIZE 31 #define HASH_SIZE 4 #define LDM_HASH_LENGTH 4 @@ -28,7 +28,7 @@ #define RUN_BITS (8-ML_BITS) #define RUN_MASK ((1U< +#include +#include +#include + + +#include "ldm.h" +#include "util.h" + +#define HASH_EVERY 1 + +#define LDM_MEMORY_USAGE 16 +#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) +#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) + +#define LDM_OFFSET_SIZE 4 + +#define WINDOW_SIZE (1 << 20) +#define MAX_WINDOW_SIZE 31 +#define HASH_SIZE 4 +#define LDM_HASH_LENGTH 4 +#define MINMATCH 4 + +#define ML_BITS 4 +#define ML_MASK ((1U<numMatches); + printf("Average match length: %.1f\n", ((double)stats->totalMatchLength) / + (double)stats->numMatches); + printf("Average literal length: %.1f\n", + ((double)stats->totalLiteralLength) / (double)stats->numMatches); + printf("Average offset length: %.1f\n", + ((double)stats->totalOffset) / (double)stats->numMatches); + printf("=====================\n"); +} + +typedef 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_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; + + const BYTE *lastPosHashed; /* Last position hashed */ + hash_t lastHash; /* Hash corresponding to lastPosHashed */ + const BYTE *nextIp; + hash_t nextHash; /* Hash corresponding to nextIp */ + + unsigned step; +} LDM_CCtx; + +#ifdef LDM_ROLLING_HASH +/** + * Convert a sum computed from LDM_getRollingHash to a hash value in the range + * of the hash table. + */ +static hash_t LDM_sumToHash(U32 sum) { + return sum % (LDM_HASHTABLESIZE >> 2); +// return sum & (LDM_HASHTABLESIZE - 1); +} + +static U32 LDM_getRollingHash(const char *data, U32 len) { + U32 i; + U32 s1, s2; + const schar *buf = (const schar *)data; + + s1 = s2 = 0; + for (i = 0; i < (len - 4); i += 4) { + s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + + (2 * buf[i + 2]) + (buf[i + 3]); + s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; + } + for(; i < len; i++) { + s1 += buf[i]; + s2 += s1; + } + return (s1 & 0xffff) + (s2 << 16); +} + +static hash_t LDM_hashPosition(const void * const p) { + return LDM_sumToHash(LDM_getRollingHash((const char *)p, LDM_HASH_LENGTH)); +} + +typedef struct LDM_sumStruct { + U16 s1, s2; +} LDM_sumStruct; + +static void LDM_getRollingHashParts(U32 sum, LDM_sumStruct *sumStruct) { + sumStruct->s1 = sum & 0xffff; + sumStruct->s2 = sum >> 16; +} + +#else +static hash_t LDM_hash(U32 sequence) { + return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); +} + +static hash_t LDM_hashPosition(const void * const p) { + return LDM_hash(LDM_read32(p)); +} +#endif + +/* +static hash_t LDM_hash5(U64 sequence) { + static const U64 prime5bytes = 889523592379ULL; + static const U64 prime8bytes = 11400714785074694791ULL; + const U32 hashLog = LDM_HASHLOG; + if (LDM_isLittleEndian()) + return (((sequence << 24) * prime5bytes) >> (64 - hashLog)); + else + return (((sequence >> 24) * prime8bytes) >> (64 - hashLog)); +} +*/ + +static void LDM_putHashOfCurrentPositionFromHash( + LDM_CCtx *cctx, hash_t hash) { + if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { + return; + } + (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; + cctx->lastPosHashed = cctx->ip; + cctx->lastHash = hash; +} + +static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { + hash_t hash = LDM_hashPosition(cctx->ip); + LDM_putHashOfCurrentPositionFromHash(cctx, hash); +} + +static const BYTE *LDM_get_position_on_hash( + hash_t h, void *tableBase, const BYTE *srcBase) { + const LDM_hashEntry * const hashTable = (LDM_hashEntry *)tableBase; + return hashTable[h].offset + srcBase; +} + +static BYTE LDM_read_byte(const void *memPtr) { + BYTE val; + memcpy(&val, memPtr, 1); + return val; +} + +static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { + const BYTE * const pStart = pIn; + while (pIn < pInLimit - 1) { + BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); + if (!diff) { + pIn++; + pMatch++; + continue; + } + return (unsigned)(pIn - pStart); + } + return (unsigned)(pIn - pStart); +} + +void LDM_readHeader(const void *src, size_t *compressSize, + size_t *decompressSize) { + const U32 *ip = (const U32 *)src; + *compressSize = *ip++; + *decompressSize = *ip; +} + +static void 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 - HASH_SIZE; + cctx->imatchLimit = cctx->iend - MINMATCH; + + cctx->obase = (BYTE *)dst; + cctx->op = (BYTE *)dst; + + cctx->anchor = cctx->ibase; + + memset(&(cctx->stats), 0, sizeof(cctx->stats)); + memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); + + cctx->lastPosHashed = NULL; + cctx->nextIp = NULL; + + cctx->step = 1; +} + +static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { + cctx->nextIp = cctx->ip; + + do { + hash_t const h = cctx->nextHash; + cctx->ip = cctx->nextIp; + cctx->nextIp += cctx->step; + + if (cctx->nextIp > cctx->imatchLimit) { + return 1; + } + + *match = LDM_get_position_on_hash(h, cctx->hashTable, cctx->ibase); + + cctx->nextHash = LDM_hashPosition(cctx->nextIp); + LDM_putHashOfCurrentPositionFromHash(cctx, h); + } while (cctx->ip - *match > WINDOW_SIZE || + LDM_read64(*match) != LDM_read64(cctx->ip)); + return 0; +} + +// TODO: srcSize and maxDstSize is unused +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { + LDM_CCtx cctx; + LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); + + /* Hash the first position and put it into the hash table. */ + LDM_putHashOfCurrentPosition(&cctx); + cctx.ip++; + cctx.nextHash = LDM_hashPosition(cctx.ip); + + // TODO: loop condition is not accurate. + while (1) { + const BYTE *match; + + /** + * 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. + */ + if (LDM_findBestMatch(&cctx, &match) != 0) { + goto _last_literals; + } + + cctx.stats.numMatches++; + + /** + * Catch up: look back to extend the match backwards from the found match. + */ + while (cctx.ip > cctx.anchor && match > cctx.ibase && + cctx.ip[-1] == match[-1]) { + cctx.ip--; + match--; + } + + /** + * Write current block (literals, literal length, match offset, match + * length) and update pointers and hashes. + */ + { + unsigned const literalLength = (unsigned)(cctx.ip - cctx.anchor); + unsigned const offset = cctx.ip - match; + unsigned const matchLength = LDM_count( + cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); + BYTE *token = cctx.op++; + + cctx.stats.totalLiteralLength += literalLength; + cctx.stats.totalOffset += offset; + cctx.stats.totalMatchLength += matchLength + MINMATCH; + + /* Encode the literal length. */ + if (literalLength >= RUN_MASK) { + int len = (int)literalLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) { + *(cctx.op)++ = 255; + } + *(cctx.op)++ = (BYTE)len; + } else { + *token = (BYTE)(literalLength << ML_BITS); + } + + /* Encode the literals. */ + memcpy(cctx.op, cctx.anchor, literalLength); + cctx.op += literalLength; + + /* Encode the offset. */ + LDM_write32(cctx.op, offset); + cctx.op += LDM_OFFSET_SIZE; + + /* Encode match length */ + if (matchLength >= ML_MASK) { + unsigned matchLengthRemaining = matchLength; + *token += ML_MASK; + matchLengthRemaining -= ML_MASK; + LDM_write32(cctx.op, 0xFFFFFFFF); + while (matchLengthRemaining >= 4*0xFF) { + cctx.op += 4; + LDM_write32(cctx.op, 0xffffffff); + matchLengthRemaining -= 4*0xFF; + } + cctx.op += matchLengthRemaining / 255; + *(cctx.op)++ = (BYTE)(matchLengthRemaining % 255); + } else { + *token += (BYTE)(matchLength); + } + + /* Update input pointer, inserting hashes into hash table along the + * way. + */ + while (cctx.ip < cctx.anchor + MINMATCH + matchLength + literalLength) { + LDM_putHashOfCurrentPosition(&cctx); + cctx.ip++; + } + } + + // Set start of next block to current input pointer. + cctx.anchor = cctx.ip; + LDM_putHashOfCurrentPosition(&cctx); + cctx.nextHash = LDM_hashPosition(++cctx.ip); + } +_last_literals: + /* Encode the last literals (no more matches). */ + { + size_t const lastRun = (size_t)(cctx.iend - cctx.anchor); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *(cctx.op)++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255; accumulator -= 255) { + *(cctx.op)++ = 255; + } + *(cctx.op)++ = (BYTE)accumulator; + } else { + *(cctx.op)++ = (BYTE)(lastRun << ML_BITS); + } + memcpy(cctx.op, cctx.anchor, lastRun); + cctx.op += lastRun; + } + LDM_printCompressStats(&cctx.stats); + return (cctx.op - (const BYTE *)cctx.obase); +} + +typedef struct LDM_DCtx { + size_t compressSize; + size_t maxDecompressSize; + + 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 */ +} LDM_DCtx; + +static void LDM_initializeDCtx(LDM_DCtx *dctx, + const void *src, size_t compressSize, + void *dst, size_t maxDecompressSize) { + dctx->compressSize = compressSize; + dctx->maxDecompressSize = maxDecompressSize; + + dctx->ibase = src; + dctx->ip = (const BYTE *)src; + dctx->iend = dctx->ip + dctx->compressSize; + dctx->op = dst; + dctx->oend = dctx->op + dctx->maxDecompressSize; + +} + +size_t LDM_decompress(const void *src, size_t compressSize, + void *dst, size_t maxDecompressSize) { + LDM_DCtx dctx; + LDM_initializeDCtx(&dctx, src, compressSize, dst, maxDecompressSize); + + while (dctx.ip < dctx.iend) { + BYTE *cpy; + const BYTE *match; + size_t length, offset; + + /* Get the literal length. */ + unsigned const token = *(dctx.ip)++; + if ((length = (token >> ML_BITS)) == RUN_MASK) { + unsigned s; + do { + s = *(dctx.ip)++; + length += s; + } while (s == 255); + } + + /* Copy literals. */ + cpy = dctx.op + length; + memcpy(dctx.op, dctx.ip, length); + dctx.ip += length; + dctx.op = cpy; + + //TODO : dynamic offset size + offset = LDM_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 += MINMATCH; + + /* Copy match. */ + cpy = dctx.op + length; + + // Inefficient for now + while (match < cpy - offset && dctx.op < dctx.oend) { + *(dctx.op)++ = *match++; + } + } + return dctx.op - (BYTE *)dst; +} + + diff --git a/contrib/long_distance_matching/versions/v3/ldm.h b/contrib/long_distance_matching/versions/v3/ldm.h new file mode 100644 index 000000000..287d444dd --- /dev/null +++ b/contrib/long_distance_matching/versions/v3/ldm.h @@ -0,0 +1,19 @@ +#ifndef LDM_H +#define LDM_H + +#include /* size_t */ + +#define LDM_COMPRESS_SIZE 4 +#define LDM_DECOMPRESS_SIZE 4 +#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) + +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +size_t LDM_decompress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +void LDM_readHeader(const void *src, size_t *compressSize, + size_t *decompressSize); + +#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v3/main-ldm.c b/contrib/long_distance_matching/versions/v3/main-ldm.c new file mode 100644 index 000000000..724d735dd --- /dev/null +++ b/contrib/long_distance_matching/versions/v3/main-ldm.c @@ -0,0 +1,479 @@ +// TODO: file size must fit into a U32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ldm.h" + +// #define BUF_SIZE 16*1024 // Block size +#define DEBUG + +//#define ZSTD + +/* Compress file given by fname and output to oname. + * Returns 0 if successful, error code otherwise. + */ +static int compress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + size_t maxCompressSize, compressSize; + + /* 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; + } + + maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; + + /* Go to the location corresponding to the last byte. */ + /* TODO: fallocate? */ + if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* Write a dummy byte at the last location. */ + if (write(fdout, "", 1) != 1) { + perror("write 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; + } + + /* mmap the output file */ + if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + +#ifdef ZSTD + compressSize = ZSTD_compress(dst, statbuf.st_size, + src, statbuf.st_size, 1); +#else + compressSize = LDM_HEADER_SIZE + + LDM_compress(src, statbuf.st_size, + dst + LDM_HEADER_SIZE, statbuf.st_size); + + // Write compress and decompress size to header + // TODO: should depend on LDM_DECOMPRESS_SIZE write32 + memcpy(dst, &compressSize, 4); + memcpy(dst + 4, &(statbuf.st_size), 4); + +#ifdef DEBUG + printf("Compressed size: %zu\n", compressSize); + printf("Decompressed size: %zu\n", (size_t)statbuf.st_size); +#endif +#endif + + // Truncate file to compressSize. + ftruncate(fdout, compressSize); + + printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, + (unsigned)statbuf.st_size, (unsigned)compressSize, oname, + (double)compressSize / (statbuf.st_size) * 100); + + // Close files. + close(fdin); + close(fdout); + return 0; +} + +/* 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; + size_t compressSize, decompressSize, 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, &compressSize, &decompressSize); + +#ifdef DEBUG + printf("Size, compressSize, decompressSize: %zu %zu %zu\n", + (size_t)statbuf.st_size, compressSize, decompressSize); +#endif + + /* Go to the location corresponding to the last byte. */ + if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write error"); + return 1; + } + + /* mmap the output file */ + if ((dst = mmap(0, decompressSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + +#ifdef ZSTD + outSize = ZSTD_decompress(dst, decomrpessed_size, + src + LDM_HEADER_SIZE, + statbuf.st_size - LDM_HEADER_SIZE); +#else + outSize = LDM_decompress( + src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, + dst, decompressSize); + + printf("Ret size out: %zu\n", outSize); + #endif + ftruncate(fdout, 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 void verify(const char *inpFilename, const char *decFilename) { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + } + + fclose(decFp); + fclose(inpFp); +} + +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 */ + { + struct timeval tv1, tv2; + gettimeofday(&tv1, NULL); + if (compress(inpFilename, ldmFilename)) { + printf("Compress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + } + + /* Decompress */ + { + struct timeval tv1, tv2; + gettimeofday(&tv1, NULL); + if (decompress(ldmFilename, decFilename)) { + printf("Decompress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + } + /* verify */ + verify(inpFilename, decFilename); + return 0; +} + + +#if 0 +static size_t compress_file(FILE *in, FILE *out, size_t *size_in, + size_t *size_out) { + char *src, *buf = NULL; + size_t r = 1; + size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; + + src = malloc(BUF_SIZE); + if (!src) { + printf("Not enough memory\n"); + goto cleanup; + } + + size = BUF_SIZE + LDM_HEADER_SIZE; + buf = malloc(size); + if (!buf) { + printf("Not enough memory\n"); + goto cleanup; + } + + + for (;;) { + k = fread(src, 1, BUF_SIZE, in); + if (k == 0) + break; + count_in += k; + + n = LDM_compress(src, buf, k, BUF_SIZE); + + // n = k; + // offset += n; + offset = k; + count_out += k; + +// k = fwrite(src, 1, offset, out); + + k = fwrite(buf, 1, offset, out); + if (k < offset) { + if (ferror(out)) + printf("Write failed\n"); + else + printf("Short write\n"); + goto cleanup; + } + + } + *size_in = count_in; + *size_out = count_out; + r = 0; + cleanup: + free(src); + free(buf); + return r; +} + +static size_t decompress_file(FILE *in, FILE *out) { + void *src = malloc(BUF_SIZE); + void *dst = NULL; + size_t dst_capacity = BUF_SIZE; + size_t ret = 1; + size_t bytes_written = 0; + + if (!src) { + perror("decompress_file(src)"); + goto cleanup; + } + + while (ret != 0) { + /* Load more input */ + size_t src_size = fread(src, 1, BUF_SIZE, in); + void *src_ptr = src; + void *src_end = src_ptr + src_size; + if (src_size == 0 || ferror(in)) { + printf("(TODO): Decompress: not enough input or error reading file\n"); + //TODO + ret = 0; + goto cleanup; + } + + /* Allocate destination buffer if it hasn't been allocated already */ + if (!dst) { + dst = malloc(dst_capacity); + if (!dst) { + perror("decompress_file(dst)"); + goto cleanup; + } + } + + // TODO + + /* Decompress: + * Continue while there is more input to read. + */ + while (src_ptr != src_end && ret != 0) { + // size_t dst_size = src_size; + size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); + size_t written = fwrite(dst, 1, dst_size, out); +// printf("Writing %zu bytes\n", dst_size); + bytes_written += dst_size; + if (written != dst_size) { + printf("Decompress: Failed to write to file\n"); + goto cleanup; + } + src_ptr += src_size; + src_size = src_end - src_ptr; + } + + /* Update input */ + + } + + printf("Wrote %zu bytes\n", bytes_written); + + cleanup: + free(src); + free(dst); + + return ret; +} + +int main2(int argc, char *argv[]) { + char inpFilename[256] = { 0 }; + char ldmFilename[256] = { 0 }; + char decFilename[256] = { 0 }; + + if (argc < 2) { + printf("Please specify input filename\n"); + return 0; + } + 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 */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *outFp = fopen(ldmFilename, "wb"); + size_t sizeIn = 0; + size_t sizeOut = 0; + size_t ret; + printf("compress : %s -> %s\n", inpFilename, ldmFilename); + ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); + if (ret) { + printf("compress : failed with code %zu\n", ret); + return ret; + } + printf("%s: %zu → %zu bytes, %.1f%%\n", + inpFilename, sizeIn, sizeOut, + (double)sizeOut / sizeIn * 100); + printf("compress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* decompress */ + { + FILE *inpFp = fopen(ldmFilename, "rb"); + FILE *outFp = fopen(decFilename, "wb"); + size_t ret; + + printf("decompress : %s -> %s\n", ldmFilename, decFilename); + ret = decompress_file(inpFp, outFp); + if (ret) { + printf("decompress : failed with code %zu\n", ret); + return ret; + } + printf("decompress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* verify */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); + } + return 0; +} +#endif + diff --git a/contrib/long_distance_matching/versions/v3/util.c b/contrib/long_distance_matching/versions/v3/util.c new file mode 100644 index 000000000..9ea4ca1e5 --- /dev/null +++ b/contrib/long_distance_matching/versions/v3/util.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +#include "util.h" + +typedef uint8_t BYTE; +typedef uint16_t U16; +typedef uint32_t U32; +typedef int32_t S32; +typedef uint64_t U64; + +unsigned LDM_isLittleEndian(void) { + const union { U32 u; BYTE c[4]; } one = { 1 }; + return one.c[0]; +} + +U16 LDM_read16(const void *memPtr) { + U16 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +U16 LDM_readLE16(const void *memPtr) { + if (LDM_isLittleEndian()) { + return LDM_read16(memPtr); + } else { + const BYTE *p = (const BYTE *)memPtr; + return (U16)((U16)p[0] + (p[1] << 8)); + } +} + +void LDM_write16(void *memPtr, U16 value){ + memcpy(memPtr, &value, sizeof(value)); +} + +void LDM_write32(void *memPtr, U32 value) { + memcpy(memPtr, &value, sizeof(value)); +} + +void LDM_writeLE16(void *memPtr, U16 value) { + if (LDM_isLittleEndian()) { + LDM_write16(memPtr, value); + } else { + BYTE* p = (BYTE *)memPtr; + p[0] = (BYTE) value; + p[1] = (BYTE)(value>>8); + } +} + +U32 LDM_read32(const void *ptr) { + return *(const U32 *)ptr; +} + +U64 LDM_read64(const void *ptr) { + return *(const U64 *)ptr; +} + +void LDM_copy8(void *dst, const void *src) { + memcpy(dst, src, 8); +} + + diff --git a/contrib/long_distance_matching/versions/v3/util.h b/contrib/long_distance_matching/versions/v3/util.h new file mode 100644 index 000000000..90726412e --- /dev/null +++ b/contrib/long_distance_matching/versions/v3/util.h @@ -0,0 +1,23 @@ +#ifndef LDM_UTIL_H +#define LDM_UTIL_H + +unsigned LDM_isLittleEndian(void); + +uint16_t LDM_read16(const void *memPtr); + +uint16_t LDM_readLE16(const void *memPtr); + +void LDM_write16(void *memPtr, uint16_t value); + +void LDM_write32(void *memPtr, uint32_t value); + +void LDM_writeLE16(void *memPtr, uint16_t value); + +uint32_t LDM_read32(const void *ptr); + +uint64_t LDM_read64(const void *ptr); + +void LDM_copy8(void *dst, const void *src); + + +#endif /* LDM_UTIL_H */ From 7ec5928626b74f9f91b3de09026584ab41b7d4ad Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 10:23:25 -0700 Subject: [PATCH 101/318] fixed an error where -c argument wasn't working for single files --- contrib/adaptive-compression/adapt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 2bf8bb81e..e03bcc8e1 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -2,7 +2,7 @@ #define PRINT(...) fprintf(stdout, __VA_ARGS__) #define DEBUG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 -#define MAX_NUM_JOBS 2; +#define MAX_NUM_JOBS 2 #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 @@ -612,6 +612,7 @@ int main(int argCount, const char* argv[]) break; case 'c': forceStdout = 1; + outFilename = stdoutmark; break; default: DISPLAY("Error: invalid argument provided\n"); From c325c8db82808801a3586694327c7e64bff585f7 Mon Sep 17 00:00:00 2001 From: Jacques Germishuys Date: Tue, 11 Jul 2017 19:25:14 +0200 Subject: [PATCH 102/318] fix missing symbol 'nanosleep' for Solaris --- build/cmake/programs/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/cmake/programs/CMakeLists.txt b/build/cmake/programs/CMakeLists.txt index 98ca6cca0..13dd31572 100644 --- a/build/cmake/programs/CMakeLists.txt +++ b/build/cmake/programs/CMakeLists.txt @@ -31,6 +31,9 @@ ENDIF (MSVC) ADD_EXECUTABLE(zstd ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/fileio.c ${PROGRAMS_DIR}/bench.c ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/dibio.c ${PlatformDependResources}) TARGET_LINK_LIBRARIES(zstd libzstd_static) +IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + TARGET_LINK_LIBRARIES(zstd rt) +ENDIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") INSTALL(TARGETS zstd RUNTIME DESTINATION "bin") IF (UNIX) From 34afb9b23e0df74e8a9261d8e7e05b8f762d5738 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 11:50:00 -0700 Subject: [PATCH 103/318] changed to using ZSTD_compressBegin_usingDict() and fixed strange issue with ZSTD_compressContinue() --- contrib/adaptive-compression/adapt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index e03bcc8e1..9a527ff5e 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -252,13 +252,12 @@ static void* compressionThread(void* arg) /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); - ZSTD_parameters params = ZSTD_getParams(cLevel, job->src.size, 0); DEBUG(2, "cLevel used: %u\n", cLevel); /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_advanced(ctx->cctx, job->dict.start, job->dict.size, params, 0); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->dict.start, job->dict.size, cLevel); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); @@ -269,7 +268,7 @@ static void* compressionThread(void* arg) /* continue compression */ if (currJob != 0) { /* not first job */ - size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, 0); + size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size); if (ZSTD_isError(hSize)) { job->compressedSize = hSize; ctx->threadError = 1; From 16261e6951910e56f0c447f012900d7e06aba733 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 11 Jul 2017 14:14:07 -0700 Subject: [PATCH 104/318] buffer pool can be invoked from multiple threads --- lib/common/pool.c | 20 +++++++++--------- lib/common/pool.h | 10 ++++----- lib/compress/zstdmt_compress.c | 38 ++++++++++++++++++++++------------ 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index 749fa4f2f..06d8a5f57 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -92,7 +92,7 @@ POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { * and full queues. */ ctx->queueSize = queueSize + 1; - ctx->queue = (POOL_job *)malloc(ctx->queueSize * sizeof(POOL_job)); + ctx->queue = (POOL_job*) malloc(ctx->queueSize * sizeof(POOL_job)); ctx->queueHead = 0; ctx->queueTail = 0; pthread_mutex_init(&ctx->queueMutex, NULL); @@ -100,7 +100,7 @@ POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { pthread_cond_init(&ctx->queuePopCond, NULL); ctx->shutdown = 0; /* Allocate space for the thread handles */ - ctx->threads = (pthread_t *)malloc(numThreads * sizeof(pthread_t)); + ctx->threads = (pthread_t*)malloc(numThreads * sizeof(pthread_t)); ctx->numThreads = 0; /* Check for errors */ if (!ctx->threads || !ctx->queue) { POOL_free(ctx); return NULL; } @@ -153,8 +153,8 @@ size_t POOL_sizeof(POOL_ctx *ctx) { + ctx->numThreads * sizeof(pthread_t); } -void POOL_add(void *ctxVoid, POOL_function function, void *opaque) { - POOL_ctx *ctx = (POOL_ctx *)ctxVoid; +void POOL_add(void* ctxVoid, POOL_function function, void *opaque) { + POOL_ctx* const ctx = (POOL_ctx*)ctxVoid; if (!ctx) { return; } pthread_mutex_lock(&ctx->queueMutex); @@ -183,22 +183,22 @@ struct POOL_ctx_s { int data; }; -POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { +POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) { (void)numThreads; (void)queueSize; - return (POOL_ctx *)malloc(sizeof(POOL_ctx)); + return (POOL_ctx*)malloc(sizeof(POOL_ctx)); } -void POOL_free(POOL_ctx *ctx) { - if (ctx) free(ctx); +void POOL_free(POOL_ctx* ctx) { + free(ctx); } -void POOL_add(void *ctx, POOL_function function, void *opaque) { +void POOL_add(void* ctx, POOL_function function, void* opaque) { (void)ctx; function(opaque); } -size_t POOL_sizeof(POOL_ctx *ctx) { +size_t POOL_sizeof(POOL_ctx* ctx) { if (ctx==NULL) return 0; /* supports sizeof NULL */ return sizeof(*ctx); } diff --git a/lib/common/pool.h b/lib/common/pool.h index 386cd674b..957100f46 100644 --- a/lib/common/pool.h +++ b/lib/common/pool.h @@ -19,11 +19,11 @@ extern "C" { typedef struct POOL_ctx_s POOL_ctx; /*! POOL_create() : - Create a thread pool with at most `numThreads` threads. - `numThreads` must be at least 1. - The maximum number of queued jobs before blocking is `queueSize`. - `queueSize` must be at least 1. - @return : The POOL_ctx pointer on success else NULL. + * Create a thread pool with at most `numThreads` threads. + * `numThreads` must be at least 1. + * The maximum number of queued jobs before blocking is `queueSize`. + * `queueSize` must be at least 1. + * @return : POOL_ctx pointer on success, else NULL. */ POOL_ctx *POOL_create(size_t numThreads, size_t queueSize); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 255b9c7ed..c4547c81b 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -73,6 +73,7 @@ static unsigned long long GetCurrentClockTimeMicroseconds(void) /* ===== Buffer Pool ===== */ +/* a single Buffer Pool can be invoked from multiple threads in parallel */ typedef struct buffer_s { void* start; @@ -82,6 +83,7 @@ typedef struct buffer_s { static const buffer_t g_nullBuffer = { NULL, 0 }; typedef struct ZSTDMT_bufferPool_s { + pthread_mutex_t poolMutex; unsigned totalBuffers; unsigned nbBuffers; ZSTD_customMem cMem; @@ -94,6 +96,7 @@ static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbThreads, ZSTD_custo ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)ZSTD_calloc( sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t), cMem); if (bufPool==NULL) return NULL; + pthread_mutex_init(&bufPool->poolMutex, NULL); bufPool->totalBuffers = maxNbBuffers; bufPool->nbBuffers = 0; bufPool->cMem = cMem; @@ -106,6 +109,7 @@ static void ZSTDMT_freeBufferPool(ZSTDMT_bufferPool* bufPool) if (!bufPool) return; /* compatibility with free on NULL */ for (u=0; utotalBuffers; u++) ZSTD_free(bufPool->bTable[u].start, bufPool->cMem); + pthread_mutex_destroy(&bufPool->poolMutex); ZSTD_free(bufPool, bufPool->cMem); } @@ -116,31 +120,37 @@ static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool) + (bufPool->totalBuffers - 1) * sizeof(buffer_t); unsigned u; size_t totalBufferSize = 0; + pthread_mutex_lock(&bufPool->poolMutex); for (u=0; utotalBuffers; u++) totalBufferSize += bufPool->bTable[u].size; + pthread_mutex_unlock(&bufPool->poolMutex); return poolSize + totalBufferSize; } /** ZSTDMT_getBuffer() : * assumption : invocation from main thread only ! */ -static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) +static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool, size_t bSize) { DEBUGLOG(2, "ZSTDMT_getBuffer"); - if (pool->nbBuffers) { /* try to use an existing buffer */ - buffer_t const buf = pool->bTable[--(pool->nbBuffers)]; + 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; - if ((availBufferSize >= bSize) & (availBufferSize <= 10*bSize)) + if ((availBufferSize >= bSize) & (availBufferSize <= 10*bSize)) { /* large enough, but not too much */ + pthread_mutex_unlock(&bufPool->poolMutex); return buf; + } /* size conditions not respected : scratch this buffer, create new one */ DEBUGLOG(2, "existing buffer does not meet size conditions => freeing"); - ZSTD_free(buf.start, pool->cMem); + ZSTD_free(buf.start, bufPool->cMem); } + pthread_mutex_unlock(&bufPool->poolMutex); /* create new buffer */ DEBUGLOG(2, "create a new buffer"); { buffer_t buffer; - void* const start = ZSTD_malloc(bSize, pool->cMem); + void* const start = ZSTD_malloc(bSize, bufPool->cMem); if (start==NULL) bSize = 0; buffer.start = start; /* note : start can be NULL if malloc fails ! */ buffer.size = bSize; @@ -149,23 +159,25 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) } /* store buffer for later re-use, up to pool capacity */ -static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf) +static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf) { DEBUGLOG(2, "ZSTDMT_releaseBuffer"); if (buf.start == NULL) return; /* release on NULL */ - if (pool->nbBuffers < pool->totalBuffers) { - pool->bTable[pool->nbBuffers++] = buf; /* store for later re-use */ + pthread_mutex_lock(&bufPool->poolMutex); + if (bufPool->nbBuffers < bufPool->totalBuffers) { + bufPool->bTable[bufPool->nbBuffers++] = buf; /* store for later re-use */ + pthread_mutex_unlock(&bufPool->poolMutex); return; } + pthread_mutex_unlock(&bufPool->poolMutex); /* Reached bufferPool capacity (should not happen) */ DEBUGLOG(2, "buffer pool capacity reached => freeing "); - ZSTD_free(buf.start, pool->cMem); + ZSTD_free(buf.start, bufPool->cMem); } /* ===== CCtx Pool ===== */ - -/* a single cctxPool can be called from multiple threads in parallel */ +/* a single CCtx Pool can be invoked from multiple threads in parallel */ typedef struct { pthread_mutex_t poolMutex; @@ -314,7 +326,7 @@ void ZSTDMT_compressChunk(void* jobDescription) 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)", + DEBUGLOG(2, "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)); From 34b2b956314581f2e191697ce3b18d170cfe3d42 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 11 Jul 2017 14:59:10 -0700 Subject: [PATCH 105/318] zstdmt : intermediate outBuffer allocated from within worker reduces total amount of memory needed, since jobs in queue do not have an outBuffer pre-reserved now --- lib/compress/zstdmt_compress.c | 81 ++++++++++++++++------------------ 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index c4547c81b..9f30d3181 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -286,6 +286,7 @@ typedef struct { ZSTD_parameters params; const ZSTD_CDict* cdict; ZSTDMT_CCtxPool* cctxPool; + ZSTDMT_bufferPool* bufPool; unsigned long long fullFrameSize; } ZSTDMT_jobDescription; @@ -295,7 +296,7 @@ void ZSTDMT_compressChunk(void* jobDescription) ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; ZSTD_CCtx* cctx = ZSTDMT_getCCtx(job->cctxPool); const void* const src = (const char*)job->srcStart + job->dictSize; - buffer_t const dstBuff = job->dstBuff; + buffer_t dstBuff = job->dstBuff; DEBUGLOG(5, "job (first:%u) (last:%u) : dictSize %u, srcSize %u", job->firstChunk, job->lastChunk, (U32)job->dictSize, (U32)job->srcSize); @@ -304,6 +305,16 @@ void ZSTDMT_compressChunk(void* jobDescription) goto _endJob; } + if (dstBuff.start == NULL) { + size_t const dstCapacity = ZSTD_compressBound(job->srcSize); + dstBuff = ZSTDMT_getBuffer(job->bufPool, dstCapacity); + if (dstBuff.start==NULL) { + job->cSize = ERROR(memory_allocation); + goto _endJob; + } + job->dstBuff = dstBuff; + } + if (job->cdict) { /* should only happen for first segment */ size_t const initError = ZSTD_compressBegin_usingCDict_advanced(cctx, job->cdict, job->params.fParams, job->fullFrameSize); DEBUGLOG(5, "using CDict"); @@ -347,7 +358,7 @@ _endJob: struct ZSTDMT_CCtx_s { POOL_ctx* factory; ZSTDMT_jobDescription* jobs; - ZSTDMT_bufferPool* buffPool; + ZSTDMT_bufferPool* bufPool; ZSTDMT_CCtxPool* cctxPool; pthread_mutex_t jobCompleted_mutex; pthread_cond_t jobCompleted_cond; @@ -402,9 +413,9 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem) mtctx->factory = POOL_create(nbThreads, 1); mtctx->jobs = ZSTDMT_allocJobsTable(&nbJobs, cMem); mtctx->jobIDMask = nbJobs - 1; - mtctx->buffPool = ZSTDMT_createBufferPool(nbThreads, cMem); + mtctx->bufPool = ZSTDMT_createBufferPool(nbThreads, cMem); mtctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads, cMem); - if (!mtctx->factory | !mtctx->jobs | !mtctx->buffPool | !mtctx->cctxPool) { + if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool) { ZSTDMT_freeCCtx(mtctx); return NULL; } @@ -426,13 +437,13 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx) unsigned jobID; DEBUGLOG(3, "ZSTDMT_releaseAllJobResources"); for (jobID=0; jobID <= mtctx->jobIDMask; jobID++) { - ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[jobID].dstBuff); + ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff); mtctx->jobs[jobID].dstBuff = g_nullBuffer; - ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[jobID].src); + ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].src); mtctx->jobs[jobID].src = g_nullBuffer; } memset(mtctx->jobs, 0, (mtctx->jobIDMask+1)*sizeof(ZSTDMT_jobDescription)); - ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->inBuff.buffer); + ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->inBuff.buffer); mtctx->inBuff.buffer = g_nullBuffer; mtctx->allJobsCompleted = 1; } @@ -442,7 +453,7 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) if (mtctx==NULL) return 0; /* compatible with free on NULL */ POOL_free(mtctx->factory); if (!mtctx->allJobsCompleted) ZSTDMT_releaseAllJobResources(mtctx); /* stop workers first */ - ZSTDMT_freeBufferPool(mtctx->buffPool); /* release job resources into pools first */ + ZSTDMT_freeBufferPool(mtctx->bufPool); /* release job resources into pools first */ ZSTD_free(mtctx->jobs, mtctx->cMem); ZSTDMT_freeCCtxPool(mtctx->cctxPool); ZSTD_freeCDict(mtctx->cdictLocal); @@ -456,11 +467,11 @@ size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx) { if (mtctx == NULL) return 0; /* supports sizeof NULL */ return sizeof(*mtctx) - + POOL_sizeof(mtctx->factory) - + ZSTDMT_sizeof_bufferPool(mtctx->buffPool) - + (mtctx->jobIDMask+1) * sizeof(ZSTDMT_jobDescription) - + ZSTDMT_sizeof_CCtxPool(mtctx->cctxPool) - + ZSTD_sizeof_CDict(mtctx->cdictLocal); + + POOL_sizeof(mtctx->factory) + + ZSTDMT_sizeof_bufferPool(mtctx->bufPool) + + (mtctx->jobIDMask+1) * sizeof(ZSTDMT_jobDescription) + + ZSTDMT_sizeof_CCtxPool(mtctx->cctxPool) + + ZSTD_sizeof_CDict(mtctx->cdictLocal); } size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, unsigned value) @@ -534,16 +545,9 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, size_t const chunkSize = MIN(remainingSrcSize, avgChunkSize); size_t const dstBufferCapacity = ZSTD_compressBound(chunkSize); buffer_t const dstAsBuffer = { (char*)dst + dstBufferPos, dstBufferCapacity }; - buffer_t const dstBuffer = u < compressWithinDst ? dstAsBuffer : ZSTDMT_getBuffer(mtctx->buffPool, dstBufferCapacity); + buffer_t const dstBuffer = u < compressWithinDst ? dstAsBuffer : g_nullBuffer; size_t dictSize = u ? overlapSize : 0; - if (dstBuffer.start==NULL) { - mtctx->jobs[u].cSize = ERROR(memory_allocation); /* job result */ - mtctx->jobs[u].jobCompleted = 1; - nbChunks = u+1; /* only wait and free u jobs, instead of initially expected nbChunks ones */ - break; /* let's wait for previous jobs to complete, but don't start new ones */ - } - mtctx->jobs[u].srcStart = srcStart + frameStartPos - dictSize; mtctx->jobs[u].dictSize = dictSize; mtctx->jobs[u].srcSize = chunkSize; @@ -554,6 +558,7 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, 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].firstChunk = (u==0); mtctx->jobs[u].lastChunk = (u==nbChunks-1); mtctx->jobs[u].jobCompleted = 0; @@ -591,13 +596,13 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, 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->buffPool, mtctx->jobs[chunkID].dstBuff); + ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[chunkID].dstBuff); } mtctx->jobs[chunkID].dstBuff = g_nullBuffer; } dstPos += cSize ; } - } + } /* for (chunkID=0; chunkIDbuffPool, dstBufferCapacity); unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; - if (dstBuffer.start==NULL) { - zcs->jobs[jobID].jobCompleted = 1; - zcs->nextJobID++; - ZSTDMT_waitForAllJobsCompleted(zcs); - ZSTDMT_releaseAllJobResources(zcs); - return ERROR(memory_allocation); - } - DEBUGLOG(4, "preparing job %u to compress %u bytes with %u preload ", zcs->nextJobID, (U32)srcSize, (U32)zcs->dictSize); zcs->jobs[jobID].src = zcs->inBuff.buffer; @@ -757,8 +753,9 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi 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 = dstBuffer; + 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; @@ -770,7 +767,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi if (!endFrame) { size_t const newDictSize = MIN(srcSize + zcs->dictSize, zcs->targetDictSize); DEBUGLOG(5, "ZSTDMT_createCompressionJob::endFrame = %u", endFrame); - zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); + zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->bufPool, zcs->inBuffSize); if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */ zcs->jobs[jobID].jobCompleted = 1; zcs->nextJobID++; @@ -845,19 +842,19 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi job.cSize += 4; zcs->jobs[wJobID].cSize += 4; } } - ZSTDMT_releaseBuffer(zcs->buffPool, job.src); + ZSTDMT_releaseBuffer(zcs->bufPool, job.src); zcs->jobs[wJobID].srcStart = NULL; zcs->jobs[wJobID].src = g_nullBuffer; 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(2, "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 */ - ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); + ZSTDMT_releaseBuffer(zcs->bufPool, job.dstBuff); zcs->jobs[wJobID].dstBuff = g_nullBuffer; zcs->jobs[wJobID].jobCompleted = 0; zcs->doneJobID++; @@ -904,7 +901,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, if (ZSTD_isError(cSize)) return cSize; input->pos = input->size; output->pos += cSize; - ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->inBuff.buffer); /* was allocated in initStream */ + ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->inBuff.buffer); /* was allocated in initStream */ mtctx->allJobsCompleted = 1; mtctx->frameEnded = 1; return 0; @@ -913,7 +910,7 @@ 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->buffPool, mtctx->inBuffSize); + mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool, mtctx->inBuffSize); if (mtctx->inBuff.buffer.start == NULL) return ERROR(memory_allocation); mtctx->inBuff.filled = 0; } From a3c077b8c6a88e5c1f3bcfe61cc010c3f1721760 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 15:00:52 -0700 Subject: [PATCH 106/318] added error message, updated copying dictionary into the input buffer --- contrib/adaptive-compression/adapt.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 9a527ff5e..2bd1c4f95 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -270,6 +270,7 @@ static void* compressionThread(void* arg) if (currJob != 0) { /* not first job */ size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size); if (ZSTD_isError(hSize)) { + DISPLAY("Error: something went wrong while continuing compression\n"); job->compressedSize = hSize; ctx->threadError = 1; return arg; @@ -410,8 +411,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) /* if not on the last job, reuse data as dictionary in next job */ if (!last) { - ctx->input.filled = srcSize; - memmove(ctx->input.buffer.start, ctx->input.buffer.start + ctx->input.filled, srcSize); + size_t const newDictSize = srcSize; + size_t const oldDictSize = ctx->input.filled; + memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize + srcSize - newDictSize, newDictSize); + ctx->input.filled = newDictSize; } return 0; } From 7c54e09347e47807b5c037cb5d90441560dc80ff Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 15:15:41 -0700 Subject: [PATCH 107/318] updated DEBUG statements --- contrib/adaptive-compression/adapt.c | 48 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 2bd1c4f95..89a20a50a 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -210,17 +210,17 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; - DEBUG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); + DEBUG(3, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; } else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUG(2, "increasing compression level %u\n", ctx->compressionLevel); + DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); ctx->compressionLevel++; reset = 1; } else if (compressSlow && ctx->compressionLevel > 1) { - DEBUG(2, "decreasing compression level %u\n", ctx->compressionLevel); + DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); ctx->compressionLevel--; reset = 1; } @@ -239,20 +239,20 @@ static void* compressionThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUG(2, "compressionThread(): waiting on job ready\n"); + DEBUG(3, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { ctx->stats.waitReady++; ctx->stats.readyCounter++; - DEBUG(2, "waiting on job ready, nextJob: %u\n", currJob); + DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } pthread_mutex_unlock(&ctx->jobReady_mutex); - DEBUG(2, "compressionThread(): continuing after job ready\n"); + DEBUG(3, "compressionThread(): continuing after job ready\n"); /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); - DEBUG(2, "cLevel used: %u\n", cLevel); + DEBUG(3, "cLevel used: %u\n", cLevel); /* begin compression */ { @@ -288,14 +288,14 @@ static void* compressionThread(void* arg) } pthread_mutex_lock(&ctx->jobCompressed_mutex); ctx->jobCompressedID++; - DEBUG(2, "signaling for job %u\n", currJob); + DEBUG(3, "signaling for job %u\n", currJob); pthread_cond_signal(&ctx->jobCompressed_cond); pthread_mutex_unlock(&ctx->jobCompressed_mutex); - DEBUG(2, "finished job compression %u\n", currJob); + DEBUG(3, "finished job compression %u\n", currJob); currJob++; if (job->lastJob || ctx->threadError) { /* finished compressing all jobs */ - DEBUG(2, "all jobs finished compressing\n"); + DEBUG(3, "all jobs finished compressing\n"); break; } } @@ -327,16 +327,16 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUG(2, "outputThread(): waiting on job compressed\n"); + DEBUG(3, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex); while (currJob + 1 > ctx->jobCompressedID) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; - DEBUG(2, "waiting on job compressed, nextJob: %u\n", currJob); + DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond, &ctx->jobCompressed_mutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex); - DEBUG(2, "outputThread(): continuing after job compressed\n"); + DEBUG(3, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { @@ -353,19 +353,19 @@ static void* outputThread(void* arg) } } } - DEBUG(2, "finished job write %u\n", currJob); + DEBUG(3, "finished job write %u\n", currJob); currJob++; displayProgress(currJob, ctx->compressionLevel, job->lastJob); - DEBUG(2, "locking job write mutex\n"); + DEBUG(3, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); ctx->jobWriteID++; pthread_cond_signal(&ctx->jobWrite_cond); pthread_mutex_unlock(&ctx->jobWrite_mutex); - DEBUG(2, "unlocking job write mutex\n"); + DEBUG(3, "unlocking job write mutex\n"); if (job->lastJob || ctx->threadError) { /* finished with all jobs */ - DEBUG(2, "all jobs finished writing\n"); + DEBUG(3, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex); ctx->allJobsCompleted = 1; pthread_cond_signal(&ctx->allJobsCompleted_cond); @@ -381,17 +381,17 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; - DEBUG(2, "createCompressionJob(): wait for job write\n"); + DEBUG(3, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - DEBUG(2, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); + DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; - DEBUG(2, "waiting on job Write, nextJob: %u\n", nextJob); + DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); - DEBUG(2, "createCompressionJob(): continuing after job write\n"); + DEBUG(3, "createCompressionJob(): continuing after job write\n"); job->compressionLevel = ctx->compressionLevel; @@ -406,7 +406,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); pthread_mutex_unlock(&ctx->jobReady_mutex); - DEBUG(2, "finished job creation %u\n", nextJob); + DEBUG(3, "finished job creation %u\n", nextJob); ctx->nextJobID++; /* if not on the last job, reuse data as dictionary in next job */ @@ -504,7 +504,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst } } if (feof(srcFile)) { - DEBUG(2, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); + DEBUG(3, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); break; } } @@ -601,7 +601,7 @@ int main(int argCount, const char* argv[]) case 'i': argument += 2; g_compressionLevel = readU32FromChar(&argument); - DEBUG(2, "g_compressionLevel: %u\n", g_compressionLevel); + DEBUG(3, "g_compressionLevel: %u\n", g_compressionLevel); break; case 's': g_displayStats = 1; From 57236184afb9dad253bee28d9a34f31f0491dca7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 11 Jul 2017 15:17:25 -0700 Subject: [PATCH 108/318] buffer pool : all buffers have same size to reduce memory fragmentation. They can be used for in or out, interchangeably. --- lib/compress/zstdmt_compress.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 9f30d3181..703d25e30 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -84,6 +84,7 @@ static const buffer_t g_nullBuffer = { NULL, 0 }; typedef struct ZSTDMT_bufferPool_s { pthread_mutex_t poolMutex; + size_t bufferSize; unsigned totalBuffers; unsigned nbBuffers; ZSTD_customMem cMem; @@ -97,6 +98,7 @@ static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbThreads, ZSTD_custo sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t), cMem); if (bufPool==NULL) return NULL; pthread_mutex_init(&bufPool->poolMutex, NULL); + bufPool->bufferSize = 64 KB; bufPool->totalBuffers = maxNbBuffers; bufPool->nbBuffers = 0; bufPool->cMem = cMem; @@ -128,10 +130,16 @@ static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool) return poolSize + totalBufferSize; } -/** ZSTDMT_getBuffer() : - * assumption : invocation from main thread only ! */ -static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool, size_t bSize) +static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* bufPool, size_t bSize) { + bufPool->bufferSize = bSize; +} + +/** ZSTDMT_getBuffer() : + * assumption : bufPool must be valid */ +static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool) +{ + size_t const bSize = bufPool->bufferSize; DEBUGLOG(2, "ZSTDMT_getBuffer"); pthread_mutex_lock(&bufPool->poolMutex); if (bufPool->nbBuffers) { /* try to use an existing buffer */ @@ -151,9 +159,8 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool, size_t bSize) DEBUGLOG(2, "create a new buffer"); { buffer_t buffer; void* const start = ZSTD_malloc(bSize, bufPool->cMem); - if (start==NULL) bSize = 0; buffer.start = start; /* note : start can be NULL if malloc fails ! */ - buffer.size = bSize; + buffer.size = (start==NULL) ? 0 : bSize; return buffer; } } @@ -306,8 +313,7 @@ void ZSTDMT_compressChunk(void* jobDescription) } if (dstBuff.start == NULL) { - size_t const dstCapacity = ZSTD_compressBound(job->srcSize); - dstBuff = ZSTDMT_getBuffer(job->bufPool, dstCapacity); + dstBuff = ZSTDMT_getBuffer(job->bufPool); if (dstBuff.start==NULL) { job->cSize = ERROR(memory_allocation); goto _endJob; @@ -530,6 +536,7 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, return ZSTD_compress_advanced(cctx, dst, dstCapacity, src, srcSize, NULL, 0, params); } assert(avgChunkSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), which is useful to avoid allocating extra buffers */ + ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgChunkSize) ); if (nbChunks > mtctx->jobIDMask+1) { /* enlarge job table */ U32 nbJobs = nbChunks; @@ -690,6 +697,7 @@ size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, zcs->targetSectionSize = MAX(zcs->targetDictSize, zcs->targetSectionSize); DEBUGLOG(4, "Section Size : %u KB", (U32)(zcs->targetSectionSize>>10)); zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize; + ZSTDMT_setBufferSize(zcs->bufPool, MAX(zcs->inBuffSize, ZSTD_compressBound(zcs->targetSectionSize)) ); zcs->inBuff.buffer = g_nullBuffer; zcs->dictSize = 0; zcs->doneJobID = 0; @@ -767,7 +775,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi if (!endFrame) { size_t const newDictSize = MIN(srcSize + zcs->dictSize, zcs->targetDictSize); DEBUGLOG(5, "ZSTDMT_createCompressionJob::endFrame = %u", endFrame); - zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->bufPool, zcs->inBuffSize); + 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++; @@ -910,7 +918,7 @@ 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, mtctx->inBuffSize); + mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool); if (mtctx->inBuff.buffer.start == NULL) return ERROR(memory_allocation); mtctx->inBuff.filled = 0; } From 72a183efad16ace8a3fb48e9fc40ff6df56135c9 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 15:49:52 -0700 Subject: [PATCH 109/318] changed dictionary size, added debugging statements --- contrib/adaptive-compression/adapt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 89a20a50a..ad3d7f7a6 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -253,7 +253,7 @@ static void* compressionThread(void* arg) { unsigned const cLevel = adaptCompressionLevel(ctx); DEBUG(3, "cLevel used: %u\n", cLevel); - + DEBUG(2, "dictSize: %zu, srcSize: %zu\n", job->dict.size, job->src.size); /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); @@ -408,10 +408,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_unlock(&ctx->jobReady_mutex); DEBUG(3, "finished job creation %u\n", nextJob); ctx->nextJobID++; - + DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); /* if not on the last job, reuse data as dictionary in next job */ if (!last) { - size_t const newDictSize = srcSize; + size_t const newDictSize = srcSize/16; size_t const oldDictSize = ctx->input.filled; memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize + srcSize - newDictSize, newDictSize); ctx->input.filled = newDictSize; From 2a62f48bf4df42f3ca389ee891ff76da56623802 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 11 Jul 2017 15:56:40 -0700 Subject: [PATCH 110/318] release input buffers from inside worker thread buffers are released sooner, which makes them available faster for next job. => decreases total nb of buffers necessary --- lib/compress/zstdmt_compress.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 703d25e30..7cf637f59 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -169,7 +169,7 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool) static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf) { DEBUGLOG(2, "ZSTDMT_releaseBuffer"); - if (buf.start == NULL) return; /* release on NULL */ + if (buf.start == NULL) return; /* compatible with release on NULL */ pthread_mutex_lock(&bufPool->poolMutex); if (bufPool->nbBuffers < bufPool->totalBuffers) { bufPool->bTable[bufPool->nbBuffers++] = buf; /* store for later re-use */ @@ -271,16 +271,11 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) /* ===== Thread worker ===== */ -typedef struct { - buffer_t buffer; - size_t filled; -} inBuff_t; - typedef struct { buffer_t src; const void* srcStart; - size_t srcSize; size_t dictSize; + size_t srcSize; buffer_t dstBuff; size_t cSize; size_t dstFlushed; @@ -349,6 +344,8 @@ void ZSTDMT_compressChunk(void* jobDescription) _endJob: ZSTDMT_releaseCCtx(job->cctxPool, cctx); + ZSTDMT_releaseBuffer(job->bufPool, job->src); + job->src = g_nullBuffer; job->srcStart = NULL; PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); job->jobCompleted = 1; job->jobScanned = 0; @@ -361,6 +358,11 @@ _endJob: /* ===== Multi-threaded compression ===== */ /* ------------------------------------------ */ +typedef struct { + buffer_t buffer; + size_t filled; +} inBuff_t; + struct ZSTDMT_CCtx_s { POOL_ctx* factory; ZSTDMT_jobDescription* jobs; @@ -513,6 +515,7 @@ static unsigned computeNbChunks(size_t srcSize, unsigned windowLog, unsigned nbT } +/* Note : missing checksum at the end ! */ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, @@ -555,6 +558,7 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, 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].dictSize = dictSize; mtctx->jobs[u].srcSize = chunkSize; @@ -771,10 +775,12 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi 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->dictSize, srcSize); + /* get a new buffer for next input */ if (!endFrame) { size_t const newDictSize = MIN(srcSize + zcs->dictSize, zcs->targetDictSize); - DEBUGLOG(5, "ZSTDMT_createCompressionJob::endFrame = %u", endFrame); 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; @@ -783,18 +789,12 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi ZSTDMT_releaseAllJobResources(zcs); return ERROR(memory_allocation); } - DEBUGLOG(5, "inBuff currently filled to %u", (U32)zcs->inBuff.filled); zcs->inBuff.filled -= srcSize + zcs->dictSize - newDictSize; - DEBUGLOG(5, "new job : inBuff filled to %u, with %u dict and %u src", - (U32)zcs->inBuff.filled, (U32)newDictSize, - (U32)(zcs->inBuff.filled - newDictSize)); memmove(zcs->inBuff.buffer.start, (const char*)zcs->jobs[jobID].srcStart + zcs->dictSize + srcSize - newDictSize, zcs->inBuff.filled); - DEBUGLOG(5, "new inBuff pre-filled"); zcs->dictSize = newDictSize; } else { /* if (endFrame==1) */ - DEBUGLOG(5, "ZSTDMT_createCompressionJob::endFrame = %u", endFrame); zcs->inBuff.buffer = g_nullBuffer; zcs->inBuff.filled = 0; zcs->dictSize = 0; @@ -842,7 +842,6 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi } DEBUGLOG(5, "zcs->params.fParams.checksumFlag : %u ", zcs->params.fParams.checksumFlag); if (zcs->params.fParams.checksumFlag) { - XXH64_update(&zcs->xxhState, (const char*)job.srcStart + job.dictSize, job.srcSize); 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); @@ -850,9 +849,6 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi job.cSize += 4; zcs->jobs[wJobID].cSize += 4; } } - ZSTDMT_releaseBuffer(zcs->bufPool, job.src); - zcs->jobs[wJobID].srcStart = NULL; - zcs->jobs[wJobID].src = g_nullBuffer; zcs->jobs[wJobID].jobScanned = 1; } { size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); From 0a401852c46b3ab238f20e3e775e0e1966afc05f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 16:50:50 -0700 Subject: [PATCH 111/318] added debug statement --- contrib/adaptive-compression/adapt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ad3d7f7a6..b0bcb74f2 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -254,6 +254,7 @@ static void* compressionThread(void* arg) unsigned const cLevel = adaptCompressionLevel(ctx); DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(2, "dictSize: %zu, srcSize: %zu\n", job->dict.size, job->src.size); + DEBUG(2, "compression level used: %u\n", cLevel); /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); From 052a95f77c4a7c0c13c1c94e7b805616b5924df7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 11 Jul 2017 17:18:26 -0700 Subject: [PATCH 112/318] fix : ZSTDMT_compress_advanced() correctly generates checksum when params.fParams.checksumFlag==1. This use case used to be impossible when only ZSTD_compress() was available --- lib/compress/zstdmt_compress.c | 49 +++++++++++++++++++++++----------- lib/compress/zstdmt_compress.h | 6 ++--- tests/fuzzer.c | 20 +++++++++++--- tests/zstreamtest.c | 7 +++-- 4 files changed, 56 insertions(+), 26 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 7cf637f59..3fdfe9e14 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -140,7 +140,7 @@ static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* bufPool, size_t bSize) static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool) { size_t const bSize = bufPool->bufferSize; - DEBUGLOG(2, "ZSTDMT_getBuffer"); + DEBUGLOG(5, "ZSTDMT_getBuffer"); pthread_mutex_lock(&bufPool->poolMutex); if (bufPool->nbBuffers) { /* try to use an existing buffer */ buffer_t const buf = bufPool->bTable[--(bufPool->nbBuffers)]; @@ -151,12 +151,12 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool) return buf; } /* size conditions not respected : scratch this buffer, create new one */ - DEBUGLOG(2, "existing buffer does not meet size conditions => freeing"); + DEBUGLOG(5, "existing buffer does not meet size conditions => freeing"); ZSTD_free(buf.start, bufPool->cMem); } pthread_mutex_unlock(&bufPool->poolMutex); /* create new buffer */ - DEBUGLOG(2, "create a new buffer"); + DEBUGLOG(5, "create a new buffer"); { buffer_t buffer; void* const start = ZSTD_malloc(bSize, bufPool->cMem); buffer.start = start; /* note : start can be NULL if malloc fails ! */ @@ -168,17 +168,17 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool) /* store buffer for later re-use, up to pool capacity */ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf) { - DEBUGLOG(2, "ZSTDMT_releaseBuffer"); if (buf.start == NULL) return; /* compatible with release on NULL */ + DEBUGLOG(5, "ZSTDMT_releaseBuffer"); pthread_mutex_lock(&bufPool->poolMutex); if (bufPool->nbBuffers < bufPool->totalBuffers) { - bufPool->bTable[bufPool->nbBuffers++] = buf; /* store for later re-use */ + bufPool->bTable[bufPool->nbBuffers++] = buf; /* stored for later use */ pthread_mutex_unlock(&bufPool->poolMutex); return; } pthread_mutex_unlock(&bufPool->poolMutex); /* Reached bufferPool capacity (should not happen) */ - DEBUGLOG(2, "buffer pool capacity reached => freeing "); + DEBUGLOG(5, "buffer pool capacity reached => freeing "); ZSTD_free(buf.start, bufPool->cMem); } @@ -338,7 +338,7 @@ void ZSTDMT_compressChunk(void* jobDescription) 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(2, "compressed %u bytes into %u bytes (first:%u) (last:%u)", + 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)); @@ -515,13 +515,12 @@ static unsigned computeNbChunks(size_t srcSize, unsigned windowLog, unsigned nbT } -/* Note : missing checksum at the end ! */ 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, - unsigned overlapRLog) + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const ZSTD_CDict* cdict, + ZSTD_parameters const params, + unsigned overlapRLog) { size_t const overlapSize = (overlapRLog>=9) ? 0 : (size_t)1 << (params.cParams.windowLog - overlapRLog); unsigned nbChunks = computeNbChunks(srcSize, params.cParams.windowLog, mtctx->nbThreads); @@ -531,6 +530,7 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, 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 */ size_t frameStartPos = 0, dstBufferPos = 0; + XXH64_state_t xxh64; DEBUGLOG(4, "nbChunks : %2u (chunkSize : %u bytes) ", nbChunks, (U32)avgChunkSize); if (nbChunks==1) { /* fallback to single-thread mode */ @@ -540,6 +540,7 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, } assert(avgChunkSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), which is useful to avoid allocating extra buffers */ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgChunkSize) ); + XXH64_reset(&xxh64, 0); if (nbChunks > mtctx->jobIDMask+1) { /* enlarge job table */ U32 nbJobs = nbChunks; @@ -576,6 +577,10 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, mtctx->jobs[u].jobCompleted_mutex = &mtctx->jobCompleted_mutex; mtctx->jobs[u].jobCompleted_cond = &mtctx->jobCompleted_cond; + if (params.fParams.checksumFlag) { + XXH64_update(&xxh64, srcStart + frameStartPos, chunkSize); + } + DEBUGLOG(5, "posting job %u (%u bytes)", u, (U32)chunkSize); DEBUG_PRINTHEX(6, mtctx->jobs[u].srcStart, 12); POOL_add(mtctx->factory, ZSTDMT_compressChunk, &mtctx->jobs[u]); @@ -586,8 +591,8 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, } } /* collect result */ - { unsigned chunkID; - size_t error = 0, dstPos = 0; + { size_t error = 0, dstPos = 0; + unsigned chunkID; for (chunkID=0; chunkIDjobCompleted_mutex); @@ -614,6 +619,18 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, dstPos += cSize ; } } /* for (chunkID=0; chunkID dstCapacity) { + error = ERROR(dstSize_tooSmall); + } else { + DEBUGLOG(4, "writing checksum : %08X \n", checksum); + MEM_writeLE32((char*)dst + dstPos, checksum); + dstPos += 4; + } } + if (!error) DEBUGLOG(4, "compressed size : %u ", (U32)dstPos); return error ? error : dstPos; } @@ -852,7 +869,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(2, "Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); + DEBUGLOG(5, "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; diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index a6e1759b9..7584007f1 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -16,8 +16,8 @@ /* Note : This is an internal API. - * Some methods are still exposed (ZSTDLIB_API), because for some time, - * it used to be the only way to invoke MT compression. + * Some methods are still exposed (ZSTDLIB_API), + * because it used to be the only way to invoke MT compression. * Now, it's recommended to use ZSTD_compress_generic() instead. * These methods will stop being exposed in a future version */ @@ -68,7 +68,7 @@ ZSTDLIB_API size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, const void* src, size_t srcSize, const ZSTD_CDict* cdict, ZSTD_parameters const params, - unsigned overlapRLog); + unsigned overlapRLog); /* overlapRLog = 9 - overlapLog */ ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, const void* dict, size_t dictSize, /* dict can be released after init, a local copy is preserved within zcs */ diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 6bed9ec51..904ce6fd8 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -278,9 +278,6 @@ static int basicUnitTests(U32 seed, double compressibility) } RDG_genBuffer(CNBuffer, CNBuffSize, compressibility, 0., seed); - /* memory tests */ - FUZ_mallocTests(seed, compressibility); - /* Basic tests */ DISPLAYLEVEL(4, "test%3i : ZSTD_getErrorName : ", testNb++); { const char* errorString = ZSTD_getErrorName(0); @@ -479,6 +476,23 @@ static int basicUnitTests(U32 seed, double compressibility) } } DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(4, "test%3i : compress -T2 with checksum : ", testNb++); + { ZSTD_parameters params = ZSTD_getParams(1, CNBuffSize, 0); + params.fParams.checksumFlag = 1; + params.fParams.contentSizeFlag = 1; + CHECKPLUS(r, ZSTDMT_compress_advanced(mtctx, + compressedBuffer, compressedBufferSize, + CNBuffer, CNBuffSize, + NULL, params, 3 /*overlapRLog*/), + cSize=r ); + } + DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); + + DISPLAYLEVEL(4, "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"); + ZSTDMT_freeCCtx(mtctx); } diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 8b84400db..3e551e331 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -1377,13 +1377,12 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double /* multi-segments compression test */ XXH64_reset(&xxhState, 0); { ZSTD_outBuffer outBuff = { cBuffer, cBufferSize, 0 } ; - U32 n; - for (n=0, cSize=0, totalTestSize=0 ; totalTestSize < maxTestSize ; n++) { + for (cSize=0, totalTestSize=0 ; (totalTestSize < maxTestSize) ; ) { /* compress random chunks into randomly sized dst buffers */ size_t const randomSrcSize = FUZ_randomLength(&lseed, maxSampleLog); size_t const srcSize = MIN(maxTestSize-totalTestSize, randomSrcSize); size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize); - size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); + size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog+1); size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize); ZSTD_EndDirective const flush = (FUZ_rand(&lseed) & 15) ? ZSTD_e_continue : ZSTD_e_flush; ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 }; @@ -1402,7 +1401,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double { size_t remainingToFlush = (size_t)(-1); while (remainingToFlush) { ZSTD_inBuffer inBuff = { NULL, 0, 0 }; - size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); + size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog+1); size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize); outBuff.size = outBuff.pos + adjustedDstSize; DISPLAYLEVEL(5, "End-flush into dst buffer of size %u \n", (U32)adjustedDstSize); From 583dda17a811a9f4aab42ed4178d296ca5551447 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Tue, 11 Jul 2017 18:13:26 -0700 Subject: [PATCH 113/318] Update rolling hash --- contrib/long_distance_matching/ldm.c | 435 ++++++++++++---- contrib/long_distance_matching/ldm.h | 3 + contrib/long_distance_matching/main-ldm.c | 15 +- contrib/long_distance_matching/util.c | 5 + contrib/long_distance_matching/util.h | 2 + .../versions/v0.1/ldm.c | 394 ++++++++++++++ .../versions/v0.1/ldm.h | 19 + .../versions/v0.1/main-ldm.c | 459 +++++++++++++++++ .../versions/v0.2/Makefile | 32 ++ .../versions/v0.2/ldm.c | 436 ++++++++++++++++ .../versions/v0.2/ldm.h | 19 + .../versions/v0.2/main-ldm.c | 474 +++++++++++++++++ .../versions/v0.3/Makefile | 40 ++ .../versions/v0.3/ldm.c | 464 +++++++++++++++++ .../versions/v0.3/ldm.h | 19 + .../versions/v0.3/main-ldm.c | 479 ++++++++++++++++++ .../versions/v0.3/util.c | 64 +++ .../versions/v0.3/util.h | 23 + 18 files changed, 3282 insertions(+), 100 deletions(-) create mode 100644 contrib/long_distance_matching/versions/v0.1/ldm.c create mode 100644 contrib/long_distance_matching/versions/v0.1/ldm.h create mode 100644 contrib/long_distance_matching/versions/v0.1/main-ldm.c create mode 100644 contrib/long_distance_matching/versions/v0.2/Makefile create mode 100644 contrib/long_distance_matching/versions/v0.2/ldm.c create mode 100644 contrib/long_distance_matching/versions/v0.2/ldm.h create mode 100644 contrib/long_distance_matching/versions/v0.2/main-ldm.c create mode 100644 contrib/long_distance_matching/versions/v0.3/Makefile create mode 100644 contrib/long_distance_matching/versions/v0.3/ldm.c create mode 100644 contrib/long_distance_matching/versions/v0.3/ldm.h create mode 100644 contrib/long_distance_matching/versions/v0.3/main-ldm.c create mode 100644 contrib/long_distance_matching/versions/v0.3/util.c create mode 100644 contrib/long_distance_matching/versions/v0.3/util.h diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 1dedf5c37..ca4f0f2cf 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -9,7 +9,7 @@ #define HASH_EVERY 1 -#define LDM_MEMORY_USAGE 16 +#define LDM_MEMORY_USAGE 22 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) @@ -17,10 +17,12 @@ #define LDM_OFFSET_SIZE 4 -#define WINDOW_SIZE (1 << 20) +#define WINDOW_SIZE (1 << 23) #define MAX_WINDOW_SIZE 31 #define HASH_SIZE 4 -#define LDM_HASH_LENGTH 4 +#define LDM_HASH_LENGTH 100 + +// Should be multiple of four #define MINMATCH 4 #define ML_BITS 4 @@ -28,7 +30,9 @@ #define RUN_BITS (8-ML_BITS) #define RUN_MASK ((1U<totalLiteralLength) / (double)stats->numMatches); printf("Average offset length: %.1f\n", ((double)stats->totalOffset) / (double)stats->numMatches); + printf("Num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", + stats->numCollisions, stats->numHashInserts, + stats->numHashInserts == 0 ? + 1.0 : (100.0 * (double)stats->numCollisions) / + (double)stats->numHashInserts); printf("=====================\n"); } @@ -95,17 +108,43 @@ typedef struct LDM_CCtx { const BYTE *nextIp; hash_t nextHash; /* Hash corresponding to nextIp */ + // Members for rolling hash. + U32 lastSum; + U32 nextSum; + unsigned step; + + // DEBUG + const BYTE *DEBUG_setNextHash; } LDM_CCtx; +static int LDM_isValidMatch(const BYTE *p, const BYTE *match) { + U16 lengthLeft = MINMATCH; + const BYTE *curP = p; + const BYTE *curMatch = match; + + for (; lengthLeft >= 8; lengthLeft -= 8) { + if (LDM_read64(curP) != LDM_read64(curMatch)) { + return 0; + } + curP += 8; + curMatch += 8; + } + if (lengthLeft > 0) { + return LDM_read32(curP) == LDM_read32(curMatch); + } + return 1; +} + + + #ifdef LDM_ROLLING_HASH /** * Convert a sum computed from LDM_getRollingHash to a hash value in the range * of the hash table. */ static hash_t LDM_sumToHash(U32 sum) { - return sum % (LDM_HASHTABLESIZE >> 2); -// return sum & (LDM_HASHTABLESIZE - 1); + return sum & (LDM_HASH_SIZE_U32 - 1); } static U32 LDM_getRollingHash(const char *data, U32 len) { @@ -126,18 +165,102 @@ static U32 LDM_getRollingHash(const char *data, U32 len) { return (s1 & 0xffff) + (s2 << 16); } -static hash_t LDM_hashPosition(const void * const p) { - return LDM_sumToHash(LDM_getRollingHash((const char *)p, LDM_HASH_LENGTH)); -} - typedef struct LDM_sumStruct { U16 s1, s2; } LDM_sumStruct; +static U32 LDM_updateRollingHash(U32 sum, U32 len, + schar toRemove, schar toAdd) { + U32 s1 = (sum & 0xffff) - toRemove + toAdd; + U32 s2 = (sum >> 16) - (toRemove * len) + s1; + + return (s1 & 0xffff) + (s2 << 16); +} + + +/* +static hash_t LDM_hashPosition(const void * const p) { + return LDM_sumToHash(LDM_getRollingHash((const char *)p, LDM_HASH_LENGTH)); +} +*/ + +/* static void LDM_getRollingHashParts(U32 sum, LDM_sumStruct *sumStruct) { sumStruct->s1 = sum & 0xffff; sumStruct->s2 = sum >> 16; } +*/ + +static void LDM_setNextHash(LDM_CCtx *cctx) { + U32 check; + +#ifdef RUN_CHECKS + if ((cctx->nextIp - cctx->ibase != 1) && + (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { + printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, + cctx->DEBUG_setNextHash - cctx->ibase); + } + + cctx->DEBUG_setNextHash = cctx->nextIp; +#endif + + cctx->nextSum = LDM_getRollingHash((const char *)cctx->nextIp, LDM_HASH_LENGTH); + /* + check = LDM_updateRollingHash( + cctx->lastSum, LDM_HASH_LENGTH, + (schar)((cctx->lastPosHashed)[0]), + (schar)((cctx->lastPosHashed)[LDM_HASH_LENGTH])); + */ + +#ifdef RUN_CHECKS + if (check != cctx->nextSum) { + printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); +// printf("INFO: %u %u %u\n", LDM_read32(cctx->nextIp), + } else { +// printf("CHECK: setNextHash passed\n"); + } +#endif + cctx->nextHash = LDM_sumToHash(cctx->nextSum); + +#ifdef RUN_CHECKS + if ((cctx->nextIp - cctx->lastPosHashed) != 1) { + printf("setNextHash: nextIp != lastPosHashed + 1. %zu %zu %zu\n", + cctx->nextIp - cctx->ibase, cctx->lastPosHashed - cctx->ibase, + cctx->ip - cctx->ibase); + } +#endif + +} + +static void LDM_putHashOfCurrentPositionFromHash( + LDM_CCtx *cctx, hash_t hash, U32 sum) { + /* + if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { + return; + } + */ +#ifdef COMPUTE_STATS + if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { + offset_t offset = (cctx->hashTable)[hash].offset; + cctx->stats.numHashInserts++; + if (offset == 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { + cctx->stats.numCollisions++; + } + } +#endif + (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; + cctx->lastPosHashed = cctx->ip; + cctx->lastHash = hash; + cctx->lastSum = sum; +} + +static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { + U32 sum = LDM_getRollingHash((const char *)cctx->ip, LDM_HASH_LENGTH); + hash_t hash = LDM_sumToHash(sum); +// hash_t hash = LDM_hashPosition(cctx->ip); + LDM_putHashOfCurrentPositionFromHash(cctx, hash, sum); +// printf("Offset %zu\n", cctx->ip - cctx->ibase); +} #else static hash_t LDM_hash(U32 sequence) { @@ -147,6 +270,39 @@ static hash_t LDM_hash(U32 sequence) { static hash_t LDM_hashPosition(const void * const p) { return LDM_hash(LDM_read32(p)); } + +static void LDM_putHashOfCurrentPositionFromHash( + LDM_CCtx *cctx, hash_t hash) { + /* + if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { + return; + } + */ +#ifdef COMPUTE_STATS + if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { + offset_t offset = (cctx->hashTable)[hash].offset; + cctx->stats.numHashInserts++; + if (offset == 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { + cctx->stats.numCollisions++; + } + } +#endif + + (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; +#ifdef RUN_CHECKS + if (cctx->ip - cctx->lastPosHashed != 1) { + printf("putHashError\n"); + } +#endif + cctx->lastPosHashed = cctx->ip; + cctx->lastHash = hash; +} + +static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { + hash_t hash = LDM_hashPosition(cctx->ip); + LDM_putHashOfCurrentPositionFromHash(cctx, hash); +} + #endif /* @@ -161,38 +317,19 @@ static hash_t LDM_hash5(U64 sequence) { } */ -static void LDM_putHashOfCurrentPositionFromHash( - LDM_CCtx *cctx, hash_t hash) { - if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { - return; - } - (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; - cctx->lastPosHashed = cctx->ip; - cctx->lastHash = hash; -} -static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - hash_t hash = LDM_hashPosition(cctx->ip); - LDM_putHashOfCurrentPositionFromHash(cctx, hash); -} - -static const BYTE *LDM_get_position_on_hash( +static const BYTE *LDM_getPositionOnHash( hash_t h, void *tableBase, const BYTE *srcBase) { const LDM_hashEntry * const hashTable = (LDM_hashEntry *)tableBase; return hashTable[h].offset + srcBase; } -static BYTE LDM_read_byte(const void *memPtr) { - BYTE val; - memcpy(&val, memPtr, 1); - return val; -} static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, const BYTE *pInLimit) { const BYTE * const pStart = pIn; while (pIn < pInLimit - 1) { - BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); + BYTE const diff = LDM_readByte(pMatch) ^ LDM_readByte(pIn); if (!diff) { pIn++; pMatch++; @@ -220,7 +357,11 @@ static void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->ip = cctx->ibase; cctx->iend = cctx->ibase + srcSize; +#ifdef LDM_ROLLING_HASH + cctx->ihashLimit = cctx->iend - LDM_HASH_LENGTH; +#else cctx->ihashLimit = cctx->iend - HASH_SIZE; +#endif cctx->imatchLimit = cctx->iend - MINMATCH; cctx->obase = (BYTE *)dst; @@ -232,11 +373,46 @@ static void LDM_initializeCCtx(LDM_CCtx *cctx, memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); cctx->lastPosHashed = NULL; - cctx->nextIp = NULL; cctx->step = 1; + cctx->nextIp = cctx->ip + cctx->step; + + cctx->DEBUG_setNextHash = 0; } +#ifdef LDM_ROLLING_HASH +static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { + cctx->nextIp = cctx->ip + cctx->step; + + do { + hash_t h; + U32 sum; +// printf("Call A\n"); + LDM_setNextHash(cctx); +// printf("End call a\n"); + h = cctx->nextHash; + sum = cctx->nextSum; + cctx->ip = cctx->nextIp; + cctx->nextIp += cctx->step; + + if (cctx->ip > cctx->imatchLimit) { + return 1; + } + + *match = LDM_getPositionOnHash(h, cctx->hashTable, cctx->ibase); + +// // Compute cctx->nextSum and cctx->nextHash from cctx->nextIp. +// LDM_setNextHash(cctx); + LDM_putHashOfCurrentPositionFromHash(cctx, h, sum); + +// printf("%u %u\n", cctx->lastHash, cctx->nextHash); + } while (cctx->ip - *match > WINDOW_SIZE || + !LDM_isValidMatch(cctx->ip, *match)); +// LDM_read64(*match) != LDM_read64(cctx->ip)); + LDM_setNextHash(cctx); + return 0; +} +#else static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { cctx->nextIp = cctx->ip; @@ -245,33 +421,131 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { cctx->ip = cctx->nextIp; cctx->nextIp += cctx->step; - if (cctx->nextIp > cctx->imatchLimit) { + if (cctx->ip > cctx->imatchLimit) { return 1; } - *match = LDM_get_position_on_hash(h, cctx->hashTable, cctx->ibase); + *match = LDM_getPositionOnHash(h, cctx->hashTable, cctx->ibase); cctx->nextHash = LDM_hashPosition(cctx->nextIp); LDM_putHashOfCurrentPositionFromHash(cctx, h); + } while (cctx->ip - *match > WINDOW_SIZE || - LDM_read64(*match) != LDM_read64(cctx->ip)); + !LDM_isValidMatch(cctx->ip, *match)); return 0; } +#endif + +/** + * Write current block (literals, literal length, match offset, + * match length). + * + * Update input pointer, inserting hashes into hash table along the + * way. + */ +static void LDM_outputBlock(LDM_CCtx *cctx, const BYTE *match) { + unsigned const literalLength = (unsigned)(cctx->ip - cctx->anchor); + unsigned const offset = cctx->ip - match; + unsigned const matchLength = LDM_count( + cctx->ip + MINMATCH, match + MINMATCH, cctx->ihashLimit); + BYTE *token = cctx->op++; + + cctx->stats.totalLiteralLength += literalLength; + cctx->stats.totalOffset += offset; + cctx->stats.totalMatchLength += matchLength + MINMATCH; + + /* Encode the literal length. */ + if (literalLength >= RUN_MASK) { + int len = (int)literalLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) { + *(cctx->op)++ = 255; + } + *(cctx->op)++ = (BYTE)len; + } else { + *token = (BYTE)(literalLength << ML_BITS); + } + + /* Encode the literals. */ + memcpy(cctx->op, cctx->anchor, literalLength); + cctx->op += literalLength; + + /* Encode the offset. */ + LDM_write32(cctx->op, offset); + cctx->op += LDM_OFFSET_SIZE; + + /* Encode match length */ + if (matchLength >= ML_MASK) { + unsigned matchLengthRemaining = matchLength; + *token += ML_MASK; + matchLengthRemaining -= ML_MASK; + LDM_write32(cctx->op, 0xFFFFFFFF); + while (matchLengthRemaining >= 4*0xFF) { + cctx->op += 4; + LDM_write32(cctx->op, 0xffffffff); + matchLengthRemaining -= 4*0xFF; + } + cctx->op += matchLengthRemaining / 255; + *(cctx->op)++ = (BYTE)(matchLengthRemaining % 255); + } else { + *token += (BYTE)(matchLength); + } + +// LDM_setNextHash(cctx); +// cctx->ip = cctx->lastPosHashed + 1; +// cctx->nextIp = cctx->ip + cctx->step; +// printf("HERE: %zu %zu %zu\n", cctx->ip - cctx->ibase, +// cctx->lastPosHashed - cctx->ibase, cctx->nextIp - cctx->ibase); + + cctx->nextIp = cctx->ip + cctx->step; + + while (cctx->ip < cctx->anchor + MINMATCH + matchLength + literalLength) { +// printf("Loop\n"); + if (cctx->ip > cctx->lastPosHashed) { + LDM_putHashOfCurrentPosition(cctx); +#ifdef LDM_ROLLING_HASH + LDM_setNextHash(cctx); +#endif + } + /* + printf("Call b %zu %zu %zu\n", + cctx->lastPosHashed - cctx->ibase, + cctx->nextIp - cctx->ibase, + cctx->ip - cctx->ibase); + */ +// printf("end call b\n"); + cctx->ip++; + cctx->nextIp++; + } + +// printf("There: %zu %zu\n", cctx->ip - cctx->ibase, cctx->lastPosHashed - cctx->ibase); +} + // TODO: srcSize and maxDstSize is unused size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; + U32 tmp_hash; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); +#ifdef LDM_ROLLING_HASH +// LDM_setNextHash(&cctx); +// tmp_hash = LDM_updateRollingHash(cctx.lastSum, LDM_HASH_LENGTH, +// cctx.ip[0], cctx.ip[LDM_HASH_LENGTH]); +// printf("Update test: %u %u\n", tmp_hash, cctx.nextSum); +// cctx.ip++; +#else cctx.ip++; cctx.nextHash = LDM_hashPosition(cctx.ip); +#endif // TODO: loop condition is not accurate. while (1) { const BYTE *match; +// printf("Start of loop\n"); /** * Find a match. @@ -282,6 +556,7 @@ size_t LDM_compress(const void *src, size_t srcSize, if (LDM_findBestMatch(&cctx, &match) != 0) { goto _last_literals; } +// printf("End of match finding\n"); cctx.stats.numMatches++; @@ -290,6 +565,7 @@ size_t LDM_compress(const void *src, size_t srcSize, */ while (cctx.ip > cctx.anchor && match > cctx.ibase && cctx.ip[-1] == match[-1]) { +// printf("Catch up\n"); cctx.ip--; match--; } @@ -298,67 +574,24 @@ size_t LDM_compress(const void *src, size_t srcSize, * Write current block (literals, literal length, match offset, match * length) and update pointers and hashes. */ - { - unsigned const literalLength = (unsigned)(cctx.ip - cctx.anchor); - unsigned const offset = cctx.ip - match; - unsigned const matchLength = LDM_count( - cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); - BYTE *token = cctx.op++; - - cctx.stats.totalLiteralLength += literalLength; - cctx.stats.totalOffset += offset; - cctx.stats.totalMatchLength += matchLength + MINMATCH; - - /* Encode the literal length. */ - if (literalLength >= RUN_MASK) { - int len = (int)literalLength - RUN_MASK; - *token = (RUN_MASK << ML_BITS); - for (; len >= 255; len -= 255) { - *(cctx.op)++ = 255; - } - *(cctx.op)++ = (BYTE)len; - } else { - *token = (BYTE)(literalLength << ML_BITS); - } - - /* Encode the literals. */ - memcpy(cctx.op, cctx.anchor, literalLength); - cctx.op += literalLength; - - /* Encode the offset. */ - LDM_write32(cctx.op, offset); - cctx.op += LDM_OFFSET_SIZE; - - /* Encode match length */ - if (matchLength >= ML_MASK) { - unsigned matchLengthRemaining = matchLength; - *token += ML_MASK; - matchLengthRemaining -= ML_MASK; - LDM_write32(cctx.op, 0xFFFFFFFF); - while (matchLengthRemaining >= 4*0xFF) { - cctx.op += 4; - LDM_write32(cctx.op, 0xffffffff); - matchLengthRemaining -= 4*0xFF; - } - cctx.op += matchLengthRemaining / 255; - *(cctx.op)++ = (BYTE)(matchLengthRemaining % 255); - } else { - *token += (BYTE)(matchLength); - } - - /* Update input pointer, inserting hashes into hash table along the - * way. - */ - while (cctx.ip < cctx.anchor + MINMATCH + matchLength + literalLength) { - LDM_putHashOfCurrentPosition(&cctx); - cctx.ip++; - } - } + LDM_outputBlock(&cctx, match); +// printf("End of loop\n"); // Set start of next block to current input pointer. cctx.anchor = cctx.ip; LDM_putHashOfCurrentPosition(&cctx); - cctx.nextHash = LDM_hashPosition(++cctx.ip); +#ifndef LDM_ROLLING_HASH + cctx.ip++; +#endif + + /* + LDM_putHashOfCurrentPosition(&cctx); + printf("Call c\n"); + LDM_setNextHash(&cctx); + printf("End call c\n"); + cctx.ip++; + cctx.nextIp++; + */ } _last_literals: /* Encode the last literals (no more matches). */ @@ -453,7 +686,7 @@ size_t LDM_decompress(const void *src, size_t compressSize, /* Copy match. */ cpy = dctx.op + length; - // Inefficient for now + // Inefficient for now. while (match < cpy - offset && dctx.op < dctx.oend) { *(dctx.op)++ = *match++; } @@ -461,4 +694,20 @@ size_t LDM_decompress(const void *src, size_t compressSize, return dctx.op - (BYTE *)dst; } +void LDM_test(const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { +#ifdef LDM_ROLLING_HASH + const BYTE *ip = (const BYTE *)src + 1125; + U32 sum = LDM_getRollingHash((const char *)ip, LDM_HASH_LENGTH); + U32 sum2; + ++ip; + for (; ip < (const BYTE *)src + 1125 + 100; ip++) { + sum2 = LDM_updateRollingHash(sum, LDM_HASH_LENGTH, + ip[-1], ip[LDM_HASH_LENGTH - 1]); + sum = LDM_getRollingHash((const char *)ip, LDM_HASH_LENGTH); + printf("TEST HASH: %zu %u %u\n", ip - (const BYTE *)src, sum, sum2); + } +#endif +} + diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 287d444dd..a34faac4f 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -16,4 +16,7 @@ size_t LDM_decompress(const void *src, size_t srcSize, void LDM_readHeader(const void *src, size_t *compressSize, size_t *decompressSize); +void LDM_test(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + #endif /* LDM_H */ diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 724d735dd..f8ae54698 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -15,6 +15,7 @@ // #define BUF_SIZE 16*1024 // Block size #define DEBUG +//#define TEST //#define ZSTD @@ -74,6 +75,11 @@ static int compress(const char *fname, const char *oname) { return 1; } +#ifdef TEST + LDM_test(src, statbuf.st_size, + dst + LDM_HEADER_SIZE, statbuf.st_size); +#endif + #ifdef ZSTD compressSize = ZSTD_compress(dst, statbuf.st_size, src, statbuf.st_size, 1); @@ -144,11 +150,6 @@ static int decompress(const char *fname, const char *oname) { /* Read the header. */ LDM_readHeader(src, &compressSize, &decompressSize); -#ifdef DEBUG - printf("Size, compressSize, decompressSize: %zu %zu %zu\n", - (size_t)statbuf.st_size, compressSize, decompressSize); -#endif - /* Go to the location corresponding to the last byte. */ if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { perror("lseek error"); @@ -256,7 +257,7 @@ int main(int argc, const char *argv[]) { return 1; } gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", + printf("Total compress time = %f seconds\n", (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + (double) (tv2.tv_sec - tv1.tv_sec)); } @@ -270,7 +271,7 @@ int main(int argc, const char *argv[]) { return 1; } gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", + printf("Total decompress time = %f seconds\n", (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + (double) (tv2.tv_sec - tv1.tv_sec)); } diff --git a/contrib/long_distance_matching/util.c b/contrib/long_distance_matching/util.c index 9ea4ca1e5..70fcbc2ce 100644 --- a/contrib/long_distance_matching/util.c +++ b/contrib/long_distance_matching/util.c @@ -61,4 +61,9 @@ void LDM_copy8(void *dst, const void *src) { memcpy(dst, src, 8); } +BYTE LDM_readByte(const void *memPtr) { + BYTE val; + memcpy(&val, memPtr, 1); + return val; +} diff --git a/contrib/long_distance_matching/util.h b/contrib/long_distance_matching/util.h index 90726412e..d1c3c999b 100644 --- a/contrib/long_distance_matching/util.h +++ b/contrib/long_distance_matching/util.h @@ -19,5 +19,7 @@ uint64_t LDM_read64(const void *ptr); void LDM_copy8(void *dst, const void *src); +uint8_t LDM_readByte(const void *ptr); + #endif /* LDM_UTIL_H */ diff --git a/contrib/long_distance_matching/versions/v0.1/ldm.c b/contrib/long_distance_matching/versions/v0.1/ldm.c new file mode 100644 index 000000000..266425f8a --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.1/ldm.c @@ -0,0 +1,394 @@ +#include +#include +#include +#include + +#include "ldm.h" + +#define LDM_MEMORY_USAGE 14 +#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) +#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) + +#define WINDOW_SIZE (1 << 20) +#define MAX_WINDOW_SIZE 31 +#define HASH_SIZE 4 +#define MINMATCH 4 + +#define ML_BITS 4 +#define ML_MASK ((1U<>8); + } +} + +static U32 LDM_read32(const void *ptr) { + return *(const U32 *)ptr; +} + +static U64 LDM_read64(const void *ptr) { + return *(const U64 *)ptr; +} + + +static void LDM_copy8(void *dst, const void *src) { + memcpy(dst, src, 8); +} + +static void LDM_wild_copy(void *dstPtr, const void *srcPtr, void *dstEnd) { + BYTE *d = (BYTE *)dstPtr; + const BYTE *s = (const BYTE *)srcPtr; + BYTE * const e = (BYTE *)dstEnd; + + do { + LDM_copy8(d, s); + d += 8; + s += 8; + } while (d < e); + +} + +struct hash_entry { + U64 offset; + tag t; +}; + +static U32 LDM_hash(U32 sequence) { + return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); +} + +static U32 LDM_hash5(U64 sequence) { + static const U64 prime5bytes = 889523592379ULL; + static const U64 prime8bytes = 11400714785074694791ULL; + const U32 hashLog = LDM_HASHLOG; + if (LDM_isLittleEndian()) + return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); + else + return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); +} + +static U32 LDM_hash_position(const void * const p) { + return LDM_hash(LDM_read32(p)); +} + +static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, + const BYTE *srcBase) { + U32 *hashTable = (U32 *) tableBase; + hashTable[h] = (U32)(p - srcBase); +} + +static void LDM_put_position(const BYTE *p, void *tableBase, + const BYTE *srcBase) { + U32 const h = LDM_hash_position(p); + LDM_put_position_on_hash(p, h, tableBase, srcBase); +} + +static const BYTE *LDM_get_position_on_hash( + U32 h, void *tableBase, const BYTE *srcBase) { + const U32 * const hashTable = (U32*)tableBase; + return hashTable[h] + srcBase; +} + +static BYTE LDM_read_byte(const void *memPtr) { + BYTE val; + memcpy(&val, memPtr, 1); + return val; +} + +static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { + const BYTE * const pStart = pIn; + while (pIn < pInLimit - 1) { + BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); + if (!diff) { + pIn++; + pMatch++; + continue; + } + return (unsigned)(pIn - pStart); + } + return (unsigned)(pIn - pStart); +} + +void LDM_read_header(void const *source, size_t *compressed_size, + size_t *decompressed_size) { + const U32 *ip = (const U32 *)source; + *compressed_size = *ip++; + *decompressed_size = *ip; +} + +size_t LDM_compress(void const *source, void *dest, size_t source_size, + size_t max_dest_size) { + const BYTE * const istart = (const BYTE*)source; + const BYTE *ip = istart; + const BYTE * const iend = istart + source_size; + const BYTE *ilimit = iend - HASH_SIZE; + const BYTE * const matchlimit = iend - HASH_SIZE; + const BYTE * const mflimit = iend - MINMATCH; + BYTE *op = (BYTE*) dest; + U32 hashTable[LDM_HASHTABLESIZE_U32]; + memset(hashTable, 0, sizeof(hashTable)); + + const BYTE *anchor = (const BYTE *)source; +// struct LDM_cctx cctx; + size_t output_size = 0; + + U32 forwardH; + + /* Hash first byte: put into hash table */ + + LDM_put_position(ip, hashTable, istart); + ip++; + forwardH = LDM_hash_position(ip); + + //TODO Loop terminates before ip>=ilimit. + while (ip < ilimit) { + const BYTE *match; + BYTE *token; + + /* Find a match */ + { + const BYTE *forwardIp = ip; + unsigned step = 1; + + do { + U32 const h = forwardH; + ip = forwardIp; + forwardIp += step; + + if (forwardIp > mflimit) { + goto _last_literals; + } + + match = LDM_get_position_on_hash(h, hashTable, istart); + + forwardH = LDM_hash_position(forwardIp); + LDM_put_position_on_hash(ip, h, hashTable, istart); + } while (ip - match > WINDOW_SIZE || + LDM_read64(match) != LDM_read64(ip)); + } + + // TODO catchup + while (ip > anchor && match > istart && ip[-1] == match[-1]) { + ip--; + match--; + } + + /* Encode literals */ + { + unsigned const litLength = (unsigned)(ip - anchor); + token = op++; + +#ifdef LDM_DEBUG + printf("Cur position: %zu\n", anchor - istart); + printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match); +#endif + /* + fwrite(match, 4, 1, stdout); + printf("\n"); + */ + + if (litLength >= RUN_MASK) { + int len = (int)litLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) { + *op++ = 255; + } + *op++ = (BYTE)len; + } else { + *token = (BYTE)(litLength << ML_BITS); + } +#ifdef LDM_DEBUG + printf("Literals "); + fwrite(anchor, litLength, 1, stdout); + printf("\n"); +#endif + memcpy(op, anchor, litLength); + //LDM_wild_copy(op, anchor, op + litLength); + op += litLength; + } +_next_match: + /* Encode offset */ + { + LDM_write32(op, ip - match); + op += 4; + } + + /* Encode Match Length */ + { + unsigned matchCode; + matchCode = LDM_count(ip + MINMATCH, match + MINMATCH, + matchlimit); +#ifdef LDM_DEBUG + printf("Match length %zu\n", matchCode + MINMATCH); + fwrite(ip, MINMATCH + matchCode, 1, stdout); + printf("\n"); +#endif + ip += MINMATCH + matchCode; + if (matchCode >= ML_MASK) { + *token += ML_MASK; + matchCode -= ML_MASK; + LDM_write32(op, 0xFFFFFFFF); + while (matchCode >= 4*0xFF) { + op += 4; + LDM_write32(op, 0xffffffff); + matchCode -= 4*0xFF; + } + op += matchCode / 255; + *op++ = (BYTE)(matchCode % 255); + } else { + *token += (BYTE)(matchCode); + } +#ifdef LDM_DEBUG + printf("\n"); +#endif + } + + anchor = ip; + + LDM_put_position(ip, hashTable, istart); + forwardH = LDM_hash_position(++ip); + } +_last_literals: + /* Encode last literals */ + { + size_t const lastRun = (size_t)(iend - anchor); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255; accumulator -= 255) { + *op++ = 255; + } + *op++ = (BYTE)accumulator; + } else { + *op++ = (BYTE)(lastRun << ML_BITS); + } + memcpy(op, anchor, lastRun); + op += lastRun; + } + return (op - (BYTE *)dest); +} + +size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, + size_t max_decompressed_size) { + const BYTE *ip = (const BYTE *)source; + const BYTE * const iend = ip + compressed_size; + BYTE *op = (BYTE *)dest; + BYTE * const oend = op + max_decompressed_size; + BYTE *cpy; + + while (ip < iend) { + size_t length; + const BYTE *match; + size_t offset; + + /* get literal length */ + unsigned const token = *ip++; + if ((length=(token >> ML_BITS)) == RUN_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while (s == 255); + } +#ifdef LDM_DEBUG + printf("Literal length: %zu\n", length); +#endif + + /* copy literals */ + cpy = op + length; +#ifdef LDM_DEBUG + printf("Literals "); + fwrite(ip, length, 1, stdout); + printf("\n"); +#endif + memcpy(op, ip, length); +// LDM_wild_copy(op, ip, cpy); + ip += length; + op = cpy; + + /* get offset */ + offset = LDM_read32(ip); + +#ifdef LDM_DEBUG + printf("Offset: %zu\n", offset); +#endif + ip += 4; + match = op - offset; + // LDM_write32(op, (U32)offset); + + /* get matchlength */ + length = token & ML_MASK; + if (length == ML_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while (s == 255); + } + length += MINMATCH; +#ifdef LDM_DEBUG + printf("Match length: %zu\n", length); +#endif + /* copy match */ + cpy = op + length; + + // Inefficient for now + + while (match < cpy - offset && op < oend) { + *op++ = *match++; + } + } +// memcpy(dest, source, compressed_size); + return op - (BYTE *)dest; +} + + diff --git a/contrib/long_distance_matching/versions/v0.1/ldm.h b/contrib/long_distance_matching/versions/v0.1/ldm.h new file mode 100644 index 000000000..f4ca25a38 --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.1/ldm.h @@ -0,0 +1,19 @@ +#ifndef LDM_H +#define LDM_H + +#include /* size_t */ + +#define LDM_COMPRESS_SIZE 4 +#define LDM_DECOMPRESS_SIZE 4 +#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) + +size_t LDM_compress(void const *source, void *dest, size_t source_size, + size_t max_dest_size); + +size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, + size_t max_decompressed_size); + +void LDM_read_header(void const *source, size_t *compressed_size, + size_t *decompressed_size); + +#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v0.1/main-ldm.c b/contrib/long_distance_matching/versions/v0.1/main-ldm.c new file mode 100644 index 000000000..10869cce3 --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.1/main-ldm.c @@ -0,0 +1,459 @@ +// TODO: file size must fit into a U32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ldm.h" + +// #define BUF_SIZE 16*1024 // Block size +#define DEBUG + +//#define ZSTD + +#if 0 +static size_t compress_file(FILE *in, FILE *out, size_t *size_in, + size_t *size_out) { + char *src, *buf = NULL; + size_t r = 1; + size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; + + src = malloc(BUF_SIZE); + if (!src) { + printf("Not enough memory\n"); + goto cleanup; + } + + size = BUF_SIZE + LDM_HEADER_SIZE; + buf = malloc(size); + if (!buf) { + printf("Not enough memory\n"); + goto cleanup; + } + + + for (;;) { + k = fread(src, 1, BUF_SIZE, in); + if (k == 0) + break; + count_in += k; + + n = LDM_compress(src, buf, k, BUF_SIZE); + + // n = k; + // offset += n; + offset = k; + count_out += k; + +// k = fwrite(src, 1, offset, out); + + k = fwrite(buf, 1, offset, out); + if (k < offset) { + if (ferror(out)) + printf("Write failed\n"); + else + printf("Short write\n"); + goto cleanup; + } + + } + *size_in = count_in; + *size_out = count_out; + r = 0; + cleanup: + free(src); + free(buf); + return r; +} + +static size_t decompress_file(FILE *in, FILE *out) { + void *src = malloc(BUF_SIZE); + void *dst = NULL; + size_t dst_capacity = BUF_SIZE; + size_t ret = 1; + size_t bytes_written = 0; + + if (!src) { + perror("decompress_file(src)"); + goto cleanup; + } + + while (ret != 0) { + /* Load more input */ + size_t src_size = fread(src, 1, BUF_SIZE, in); + void *src_ptr = src; + void *src_end = src_ptr + src_size; + if (src_size == 0 || ferror(in)) { + printf("(TODO): Decompress: not enough input or error reading file\n"); + //TODO + ret = 0; + goto cleanup; + } + + /* Allocate destination buffer if it hasn't been allocated already */ + if (!dst) { + dst = malloc(dst_capacity); + if (!dst) { + perror("decompress_file(dst)"); + goto cleanup; + } + } + + // TODO + + /* Decompress: + * Continue while there is more input to read. + */ + while (src_ptr != src_end && ret != 0) { + // size_t dst_size = src_size; + size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); + size_t written = fwrite(dst, 1, dst_size, out); +// printf("Writing %zu bytes\n", dst_size); + bytes_written += dst_size; + if (written != dst_size) { + printf("Decompress: Failed to write to file\n"); + goto cleanup; + } + src_ptr += src_size; + src_size = src_end - src_ptr; + } + + /* Update input */ + + } + + printf("Wrote %zu bytes\n", bytes_written); + + cleanup: + free(src); + free(dst); + + return ret; +} +#endif + +static size_t compress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + + /* 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 size of input file */ + if (fstat (fdin, &statbuf) < 0) { + perror("Fstat error"); + return 1; + } + size_t size_in = statbuf.st_size; + + /* go to the location corresponding to the last byte */ + if (lseek(fdout, size_in + LDM_HEADER_SIZE - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write 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; + } + size_t out_size = statbuf.st_size + LDM_HEADER_SIZE; + + /* mmap the output file */ + if ((dst = mmap(0, out_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + + #ifdef ZSTD + size_t size_out = ZSTD_compress(dst, statbuf.st_size, + src, statbuf.st_size, 1); + #else + size_t size_out = LDM_compress(src, dst + LDM_HEADER_SIZE, statbuf.st_size, + statbuf.st_size); + size_out += LDM_HEADER_SIZE; + + // TODO: should depend on LDM_DECOMPRESS_SIZE write32 + memcpy(dst, &size_out, 4); + memcpy(dst + 4, &(statbuf.st_size), 4); + printf("Compressed size: %zu\n", size_out); + printf("Decompressed size: %zu\n", statbuf.st_size); + #endif + ftruncate(fdout, size_out); + + printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, + (unsigned)statbuf.st_size, (unsigned)size_out, oname, + (double)size_out / (statbuf.st_size) * 100); + + close(fdin); + close(fdout); + return 0; +} + +static size_t decompress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + + /* 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 size of 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 header */ + size_t compressed_size, decompressed_size; + LDM_read_header(src, &compressed_size, &decompressed_size); + + printf("Size, compressed_size, decompressed_size: %zu %zu %zu\n", + statbuf.st_size, compressed_size, decompressed_size); + + /* go to the location corresponding to the last byte */ + if (lseek(fdout, decompressed_size - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write error"); + return 1; + } + + /* mmap the output file */ + if ((dst = mmap(0, decompressed_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + + /* Copy input file to output file */ +// memcpy(dst, src, statbuf.st_size); + + #ifdef ZSTD + size_t size_out = ZSTD_decompress(dst, decomrpessed_size, + src + LDM_HEADER_SIZE, + statbuf.st_size - LDM_HEADER_SIZE); + #else + size_t size_out = LDM_decompress(src + LDM_HEADER_SIZE, dst, + statbuf.st_size - LDM_HEADER_SIZE, + decompressed_size); + printf("Ret size out: %zu\n", size_out); + #endif + ftruncate(fdout, size_out); + + close(fdin); + close(fdout); + return 0; +} + +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; +} + +static void verify(const char *inpFilename, const char *decFilename) { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); +} + +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); + + struct timeval tv1, tv2; + /* compress */ + { + gettimeofday(&tv1, NULL); + if (compress(inpFilename, ldmFilename)) { + printf("Compress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + } + + /* decompress */ + + gettimeofday(&tv1, NULL); + if (decompress(ldmFilename, decFilename)) { + printf("Decompress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + + /* verify */ + verify(inpFilename, decFilename); + return 0; +} + +#if 0 +int main2(int argc, char *argv[]) { + char inpFilename[256] = { 0 }; + char ldmFilename[256] = { 0 }; + char decFilename[256] = { 0 }; + + if (argc < 2) { + printf("Please specify input filename\n"); + return 0; + } + 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 */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *outFp = fopen(ldmFilename, "wb"); + size_t sizeIn = 0; + size_t sizeOut = 0; + size_t ret; + printf("compress : %s -> %s\n", inpFilename, ldmFilename); + ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); + if (ret) { + printf("compress : failed with code %zu\n", ret); + return ret; + } + printf("%s: %zu → %zu bytes, %.1f%%\n", + inpFilename, sizeIn, sizeOut, + (double)sizeOut / sizeIn * 100); + printf("compress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* decompress */ + { + FILE *inpFp = fopen(ldmFilename, "rb"); + FILE *outFp = fopen(decFilename, "wb"); + size_t ret; + + printf("decompress : %s -> %s\n", ldmFilename, decFilename); + ret = decompress_file(inpFp, outFp); + if (ret) { + printf("decompress : failed with code %zu\n", ret); + return ret; + } + printf("decompress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* verify */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); + } + return 0; +} +#endif + diff --git a/contrib/long_distance_matching/versions/v0.2/Makefile b/contrib/long_distance_matching/versions/v0.2/Makefile new file mode 100644 index 000000000..4e04fd6a2 --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.2/Makefile @@ -0,0 +1,32 @@ +# ################################################################ +# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. +# ################################################################ + +# This Makefile presumes libzstd is installed, using `sudo make install` + + +LDFLAGS += -lzstd + +.PHONY: default all clean + +default: all + +all: main-ldm + + +#main : ldm.c main.c +# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +main-ldm : ldm.c main-ldm.c + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +clean: + @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ + main main-ldm + @echo Cleaning completed + diff --git a/contrib/long_distance_matching/versions/v0.2/ldm.c b/contrib/long_distance_matching/versions/v0.2/ldm.c new file mode 100644 index 000000000..9081d1362 --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.2/ldm.c @@ -0,0 +1,436 @@ +#include +#include +#include +#include + +#include "ldm.h" + +#define HASH_EVERY 7 + +#define LDM_MEMORY_USAGE 14 +#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) +#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) + +#define WINDOW_SIZE (1 << 20) +#define MAX_WINDOW_SIZE 31 +#define HASH_SIZE 8 +#define MINMATCH 8 + +#define ML_BITS 4 +#define ML_MASK ((1U<>8); + } +} + +static U32 LDM_read32(const void *ptr) { + return *(const U32 *)ptr; +} + +static U64 LDM_read64(const void *ptr) { + return *(const U64 *)ptr; +} + +static void LDM_copy8(void *dst, const void *src) { + memcpy(dst, src, 8); +} + +typedef struct compress_stats { + U32 num_matches; + U32 total_match_length; + U32 total_literal_length; + U64 total_offset; +} compress_stats; + +static void LDM_printCompressStats(const compress_stats *stats) { + printf("=====================\n"); + printf("Compression statistics\n"); + printf("Total number of matches: %u\n", stats->num_matches); + printf("Average match length: %.1f\n", ((double)stats->total_match_length) / + (double)stats->num_matches); + printf("Average literal length: %.1f\n", + ((double)stats->total_literal_length) / (double)stats->num_matches); + printf("Average offset length: %.1f\n", + ((double)stats->total_offset) / (double)stats->num_matches); + printf("=====================\n"); +} + +// TODO: unused. +struct hash_entry { + U64 offset; + tag t; +}; + +static U32 LDM_hash(U32 sequence) { + return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); +} + +static U32 LDM_hash5(U64 sequence) { + static const U64 prime5bytes = 889523592379ULL; + static const U64 prime8bytes = 11400714785074694791ULL; + const U32 hashLog = LDM_HASHLOG; + if (LDM_isLittleEndian()) + return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); + else + return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); +} + +static U32 LDM_hash_position(const void * const p) { + return LDM_hash(LDM_read32(p)); +} + +static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, + const BYTE *srcBase) { + if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { + return; + } + + U32 *hashTable = (U32 *) tableBase; + hashTable[h] = (U32)(p - srcBase); +} + +static void LDM_put_position(const BYTE *p, void *tableBase, + const BYTE *srcBase) { + if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { + return; + } + U32 const h = LDM_hash_position(p); + LDM_put_position_on_hash(p, h, tableBase, srcBase); +} + +static const BYTE *LDM_get_position_on_hash( + U32 h, void *tableBase, const BYTE *srcBase) { + const U32 * const hashTable = (U32*)tableBase; + return hashTable[h] + srcBase; +} + +static BYTE LDM_read_byte(const void *memPtr) { + BYTE val; + memcpy(&val, memPtr, 1); + return val; +} + +static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { + const BYTE * const pStart = pIn; + while (pIn < pInLimit - 1) { + BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); + if (!diff) { + pIn++; + pMatch++; + continue; + } + return (unsigned)(pIn - pStart); + } + return (unsigned)(pIn - pStart); +} + +void LDM_read_header(const void *src, size_t *compressSize, + size_t *decompressSize) { + const U32 *ip = (const U32 *)src; + *compressSize = *ip++; + *decompressSize = *ip; +} + +// TODO: maxDstSize is unused +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { + const BYTE * const istart = (const BYTE*)src; + const BYTE *ip = istart; + const BYTE * const iend = istart + srcSize; + const BYTE *ilimit = iend - HASH_SIZE; + const BYTE * const matchlimit = iend - HASH_SIZE; + const BYTE * const mflimit = iend - MINMATCH; + BYTE *op = (BYTE*) dst; + + compress_stats compressStats = { 0 }; + + U32 hashTable[LDM_HASHTABLESIZE_U32]; + memset(hashTable, 0, sizeof(hashTable)); + + const BYTE *anchor = (const BYTE *)src; +// struct LDM_cctx cctx; + size_t output_size = 0; + + U32 forwardH; + + /* Hash first byte: put into hash table */ + + LDM_put_position(ip, hashTable, istart); + const BYTE *lastHash = ip; + ip++; + forwardH = LDM_hash_position(ip); + + //TODO Loop terminates before ip>=ilimit. + while (ip < ilimit) { + const BYTE *match; + BYTE *token; + + /* Find a match */ + { + const BYTE *forwardIp = ip; + unsigned step = 1; + + do { + U32 const h = forwardH; + ip = forwardIp; + forwardIp += step; + + if (forwardIp > mflimit) { + goto _last_literals; + } + + match = LDM_get_position_on_hash(h, hashTable, istart); + + forwardH = LDM_hash_position(forwardIp); + LDM_put_position_on_hash(ip, h, hashTable, istart); + lastHash = ip; + } while (ip - match > WINDOW_SIZE || + LDM_read64(match) != LDM_read64(ip)); + } + compressStats.num_matches++; + + /* Catchup: look back to extend match from found match */ + while (ip > anchor && match > istart && ip[-1] == match[-1]) { + ip--; + match--; + } + + /* Encode literals */ + { + unsigned const litLength = (unsigned)(ip - anchor); + token = op++; + + compressStats.total_literal_length += litLength; + +#ifdef LDM_DEBUG + printf("Cur position: %zu\n", anchor - istart); + printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match); +#endif + + if (litLength >= RUN_MASK) { + int len = (int)litLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) { + *op++ = 255; + } + *op++ = (BYTE)len; + } else { + *token = (BYTE)(litLength << ML_BITS); + } +#ifdef LDM_DEBUG + printf("Literals "); + fwrite(anchor, litLength, 1, stdout); + printf("\n"); +#endif + memcpy(op, anchor, litLength); + op += litLength; + } +_next_match: + /* Encode offset */ + { + /* + LDM_writeLE16(op, ip-match); + op += 2; + */ + LDM_write32(op, ip - match); + op += 4; + compressStats.total_offset += (ip - match); + } + + /* Encode Match Length */ + { + unsigned matchCode; + matchCode = LDM_count(ip + MINMATCH, match + MINMATCH, + matchlimit); +#ifdef LDM_DEBUG + printf("Match length %zu\n", matchCode + MINMATCH); + fwrite(ip, MINMATCH + matchCode, 1, stdout); + printf("\n"); +#endif + compressStats.total_match_length += matchCode + MINMATCH; + unsigned ctr = 1; + ip++; + for (; ctr < MINMATCH + matchCode; ip++, ctr++) { + LDM_put_position(ip, hashTable, istart); + } +// ip += MINMATCH + matchCode; + if (matchCode >= ML_MASK) { + *token += ML_MASK; + matchCode -= ML_MASK; + LDM_write32(op, 0xFFFFFFFF); + while (matchCode >= 4*0xFF) { + op += 4; + LDM_write32(op, 0xffffffff); + matchCode -= 4*0xFF; + } + op += matchCode / 255; + *op++ = (BYTE)(matchCode % 255); + } else { + *token += (BYTE)(matchCode); + } +#ifdef LDM_DEBUG + printf("\n"); + +#endif + } + + anchor = ip; + + LDM_put_position(ip, hashTable, istart); + forwardH = LDM_hash_position(++ip); + lastHash = ip; + } +_last_literals: + /* Encode last literals */ + { + size_t const lastRun = (size_t)(iend - anchor); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255; accumulator -= 255) { + *op++ = 255; + } + *op++ = (BYTE)accumulator; + } else { + *op++ = (BYTE)(lastRun << ML_BITS); + } + memcpy(op, anchor, lastRun); + op += lastRun; + } + LDM_printCompressStats(&compressStats); + return (op - (BYTE *)dst); +} + +typedef struct LDM_DCtx { + const BYTE * const ibase; /* Pointer to base of input */ + const BYTE *ip; /* Pointer to current input position */ + const BYTE *iend; /* End of source */ + BYTE *op; /* Pointer to output */ + const BYTE * const oend; /* Pointer to end of output */ + +} LDM_DCtx; + +size_t LDM_decompress(const void *src, size_t compressed_size, + void *dst, size_t max_decompressed_size) { + const BYTE *ip = (const BYTE *)src; + const BYTE * const iend = ip + compressed_size; + BYTE *op = (BYTE *)dst; + BYTE * const oend = op + max_decompressed_size; + BYTE *cpy; + + while (ip < iend) { + size_t length; + const BYTE *match; + size_t offset; + + /* get literal length */ + unsigned const token = *ip++; + if ((length=(token >> ML_BITS)) == RUN_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while (s == 255); + } +#ifdef LDM_DEBUG + printf("Literal length: %zu\n", length); +#endif + + /* copy literals */ + cpy = op + length; +#ifdef LDM_DEBUG + printf("Literals "); + fwrite(ip, length, 1, stdout); + printf("\n"); +#endif + memcpy(op, ip, length); + ip += length; + op = cpy; + + /* get offset */ + /* + offset = LDM_readLE16(ip); + ip += 2; + */ + offset = LDM_read32(ip); + ip += 4; +#ifdef LDM_DEBUG + printf("Offset: %zu\n", offset); +#endif + match = op - offset; + // LDM_write32(op, (U32)offset); + + /* get matchlength */ + length = token & ML_MASK; + if (length == ML_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while (s == 255); + } + length += MINMATCH; +#ifdef LDM_DEBUG + printf("Match length: %zu\n", length); +#endif + /* copy match */ + cpy = op + length; + + // Inefficient for now + while (match < cpy - offset && op < oend) { + *op++ = *match++; + } + } + return op - (BYTE *)dst; +} + + diff --git a/contrib/long_distance_matching/versions/v0.2/ldm.h b/contrib/long_distance_matching/versions/v0.2/ldm.h new file mode 100644 index 000000000..0ac7b2ece --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.2/ldm.h @@ -0,0 +1,19 @@ +#ifndef LDM_H +#define LDM_H + +#include /* size_t */ + +#define LDM_COMPRESS_SIZE 4 +#define LDM_DECOMPRESS_SIZE 4 +#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) + +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +size_t LDM_decompress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +void LDM_read_header(const void *src, size_t *compressSize, + size_t *decompressSize); + +#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v0.2/main-ldm.c b/contrib/long_distance_matching/versions/v0.2/main-ldm.c new file mode 100644 index 000000000..0017335b8 --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.2/main-ldm.c @@ -0,0 +1,474 @@ +// TODO: file size must fit into a U32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ldm.h" + +// #define BUF_SIZE 16*1024 // Block size +#define DEBUG + +//#define ZSTD + +/* Compress file given by fname and output to oname. + * Returns 0 if successful, error code otherwise. + */ +static int compress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + + /* 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; + } + + size_t maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; + + /* Go to the location corresponding to the last byte. */ + /* TODO: fallocate? */ + if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* Write a dummy byte at the last location. */ + if (write(fdout, "", 1) != 1) { + perror("write 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; + } + + /* mmap the output file */ + if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + +#ifdef ZSTD + size_t compressSize = ZSTD_compress(dst, statbuf.st_size, + src, statbuf.st_size, 1); +#else + size_t compressSize = LDM_HEADER_SIZE + + LDM_compress(src, statbuf.st_size, + dst + LDM_HEADER_SIZE, statbuf.st_size); + + // Write compress and decompress size to header + // TODO: should depend on LDM_DECOMPRESS_SIZE write32 + memcpy(dst, &compressSize, 4); + memcpy(dst + 4, &(statbuf.st_size), 4); + +#ifdef DEBUG + printf("Compressed size: %zu\n", compressSize); + printf("Decompressed size: %zu\n", statbuf.st_size); +#endif +#endif + + // Truncate file to compressSize. + ftruncate(fdout, compressSize); + + printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, + (unsigned)statbuf.st_size, (unsigned)compressSize, oname, + (double)compressSize / (statbuf.st_size) * 100); + + // Close files. + close(fdin); + close(fdout); + return 0; +} + +/* 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; + + /* 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. */ + size_t compressSize, decompressSize; + LDM_read_header(src, &compressSize, &decompressSize); + +#ifdef DEBUG + printf("Size, compressSize, decompressSize: %zu %zu %zu\n", + statbuf.st_size, compressSize, decompressSize); +#endif + + /* Go to the location corresponding to the last byte. */ + if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write error"); + return 1; + } + + /* mmap the output file */ + if ((dst = mmap(0, decompressSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + +#ifdef ZSTD + size_t outSize = ZSTD_decompress(dst, decomrpessed_size, + src + LDM_HEADER_SIZE, + statbuf.st_size - LDM_HEADER_SIZE); +#else + size_t outSize = LDM_decompress( + src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, + dst, decompressSize); + + printf("Ret size out: %zu\n", outSize); + #endif + ftruncate(fdout, 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 void verify(const char *inpFilename, const char *decFilename) { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); +} + +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); + + struct timeval tv1, tv2; + + /* Compress */ + + gettimeofday(&tv1, NULL); + if (compress(inpFilename, ldmFilename)) { + printf("Compress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + + /* Decompress */ + + gettimeofday(&tv1, NULL); + if (decompress(ldmFilename, decFilename)) { + printf("Decompress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + + /* verify */ + verify(inpFilename, decFilename); + return 0; +} + + +#if 0 +static size_t compress_file(FILE *in, FILE *out, size_t *size_in, + size_t *size_out) { + char *src, *buf = NULL; + size_t r = 1; + size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; + + src = malloc(BUF_SIZE); + if (!src) { + printf("Not enough memory\n"); + goto cleanup; + } + + size = BUF_SIZE + LDM_HEADER_SIZE; + buf = malloc(size); + if (!buf) { + printf("Not enough memory\n"); + goto cleanup; + } + + + for (;;) { + k = fread(src, 1, BUF_SIZE, in); + if (k == 0) + break; + count_in += k; + + n = LDM_compress(src, buf, k, BUF_SIZE); + + // n = k; + // offset += n; + offset = k; + count_out += k; + +// k = fwrite(src, 1, offset, out); + + k = fwrite(buf, 1, offset, out); + if (k < offset) { + if (ferror(out)) + printf("Write failed\n"); + else + printf("Short write\n"); + goto cleanup; + } + + } + *size_in = count_in; + *size_out = count_out; + r = 0; + cleanup: + free(src); + free(buf); + return r; +} + +static size_t decompress_file(FILE *in, FILE *out) { + void *src = malloc(BUF_SIZE); + void *dst = NULL; + size_t dst_capacity = BUF_SIZE; + size_t ret = 1; + size_t bytes_written = 0; + + if (!src) { + perror("decompress_file(src)"); + goto cleanup; + } + + while (ret != 0) { + /* Load more input */ + size_t src_size = fread(src, 1, BUF_SIZE, in); + void *src_ptr = src; + void *src_end = src_ptr + src_size; + if (src_size == 0 || ferror(in)) { + printf("(TODO): Decompress: not enough input or error reading file\n"); + //TODO + ret = 0; + goto cleanup; + } + + /* Allocate destination buffer if it hasn't been allocated already */ + if (!dst) { + dst = malloc(dst_capacity); + if (!dst) { + perror("decompress_file(dst)"); + goto cleanup; + } + } + + // TODO + + /* Decompress: + * Continue while there is more input to read. + */ + while (src_ptr != src_end && ret != 0) { + // size_t dst_size = src_size; + size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); + size_t written = fwrite(dst, 1, dst_size, out); +// printf("Writing %zu bytes\n", dst_size); + bytes_written += dst_size; + if (written != dst_size) { + printf("Decompress: Failed to write to file\n"); + goto cleanup; + } + src_ptr += src_size; + src_size = src_end - src_ptr; + } + + /* Update input */ + + } + + printf("Wrote %zu bytes\n", bytes_written); + + cleanup: + free(src); + free(dst); + + return ret; +} + +int main2(int argc, char *argv[]) { + char inpFilename[256] = { 0 }; + char ldmFilename[256] = { 0 }; + char decFilename[256] = { 0 }; + + if (argc < 2) { + printf("Please specify input filename\n"); + return 0; + } + 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 */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *outFp = fopen(ldmFilename, "wb"); + size_t sizeIn = 0; + size_t sizeOut = 0; + size_t ret; + printf("compress : %s -> %s\n", inpFilename, ldmFilename); + ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); + if (ret) { + printf("compress : failed with code %zu\n", ret); + return ret; + } + printf("%s: %zu → %zu bytes, %.1f%%\n", + inpFilename, sizeIn, sizeOut, + (double)sizeOut / sizeIn * 100); + printf("compress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* decompress */ + { + FILE *inpFp = fopen(ldmFilename, "rb"); + FILE *outFp = fopen(decFilename, "wb"); + size_t ret; + + printf("decompress : %s -> %s\n", ldmFilename, decFilename); + ret = decompress_file(inpFp, outFp); + if (ret) { + printf("decompress : failed with code %zu\n", ret); + return ret; + } + printf("decompress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* verify */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); + } + return 0; +} +#endif + diff --git a/contrib/long_distance_matching/versions/v0.3/Makefile b/contrib/long_distance_matching/versions/v0.3/Makefile new file mode 100644 index 000000000..5ffd4eafe --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.3/Makefile @@ -0,0 +1,40 @@ +# ################################################################ +# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. +# ################################################################ + +# This Makefile presumes libzstd is installed, using `sudo make install` + +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: main-ldm + + +#main : ldm.c main.c +# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +main-ldm : util.c ldm.c main-ldm.c + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +clean: + @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ + main main-ldm + @echo Cleaning completed + diff --git a/contrib/long_distance_matching/versions/v0.3/ldm.c b/contrib/long_distance_matching/versions/v0.3/ldm.c new file mode 100644 index 000000000..1dedf5c37 --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.3/ldm.c @@ -0,0 +1,464 @@ +#include +#include +#include +#include + + +#include "ldm.h" +#include "util.h" + +#define HASH_EVERY 1 + +#define LDM_MEMORY_USAGE 16 +#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) +#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) + +#define LDM_OFFSET_SIZE 4 + +#define WINDOW_SIZE (1 << 20) +#define MAX_WINDOW_SIZE 31 +#define HASH_SIZE 4 +#define LDM_HASH_LENGTH 4 +#define MINMATCH 4 + +#define ML_BITS 4 +#define ML_MASK ((1U<numMatches); + printf("Average match length: %.1f\n", ((double)stats->totalMatchLength) / + (double)stats->numMatches); + printf("Average literal length: %.1f\n", + ((double)stats->totalLiteralLength) / (double)stats->numMatches); + printf("Average offset length: %.1f\n", + ((double)stats->totalOffset) / (double)stats->numMatches); + printf("=====================\n"); +} + +typedef 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_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; + + const BYTE *lastPosHashed; /* Last position hashed */ + hash_t lastHash; /* Hash corresponding to lastPosHashed */ + const BYTE *nextIp; + hash_t nextHash; /* Hash corresponding to nextIp */ + + unsigned step; +} LDM_CCtx; + +#ifdef LDM_ROLLING_HASH +/** + * Convert a sum computed from LDM_getRollingHash to a hash value in the range + * of the hash table. + */ +static hash_t LDM_sumToHash(U32 sum) { + return sum % (LDM_HASHTABLESIZE >> 2); +// return sum & (LDM_HASHTABLESIZE - 1); +} + +static U32 LDM_getRollingHash(const char *data, U32 len) { + U32 i; + U32 s1, s2; + const schar *buf = (const schar *)data; + + s1 = s2 = 0; + for (i = 0; i < (len - 4); i += 4) { + s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + + (2 * buf[i + 2]) + (buf[i + 3]); + s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; + } + for(; i < len; i++) { + s1 += buf[i]; + s2 += s1; + } + return (s1 & 0xffff) + (s2 << 16); +} + +static hash_t LDM_hashPosition(const void * const p) { + return LDM_sumToHash(LDM_getRollingHash((const char *)p, LDM_HASH_LENGTH)); +} + +typedef struct LDM_sumStruct { + U16 s1, s2; +} LDM_sumStruct; + +static void LDM_getRollingHashParts(U32 sum, LDM_sumStruct *sumStruct) { + sumStruct->s1 = sum & 0xffff; + sumStruct->s2 = sum >> 16; +} + +#else +static hash_t LDM_hash(U32 sequence) { + return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); +} + +static hash_t LDM_hashPosition(const void * const p) { + return LDM_hash(LDM_read32(p)); +} +#endif + +/* +static hash_t LDM_hash5(U64 sequence) { + static const U64 prime5bytes = 889523592379ULL; + static const U64 prime8bytes = 11400714785074694791ULL; + const U32 hashLog = LDM_HASHLOG; + if (LDM_isLittleEndian()) + return (((sequence << 24) * prime5bytes) >> (64 - hashLog)); + else + return (((sequence >> 24) * prime8bytes) >> (64 - hashLog)); +} +*/ + +static void LDM_putHashOfCurrentPositionFromHash( + LDM_CCtx *cctx, hash_t hash) { + if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { + return; + } + (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; + cctx->lastPosHashed = cctx->ip; + cctx->lastHash = hash; +} + +static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { + hash_t hash = LDM_hashPosition(cctx->ip); + LDM_putHashOfCurrentPositionFromHash(cctx, hash); +} + +static const BYTE *LDM_get_position_on_hash( + hash_t h, void *tableBase, const BYTE *srcBase) { + const LDM_hashEntry * const hashTable = (LDM_hashEntry *)tableBase; + return hashTable[h].offset + srcBase; +} + +static BYTE LDM_read_byte(const void *memPtr) { + BYTE val; + memcpy(&val, memPtr, 1); + return val; +} + +static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { + const BYTE * const pStart = pIn; + while (pIn < pInLimit - 1) { + BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); + if (!diff) { + pIn++; + pMatch++; + continue; + } + return (unsigned)(pIn - pStart); + } + return (unsigned)(pIn - pStart); +} + +void LDM_readHeader(const void *src, size_t *compressSize, + size_t *decompressSize) { + const U32 *ip = (const U32 *)src; + *compressSize = *ip++; + *decompressSize = *ip; +} + +static void 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 - HASH_SIZE; + cctx->imatchLimit = cctx->iend - MINMATCH; + + cctx->obase = (BYTE *)dst; + cctx->op = (BYTE *)dst; + + cctx->anchor = cctx->ibase; + + memset(&(cctx->stats), 0, sizeof(cctx->stats)); + memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); + + cctx->lastPosHashed = NULL; + cctx->nextIp = NULL; + + cctx->step = 1; +} + +static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { + cctx->nextIp = cctx->ip; + + do { + hash_t const h = cctx->nextHash; + cctx->ip = cctx->nextIp; + cctx->nextIp += cctx->step; + + if (cctx->nextIp > cctx->imatchLimit) { + return 1; + } + + *match = LDM_get_position_on_hash(h, cctx->hashTable, cctx->ibase); + + cctx->nextHash = LDM_hashPosition(cctx->nextIp); + LDM_putHashOfCurrentPositionFromHash(cctx, h); + } while (cctx->ip - *match > WINDOW_SIZE || + LDM_read64(*match) != LDM_read64(cctx->ip)); + return 0; +} + +// TODO: srcSize and maxDstSize is unused +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { + LDM_CCtx cctx; + LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); + + /* Hash the first position and put it into the hash table. */ + LDM_putHashOfCurrentPosition(&cctx); + cctx.ip++; + cctx.nextHash = LDM_hashPosition(cctx.ip); + + // TODO: loop condition is not accurate. + while (1) { + const BYTE *match; + + /** + * 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. + */ + if (LDM_findBestMatch(&cctx, &match) != 0) { + goto _last_literals; + } + + cctx.stats.numMatches++; + + /** + * Catch up: look back to extend the match backwards from the found match. + */ + while (cctx.ip > cctx.anchor && match > cctx.ibase && + cctx.ip[-1] == match[-1]) { + cctx.ip--; + match--; + } + + /** + * Write current block (literals, literal length, match offset, match + * length) and update pointers and hashes. + */ + { + unsigned const literalLength = (unsigned)(cctx.ip - cctx.anchor); + unsigned const offset = cctx.ip - match; + unsigned const matchLength = LDM_count( + cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); + BYTE *token = cctx.op++; + + cctx.stats.totalLiteralLength += literalLength; + cctx.stats.totalOffset += offset; + cctx.stats.totalMatchLength += matchLength + MINMATCH; + + /* Encode the literal length. */ + if (literalLength >= RUN_MASK) { + int len = (int)literalLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) { + *(cctx.op)++ = 255; + } + *(cctx.op)++ = (BYTE)len; + } else { + *token = (BYTE)(literalLength << ML_BITS); + } + + /* Encode the literals. */ + memcpy(cctx.op, cctx.anchor, literalLength); + cctx.op += literalLength; + + /* Encode the offset. */ + LDM_write32(cctx.op, offset); + cctx.op += LDM_OFFSET_SIZE; + + /* Encode match length */ + if (matchLength >= ML_MASK) { + unsigned matchLengthRemaining = matchLength; + *token += ML_MASK; + matchLengthRemaining -= ML_MASK; + LDM_write32(cctx.op, 0xFFFFFFFF); + while (matchLengthRemaining >= 4*0xFF) { + cctx.op += 4; + LDM_write32(cctx.op, 0xffffffff); + matchLengthRemaining -= 4*0xFF; + } + cctx.op += matchLengthRemaining / 255; + *(cctx.op)++ = (BYTE)(matchLengthRemaining % 255); + } else { + *token += (BYTE)(matchLength); + } + + /* Update input pointer, inserting hashes into hash table along the + * way. + */ + while (cctx.ip < cctx.anchor + MINMATCH + matchLength + literalLength) { + LDM_putHashOfCurrentPosition(&cctx); + cctx.ip++; + } + } + + // Set start of next block to current input pointer. + cctx.anchor = cctx.ip; + LDM_putHashOfCurrentPosition(&cctx); + cctx.nextHash = LDM_hashPosition(++cctx.ip); + } +_last_literals: + /* Encode the last literals (no more matches). */ + { + size_t const lastRun = (size_t)(cctx.iend - cctx.anchor); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *(cctx.op)++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255; accumulator -= 255) { + *(cctx.op)++ = 255; + } + *(cctx.op)++ = (BYTE)accumulator; + } else { + *(cctx.op)++ = (BYTE)(lastRun << ML_BITS); + } + memcpy(cctx.op, cctx.anchor, lastRun); + cctx.op += lastRun; + } + LDM_printCompressStats(&cctx.stats); + return (cctx.op - (const BYTE *)cctx.obase); +} + +typedef struct LDM_DCtx { + size_t compressSize; + size_t maxDecompressSize; + + 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 */ +} LDM_DCtx; + +static void LDM_initializeDCtx(LDM_DCtx *dctx, + const void *src, size_t compressSize, + void *dst, size_t maxDecompressSize) { + dctx->compressSize = compressSize; + dctx->maxDecompressSize = maxDecompressSize; + + dctx->ibase = src; + dctx->ip = (const BYTE *)src; + dctx->iend = dctx->ip + dctx->compressSize; + dctx->op = dst; + dctx->oend = dctx->op + dctx->maxDecompressSize; + +} + +size_t LDM_decompress(const void *src, size_t compressSize, + void *dst, size_t maxDecompressSize) { + LDM_DCtx dctx; + LDM_initializeDCtx(&dctx, src, compressSize, dst, maxDecompressSize); + + while (dctx.ip < dctx.iend) { + BYTE *cpy; + const BYTE *match; + size_t length, offset; + + /* Get the literal length. */ + unsigned const token = *(dctx.ip)++; + if ((length = (token >> ML_BITS)) == RUN_MASK) { + unsigned s; + do { + s = *(dctx.ip)++; + length += s; + } while (s == 255); + } + + /* Copy literals. */ + cpy = dctx.op + length; + memcpy(dctx.op, dctx.ip, length); + dctx.ip += length; + dctx.op = cpy; + + //TODO : dynamic offset size + offset = LDM_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 += MINMATCH; + + /* Copy match. */ + cpy = dctx.op + length; + + // Inefficient for now + while (match < cpy - offset && dctx.op < dctx.oend) { + *(dctx.op)++ = *match++; + } + } + return dctx.op - (BYTE *)dst; +} + + diff --git a/contrib/long_distance_matching/versions/v0.3/ldm.h b/contrib/long_distance_matching/versions/v0.3/ldm.h new file mode 100644 index 000000000..287d444dd --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.3/ldm.h @@ -0,0 +1,19 @@ +#ifndef LDM_H +#define LDM_H + +#include /* size_t */ + +#define LDM_COMPRESS_SIZE 4 +#define LDM_DECOMPRESS_SIZE 4 +#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) + +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +size_t LDM_decompress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +void LDM_readHeader(const void *src, size_t *compressSize, + size_t *decompressSize); + +#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v0.3/main-ldm.c b/contrib/long_distance_matching/versions/v0.3/main-ldm.c new file mode 100644 index 000000000..724d735dd --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.3/main-ldm.c @@ -0,0 +1,479 @@ +// TODO: file size must fit into a U32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ldm.h" + +// #define BUF_SIZE 16*1024 // Block size +#define DEBUG + +//#define ZSTD + +/* Compress file given by fname and output to oname. + * Returns 0 if successful, error code otherwise. + */ +static int compress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + size_t maxCompressSize, compressSize; + + /* 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; + } + + maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; + + /* Go to the location corresponding to the last byte. */ + /* TODO: fallocate? */ + if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* Write a dummy byte at the last location. */ + if (write(fdout, "", 1) != 1) { + perror("write 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; + } + + /* mmap the output file */ + if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + +#ifdef ZSTD + compressSize = ZSTD_compress(dst, statbuf.st_size, + src, statbuf.st_size, 1); +#else + compressSize = LDM_HEADER_SIZE + + LDM_compress(src, statbuf.st_size, + dst + LDM_HEADER_SIZE, statbuf.st_size); + + // Write compress and decompress size to header + // TODO: should depend on LDM_DECOMPRESS_SIZE write32 + memcpy(dst, &compressSize, 4); + memcpy(dst + 4, &(statbuf.st_size), 4); + +#ifdef DEBUG + printf("Compressed size: %zu\n", compressSize); + printf("Decompressed size: %zu\n", (size_t)statbuf.st_size); +#endif +#endif + + // Truncate file to compressSize. + ftruncate(fdout, compressSize); + + printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, + (unsigned)statbuf.st_size, (unsigned)compressSize, oname, + (double)compressSize / (statbuf.st_size) * 100); + + // Close files. + close(fdin); + close(fdout); + return 0; +} + +/* 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; + size_t compressSize, decompressSize, 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, &compressSize, &decompressSize); + +#ifdef DEBUG + printf("Size, compressSize, decompressSize: %zu %zu %zu\n", + (size_t)statbuf.st_size, compressSize, decompressSize); +#endif + + /* Go to the location corresponding to the last byte. */ + if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write error"); + return 1; + } + + /* mmap the output file */ + if ((dst = mmap(0, decompressSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + +#ifdef ZSTD + outSize = ZSTD_decompress(dst, decomrpessed_size, + src + LDM_HEADER_SIZE, + statbuf.st_size - LDM_HEADER_SIZE); +#else + outSize = LDM_decompress( + src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, + dst, decompressSize); + + printf("Ret size out: %zu\n", outSize); + #endif + ftruncate(fdout, 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 void verify(const char *inpFilename, const char *decFilename) { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + } + + fclose(decFp); + fclose(inpFp); +} + +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 */ + { + struct timeval tv1, tv2; + gettimeofday(&tv1, NULL); + if (compress(inpFilename, ldmFilename)) { + printf("Compress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + } + + /* Decompress */ + { + struct timeval tv1, tv2; + gettimeofday(&tv1, NULL); + if (decompress(ldmFilename, decFilename)) { + printf("Decompress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + } + /* verify */ + verify(inpFilename, decFilename); + return 0; +} + + +#if 0 +static size_t compress_file(FILE *in, FILE *out, size_t *size_in, + size_t *size_out) { + char *src, *buf = NULL; + size_t r = 1; + size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; + + src = malloc(BUF_SIZE); + if (!src) { + printf("Not enough memory\n"); + goto cleanup; + } + + size = BUF_SIZE + LDM_HEADER_SIZE; + buf = malloc(size); + if (!buf) { + printf("Not enough memory\n"); + goto cleanup; + } + + + for (;;) { + k = fread(src, 1, BUF_SIZE, in); + if (k == 0) + break; + count_in += k; + + n = LDM_compress(src, buf, k, BUF_SIZE); + + // n = k; + // offset += n; + offset = k; + count_out += k; + +// k = fwrite(src, 1, offset, out); + + k = fwrite(buf, 1, offset, out); + if (k < offset) { + if (ferror(out)) + printf("Write failed\n"); + else + printf("Short write\n"); + goto cleanup; + } + + } + *size_in = count_in; + *size_out = count_out; + r = 0; + cleanup: + free(src); + free(buf); + return r; +} + +static size_t decompress_file(FILE *in, FILE *out) { + void *src = malloc(BUF_SIZE); + void *dst = NULL; + size_t dst_capacity = BUF_SIZE; + size_t ret = 1; + size_t bytes_written = 0; + + if (!src) { + perror("decompress_file(src)"); + goto cleanup; + } + + while (ret != 0) { + /* Load more input */ + size_t src_size = fread(src, 1, BUF_SIZE, in); + void *src_ptr = src; + void *src_end = src_ptr + src_size; + if (src_size == 0 || ferror(in)) { + printf("(TODO): Decompress: not enough input or error reading file\n"); + //TODO + ret = 0; + goto cleanup; + } + + /* Allocate destination buffer if it hasn't been allocated already */ + if (!dst) { + dst = malloc(dst_capacity); + if (!dst) { + perror("decompress_file(dst)"); + goto cleanup; + } + } + + // TODO + + /* Decompress: + * Continue while there is more input to read. + */ + while (src_ptr != src_end && ret != 0) { + // size_t dst_size = src_size; + size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); + size_t written = fwrite(dst, 1, dst_size, out); +// printf("Writing %zu bytes\n", dst_size); + bytes_written += dst_size; + if (written != dst_size) { + printf("Decompress: Failed to write to file\n"); + goto cleanup; + } + src_ptr += src_size; + src_size = src_end - src_ptr; + } + + /* Update input */ + + } + + printf("Wrote %zu bytes\n", bytes_written); + + cleanup: + free(src); + free(dst); + + return ret; +} + +int main2(int argc, char *argv[]) { + char inpFilename[256] = { 0 }; + char ldmFilename[256] = { 0 }; + char decFilename[256] = { 0 }; + + if (argc < 2) { + printf("Please specify input filename\n"); + return 0; + } + 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 */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *outFp = fopen(ldmFilename, "wb"); + size_t sizeIn = 0; + size_t sizeOut = 0; + size_t ret; + printf("compress : %s -> %s\n", inpFilename, ldmFilename); + ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); + if (ret) { + printf("compress : failed with code %zu\n", ret); + return ret; + } + printf("%s: %zu → %zu bytes, %.1f%%\n", + inpFilename, sizeIn, sizeOut, + (double)sizeOut / sizeIn * 100); + printf("compress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* decompress */ + { + FILE *inpFp = fopen(ldmFilename, "rb"); + FILE *outFp = fopen(decFilename, "wb"); + size_t ret; + + printf("decompress : %s -> %s\n", ldmFilename, decFilename); + ret = decompress_file(inpFp, outFp); + if (ret) { + printf("decompress : failed with code %zu\n", ret); + return ret; + } + printf("decompress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* verify */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); + } + return 0; +} +#endif + diff --git a/contrib/long_distance_matching/versions/v0.3/util.c b/contrib/long_distance_matching/versions/v0.3/util.c new file mode 100644 index 000000000..9ea4ca1e5 --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.3/util.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +#include "util.h" + +typedef uint8_t BYTE; +typedef uint16_t U16; +typedef uint32_t U32; +typedef int32_t S32; +typedef uint64_t U64; + +unsigned LDM_isLittleEndian(void) { + const union { U32 u; BYTE c[4]; } one = { 1 }; + return one.c[0]; +} + +U16 LDM_read16(const void *memPtr) { + U16 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +U16 LDM_readLE16(const void *memPtr) { + if (LDM_isLittleEndian()) { + return LDM_read16(memPtr); + } else { + const BYTE *p = (const BYTE *)memPtr; + return (U16)((U16)p[0] + (p[1] << 8)); + } +} + +void LDM_write16(void *memPtr, U16 value){ + memcpy(memPtr, &value, sizeof(value)); +} + +void LDM_write32(void *memPtr, U32 value) { + memcpy(memPtr, &value, sizeof(value)); +} + +void LDM_writeLE16(void *memPtr, U16 value) { + if (LDM_isLittleEndian()) { + LDM_write16(memPtr, value); + } else { + BYTE* p = (BYTE *)memPtr; + p[0] = (BYTE) value; + p[1] = (BYTE)(value>>8); + } +} + +U32 LDM_read32(const void *ptr) { + return *(const U32 *)ptr; +} + +U64 LDM_read64(const void *ptr) { + return *(const U64 *)ptr; +} + +void LDM_copy8(void *dst, const void *src) { + memcpy(dst, src, 8); +} + + diff --git a/contrib/long_distance_matching/versions/v0.3/util.h b/contrib/long_distance_matching/versions/v0.3/util.h new file mode 100644 index 000000000..90726412e --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.3/util.h @@ -0,0 +1,23 @@ +#ifndef LDM_UTIL_H +#define LDM_UTIL_H + +unsigned LDM_isLittleEndian(void); + +uint16_t LDM_read16(const void *memPtr); + +uint16_t LDM_readLE16(const void *memPtr); + +void LDM_write16(void *memPtr, uint16_t value); + +void LDM_write32(void *memPtr, uint32_t value); + +void LDM_writeLE16(void *memPtr, uint16_t value); + +uint32_t LDM_read32(const void *ptr); + +uint64_t LDM_read64(const void *ptr); + +void LDM_copy8(void *dst, const void *src); + + +#endif /* LDM_UTIL_H */ From 50502519fbd8adeeebed6f0b5232bcfc100bc63a Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 12 Jul 2017 09:47:00 -0700 Subject: [PATCH 114/318] Switch to using rolling hash only --- contrib/long_distance_matching/ldm.c | 40 +- .../versions/{v3 => v0.4}/Makefile | 0 .../versions/{v3 => v0.4}/ldm.c | 451 +++++++++++++---- .../versions/{v3 => v0.4}/ldm.h | 3 + .../versions/{v3 => v0.4}/main-ldm.c | 15 +- .../versions/{v3 => v0.4}/util.c | 5 + .../versions/{v3 => v0.4}/util.h | 2 + .../long_distance_matching/versions/v1/ldm.c | 394 --------------- .../long_distance_matching/versions/v1/ldm.h | 19 - .../versions/v1/main-ldm.c | 459 ----------------- .../versions/v2/Makefile | 32 -- .../long_distance_matching/versions/v2/ldm.c | 436 ---------------- .../long_distance_matching/versions/v2/ldm.h | 19 - .../versions/v2/main-ldm.c | 474 ------------------ 14 files changed, 404 insertions(+), 1945 deletions(-) rename contrib/long_distance_matching/versions/{v3 => v0.4}/Makefile (100%) rename contrib/long_distance_matching/versions/{v3 => v0.4}/ldm.c (51%) rename contrib/long_distance_matching/versions/{v3 => v0.4}/ldm.h (85%) rename contrib/long_distance_matching/versions/{v3 => v0.4}/main-ldm.c (98%) rename contrib/long_distance_matching/versions/{v3 => v0.4}/util.c (92%) rename contrib/long_distance_matching/versions/{v3 => v0.4}/util.h (91%) delete mode 100644 contrib/long_distance_matching/versions/v1/ldm.c delete mode 100644 contrib/long_distance_matching/versions/v1/ldm.h delete mode 100644 contrib/long_distance_matching/versions/v1/main-ldm.c delete mode 100644 contrib/long_distance_matching/versions/v2/Makefile delete mode 100644 contrib/long_distance_matching/versions/v2/ldm.c delete mode 100644 contrib/long_distance_matching/versions/v2/ldm.h delete mode 100644 contrib/long_distance_matching/versions/v2/main-ldm.c diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index ca4f0f2cf..79648097a 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -20,7 +20,7 @@ #define WINDOW_SIZE (1 << 23) #define MAX_WINDOW_SIZE 31 #define HASH_SIZE 4 -#define LDM_HASH_LENGTH 100 +#define LDM_HASH_LENGTH 4 // Should be multiple of four #define MINMATCH 4 @@ -106,7 +106,8 @@ typedef struct LDM_CCtx { const BYTE *lastPosHashed; /* Last position hashed */ hash_t lastHash; /* Hash corresponding to lastPosHashed */ const BYTE *nextIp; - hash_t nextHash; /* Hash corresponding to nextIp */ + const BYTE *nextPosHashed; + hash_t nextHash; /* Hash corresponding to nextPosHashed */ // Members for rolling hash. U32 lastSum; @@ -192,9 +193,9 @@ static void LDM_getRollingHashParts(U32 sum, LDM_sumStruct *sumStruct) { */ static void LDM_setNextHash(LDM_CCtx *cctx) { - U32 check; #ifdef RUN_CHECKS + U32 check; if ((cctx->nextIp - cctx->ibase != 1) && (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, @@ -204,22 +205,21 @@ static void LDM_setNextHash(LDM_CCtx *cctx) { cctx->DEBUG_setNextHash = cctx->nextIp; #endif - cctx->nextSum = LDM_getRollingHash((const char *)cctx->nextIp, LDM_HASH_LENGTH); - /* - check = LDM_updateRollingHash( +// cctx->nextSum = LDM_getRollingHash((const char *)cctx->nextIp, LDM_HASH_LENGTH); + cctx->nextSum = LDM_updateRollingHash( cctx->lastSum, LDM_HASH_LENGTH, (schar)((cctx->lastPosHashed)[0]), (schar)((cctx->lastPosHashed)[LDM_HASH_LENGTH])); - */ #ifdef RUN_CHECKS + check = LDM_getRollingHash((const char *)cctx->nextIp, LDM_HASH_LENGTH); + if (check != cctx->nextSum) { printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); // printf("INFO: %u %u %u\n", LDM_read32(cctx->nextIp), - } else { -// printf("CHECK: setNextHash passed\n"); } #endif + cctx->nextPosHashed = cctx->nextIp; cctx->nextHash = LDM_sumToHash(cctx->nextSum); #ifdef RUN_CHECKS @@ -254,9 +254,23 @@ static void LDM_putHashOfCurrentPositionFromHash( cctx->lastSum = sum; } +static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { +#ifdef RUN_CHECKS + if (cctx->ip != cctx->nextPosHashed) { + printf("CHECK failed: updateLastHashFromNextHash %zu\n", cctx->ip - cctx->ibase); + } +#endif + LDM_putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); +} + static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { U32 sum = LDM_getRollingHash((const char *)cctx->ip, LDM_HASH_LENGTH); hash_t hash = LDM_sumToHash(sum); +#ifdef RUN_CHECKS + if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { + printf("CHECK failed: putHashOfCurrentPosition %zu\n", cctx->ip - cctx->ibase); + } +#endif // hash_t hash = LDM_hashPosition(cctx->ip); LDM_putHashOfCurrentPositionFromHash(cctx, hash, sum); // printf("Offset %zu\n", cctx->ip - cctx->ibase); @@ -376,6 +390,7 @@ static void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->step = 1; cctx->nextIp = cctx->ip + cctx->step; + cctx->nextPosHashed = 0; cctx->DEBUG_setNextHash = 0; } @@ -503,7 +518,8 @@ static void LDM_outputBlock(LDM_CCtx *cctx, const BYTE *match) { while (cctx->ip < cctx->anchor + MINMATCH + matchLength + literalLength) { // printf("Loop\n"); if (cctx->ip > cctx->lastPosHashed) { - LDM_putHashOfCurrentPosition(cctx); + LDM_updateLastHashFromNextHash(cctx); +// LDM_putHashOfCurrentPosition(cctx); #ifdef LDM_ROLLING_HASH LDM_setNextHash(cctx); #endif @@ -526,7 +542,6 @@ static void LDM_outputBlock(LDM_CCtx *cctx, const BYTE *match) { size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; - U32 tmp_hash; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); /* Hash the first position and put it into the hash table. */ @@ -579,7 +594,8 @@ size_t LDM_compress(const void *src, size_t srcSize, // Set start of next block to current input pointer. cctx.anchor = cctx.ip; - LDM_putHashOfCurrentPosition(&cctx); + LDM_updateLastHashFromNextHash(&cctx); +// LDM_putHashOfCurrentPosition(&cctx); #ifndef LDM_ROLLING_HASH cctx.ip++; #endif diff --git a/contrib/long_distance_matching/versions/v3/Makefile b/contrib/long_distance_matching/versions/v0.4/Makefile similarity index 100% rename from contrib/long_distance_matching/versions/v3/Makefile rename to contrib/long_distance_matching/versions/v0.4/Makefile diff --git a/contrib/long_distance_matching/versions/v3/ldm.c b/contrib/long_distance_matching/versions/v0.4/ldm.c similarity index 51% rename from contrib/long_distance_matching/versions/v3/ldm.c rename to contrib/long_distance_matching/versions/v0.4/ldm.c index 1dedf5c37..79648097a 100644 --- a/contrib/long_distance_matching/versions/v3/ldm.c +++ b/contrib/long_distance_matching/versions/v0.4/ldm.c @@ -9,7 +9,7 @@ #define HASH_EVERY 1 -#define LDM_MEMORY_USAGE 16 +#define LDM_MEMORY_USAGE 22 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) @@ -17,10 +17,12 @@ #define LDM_OFFSET_SIZE 4 -#define WINDOW_SIZE (1 << 20) +#define WINDOW_SIZE (1 << 23) #define MAX_WINDOW_SIZE 31 #define HASH_SIZE 4 #define LDM_HASH_LENGTH 4 + +// Should be multiple of four #define MINMATCH 4 #define ML_BITS 4 @@ -28,7 +30,9 @@ #define RUN_BITS (8-ML_BITS) #define RUN_MASK ((1U<totalLiteralLength) / (double)stats->numMatches); printf("Average offset length: %.1f\n", ((double)stats->totalOffset) / (double)stats->numMatches); + printf("Num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", + stats->numCollisions, stats->numHashInserts, + stats->numHashInserts == 0 ? + 1.0 : (100.0 * (double)stats->numCollisions) / + (double)stats->numHashInserts); printf("=====================\n"); } @@ -93,19 +106,46 @@ typedef struct LDM_CCtx { const BYTE *lastPosHashed; /* Last position hashed */ hash_t lastHash; /* Hash corresponding to lastPosHashed */ const BYTE *nextIp; - hash_t nextHash; /* Hash corresponding to nextIp */ + const BYTE *nextPosHashed; + hash_t nextHash; /* Hash corresponding to nextPosHashed */ + + // Members for rolling hash. + U32 lastSum; + U32 nextSum; unsigned step; + + // DEBUG + const BYTE *DEBUG_setNextHash; } LDM_CCtx; +static int LDM_isValidMatch(const BYTE *p, const BYTE *match) { + U16 lengthLeft = MINMATCH; + const BYTE *curP = p; + const BYTE *curMatch = match; + + for (; lengthLeft >= 8; lengthLeft -= 8) { + if (LDM_read64(curP) != LDM_read64(curMatch)) { + return 0; + } + curP += 8; + curMatch += 8; + } + if (lengthLeft > 0) { + return LDM_read32(curP) == LDM_read32(curMatch); + } + return 1; +} + + + #ifdef LDM_ROLLING_HASH /** * Convert a sum computed from LDM_getRollingHash to a hash value in the range * of the hash table. */ static hash_t LDM_sumToHash(U32 sum) { - return sum % (LDM_HASHTABLESIZE >> 2); -// return sum & (LDM_HASHTABLESIZE - 1); + return sum & (LDM_HASH_SIZE_U32 - 1); } static U32 LDM_getRollingHash(const char *data, U32 len) { @@ -126,18 +166,115 @@ static U32 LDM_getRollingHash(const char *data, U32 len) { return (s1 & 0xffff) + (s2 << 16); } -static hash_t LDM_hashPosition(const void * const p) { - return LDM_sumToHash(LDM_getRollingHash((const char *)p, LDM_HASH_LENGTH)); -} - typedef struct LDM_sumStruct { U16 s1, s2; } LDM_sumStruct; +static U32 LDM_updateRollingHash(U32 sum, U32 len, + schar toRemove, schar toAdd) { + U32 s1 = (sum & 0xffff) - toRemove + toAdd; + U32 s2 = (sum >> 16) - (toRemove * len) + s1; + + return (s1 & 0xffff) + (s2 << 16); +} + + +/* +static hash_t LDM_hashPosition(const void * const p) { + return LDM_sumToHash(LDM_getRollingHash((const char *)p, LDM_HASH_LENGTH)); +} +*/ + +/* static void LDM_getRollingHashParts(U32 sum, LDM_sumStruct *sumStruct) { sumStruct->s1 = sum & 0xffff; sumStruct->s2 = sum >> 16; } +*/ + +static void LDM_setNextHash(LDM_CCtx *cctx) { + +#ifdef RUN_CHECKS + U32 check; + if ((cctx->nextIp - cctx->ibase != 1) && + (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { + printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, + cctx->DEBUG_setNextHash - cctx->ibase); + } + + cctx->DEBUG_setNextHash = cctx->nextIp; +#endif + +// cctx->nextSum = LDM_getRollingHash((const char *)cctx->nextIp, LDM_HASH_LENGTH); + cctx->nextSum = LDM_updateRollingHash( + cctx->lastSum, LDM_HASH_LENGTH, + (schar)((cctx->lastPosHashed)[0]), + (schar)((cctx->lastPosHashed)[LDM_HASH_LENGTH])); + +#ifdef RUN_CHECKS + check = LDM_getRollingHash((const char *)cctx->nextIp, LDM_HASH_LENGTH); + + if (check != cctx->nextSum) { + printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); +// printf("INFO: %u %u %u\n", LDM_read32(cctx->nextIp), + } +#endif + cctx->nextPosHashed = cctx->nextIp; + cctx->nextHash = LDM_sumToHash(cctx->nextSum); + +#ifdef RUN_CHECKS + if ((cctx->nextIp - cctx->lastPosHashed) != 1) { + printf("setNextHash: nextIp != lastPosHashed + 1. %zu %zu %zu\n", + cctx->nextIp - cctx->ibase, cctx->lastPosHashed - cctx->ibase, + cctx->ip - cctx->ibase); + } +#endif + +} + +static void LDM_putHashOfCurrentPositionFromHash( + LDM_CCtx *cctx, hash_t hash, U32 sum) { + /* + if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { + return; + } + */ +#ifdef COMPUTE_STATS + if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { + offset_t offset = (cctx->hashTable)[hash].offset; + cctx->stats.numHashInserts++; + if (offset == 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { + cctx->stats.numCollisions++; + } + } +#endif + (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; + cctx->lastPosHashed = cctx->ip; + cctx->lastHash = hash; + cctx->lastSum = sum; +} + +static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { +#ifdef RUN_CHECKS + if (cctx->ip != cctx->nextPosHashed) { + printf("CHECK failed: updateLastHashFromNextHash %zu\n", cctx->ip - cctx->ibase); + } +#endif + LDM_putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); +} + +static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { + U32 sum = LDM_getRollingHash((const char *)cctx->ip, LDM_HASH_LENGTH); + hash_t hash = LDM_sumToHash(sum); +#ifdef RUN_CHECKS + if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { + printf("CHECK failed: putHashOfCurrentPosition %zu\n", cctx->ip - cctx->ibase); + } +#endif +// hash_t hash = LDM_hashPosition(cctx->ip); + LDM_putHashOfCurrentPositionFromHash(cctx, hash, sum); +// printf("Offset %zu\n", cctx->ip - cctx->ibase); +} #else static hash_t LDM_hash(U32 sequence) { @@ -147,6 +284,39 @@ static hash_t LDM_hash(U32 sequence) { static hash_t LDM_hashPosition(const void * const p) { return LDM_hash(LDM_read32(p)); } + +static void LDM_putHashOfCurrentPositionFromHash( + LDM_CCtx *cctx, hash_t hash) { + /* + if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { + return; + } + */ +#ifdef COMPUTE_STATS + if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { + offset_t offset = (cctx->hashTable)[hash].offset; + cctx->stats.numHashInserts++; + if (offset == 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { + cctx->stats.numCollisions++; + } + } +#endif + + (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; +#ifdef RUN_CHECKS + if (cctx->ip - cctx->lastPosHashed != 1) { + printf("putHashError\n"); + } +#endif + cctx->lastPosHashed = cctx->ip; + cctx->lastHash = hash; +} + +static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { + hash_t hash = LDM_hashPosition(cctx->ip); + LDM_putHashOfCurrentPositionFromHash(cctx, hash); +} + #endif /* @@ -161,38 +331,19 @@ static hash_t LDM_hash5(U64 sequence) { } */ -static void LDM_putHashOfCurrentPositionFromHash( - LDM_CCtx *cctx, hash_t hash) { - if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { - return; - } - (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; - cctx->lastPosHashed = cctx->ip; - cctx->lastHash = hash; -} -static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - hash_t hash = LDM_hashPosition(cctx->ip); - LDM_putHashOfCurrentPositionFromHash(cctx, hash); -} - -static const BYTE *LDM_get_position_on_hash( +static const BYTE *LDM_getPositionOnHash( hash_t h, void *tableBase, const BYTE *srcBase) { const LDM_hashEntry * const hashTable = (LDM_hashEntry *)tableBase; return hashTable[h].offset + srcBase; } -static BYTE LDM_read_byte(const void *memPtr) { - BYTE val; - memcpy(&val, memPtr, 1); - return val; -} static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, const BYTE *pInLimit) { const BYTE * const pStart = pIn; while (pIn < pInLimit - 1) { - BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); + BYTE const diff = LDM_readByte(pMatch) ^ LDM_readByte(pIn); if (!diff) { pIn++; pMatch++; @@ -220,7 +371,11 @@ static void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->ip = cctx->ibase; cctx->iend = cctx->ibase + srcSize; +#ifdef LDM_ROLLING_HASH + cctx->ihashLimit = cctx->iend - LDM_HASH_LENGTH; +#else cctx->ihashLimit = cctx->iend - HASH_SIZE; +#endif cctx->imatchLimit = cctx->iend - MINMATCH; cctx->obase = (BYTE *)dst; @@ -232,11 +387,47 @@ static void LDM_initializeCCtx(LDM_CCtx *cctx, memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); cctx->lastPosHashed = NULL; - cctx->nextIp = NULL; cctx->step = 1; + cctx->nextIp = cctx->ip + cctx->step; + cctx->nextPosHashed = 0; + + cctx->DEBUG_setNextHash = 0; } +#ifdef LDM_ROLLING_HASH +static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { + cctx->nextIp = cctx->ip + cctx->step; + + do { + hash_t h; + U32 sum; +// printf("Call A\n"); + LDM_setNextHash(cctx); +// printf("End call a\n"); + h = cctx->nextHash; + sum = cctx->nextSum; + cctx->ip = cctx->nextIp; + cctx->nextIp += cctx->step; + + if (cctx->ip > cctx->imatchLimit) { + return 1; + } + + *match = LDM_getPositionOnHash(h, cctx->hashTable, cctx->ibase); + +// // Compute cctx->nextSum and cctx->nextHash from cctx->nextIp. +// LDM_setNextHash(cctx); + LDM_putHashOfCurrentPositionFromHash(cctx, h, sum); + +// printf("%u %u\n", cctx->lastHash, cctx->nextHash); + } while (cctx->ip - *match > WINDOW_SIZE || + !LDM_isValidMatch(cctx->ip, *match)); +// LDM_read64(*match) != LDM_read64(cctx->ip)); + LDM_setNextHash(cctx); + return 0; +} +#else static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { cctx->nextIp = cctx->ip; @@ -245,19 +436,108 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { cctx->ip = cctx->nextIp; cctx->nextIp += cctx->step; - if (cctx->nextIp > cctx->imatchLimit) { + if (cctx->ip > cctx->imatchLimit) { return 1; } - *match = LDM_get_position_on_hash(h, cctx->hashTable, cctx->ibase); + *match = LDM_getPositionOnHash(h, cctx->hashTable, cctx->ibase); cctx->nextHash = LDM_hashPosition(cctx->nextIp); LDM_putHashOfCurrentPositionFromHash(cctx, h); + } while (cctx->ip - *match > WINDOW_SIZE || - LDM_read64(*match) != LDM_read64(cctx->ip)); + !LDM_isValidMatch(cctx->ip, *match)); return 0; } +#endif + +/** + * Write current block (literals, literal length, match offset, + * match length). + * + * Update input pointer, inserting hashes into hash table along the + * way. + */ +static void LDM_outputBlock(LDM_CCtx *cctx, const BYTE *match) { + unsigned const literalLength = (unsigned)(cctx->ip - cctx->anchor); + unsigned const offset = cctx->ip - match; + unsigned const matchLength = LDM_count( + cctx->ip + MINMATCH, match + MINMATCH, cctx->ihashLimit); + BYTE *token = cctx->op++; + + cctx->stats.totalLiteralLength += literalLength; + cctx->stats.totalOffset += offset; + cctx->stats.totalMatchLength += matchLength + MINMATCH; + + /* Encode the literal length. */ + if (literalLength >= RUN_MASK) { + int len = (int)literalLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) { + *(cctx->op)++ = 255; + } + *(cctx->op)++ = (BYTE)len; + } else { + *token = (BYTE)(literalLength << ML_BITS); + } + + /* Encode the literals. */ + memcpy(cctx->op, cctx->anchor, literalLength); + cctx->op += literalLength; + + /* Encode the offset. */ + LDM_write32(cctx->op, offset); + cctx->op += LDM_OFFSET_SIZE; + + /* Encode match length */ + if (matchLength >= ML_MASK) { + unsigned matchLengthRemaining = matchLength; + *token += ML_MASK; + matchLengthRemaining -= ML_MASK; + LDM_write32(cctx->op, 0xFFFFFFFF); + while (matchLengthRemaining >= 4*0xFF) { + cctx->op += 4; + LDM_write32(cctx->op, 0xffffffff); + matchLengthRemaining -= 4*0xFF; + } + cctx->op += matchLengthRemaining / 255; + *(cctx->op)++ = (BYTE)(matchLengthRemaining % 255); + } else { + *token += (BYTE)(matchLength); + } + +// LDM_setNextHash(cctx); +// cctx->ip = cctx->lastPosHashed + 1; +// cctx->nextIp = cctx->ip + cctx->step; +// printf("HERE: %zu %zu %zu\n", cctx->ip - cctx->ibase, +// cctx->lastPosHashed - cctx->ibase, cctx->nextIp - cctx->ibase); + + cctx->nextIp = cctx->ip + cctx->step; + + while (cctx->ip < cctx->anchor + MINMATCH + matchLength + literalLength) { +// printf("Loop\n"); + if (cctx->ip > cctx->lastPosHashed) { + LDM_updateLastHashFromNextHash(cctx); +// LDM_putHashOfCurrentPosition(cctx); +#ifdef LDM_ROLLING_HASH + LDM_setNextHash(cctx); +#endif + } + /* + printf("Call b %zu %zu %zu\n", + cctx->lastPosHashed - cctx->ibase, + cctx->nextIp - cctx->ibase, + cctx->ip - cctx->ibase); + */ +// printf("end call b\n"); + cctx->ip++; + cctx->nextIp++; + } + +// printf("There: %zu %zu\n", cctx->ip - cctx->ibase, cctx->lastPosHashed - cctx->ibase); +} + // TODO: srcSize and maxDstSize is unused size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { @@ -266,12 +546,21 @@ size_t LDM_compress(const void *src, size_t srcSize, /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); +#ifdef LDM_ROLLING_HASH +// LDM_setNextHash(&cctx); +// tmp_hash = LDM_updateRollingHash(cctx.lastSum, LDM_HASH_LENGTH, +// cctx.ip[0], cctx.ip[LDM_HASH_LENGTH]); +// printf("Update test: %u %u\n", tmp_hash, cctx.nextSum); +// cctx.ip++; +#else cctx.ip++; cctx.nextHash = LDM_hashPosition(cctx.ip); +#endif // TODO: loop condition is not accurate. while (1) { const BYTE *match; +// printf("Start of loop\n"); /** * Find a match. @@ -282,6 +571,7 @@ size_t LDM_compress(const void *src, size_t srcSize, if (LDM_findBestMatch(&cctx, &match) != 0) { goto _last_literals; } +// printf("End of match finding\n"); cctx.stats.numMatches++; @@ -290,6 +580,7 @@ size_t LDM_compress(const void *src, size_t srcSize, */ while (cctx.ip > cctx.anchor && match > cctx.ibase && cctx.ip[-1] == match[-1]) { +// printf("Catch up\n"); cctx.ip--; match--; } @@ -298,67 +589,25 @@ size_t LDM_compress(const void *src, size_t srcSize, * Write current block (literals, literal length, match offset, match * length) and update pointers and hashes. */ - { - unsigned const literalLength = (unsigned)(cctx.ip - cctx.anchor); - unsigned const offset = cctx.ip - match; - unsigned const matchLength = LDM_count( - cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); - BYTE *token = cctx.op++; - - cctx.stats.totalLiteralLength += literalLength; - cctx.stats.totalOffset += offset; - cctx.stats.totalMatchLength += matchLength + MINMATCH; - - /* Encode the literal length. */ - if (literalLength >= RUN_MASK) { - int len = (int)literalLength - RUN_MASK; - *token = (RUN_MASK << ML_BITS); - for (; len >= 255; len -= 255) { - *(cctx.op)++ = 255; - } - *(cctx.op)++ = (BYTE)len; - } else { - *token = (BYTE)(literalLength << ML_BITS); - } - - /* Encode the literals. */ - memcpy(cctx.op, cctx.anchor, literalLength); - cctx.op += literalLength; - - /* Encode the offset. */ - LDM_write32(cctx.op, offset); - cctx.op += LDM_OFFSET_SIZE; - - /* Encode match length */ - if (matchLength >= ML_MASK) { - unsigned matchLengthRemaining = matchLength; - *token += ML_MASK; - matchLengthRemaining -= ML_MASK; - LDM_write32(cctx.op, 0xFFFFFFFF); - while (matchLengthRemaining >= 4*0xFF) { - cctx.op += 4; - LDM_write32(cctx.op, 0xffffffff); - matchLengthRemaining -= 4*0xFF; - } - cctx.op += matchLengthRemaining / 255; - *(cctx.op)++ = (BYTE)(matchLengthRemaining % 255); - } else { - *token += (BYTE)(matchLength); - } - - /* Update input pointer, inserting hashes into hash table along the - * way. - */ - while (cctx.ip < cctx.anchor + MINMATCH + matchLength + literalLength) { - LDM_putHashOfCurrentPosition(&cctx); - cctx.ip++; - } - } + LDM_outputBlock(&cctx, match); +// printf("End of loop\n"); // Set start of next block to current input pointer. cctx.anchor = cctx.ip; + LDM_updateLastHashFromNextHash(&cctx); +// LDM_putHashOfCurrentPosition(&cctx); +#ifndef LDM_ROLLING_HASH + cctx.ip++; +#endif + + /* LDM_putHashOfCurrentPosition(&cctx); - cctx.nextHash = LDM_hashPosition(++cctx.ip); + printf("Call c\n"); + LDM_setNextHash(&cctx); + printf("End call c\n"); + cctx.ip++; + cctx.nextIp++; + */ } _last_literals: /* Encode the last literals (no more matches). */ @@ -453,7 +702,7 @@ size_t LDM_decompress(const void *src, size_t compressSize, /* Copy match. */ cpy = dctx.op + length; - // Inefficient for now + // Inefficient for now. while (match < cpy - offset && dctx.op < dctx.oend) { *(dctx.op)++ = *match++; } @@ -461,4 +710,20 @@ size_t LDM_decompress(const void *src, size_t compressSize, return dctx.op - (BYTE *)dst; } +void LDM_test(const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { +#ifdef LDM_ROLLING_HASH + const BYTE *ip = (const BYTE *)src + 1125; + U32 sum = LDM_getRollingHash((const char *)ip, LDM_HASH_LENGTH); + U32 sum2; + ++ip; + for (; ip < (const BYTE *)src + 1125 + 100; ip++) { + sum2 = LDM_updateRollingHash(sum, LDM_HASH_LENGTH, + ip[-1], ip[LDM_HASH_LENGTH - 1]); + sum = LDM_getRollingHash((const char *)ip, LDM_HASH_LENGTH); + printf("TEST HASH: %zu %u %u\n", ip - (const BYTE *)src, sum, sum2); + } +#endif +} + diff --git a/contrib/long_distance_matching/versions/v3/ldm.h b/contrib/long_distance_matching/versions/v0.4/ldm.h similarity index 85% rename from contrib/long_distance_matching/versions/v3/ldm.h rename to contrib/long_distance_matching/versions/v0.4/ldm.h index 287d444dd..a34faac4f 100644 --- a/contrib/long_distance_matching/versions/v3/ldm.h +++ b/contrib/long_distance_matching/versions/v0.4/ldm.h @@ -16,4 +16,7 @@ size_t LDM_decompress(const void *src, size_t srcSize, void LDM_readHeader(const void *src, size_t *compressSize, size_t *decompressSize); +void LDM_test(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + #endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v3/main-ldm.c b/contrib/long_distance_matching/versions/v0.4/main-ldm.c similarity index 98% rename from contrib/long_distance_matching/versions/v3/main-ldm.c rename to contrib/long_distance_matching/versions/v0.4/main-ldm.c index 724d735dd..f8ae54698 100644 --- a/contrib/long_distance_matching/versions/v3/main-ldm.c +++ b/contrib/long_distance_matching/versions/v0.4/main-ldm.c @@ -15,6 +15,7 @@ // #define BUF_SIZE 16*1024 // Block size #define DEBUG +//#define TEST //#define ZSTD @@ -74,6 +75,11 @@ static int compress(const char *fname, const char *oname) { return 1; } +#ifdef TEST + LDM_test(src, statbuf.st_size, + dst + LDM_HEADER_SIZE, statbuf.st_size); +#endif + #ifdef ZSTD compressSize = ZSTD_compress(dst, statbuf.st_size, src, statbuf.st_size, 1); @@ -144,11 +150,6 @@ static int decompress(const char *fname, const char *oname) { /* Read the header. */ LDM_readHeader(src, &compressSize, &decompressSize); -#ifdef DEBUG - printf("Size, compressSize, decompressSize: %zu %zu %zu\n", - (size_t)statbuf.st_size, compressSize, decompressSize); -#endif - /* Go to the location corresponding to the last byte. */ if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { perror("lseek error"); @@ -256,7 +257,7 @@ int main(int argc, const char *argv[]) { return 1; } gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", + printf("Total compress time = %f seconds\n", (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + (double) (tv2.tv_sec - tv1.tv_sec)); } @@ -270,7 +271,7 @@ int main(int argc, const char *argv[]) { return 1; } gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", + printf("Total decompress time = %f seconds\n", (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + (double) (tv2.tv_sec - tv1.tv_sec)); } diff --git a/contrib/long_distance_matching/versions/v3/util.c b/contrib/long_distance_matching/versions/v0.4/util.c similarity index 92% rename from contrib/long_distance_matching/versions/v3/util.c rename to contrib/long_distance_matching/versions/v0.4/util.c index 9ea4ca1e5..70fcbc2ce 100644 --- a/contrib/long_distance_matching/versions/v3/util.c +++ b/contrib/long_distance_matching/versions/v0.4/util.c @@ -61,4 +61,9 @@ void LDM_copy8(void *dst, const void *src) { memcpy(dst, src, 8); } +BYTE LDM_readByte(const void *memPtr) { + BYTE val; + memcpy(&val, memPtr, 1); + return val; +} diff --git a/contrib/long_distance_matching/versions/v3/util.h b/contrib/long_distance_matching/versions/v0.4/util.h similarity index 91% rename from contrib/long_distance_matching/versions/v3/util.h rename to contrib/long_distance_matching/versions/v0.4/util.h index 90726412e..d1c3c999b 100644 --- a/contrib/long_distance_matching/versions/v3/util.h +++ b/contrib/long_distance_matching/versions/v0.4/util.h @@ -19,5 +19,7 @@ uint64_t LDM_read64(const void *ptr); void LDM_copy8(void *dst, const void *src); +uint8_t LDM_readByte(const void *ptr); + #endif /* LDM_UTIL_H */ diff --git a/contrib/long_distance_matching/versions/v1/ldm.c b/contrib/long_distance_matching/versions/v1/ldm.c deleted file mode 100644 index 266425f8a..000000000 --- a/contrib/long_distance_matching/versions/v1/ldm.c +++ /dev/null @@ -1,394 +0,0 @@ -#include -#include -#include -#include - -#include "ldm.h" - -#define LDM_MEMORY_USAGE 14 -#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) -#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) - -#define WINDOW_SIZE (1 << 20) -#define MAX_WINDOW_SIZE 31 -#define HASH_SIZE 4 -#define MINMATCH 4 - -#define ML_BITS 4 -#define ML_MASK ((1U<>8); - } -} - -static U32 LDM_read32(const void *ptr) { - return *(const U32 *)ptr; -} - -static U64 LDM_read64(const void *ptr) { - return *(const U64 *)ptr; -} - - -static void LDM_copy8(void *dst, const void *src) { - memcpy(dst, src, 8); -} - -static void LDM_wild_copy(void *dstPtr, const void *srcPtr, void *dstEnd) { - BYTE *d = (BYTE *)dstPtr; - const BYTE *s = (const BYTE *)srcPtr; - BYTE * const e = (BYTE *)dstEnd; - - do { - LDM_copy8(d, s); - d += 8; - s += 8; - } while (d < e); - -} - -struct hash_entry { - U64 offset; - tag t; -}; - -static U32 LDM_hash(U32 sequence) { - return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); -} - -static U32 LDM_hash5(U64 sequence) { - static const U64 prime5bytes = 889523592379ULL; - static const U64 prime8bytes = 11400714785074694791ULL; - const U32 hashLog = LDM_HASHLOG; - if (LDM_isLittleEndian()) - return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); - else - return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); -} - -static U32 LDM_hash_position(const void * const p) { - return LDM_hash(LDM_read32(p)); -} - -static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, - const BYTE *srcBase) { - U32 *hashTable = (U32 *) tableBase; - hashTable[h] = (U32)(p - srcBase); -} - -static void LDM_put_position(const BYTE *p, void *tableBase, - const BYTE *srcBase) { - U32 const h = LDM_hash_position(p); - LDM_put_position_on_hash(p, h, tableBase, srcBase); -} - -static const BYTE *LDM_get_position_on_hash( - U32 h, void *tableBase, const BYTE *srcBase) { - const U32 * const hashTable = (U32*)tableBase; - return hashTable[h] + srcBase; -} - -static BYTE LDM_read_byte(const void *memPtr) { - BYTE val; - memcpy(&val, memPtr, 1); - return val; -} - -static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { - const BYTE * const pStart = pIn; - while (pIn < pInLimit - 1) { - BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); - if (!diff) { - pIn++; - pMatch++; - continue; - } - return (unsigned)(pIn - pStart); - } - return (unsigned)(pIn - pStart); -} - -void LDM_read_header(void const *source, size_t *compressed_size, - size_t *decompressed_size) { - const U32 *ip = (const U32 *)source; - *compressed_size = *ip++; - *decompressed_size = *ip; -} - -size_t LDM_compress(void const *source, void *dest, size_t source_size, - size_t max_dest_size) { - const BYTE * const istart = (const BYTE*)source; - const BYTE *ip = istart; - const BYTE * const iend = istart + source_size; - const BYTE *ilimit = iend - HASH_SIZE; - const BYTE * const matchlimit = iend - HASH_SIZE; - const BYTE * const mflimit = iend - MINMATCH; - BYTE *op = (BYTE*) dest; - U32 hashTable[LDM_HASHTABLESIZE_U32]; - memset(hashTable, 0, sizeof(hashTable)); - - const BYTE *anchor = (const BYTE *)source; -// struct LDM_cctx cctx; - size_t output_size = 0; - - U32 forwardH; - - /* Hash first byte: put into hash table */ - - LDM_put_position(ip, hashTable, istart); - ip++; - forwardH = LDM_hash_position(ip); - - //TODO Loop terminates before ip>=ilimit. - while (ip < ilimit) { - const BYTE *match; - BYTE *token; - - /* Find a match */ - { - const BYTE *forwardIp = ip; - unsigned step = 1; - - do { - U32 const h = forwardH; - ip = forwardIp; - forwardIp += step; - - if (forwardIp > mflimit) { - goto _last_literals; - } - - match = LDM_get_position_on_hash(h, hashTable, istart); - - forwardH = LDM_hash_position(forwardIp); - LDM_put_position_on_hash(ip, h, hashTable, istart); - } while (ip - match > WINDOW_SIZE || - LDM_read64(match) != LDM_read64(ip)); - } - - // TODO catchup - while (ip > anchor && match > istart && ip[-1] == match[-1]) { - ip--; - match--; - } - - /* Encode literals */ - { - unsigned const litLength = (unsigned)(ip - anchor); - token = op++; - -#ifdef LDM_DEBUG - printf("Cur position: %zu\n", anchor - istart); - printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match); -#endif - /* - fwrite(match, 4, 1, stdout); - printf("\n"); - */ - - if (litLength >= RUN_MASK) { - int len = (int)litLength - RUN_MASK; - *token = (RUN_MASK << ML_BITS); - for (; len >= 255; len -= 255) { - *op++ = 255; - } - *op++ = (BYTE)len; - } else { - *token = (BYTE)(litLength << ML_BITS); - } -#ifdef LDM_DEBUG - printf("Literals "); - fwrite(anchor, litLength, 1, stdout); - printf("\n"); -#endif - memcpy(op, anchor, litLength); - //LDM_wild_copy(op, anchor, op + litLength); - op += litLength; - } -_next_match: - /* Encode offset */ - { - LDM_write32(op, ip - match); - op += 4; - } - - /* Encode Match Length */ - { - unsigned matchCode; - matchCode = LDM_count(ip + MINMATCH, match + MINMATCH, - matchlimit); -#ifdef LDM_DEBUG - printf("Match length %zu\n", matchCode + MINMATCH); - fwrite(ip, MINMATCH + matchCode, 1, stdout); - printf("\n"); -#endif - ip += MINMATCH + matchCode; - if (matchCode >= ML_MASK) { - *token += ML_MASK; - matchCode -= ML_MASK; - LDM_write32(op, 0xFFFFFFFF); - while (matchCode >= 4*0xFF) { - op += 4; - LDM_write32(op, 0xffffffff); - matchCode -= 4*0xFF; - } - op += matchCode / 255; - *op++ = (BYTE)(matchCode % 255); - } else { - *token += (BYTE)(matchCode); - } -#ifdef LDM_DEBUG - printf("\n"); -#endif - } - - anchor = ip; - - LDM_put_position(ip, hashTable, istart); - forwardH = LDM_hash_position(++ip); - } -_last_literals: - /* Encode last literals */ - { - size_t const lastRun = (size_t)(iend - anchor); - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *op++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255; accumulator -= 255) { - *op++ = 255; - } - *op++ = (BYTE)accumulator; - } else { - *op++ = (BYTE)(lastRun << ML_BITS); - } - memcpy(op, anchor, lastRun); - op += lastRun; - } - return (op - (BYTE *)dest); -} - -size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, - size_t max_decompressed_size) { - const BYTE *ip = (const BYTE *)source; - const BYTE * const iend = ip + compressed_size; - BYTE *op = (BYTE *)dest; - BYTE * const oend = op + max_decompressed_size; - BYTE *cpy; - - while (ip < iend) { - size_t length; - const BYTE *match; - size_t offset; - - /* get literal length */ - unsigned const token = *ip++; - if ((length=(token >> ML_BITS)) == RUN_MASK) { - unsigned s; - do { - s = *ip++; - length += s; - } while (s == 255); - } -#ifdef LDM_DEBUG - printf("Literal length: %zu\n", length); -#endif - - /* copy literals */ - cpy = op + length; -#ifdef LDM_DEBUG - printf("Literals "); - fwrite(ip, length, 1, stdout); - printf("\n"); -#endif - memcpy(op, ip, length); -// LDM_wild_copy(op, ip, cpy); - ip += length; - op = cpy; - - /* get offset */ - offset = LDM_read32(ip); - -#ifdef LDM_DEBUG - printf("Offset: %zu\n", offset); -#endif - ip += 4; - match = op - offset; - // LDM_write32(op, (U32)offset); - - /* get matchlength */ - length = token & ML_MASK; - if (length == ML_MASK) { - unsigned s; - do { - s = *ip++; - length += s; - } while (s == 255); - } - length += MINMATCH; -#ifdef LDM_DEBUG - printf("Match length: %zu\n", length); -#endif - /* copy match */ - cpy = op + length; - - // Inefficient for now - - while (match < cpy - offset && op < oend) { - *op++ = *match++; - } - } -// memcpy(dest, source, compressed_size); - return op - (BYTE *)dest; -} - - diff --git a/contrib/long_distance_matching/versions/v1/ldm.h b/contrib/long_distance_matching/versions/v1/ldm.h deleted file mode 100644 index f4ca25a38..000000000 --- a/contrib/long_distance_matching/versions/v1/ldm.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LDM_H -#define LDM_H - -#include /* size_t */ - -#define LDM_COMPRESS_SIZE 4 -#define LDM_DECOMPRESS_SIZE 4 -#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) - -size_t LDM_compress(void const *source, void *dest, size_t source_size, - size_t max_dest_size); - -size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, - size_t max_decompressed_size); - -void LDM_read_header(void const *source, size_t *compressed_size, - size_t *decompressed_size); - -#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v1/main-ldm.c b/contrib/long_distance_matching/versions/v1/main-ldm.c deleted file mode 100644 index 10869cce3..000000000 --- a/contrib/long_distance_matching/versions/v1/main-ldm.c +++ /dev/null @@ -1,459 +0,0 @@ -// TODO: file size must fit into a U32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "ldm.h" - -// #define BUF_SIZE 16*1024 // Block size -#define DEBUG - -//#define ZSTD - -#if 0 -static size_t compress_file(FILE *in, FILE *out, size_t *size_in, - size_t *size_out) { - char *src, *buf = NULL; - size_t r = 1; - size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; - - src = malloc(BUF_SIZE); - if (!src) { - printf("Not enough memory\n"); - goto cleanup; - } - - size = BUF_SIZE + LDM_HEADER_SIZE; - buf = malloc(size); - if (!buf) { - printf("Not enough memory\n"); - goto cleanup; - } - - - for (;;) { - k = fread(src, 1, BUF_SIZE, in); - if (k == 0) - break; - count_in += k; - - n = LDM_compress(src, buf, k, BUF_SIZE); - - // n = k; - // offset += n; - offset = k; - count_out += k; - -// k = fwrite(src, 1, offset, out); - - k = fwrite(buf, 1, offset, out); - if (k < offset) { - if (ferror(out)) - printf("Write failed\n"); - else - printf("Short write\n"); - goto cleanup; - } - - } - *size_in = count_in; - *size_out = count_out; - r = 0; - cleanup: - free(src); - free(buf); - return r; -} - -static size_t decompress_file(FILE *in, FILE *out) { - void *src = malloc(BUF_SIZE); - void *dst = NULL; - size_t dst_capacity = BUF_SIZE; - size_t ret = 1; - size_t bytes_written = 0; - - if (!src) { - perror("decompress_file(src)"); - goto cleanup; - } - - while (ret != 0) { - /* Load more input */ - size_t src_size = fread(src, 1, BUF_SIZE, in); - void *src_ptr = src; - void *src_end = src_ptr + src_size; - if (src_size == 0 || ferror(in)) { - printf("(TODO): Decompress: not enough input or error reading file\n"); - //TODO - ret = 0; - goto cleanup; - } - - /* Allocate destination buffer if it hasn't been allocated already */ - if (!dst) { - dst = malloc(dst_capacity); - if (!dst) { - perror("decompress_file(dst)"); - goto cleanup; - } - } - - // TODO - - /* Decompress: - * Continue while there is more input to read. - */ - while (src_ptr != src_end && ret != 0) { - // size_t dst_size = src_size; - size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); - size_t written = fwrite(dst, 1, dst_size, out); -// printf("Writing %zu bytes\n", dst_size); - bytes_written += dst_size; - if (written != dst_size) { - printf("Decompress: Failed to write to file\n"); - goto cleanup; - } - src_ptr += src_size; - src_size = src_end - src_ptr; - } - - /* Update input */ - - } - - printf("Wrote %zu bytes\n", bytes_written); - - cleanup: - free(src); - free(dst); - - return ret; -} -#endif - -static size_t compress(const char *fname, const char *oname) { - int fdin, fdout; - struct stat statbuf; - char *src, *dst; - - /* 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 size of input file */ - if (fstat (fdin, &statbuf) < 0) { - perror("Fstat error"); - return 1; - } - size_t size_in = statbuf.st_size; - - /* go to the location corresponding to the last byte */ - if (lseek(fdout, size_in + LDM_HEADER_SIZE - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write 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; - } - size_t out_size = statbuf.st_size + LDM_HEADER_SIZE; - - /* mmap the output file */ - if ((dst = mmap(0, out_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - - #ifdef ZSTD - size_t size_out = ZSTD_compress(dst, statbuf.st_size, - src, statbuf.st_size, 1); - #else - size_t size_out = LDM_compress(src, dst + LDM_HEADER_SIZE, statbuf.st_size, - statbuf.st_size); - size_out += LDM_HEADER_SIZE; - - // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &size_out, 4); - memcpy(dst + 4, &(statbuf.st_size), 4); - printf("Compressed size: %zu\n", size_out); - printf("Decompressed size: %zu\n", statbuf.st_size); - #endif - ftruncate(fdout, size_out); - - printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, - (unsigned)statbuf.st_size, (unsigned)size_out, oname, - (double)size_out / (statbuf.st_size) * 100); - - close(fdin); - close(fdout); - return 0; -} - -static size_t decompress(const char *fname, const char *oname) { - int fdin, fdout; - struct stat statbuf; - char *src, *dst; - - /* 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 size of 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 header */ - size_t compressed_size, decompressed_size; - LDM_read_header(src, &compressed_size, &decompressed_size); - - printf("Size, compressed_size, decompressed_size: %zu %zu %zu\n", - statbuf.st_size, compressed_size, decompressed_size); - - /* go to the location corresponding to the last byte */ - if (lseek(fdout, decompressed_size - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write error"); - return 1; - } - - /* mmap the output file */ - if ((dst = mmap(0, decompressed_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - - /* Copy input file to output file */ -// memcpy(dst, src, statbuf.st_size); - - #ifdef ZSTD - size_t size_out = ZSTD_decompress(dst, decomrpessed_size, - src + LDM_HEADER_SIZE, - statbuf.st_size - LDM_HEADER_SIZE); - #else - size_t size_out = LDM_decompress(src + LDM_HEADER_SIZE, dst, - statbuf.st_size - LDM_HEADER_SIZE, - decompressed_size); - printf("Ret size out: %zu\n", size_out); - #endif - ftruncate(fdout, size_out); - - close(fdin); - close(fdout); - return 0; -} - -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; -} - -static void verify(const char *inpFilename, const char *decFilename) { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); -} - -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); - - struct timeval tv1, tv2; - /* compress */ - { - gettimeofday(&tv1, NULL); - if (compress(inpFilename, ldmFilename)) { - printf("Compress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - } - - /* decompress */ - - gettimeofday(&tv1, NULL); - if (decompress(ldmFilename, decFilename)) { - printf("Decompress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - - /* verify */ - verify(inpFilename, decFilename); - return 0; -} - -#if 0 -int main2(int argc, char *argv[]) { - char inpFilename[256] = { 0 }; - char ldmFilename[256] = { 0 }; - char decFilename[256] = { 0 }; - - if (argc < 2) { - printf("Please specify input filename\n"); - return 0; - } - 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 */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *outFp = fopen(ldmFilename, "wb"); - size_t sizeIn = 0; - size_t sizeOut = 0; - size_t ret; - printf("compress : %s -> %s\n", inpFilename, ldmFilename); - ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); - if (ret) { - printf("compress : failed with code %zu\n", ret); - return ret; - } - printf("%s: %zu → %zu bytes, %.1f%%\n", - inpFilename, sizeIn, sizeOut, - (double)sizeOut / sizeIn * 100); - printf("compress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* decompress */ - { - FILE *inpFp = fopen(ldmFilename, "rb"); - FILE *outFp = fopen(decFilename, "wb"); - size_t ret; - - printf("decompress : %s -> %s\n", ldmFilename, decFilename); - ret = decompress_file(inpFp, outFp); - if (ret) { - printf("decompress : failed with code %zu\n", ret); - return ret; - } - printf("decompress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* verify */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); - } - return 0; -} -#endif - diff --git a/contrib/long_distance_matching/versions/v2/Makefile b/contrib/long_distance_matching/versions/v2/Makefile deleted file mode 100644 index 4e04fd6a2..000000000 --- a/contrib/long_distance_matching/versions/v2/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# ################################################################ -# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. -# All rights reserved. -# -# This source code is licensed under the BSD-style license found in the -# LICENSE file in the root directory of this source tree. An additional grant -# of patent rights can be found in the PATENTS file in the same directory. -# ################################################################ - -# This Makefile presumes libzstd is installed, using `sudo make install` - - -LDFLAGS += -lzstd - -.PHONY: default all clean - -default: all - -all: main-ldm - - -#main : ldm.c main.c -# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -main-ldm : ldm.c main-ldm.c - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -clean: - @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main main-ldm - @echo Cleaning completed - diff --git a/contrib/long_distance_matching/versions/v2/ldm.c b/contrib/long_distance_matching/versions/v2/ldm.c deleted file mode 100644 index 9081d1362..000000000 --- a/contrib/long_distance_matching/versions/v2/ldm.c +++ /dev/null @@ -1,436 +0,0 @@ -#include -#include -#include -#include - -#include "ldm.h" - -#define HASH_EVERY 7 - -#define LDM_MEMORY_USAGE 14 -#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) -#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) - -#define WINDOW_SIZE (1 << 20) -#define MAX_WINDOW_SIZE 31 -#define HASH_SIZE 8 -#define MINMATCH 8 - -#define ML_BITS 4 -#define ML_MASK ((1U<>8); - } -} - -static U32 LDM_read32(const void *ptr) { - return *(const U32 *)ptr; -} - -static U64 LDM_read64(const void *ptr) { - return *(const U64 *)ptr; -} - -static void LDM_copy8(void *dst, const void *src) { - memcpy(dst, src, 8); -} - -typedef struct compress_stats { - U32 num_matches; - U32 total_match_length; - U32 total_literal_length; - U64 total_offset; -} compress_stats; - -static void LDM_printCompressStats(const compress_stats *stats) { - printf("=====================\n"); - printf("Compression statistics\n"); - printf("Total number of matches: %u\n", stats->num_matches); - printf("Average match length: %.1f\n", ((double)stats->total_match_length) / - (double)stats->num_matches); - printf("Average literal length: %.1f\n", - ((double)stats->total_literal_length) / (double)stats->num_matches); - printf("Average offset length: %.1f\n", - ((double)stats->total_offset) / (double)stats->num_matches); - printf("=====================\n"); -} - -// TODO: unused. -struct hash_entry { - U64 offset; - tag t; -}; - -static U32 LDM_hash(U32 sequence) { - return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); -} - -static U32 LDM_hash5(U64 sequence) { - static const U64 prime5bytes = 889523592379ULL; - static const U64 prime8bytes = 11400714785074694791ULL; - const U32 hashLog = LDM_HASHLOG; - if (LDM_isLittleEndian()) - return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); - else - return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); -} - -static U32 LDM_hash_position(const void * const p) { - return LDM_hash(LDM_read32(p)); -} - -static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, - const BYTE *srcBase) { - if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { - return; - } - - U32 *hashTable = (U32 *) tableBase; - hashTable[h] = (U32)(p - srcBase); -} - -static void LDM_put_position(const BYTE *p, void *tableBase, - const BYTE *srcBase) { - if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { - return; - } - U32 const h = LDM_hash_position(p); - LDM_put_position_on_hash(p, h, tableBase, srcBase); -} - -static const BYTE *LDM_get_position_on_hash( - U32 h, void *tableBase, const BYTE *srcBase) { - const U32 * const hashTable = (U32*)tableBase; - return hashTable[h] + srcBase; -} - -static BYTE LDM_read_byte(const void *memPtr) { - BYTE val; - memcpy(&val, memPtr, 1); - return val; -} - -static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { - const BYTE * const pStart = pIn; - while (pIn < pInLimit - 1) { - BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); - if (!diff) { - pIn++; - pMatch++; - continue; - } - return (unsigned)(pIn - pStart); - } - return (unsigned)(pIn - pStart); -} - -void LDM_read_header(const void *src, size_t *compressSize, - size_t *decompressSize) { - const U32 *ip = (const U32 *)src; - *compressSize = *ip++; - *decompressSize = *ip; -} - -// TODO: maxDstSize is unused -size_t LDM_compress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { - const BYTE * const istart = (const BYTE*)src; - const BYTE *ip = istart; - const BYTE * const iend = istart + srcSize; - const BYTE *ilimit = iend - HASH_SIZE; - const BYTE * const matchlimit = iend - HASH_SIZE; - const BYTE * const mflimit = iend - MINMATCH; - BYTE *op = (BYTE*) dst; - - compress_stats compressStats = { 0 }; - - U32 hashTable[LDM_HASHTABLESIZE_U32]; - memset(hashTable, 0, sizeof(hashTable)); - - const BYTE *anchor = (const BYTE *)src; -// struct LDM_cctx cctx; - size_t output_size = 0; - - U32 forwardH; - - /* Hash first byte: put into hash table */ - - LDM_put_position(ip, hashTable, istart); - const BYTE *lastHash = ip; - ip++; - forwardH = LDM_hash_position(ip); - - //TODO Loop terminates before ip>=ilimit. - while (ip < ilimit) { - const BYTE *match; - BYTE *token; - - /* Find a match */ - { - const BYTE *forwardIp = ip; - unsigned step = 1; - - do { - U32 const h = forwardH; - ip = forwardIp; - forwardIp += step; - - if (forwardIp > mflimit) { - goto _last_literals; - } - - match = LDM_get_position_on_hash(h, hashTable, istart); - - forwardH = LDM_hash_position(forwardIp); - LDM_put_position_on_hash(ip, h, hashTable, istart); - lastHash = ip; - } while (ip - match > WINDOW_SIZE || - LDM_read64(match) != LDM_read64(ip)); - } - compressStats.num_matches++; - - /* Catchup: look back to extend match from found match */ - while (ip > anchor && match > istart && ip[-1] == match[-1]) { - ip--; - match--; - } - - /* Encode literals */ - { - unsigned const litLength = (unsigned)(ip - anchor); - token = op++; - - compressStats.total_literal_length += litLength; - -#ifdef LDM_DEBUG - printf("Cur position: %zu\n", anchor - istart); - printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match); -#endif - - if (litLength >= RUN_MASK) { - int len = (int)litLength - RUN_MASK; - *token = (RUN_MASK << ML_BITS); - for (; len >= 255; len -= 255) { - *op++ = 255; - } - *op++ = (BYTE)len; - } else { - *token = (BYTE)(litLength << ML_BITS); - } -#ifdef LDM_DEBUG - printf("Literals "); - fwrite(anchor, litLength, 1, stdout); - printf("\n"); -#endif - memcpy(op, anchor, litLength); - op += litLength; - } -_next_match: - /* Encode offset */ - { - /* - LDM_writeLE16(op, ip-match); - op += 2; - */ - LDM_write32(op, ip - match); - op += 4; - compressStats.total_offset += (ip - match); - } - - /* Encode Match Length */ - { - unsigned matchCode; - matchCode = LDM_count(ip + MINMATCH, match + MINMATCH, - matchlimit); -#ifdef LDM_DEBUG - printf("Match length %zu\n", matchCode + MINMATCH); - fwrite(ip, MINMATCH + matchCode, 1, stdout); - printf("\n"); -#endif - compressStats.total_match_length += matchCode + MINMATCH; - unsigned ctr = 1; - ip++; - for (; ctr < MINMATCH + matchCode; ip++, ctr++) { - LDM_put_position(ip, hashTable, istart); - } -// ip += MINMATCH + matchCode; - if (matchCode >= ML_MASK) { - *token += ML_MASK; - matchCode -= ML_MASK; - LDM_write32(op, 0xFFFFFFFF); - while (matchCode >= 4*0xFF) { - op += 4; - LDM_write32(op, 0xffffffff); - matchCode -= 4*0xFF; - } - op += matchCode / 255; - *op++ = (BYTE)(matchCode % 255); - } else { - *token += (BYTE)(matchCode); - } -#ifdef LDM_DEBUG - printf("\n"); - -#endif - } - - anchor = ip; - - LDM_put_position(ip, hashTable, istart); - forwardH = LDM_hash_position(++ip); - lastHash = ip; - } -_last_literals: - /* Encode last literals */ - { - size_t const lastRun = (size_t)(iend - anchor); - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *op++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255; accumulator -= 255) { - *op++ = 255; - } - *op++ = (BYTE)accumulator; - } else { - *op++ = (BYTE)(lastRun << ML_BITS); - } - memcpy(op, anchor, lastRun); - op += lastRun; - } - LDM_printCompressStats(&compressStats); - return (op - (BYTE *)dst); -} - -typedef struct LDM_DCtx { - const BYTE * const ibase; /* Pointer to base of input */ - const BYTE *ip; /* Pointer to current input position */ - const BYTE *iend; /* End of source */ - BYTE *op; /* Pointer to output */ - const BYTE * const oend; /* Pointer to end of output */ - -} LDM_DCtx; - -size_t LDM_decompress(const void *src, size_t compressed_size, - void *dst, size_t max_decompressed_size) { - const BYTE *ip = (const BYTE *)src; - const BYTE * const iend = ip + compressed_size; - BYTE *op = (BYTE *)dst; - BYTE * const oend = op + max_decompressed_size; - BYTE *cpy; - - while (ip < iend) { - size_t length; - const BYTE *match; - size_t offset; - - /* get literal length */ - unsigned const token = *ip++; - if ((length=(token >> ML_BITS)) == RUN_MASK) { - unsigned s; - do { - s = *ip++; - length += s; - } while (s == 255); - } -#ifdef LDM_DEBUG - printf("Literal length: %zu\n", length); -#endif - - /* copy literals */ - cpy = op + length; -#ifdef LDM_DEBUG - printf("Literals "); - fwrite(ip, length, 1, stdout); - printf("\n"); -#endif - memcpy(op, ip, length); - ip += length; - op = cpy; - - /* get offset */ - /* - offset = LDM_readLE16(ip); - ip += 2; - */ - offset = LDM_read32(ip); - ip += 4; -#ifdef LDM_DEBUG - printf("Offset: %zu\n", offset); -#endif - match = op - offset; - // LDM_write32(op, (U32)offset); - - /* get matchlength */ - length = token & ML_MASK; - if (length == ML_MASK) { - unsigned s; - do { - s = *ip++; - length += s; - } while (s == 255); - } - length += MINMATCH; -#ifdef LDM_DEBUG - printf("Match length: %zu\n", length); -#endif - /* copy match */ - cpy = op + length; - - // Inefficient for now - while (match < cpy - offset && op < oend) { - *op++ = *match++; - } - } - return op - (BYTE *)dst; -} - - diff --git a/contrib/long_distance_matching/versions/v2/ldm.h b/contrib/long_distance_matching/versions/v2/ldm.h deleted file mode 100644 index 0ac7b2ece..000000000 --- a/contrib/long_distance_matching/versions/v2/ldm.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LDM_H -#define LDM_H - -#include /* size_t */ - -#define LDM_COMPRESS_SIZE 4 -#define LDM_DECOMPRESS_SIZE 4 -#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) - -size_t LDM_compress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -size_t LDM_decompress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -void LDM_read_header(const void *src, size_t *compressSize, - size_t *decompressSize); - -#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v2/main-ldm.c b/contrib/long_distance_matching/versions/v2/main-ldm.c deleted file mode 100644 index 0017335b8..000000000 --- a/contrib/long_distance_matching/versions/v2/main-ldm.c +++ /dev/null @@ -1,474 +0,0 @@ -// TODO: file size must fit into a U32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "ldm.h" - -// #define BUF_SIZE 16*1024 // Block size -#define DEBUG - -//#define ZSTD - -/* Compress file given by fname and output to oname. - * Returns 0 if successful, error code otherwise. - */ -static int compress(const char *fname, const char *oname) { - int fdin, fdout; - struct stat statbuf; - char *src, *dst; - - /* 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; - } - - size_t maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; - - /* Go to the location corresponding to the last byte. */ - /* TODO: fallocate? */ - if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* Write a dummy byte at the last location. */ - if (write(fdout, "", 1) != 1) { - perror("write 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; - } - - /* mmap the output file */ - if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - -#ifdef ZSTD - size_t compressSize = ZSTD_compress(dst, statbuf.st_size, - src, statbuf.st_size, 1); -#else - size_t compressSize = LDM_HEADER_SIZE + - LDM_compress(src, statbuf.st_size, - dst + LDM_HEADER_SIZE, statbuf.st_size); - - // Write compress and decompress size to header - // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &compressSize, 4); - memcpy(dst + 4, &(statbuf.st_size), 4); - -#ifdef DEBUG - printf("Compressed size: %zu\n", compressSize); - printf("Decompressed size: %zu\n", statbuf.st_size); -#endif -#endif - - // Truncate file to compressSize. - ftruncate(fdout, compressSize); - - printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, - (unsigned)statbuf.st_size, (unsigned)compressSize, oname, - (double)compressSize / (statbuf.st_size) * 100); - - // Close files. - close(fdin); - close(fdout); - return 0; -} - -/* 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; - - /* 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. */ - size_t compressSize, decompressSize; - LDM_read_header(src, &compressSize, &decompressSize); - -#ifdef DEBUG - printf("Size, compressSize, decompressSize: %zu %zu %zu\n", - statbuf.st_size, compressSize, decompressSize); -#endif - - /* Go to the location corresponding to the last byte. */ - if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write error"); - return 1; - } - - /* mmap the output file */ - if ((dst = mmap(0, decompressSize, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - -#ifdef ZSTD - size_t outSize = ZSTD_decompress(dst, decomrpessed_size, - src + LDM_HEADER_SIZE, - statbuf.st_size - LDM_HEADER_SIZE); -#else - size_t outSize = LDM_decompress( - src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, - dst, decompressSize); - - printf("Ret size out: %zu\n", outSize); - #endif - ftruncate(fdout, 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 void verify(const char *inpFilename, const char *decFilename) { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); -} - -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); - - struct timeval tv1, tv2; - - /* Compress */ - - gettimeofday(&tv1, NULL); - if (compress(inpFilename, ldmFilename)) { - printf("Compress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - - /* Decompress */ - - gettimeofday(&tv1, NULL); - if (decompress(ldmFilename, decFilename)) { - printf("Decompress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - - /* verify */ - verify(inpFilename, decFilename); - return 0; -} - - -#if 0 -static size_t compress_file(FILE *in, FILE *out, size_t *size_in, - size_t *size_out) { - char *src, *buf = NULL; - size_t r = 1; - size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; - - src = malloc(BUF_SIZE); - if (!src) { - printf("Not enough memory\n"); - goto cleanup; - } - - size = BUF_SIZE + LDM_HEADER_SIZE; - buf = malloc(size); - if (!buf) { - printf("Not enough memory\n"); - goto cleanup; - } - - - for (;;) { - k = fread(src, 1, BUF_SIZE, in); - if (k == 0) - break; - count_in += k; - - n = LDM_compress(src, buf, k, BUF_SIZE); - - // n = k; - // offset += n; - offset = k; - count_out += k; - -// k = fwrite(src, 1, offset, out); - - k = fwrite(buf, 1, offset, out); - if (k < offset) { - if (ferror(out)) - printf("Write failed\n"); - else - printf("Short write\n"); - goto cleanup; - } - - } - *size_in = count_in; - *size_out = count_out; - r = 0; - cleanup: - free(src); - free(buf); - return r; -} - -static size_t decompress_file(FILE *in, FILE *out) { - void *src = malloc(BUF_SIZE); - void *dst = NULL; - size_t dst_capacity = BUF_SIZE; - size_t ret = 1; - size_t bytes_written = 0; - - if (!src) { - perror("decompress_file(src)"); - goto cleanup; - } - - while (ret != 0) { - /* Load more input */ - size_t src_size = fread(src, 1, BUF_SIZE, in); - void *src_ptr = src; - void *src_end = src_ptr + src_size; - if (src_size == 0 || ferror(in)) { - printf("(TODO): Decompress: not enough input or error reading file\n"); - //TODO - ret = 0; - goto cleanup; - } - - /* Allocate destination buffer if it hasn't been allocated already */ - if (!dst) { - dst = malloc(dst_capacity); - if (!dst) { - perror("decompress_file(dst)"); - goto cleanup; - } - } - - // TODO - - /* Decompress: - * Continue while there is more input to read. - */ - while (src_ptr != src_end && ret != 0) { - // size_t dst_size = src_size; - size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); - size_t written = fwrite(dst, 1, dst_size, out); -// printf("Writing %zu bytes\n", dst_size); - bytes_written += dst_size; - if (written != dst_size) { - printf("Decompress: Failed to write to file\n"); - goto cleanup; - } - src_ptr += src_size; - src_size = src_end - src_ptr; - } - - /* Update input */ - - } - - printf("Wrote %zu bytes\n", bytes_written); - - cleanup: - free(src); - free(dst); - - return ret; -} - -int main2(int argc, char *argv[]) { - char inpFilename[256] = { 0 }; - char ldmFilename[256] = { 0 }; - char decFilename[256] = { 0 }; - - if (argc < 2) { - printf("Please specify input filename\n"); - return 0; - } - 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 */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *outFp = fopen(ldmFilename, "wb"); - size_t sizeIn = 0; - size_t sizeOut = 0; - size_t ret; - printf("compress : %s -> %s\n", inpFilename, ldmFilename); - ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); - if (ret) { - printf("compress : failed with code %zu\n", ret); - return ret; - } - printf("%s: %zu → %zu bytes, %.1f%%\n", - inpFilename, sizeIn, sizeOut, - (double)sizeOut / sizeIn * 100); - printf("compress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* decompress */ - { - FILE *inpFp = fopen(ldmFilename, "rb"); - FILE *outFp = fopen(decFilename, "wb"); - size_t ret; - - printf("decompress : %s -> %s\n", ldmFilename, decFilename); - ret = decompress_file(inpFp, outFp); - if (ret) { - printf("decompress : failed with code %zu\n", ret); - return ret; - } - printf("decompress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* verify */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); - } - return 0; -} -#endif - From e0d416246403c6606bff15eb844b6c12155751de Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 12 Jul 2017 09:50:24 -0700 Subject: [PATCH 115/318] Minor fix for non-rolling hash --- contrib/long_distance_matching/ldm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 79648097a..a1fe6174b 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -518,10 +518,11 @@ static void LDM_outputBlock(LDM_CCtx *cctx, const BYTE *match) { while (cctx->ip < cctx->anchor + MINMATCH + matchLength + literalLength) { // printf("Loop\n"); if (cctx->ip > cctx->lastPosHashed) { - LDM_updateLastHashFromNextHash(cctx); -// LDM_putHashOfCurrentPosition(cctx); #ifdef LDM_ROLLING_HASH + LDM_updateLastHashFromNextHash(cctx); LDM_setNextHash(cctx); +#else + LDM_putHashOfCurrentPosition(cctx); #endif } /* @@ -594,9 +595,10 @@ size_t LDM_compress(const void *src, size_t srcSize, // Set start of next block to current input pointer. cctx.anchor = cctx.ip; +#ifdef LDM_ROLLING_HASH LDM_updateLastHashFromNextHash(&cctx); -// LDM_putHashOfCurrentPosition(&cctx); -#ifndef LDM_ROLLING_HASH +#else + LDM_putHashOfCurrentPosition(&cctx); cctx.ip++; #endif From 3a48ffd4fd7a63ea36e2abae141a4fd4b7df847d Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 12 Jul 2017 10:53:19 -0700 Subject: [PATCH 116/318] Fix sumToHash to use hash space more efficiently --- contrib/long_distance_matching/ldm.c | 52 +++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index a1fe6174b..4ed09cff7 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -7,9 +7,9 @@ #include "ldm.h" #include "util.h" -#define HASH_EVERY 1 +#define HASH_EVERY 7 -#define LDM_MEMORY_USAGE 22 +#define LDM_MEMORY_USAGE 18 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) @@ -132,7 +132,7 @@ static int LDM_isValidMatch(const BYTE *p, const BYTE *match) { curMatch += 8; } if (lengthLeft > 0) { - return LDM_read32(curP) == LDM_read32(curMatch); + return (LDM_read32(curP) == LDM_read32(curMatch)); } return 1; } @@ -144,8 +144,15 @@ static int LDM_isValidMatch(const BYTE *p, const BYTE *match) { * Convert a sum computed from LDM_getRollingHash to a hash value in the range * of the hash table. */ +#define LDM_SUM2HASH2(s1,s2) (((s1) + (s2)) & 0xFFFF) +#define LDM_SUM2HASH(sum) (LDM_SUM2HASH2((sum)&0xFFFF,(sum)>>16)) + static hash_t LDM_sumToHash(U32 sum) { - return sum & (LDM_HASH_SIZE_U32 - 1); +// return sum & (LDM_HASH_SIZE_U32 - 1); +// return sum % (LDM_HASHTABLESIZE_U32 ); + + return ((sum* 2654435761U) >> ((32)-LDM_HASHLOG)); +// return LDM_SUM2HASH2(sum&0xFFFF, sum >> 16); } static U32 LDM_getRollingHash(const char *data, U32 len) { @@ -240,15 +247,32 @@ static void LDM_putHashOfCurrentPositionFromHash( } */ #ifdef COMPUTE_STATS - if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { + if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32 ) { offset_t offset = (cctx->hashTable)[hash].offset; cctx->stats.numHashInserts++; - if (offset == 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { + if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { +// printf("%u %u %zu\n", hash, offset, cctx->ip - cctx->ibase); +// printf("TST: %u %u\n", LDM_read32(cctx->ip), LDM_read32(offset + cctx->ibase)); cctx->stats.numCollisions++; } } + #endif - (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; + + if (((cctx->ip - cctx->ibase) & HASH_EVERY) == HASH_EVERY) { +#ifdef COMPUTE_STATS + /* + offset_t offset = (cctx->hashTable)[hash].offset; + if (offset == 0) { + printf("NEW HASH: %u\n", hash); + } + */ +#endif + + (cctx->hashTable)[hash] = (LDM_hashEntry){ (offset_t)(cctx->ip - cctx->ibase) }; + } + + // Book-keeping cctx->lastPosHashed = cctx->ip; cctx->lastHash = hash; cctx->lastSum = sum; @@ -296,7 +320,8 @@ static void LDM_putHashOfCurrentPositionFromHash( if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { offset_t offset = (cctx->hashTable)[hash].offset; cctx->stats.numHashInserts++; - if (offset == 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { + + if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { cctx->stats.numCollisions++; } } @@ -629,6 +654,17 @@ _last_literals: cctx.op += lastRun; } LDM_printCompressStats(&cctx.stats); + + { + U32 tmp = 0; + U32 ctr = 0; + for (; tmp < LDM_HASH_SIZE_U32; tmp++) { + if ((cctx.hashTable)[tmp].offset == 0) { + ctr++; + } + } + printf("HASH: %u %u\n", ctr, LDM_HASH_SIZE_U32); + } return (cctx.op - (const BYTE *)cctx.obase); } From 356ddb649f6c199cb087bf4a74d028c5e80c0f38 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 12:21:21 -0700 Subject: [PATCH 117/318] working with flush job->src.size and fixed cLevel --- contrib/adaptive-compression/adapt.c | 46 ++++++++++++++++++---------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index b0bcb74f2..b841cefaf 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -29,6 +29,7 @@ static UTIL_freq_t g_ticksPerSecond; typedef struct { void* start; size_t size; + size_t capacity; } buffer_t; typedef struct { @@ -74,6 +75,8 @@ typedef struct { pthread_cond_t allJobsCompleted_cond; pthread_mutex_t jobWrite_mutex; pthread_cond_t jobWrite_cond; + size_t lastDictSize; + size_t targetDictSize; inBuff_t input; cStat_t stats; jobDescription* jobs; @@ -136,6 +139,8 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobReadyID = 0; ctx->jobCompressedID = 0; ctx->jobWriteID = 0; + ctx->targetDictSize = FILE_CHUNK_SIZE >> 1; + ctx->lastDictSize = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); /* initializing jobs */ { @@ -151,6 +156,9 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) freeCCtx(ctx); return NULL; } + job->src.capacity = FILE_CHUNK_SIZE; + job->dst.capacity = ZSTD_compressBound(FILE_CHUNK_SIZE); + job->dict.capacity = FILE_CHUNK_SIZE; } } ctx->nextJobID = 0; @@ -159,8 +167,8 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->adaptParam = DEFAULT_ADAPT_PARAM; ctx->cctx = ZSTD_createCCtx(); ctx->input.filled = 0; - ctx->input.buffer.size = 2 * FILE_CHUNK_SIZE; - ctx->input.buffer.start = malloc(ctx->input.buffer.size); + ctx->input.buffer.capacity = 2 * FILE_CHUNK_SIZE; + ctx->input.buffer.start = malloc(ctx->input.buffer.capacity); if (!ctx->input.buffer.start) { DISPLAY("Error: could not allocate input buffer\n"); freeCCtx(ctx); @@ -249,16 +257,19 @@ static void* compressionThread(void* arg) } pthread_mutex_unlock(&ctx->jobReady_mutex); DEBUG(3, "compressionThread(): continuing after job ready\n"); + DEBUG(3, "%.*s\n", (int)job->dict.size, (char*)job->dict.start); + DEBUG(3, "DICTIONARY ENDED\n"); + DEBUG(2, "%.*s", (int)job->src.size, (char*)job->src.start); /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); DEBUG(3, "cLevel used: %u\n", cLevel); - DEBUG(2, "dictSize: %zu, srcSize: %zu\n", job->dict.size, job->src.size); - DEBUG(2, "compression level used: %u\n", cLevel); + DEBUG(3, "dictSize: %zu, srcSize: %zu\n", job->dict.size, job->src.size); + DEBUG(3, "compression level used: %u\n", cLevel); /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->dict.start, job->dict.size, cLevel); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->dict.start, job->dict.size, 6); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); @@ -268,8 +279,8 @@ static void* compressionThread(void* arg) } /* continue compression */ - if (currJob != 0) { /* not first job */ - size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size); + if (currJob != 0) { /* not first job flush/overwrite the frame header */ + size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size); if (ZSTD_isError(hSize)) { DISPLAY("Error: something went wrong while continuing compression\n"); job->compressedSize = hSize; @@ -279,13 +290,14 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } job->compressedSize = (job->lastJob) ? - ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size) : - ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size); + ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size) : + ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size); if (ZSTD_isError(job->compressedSize)) { DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(job->compressedSize)); ctx->threadError = 1; return arg; } + job->dst.size = job->compressedSize; } pthread_mutex_lock(&ctx->jobCompressed_mutex); ctx->jobCompressedID++; @@ -394,15 +406,15 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_unlock(&ctx->jobWrite_mutex); DEBUG(3, "createCompressionJob(): continuing after job write\n"); - + DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); job->compressionLevel = ctx->compressionLevel; job->src.size = srcSize; - job->dst.size = ZSTD_compressBound(srcSize); job->jobID = nextJob; job->lastJob = last; - memcpy(job->src.start, ctx->input.buffer.start + ctx->input.filled, srcSize); - job->dict.size = ctx->input.filled; - memcpy(job->dict.start, ctx->input.buffer.start, ctx->input.filled); + memcpy(job->src.start, ctx->input.buffer.start + ctx->lastDictSize, srcSize); + job->dict.size = ctx->lastDictSize; + DEBUG(3, "copied %zu bytes\n", ctx->lastDictSize); + memcpy(job->dict.start, ctx->input.buffer.start, ctx->lastDictSize); pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); @@ -412,9 +424,11 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); /* if not on the last job, reuse data as dictionary in next job */ if (!last) { - size_t const newDictSize = srcSize/16; - size_t const oldDictSize = ctx->input.filled; + size_t const newDictSize = ctx->targetDictSize; + size_t const oldDictSize = ctx->lastDictSize; + DEBUG(3, "newDictSize %zu oldDictSize %zu\n", newDictSize, oldDictSize); memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize + srcSize - newDictSize, newDictSize); + ctx->lastDictSize = newDictSize; ctx->input.filled = newDictSize; } return 0; From 8ef666c32509e35ebbae8229a2b15ca9d4af493d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 12 Jul 2017 14:23:34 -0700 Subject: [PATCH 118/318] slightly increased buffer pool, to cover normal "full load" scenarios 2 buffers per active worker + 1 buffer for input loading + 1 buffer for "next input" when submitting current one + 1 buffer stuck in queue --- NEWS | 3 ++- lib/compress/zstdmt_compress.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 5bf184e8d..457105070 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ v1.3.1 +perf: substantially decreased memory usage in Multi-threading mode, thanks to reports by Tino Reichardt build: fix Visual compilation for non x86/x64 targets, reported by Greg Slazinski (#718) -API exp : breaking change : ZSTD_getframeHeader() +API exp : breaking change : ZSTD_getframeHeader() provides more information v1.3.0 cli : new : `--list` command, by Paul Cruz diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 3fdfe9e14..677e96f08 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -93,7 +93,7 @@ typedef struct ZSTDMT_bufferPool_s { static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbThreads, ZSTD_customMem cMem) { - unsigned const maxNbBuffers = 2*nbThreads + 2; + unsigned const maxNbBuffers = 2*nbThreads + 3; ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)ZSTD_calloc( sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t), cMem); if (bufPool==NULL) return NULL; From 8ff8cdb15bae9c2d5db78477958ff90172cb83b9 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 12 Jul 2017 15:11:06 -0700 Subject: [PATCH 119/318] [ldm] Clean up code --- contrib/long_distance_matching/ldm.c | 515 ++++++-------- contrib/long_distance_matching/ldm.h | 33 + contrib/long_distance_matching/main-ldm.c | 20 +- .../versions/v0.5/Makefile | 40 ++ .../versions/v0.5/ldm.c | 659 ++++++++++++++++++ .../versions/v0.5/ldm.h | 26 + .../versions/v0.5/main-ldm.c | 468 +++++++++++++ .../versions/v0.5/util.c | 69 ++ .../versions/v0.5/util.h | 25 + 9 files changed, 1528 insertions(+), 327 deletions(-) create mode 100644 contrib/long_distance_matching/versions/v0.5/Makefile create mode 100644 contrib/long_distance_matching/versions/v0.5/ldm.c create mode 100644 contrib/long_distance_matching/versions/v0.5/ldm.h create mode 100644 contrib/long_distance_matching/versions/v0.5/main-ldm.c create mode 100644 contrib/long_distance_matching/versions/v0.5/util.c create mode 100644 contrib/long_distance_matching/versions/v0.5/util.h diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 4ed09cff7..b17a0f156 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -3,34 +3,30 @@ #include #include - #include "ldm.h" #include "util.h" -#define HASH_EVERY 7 +// Insert every (HASH_ONLY_EVERY + 1) into the hash table. +#define HASH_ONLY_EVERY 0 -#define LDM_MEMORY_USAGE 18 +#define LDM_MEMORY_USAGE 20 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) -#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) #define LDM_OFFSET_SIZE 4 -#define WINDOW_SIZE (1 << 23) -#define MAX_WINDOW_SIZE 31 -#define HASH_SIZE 4 -#define LDM_HASH_LENGTH 4 +#define WINDOW_SIZE (1 << 20) -// Should be multiple of four -#define MINMATCH 4 +//These should be multiples of four. +#define LDM_HASH_LENGTH 100 +#define MINMATCH 100 #define ML_BITS 4 #define ML_MASK ((1U<numMatches); - printf("Average match length: %.1f\n", ((double)stats->totalMatchLength) / - (double)stats->numMatches); - printf("Average literal length: %.1f\n", - ((double)stats->totalLiteralLength) / (double)stats->numMatches); - printf("Average offset length: %.1f\n", - ((double)stats->totalOffset) / (double)stats->numMatches); - printf("Num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", - stats->numCollisions, stats->numHashInserts, - stats->numHashInserts == 0 ? - 1.0 : (100.0 * (double)stats->numCollisions) / - (double)stats->numHashInserts); - printf("=====================\n"); -} - typedef struct LDM_CCtx { size_t isize; /* Input size */ size_t maxOSize; /* Maximum output size */ @@ -101,25 +78,79 @@ typedef struct LDM_CCtx { LDM_compressStats stats; /* Compression statistics */ - LDM_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; + hashEntry hashTable[LDM_HASHTABLESIZE_U32]; const BYTE *lastPosHashed; /* Last position hashed */ hash_t lastHash; /* Hash corresponding to lastPosHashed */ - const BYTE *nextIp; + U32 lastSum; + + const BYTE *nextIp; // TODO: this is redundant (ip + step) const BYTE *nextPosHashed; hash_t nextHash; /* Hash corresponding to nextPosHashed */ - - // Members for rolling hash. - U32 lastSum; U32 nextSum; - unsigned step; + unsigned step; // ip step, should be 1. // DEBUG const BYTE *DEBUG_setNextHash; } LDM_CCtx; +/** + * Outputs compression statistics. + */ +static void printCompressStats(const LDM_CCtx *cctx) { + const LDM_compressStats *stats = &(cctx->stats); +#ifdef COMPUTE_STATS + printf("=====================\n"); + printf("Compression statistics\n"); + printf("Total number of matches: %u\n", stats->numMatches); + printf("Average match length: %.1f\n", ((double)stats->totalMatchLength) / + (double)stats->numMatches); + printf("Average literal length: %.1f\n", + ((double)stats->totalLiteralLength) / (double)stats->numMatches); + printf("Average offset length: %.1f\n", + ((double)stats->totalOffset) / (double)stats->numMatches); + printf("Num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", + stats->numCollisions, stats->numHashInserts, + stats->numHashInserts == 0 ? + 1.0 : (100.0 * (double)stats->numCollisions) / + (double)stats->numHashInserts); + + // Output occupancy of hash table. + { + U32 i = 0; + U32 ctr = 0; + for (; i < LDM_HASHTABLESIZE_U32; i++) { + if ((cctx->hashTable)[i].offset == 0) { + ctr++; + } + } + printf("Hash table size, empty slots, %% empty: %u %u %.3f\n", + LDM_HASHTABLESIZE_U32, ctr, + 100.0 * (double)(ctr) / (double)LDM_HASHTABLESIZE_U32); + } + + printf("=====================\n"); +#endif +} + +/** + * Checks whether the MINMATCH bytes from p are the same as the MINMATCH + * bytes from match. + * + * This assumes MINMATCH is a multiple of four. + * + * Return 1 if valid, 0 otherwise. + */ static int LDM_isValidMatch(const BYTE *p, const BYTE *match) { + /* + if (memcmp(p, match, MINMATCH) == 0) { + return 1; + } + return 0; + */ + + //TODO: This seems to be faster for some reason? U16 lengthLeft = MINMATCH; const BYTE *curP = p; const BYTE *curMatch = match; @@ -137,25 +168,22 @@ static int LDM_isValidMatch(const BYTE *p, const BYTE *match) { return 1; } - - -#ifdef LDM_ROLLING_HASH /** - * Convert a sum computed from LDM_getRollingHash to a hash value in the range + * Convert a sum computed from getChecksum to a hash value in the range * of the hash table. */ -#define LDM_SUM2HASH2(s1,s2) (((s1) + (s2)) & 0xFFFF) -#define LDM_SUM2HASH(sum) (LDM_SUM2HASH2((sum)&0xFFFF,(sum)>>16)) - -static hash_t LDM_sumToHash(U32 sum) { -// return sum & (LDM_HASH_SIZE_U32 - 1); -// return sum % (LDM_HASHTABLESIZE_U32 ); - - return ((sum* 2654435761U) >> ((32)-LDM_HASHLOG)); -// return LDM_SUM2HASH2(sum&0xFFFF, sum >> 16); +static hash_t checksumToHash(U32 sum) { + return ((sum * 2654435761U) >> ((32)-LDM_HASHLOG)); } -static U32 LDM_getRollingHash(const char *data, U32 len) { +/** + * Computes a checksum based on rsync's checksum. + * + * a(k,l) = \sum_{i = k}^l x_i (mod M) + * b(k,l) = \sum_{i = k}^l ((l - i + 1) * x_i) (mod M) + * checksum(k,l) = a(k,l) + 2^{16} * b(k,l) + */ +static U32 getChecksum(const char *data, U32 len) { U32 i; U32 s1, s2; const schar *buf = (const schar *)data; @@ -173,34 +201,31 @@ static U32 LDM_getRollingHash(const char *data, U32 len) { return (s1 & 0xffff) + (s2 << 16); } -typedef struct LDM_sumStruct { - U16 s1, s2; -} LDM_sumStruct; - -static U32 LDM_updateRollingHash(U32 sum, U32 len, - schar toRemove, schar toAdd) { +/** + * Update a checksum computed from getChecksum(data, len). + * + * The checksum can be updated along its ends as follows: + * a(k+1, l+1) = (a(k,l) - x_k + x_{l+1}) (mod M) + * b(k+1, l+1) = (b(k,l) - (l-k+1)*x_k + (a(k+1,l+1)) (mod M) + * + * Thus toRemove should correspond to data[0]. + */ +static U32 updateChecksum(U32 sum, U32 len, + schar toRemove, schar toAdd) { U32 s1 = (sum & 0xffff) - toRemove + toAdd; U32 s2 = (sum >> 16) - (toRemove * len) + s1; return (s1 & 0xffff) + (s2 << 16); } - -/* -static hash_t LDM_hashPosition(const void * const p) { - return LDM_sumToHash(LDM_getRollingHash((const char *)p, LDM_HASH_LENGTH)); -} -*/ - -/* -static void LDM_getRollingHashParts(U32 sum, LDM_sumStruct *sumStruct) { - sumStruct->s1 = sum & 0xffff; - sumStruct->s2 = sum >> 16; -} -*/ - -static void LDM_setNextHash(LDM_CCtx *cctx) { - +/** + * Update cctx->nextSum, cctx->nextHash, and cctx->nextPosHashed + * based on cctx->lastSum 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) { #ifdef RUN_CHECKS U32 check; if ((cctx->nextIp - cctx->ibase != 1) && @@ -212,160 +237,100 @@ static void LDM_setNextHash(LDM_CCtx *cctx) { cctx->DEBUG_setNextHash = cctx->nextIp; #endif -// cctx->nextSum = LDM_getRollingHash((const char *)cctx->nextIp, LDM_HASH_LENGTH); - cctx->nextSum = LDM_updateRollingHash( +// cctx->nextSum = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); + cctx->nextSum = updateChecksum( cctx->lastSum, LDM_HASH_LENGTH, (schar)((cctx->lastPosHashed)[0]), (schar)((cctx->lastPosHashed)[LDM_HASH_LENGTH])); + cctx->nextPosHashed = cctx->nextIp; + cctx->nextHash = checksumToHash(cctx->nextSum); #ifdef RUN_CHECKS - check = LDM_getRollingHash((const char *)cctx->nextIp, LDM_HASH_LENGTH); + check = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); if (check != cctx->nextSum) { printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); -// printf("INFO: %u %u %u\n", LDM_read32(cctx->nextIp), } -#endif - cctx->nextPosHashed = cctx->nextIp; - cctx->nextHash = LDM_sumToHash(cctx->nextSum); -#ifdef RUN_CHECKS if ((cctx->nextIp - cctx->lastPosHashed) != 1) { printf("setNextHash: nextIp != lastPosHashed + 1. %zu %zu %zu\n", cctx->nextIp - cctx->ibase, cctx->lastPosHashed - cctx->ibase, cctx->ip - cctx->ibase); } #endif - } -static void LDM_putHashOfCurrentPositionFromHash( +static void putHashOfCurrentPositionFromHash( LDM_CCtx *cctx, hash_t hash, U32 sum) { - /* - if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { - return; - } - */ #ifdef COMPUTE_STATS - if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32 ) { + if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { offset_t offset = (cctx->hashTable)[hash].offset; cctx->stats.numHashInserts++; if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { -// printf("%u %u %zu\n", hash, offset, cctx->ip - cctx->ibase); -// printf("TST: %u %u\n", LDM_read32(cctx->ip), LDM_read32(offset + cctx->ibase)); cctx->stats.numCollisions++; } } - #endif - if (((cctx->ip - cctx->ibase) & HASH_EVERY) == HASH_EVERY) { -#ifdef COMPUTE_STATS - /* - offset_t offset = (cctx->hashTable)[hash].offset; - if (offset == 0) { - printf("NEW HASH: %u\n", hash); - } - */ -#endif - - (cctx->hashTable)[hash] = (LDM_hashEntry){ (offset_t)(cctx->ip - cctx->ibase) }; + // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. + // Note: this works only when cctx->step is 1. + if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { + (cctx->hashTable)[hash] = (hashEntry){ (offset_t)(cctx->ip - cctx->ibase) }; } - // Book-keeping cctx->lastPosHashed = cctx->ip; cctx->lastHash = hash; cctx->lastSum = sum; } +/** + * Copy over the cctx->lastHash, cctx->lastSum, and cctx->lastPosHashed + * fields from the "next" fields. + * + * This requires that cctx->ip == cctx->nextPosHashed. + */ static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { #ifdef RUN_CHECKS if (cctx->ip != cctx->nextPosHashed) { - printf("CHECK failed: updateLastHashFromNextHash %zu\n", cctx->ip - cctx->ibase); + printf("CHECK failed: updateLastHashFromNextHash %zu\n", + cctx->ip - cctx->ibase); } #endif - LDM_putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); + putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); } +/** + * Insert hash of the current position into the hash table. + */ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - U32 sum = LDM_getRollingHash((const char *)cctx->ip, LDM_HASH_LENGTH); - hash_t hash = LDM_sumToHash(sum); + U32 sum = getChecksum((const char *)cctx->ip, LDM_HASH_LENGTH); + hash_t hash = checksumToHash(sum); + #ifdef RUN_CHECKS if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { - printf("CHECK failed: putHashOfCurrentPosition %zu\n", cctx->ip - cctx->ibase); - } -#endif -// hash_t hash = LDM_hashPosition(cctx->ip); - LDM_putHashOfCurrentPositionFromHash(cctx, hash, sum); -// printf("Offset %zu\n", cctx->ip - cctx->ibase); -} - -#else -static hash_t LDM_hash(U32 sequence) { - return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); -} - -static hash_t LDM_hashPosition(const void * const p) { - return LDM_hash(LDM_read32(p)); -} - -static void LDM_putHashOfCurrentPositionFromHash( - LDM_CCtx *cctx, hash_t hash) { - /* - if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { - return; - } - */ -#ifdef COMPUTE_STATS - if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { - offset_t offset = (cctx->hashTable)[hash].offset; - cctx->stats.numHashInserts++; - - if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { - cctx->stats.numCollisions++; - } + printf("CHECK failed: putHashOfCurrentPosition %zu\n", + cctx->ip - cctx->ibase); } #endif - (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; -#ifdef RUN_CHECKS - if (cctx->ip - cctx->lastPosHashed != 1) { - printf("putHashError\n"); - } -#endif - cctx->lastPosHashed = cctx->ip; - cctx->lastHash = hash; + putHashOfCurrentPositionFromHash(cctx, hash, sum); } -static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - hash_t hash = LDM_hashPosition(cctx->ip); - LDM_putHashOfCurrentPositionFromHash(cctx, hash); +/** + * Returns the position of the entry at hashTable[hash]. + */ +static const BYTE *getPositionOnHash(LDM_CCtx *cctx, hash_t hash) { + return cctx->hashTable[hash].offset + cctx->ibase; } -#endif - -/* -static hash_t LDM_hash5(U64 sequence) { - static const U64 prime5bytes = 889523592379ULL; - static const U64 prime8bytes = 11400714785074694791ULL; - const U32 hashLog = LDM_HASHLOG; - if (LDM_isLittleEndian()) - return (((sequence << 24) * prime5bytes) >> (64 - hashLog)); - else - return (((sequence >> 24) * prime8bytes) >> (64 - hashLog)); -} -*/ - - -static const BYTE *LDM_getPositionOnHash( - hash_t h, void *tableBase, const BYTE *srcBase) { - const LDM_hashEntry * const hashTable = (LDM_hashEntry *)tableBase; - return hashTable[h].offset + srcBase; -} - - -static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { +/** + * Counts the number of bytes that match from pIn and pMatch, + * up to pInLimit. + * + * TODO: make more efficient. + */ +static unsigned countMatchLength(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { const BYTE * const pStart = pIn; while (pIn < pInLimit - 1) { BYTE const diff = LDM_readByte(pMatch) ^ LDM_readByte(pIn); @@ -386,9 +351,12 @@ void LDM_readHeader(const void *src, size_t *compressSize, *decompressSize = *ip; } -static void LDM_initializeCCtx(LDM_CCtx *cctx, - const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { +/** + * Initialize a compression context. + */ +static void initializeCCtx(LDM_CCtx *cctx, + const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { cctx->isize = srcSize; cctx->maxOSize = maxDstSize; @@ -396,11 +364,7 @@ static void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->ip = cctx->ibase; cctx->iend = cctx->ibase + srcSize; -#ifdef LDM_ROLLING_HASH cctx->ihashLimit = cctx->iend - LDM_HASH_LENGTH; -#else - cctx->ihashLimit = cctx->iend - HASH_SIZE; -#endif cctx->imatchLimit = cctx->iend - MINMATCH; cctx->obase = (BYTE *)dst; @@ -413,23 +377,27 @@ static void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->lastPosHashed = NULL; - cctx->step = 1; + cctx->step = 1; // Fixed to be 1 for now. Changing may break things. cctx->nextIp = cctx->ip + cctx->step; cctx->nextPosHashed = 0; cctx->DEBUG_setNextHash = 0; } -#ifdef LDM_ROLLING_HASH +/** + * 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). + * + */ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { cctx->nextIp = cctx->ip + cctx->step; do { hash_t h; U32 sum; -// printf("Call A\n"); - LDM_setNextHash(cctx); -// printf("End call a\n"); + setNextHash(cctx); h = cctx->nextHash; sum = cctx->nextSum; cctx->ip = cctx->nextIp; @@ -439,62 +407,27 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { return 1; } - *match = LDM_getPositionOnHash(h, cctx->hashTable, cctx->ibase); - -// // Compute cctx->nextSum and cctx->nextHash from cctx->nextIp. -// LDM_setNextHash(cctx); - LDM_putHashOfCurrentPositionFromHash(cctx, h, sum); - -// printf("%u %u\n", cctx->lastHash, cctx->nextHash); - } while (cctx->ip - *match > WINDOW_SIZE || - !LDM_isValidMatch(cctx->ip, *match)); -// LDM_read64(*match) != LDM_read64(cctx->ip)); - LDM_setNextHash(cctx); - return 0; -} -#else -static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { - cctx->nextIp = cctx->ip; - - do { - hash_t const h = cctx->nextHash; - cctx->ip = cctx->nextIp; - cctx->nextIp += cctx->step; - - if (cctx->ip > cctx->imatchLimit) { - return 1; - } - - *match = LDM_getPositionOnHash(h, cctx->hashTable, cctx->ibase); - - cctx->nextHash = LDM_hashPosition(cctx->nextIp); - LDM_putHashOfCurrentPositionFromHash(cctx, h); + *match = getPositionOnHash(cctx, h); + putHashOfCurrentPositionFromHash(cctx, h, sum); } while (cctx->ip - *match > WINDOW_SIZE || !LDM_isValidMatch(cctx->ip, *match)); + setNextHash(cctx); return 0; } -#endif - /** * Write current block (literals, literal length, match offset, * match length). * - * Update input pointer, inserting hashes into hash table along the - * way. + * Update input pointer, inserting hashes into hash table along the way. */ -static void LDM_outputBlock(LDM_CCtx *cctx, const BYTE *match) { - unsigned const literalLength = (unsigned)(cctx->ip - cctx->anchor); - unsigned const offset = cctx->ip - match; - unsigned const matchLength = LDM_count( - cctx->ip + MINMATCH, match + MINMATCH, cctx->ihashLimit); +static void outputBlock(LDM_CCtx *cctx, + unsigned const literalLength, + unsigned const offset, + unsigned const matchLength) { BYTE *token = cctx->op++; - cctx->stats.totalLiteralLength += literalLength; - cctx->stats.totalOffset += offset; - cctx->stats.totalMatchLength += matchLength + MINMATCH; - /* Encode the literal length. */ if (literalLength >= RUN_MASK) { int len = (int)literalLength - RUN_MASK; @@ -515,7 +448,7 @@ static void LDM_outputBlock(LDM_CCtx *cctx, const BYTE *match) { LDM_write32(cctx->op, offset); cctx->op += LDM_OFFSET_SIZE; - /* Encode match length */ + /* Encode the match length. */ if (matchLength >= ML_MASK) { unsigned matchLengthRemaining = matchLength; *token += ML_MASK; @@ -531,62 +464,21 @@ static void LDM_outputBlock(LDM_CCtx *cctx, const BYTE *match) { } else { *token += (BYTE)(matchLength); } - -// LDM_setNextHash(cctx); -// cctx->ip = cctx->lastPosHashed + 1; -// cctx->nextIp = cctx->ip + cctx->step; -// printf("HERE: %zu %zu %zu\n", cctx->ip - cctx->ibase, -// cctx->lastPosHashed - cctx->ibase, cctx->nextIp - cctx->ibase); - - cctx->nextIp = cctx->ip + cctx->step; - - while (cctx->ip < cctx->anchor + MINMATCH + matchLength + literalLength) { -// printf("Loop\n"); - if (cctx->ip > cctx->lastPosHashed) { -#ifdef LDM_ROLLING_HASH - LDM_updateLastHashFromNextHash(cctx); - LDM_setNextHash(cctx); -#else - LDM_putHashOfCurrentPosition(cctx); -#endif - } - /* - printf("Call b %zu %zu %zu\n", - cctx->lastPosHashed - cctx->ibase, - cctx->nextIp - cctx->ibase, - cctx->ip - cctx->ibase); - */ -// printf("end call b\n"); - cctx->ip++; - cctx->nextIp++; - } - -// printf("There: %zu %zu\n", cctx->ip - cctx->ibase, cctx->lastPosHashed - cctx->ibase); } // TODO: srcSize and maxDstSize is unused +// This is based upon lz4. size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; - LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); + initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); -#ifdef LDM_ROLLING_HASH -// LDM_setNextHash(&cctx); -// tmp_hash = LDM_updateRollingHash(cctx.lastSum, LDM_HASH_LENGTH, -// cctx.ip[0], cctx.ip[LDM_HASH_LENGTH]); -// printf("Update test: %u %u\n", tmp_hash, cctx.nextSum); -// cctx.ip++; -#else - cctx.ip++; - cctx.nextHash = LDM_hashPosition(cctx.ip); -#endif // TODO: loop condition is not accurate. while (1) { const BYTE *match; -// printf("Start of loop\n"); /** * Find a match. @@ -597,16 +489,15 @@ size_t LDM_compress(const void *src, size_t srcSize, if (LDM_findBestMatch(&cctx, &match) != 0) { goto _last_literals; } -// printf("End of match finding\n"); - +#ifdef COMPUTE_STATS cctx.stats.numMatches++; +#endif /** * Catch up: look back to extend the match backwards from the found match. */ while (cctx.ip > cctx.anchor && match > cctx.ibase && cctx.ip[-1] == match[-1]) { -// printf("Catch up\n"); cctx.ip--; match--; } @@ -615,26 +506,35 @@ size_t LDM_compress(const void *src, size_t srcSize, * Write current block (literals, literal length, match offset, match * length) and update pointers and hashes. */ - LDM_outputBlock(&cctx, match); -// printf("End of loop\n"); + { + unsigned const literalLength = (unsigned)(cctx.ip - cctx.anchor); + unsigned const offset = cctx.ip - match; + unsigned const matchLength = countMatchLength( + cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); + +#ifdef COMPUTE_STATS + cctx.stats.totalLiteralLength += literalLength; + cctx.stats.totalOffset += offset; + cctx.stats.totalMatchLength += matchLength + MINMATCH; +#endif + outputBlock(&cctx, literalLength, offset, matchLength); + + // Move ip to end of block, inserting hashes at each position. + cctx.nextIp = cctx.ip + cctx.step; + while (cctx.ip < cctx.anchor + MINMATCH + 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; -#ifdef LDM_ROLLING_HASH LDM_updateLastHashFromNextHash(&cctx); -#else - LDM_putHashOfCurrentPosition(&cctx); - cctx.ip++; -#endif - - /* - LDM_putHashOfCurrentPosition(&cctx); - printf("Call c\n"); - LDM_setNextHash(&cctx); - printf("End call c\n"); - cctx.ip++; - cctx.nextIp++; - */ } _last_literals: /* Encode the last literals (no more matches). */ @@ -653,18 +553,11 @@ _last_literals: memcpy(cctx.op, cctx.anchor, lastRun); cctx.op += lastRun; } - LDM_printCompressStats(&cctx.stats); - { - U32 tmp = 0; - U32 ctr = 0; - for (; tmp < LDM_HASH_SIZE_U32; tmp++) { - if ((cctx.hashTable)[tmp].offset == 0) { - ctr++; - } - } - printf("HASH: %u %u\n", ctr, LDM_HASH_SIZE_U32); - } +#ifdef COMPUTE_STATS + printCompressStats(&cctx); +#endif + return (cctx.op - (const BYTE *)cctx.obase); } @@ -715,7 +608,7 @@ size_t LDM_decompress(const void *src, size_t compressSize, } while (s == 255); } - /* Copy literals. */ + /* Copy the literals. */ cpy = dctx.op + length; memcpy(dctx.op, dctx.ip, length); dctx.ip += length; @@ -748,20 +641,20 @@ size_t LDM_decompress(const void *src, size_t compressSize, return dctx.op - (BYTE *)dst; } +/* void LDM_test(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { -#ifdef LDM_ROLLING_HASH const BYTE *ip = (const BYTE *)src + 1125; - U32 sum = LDM_getRollingHash((const char *)ip, LDM_HASH_LENGTH); + U32 sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); U32 sum2; ++ip; for (; ip < (const BYTE *)src + 1125 + 100; ip++) { - sum2 = LDM_updateRollingHash(sum, LDM_HASH_LENGTH, + sum2 = updateChecksum(sum, LDM_HASH_LENGTH, ip[-1], ip[LDM_HASH_LENGTH - 1]); - sum = LDM_getRollingHash((const char *)ip, LDM_HASH_LENGTH); + sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); printf("TEST HASH: %zu %u %u\n", ip - (const BYTE *)src, sum, sum2); } -#endif } +*/ diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index a34faac4f..d7f977d9b 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -7,12 +7,45 @@ #define LDM_DECOMPRESS_SIZE 4 #define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) +/** + * Compresses src into dst. + * + * NB: This currently ignores maxDstSize and assumes enough space is available. + * + * Block format (see lz4 documentation for more information): + * github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md + * + * A block is composed of sequences. Each sequence begins with a token, which + * is a one-byte value separated into two 4-bit fields. + * + * The first field uses the four high bits of the token and encodes the literal + * length. If the field value is 0, there is no literal. If it is 15, + * additional bytes are added (each ranging from 0 to 255) to the previous + * value to produce a total length. + * + * Following the token and optional length bytes are the literals. + * + * Next are the 4 bytes representing the offset of the match (2 in lz4), + * representing the position to copy the literals. + * + * The lower four bits of the token encode the match length. With additional + * bytes added similarly to the additional literal length bytes after the offset. + * + * The last sequence is incomplete and stops right after the lieterals. + * + */ size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize); size_t LDM_decompress(const void *src, size_t srcSize, void *dst, size_t maxDstSize); +/** + * Reads the header from src and writes the compressed size and + * decompressed size into compressSize and decompressSize respectively. + * + * NB: LDM_compress and LDM_decompress currently do not add/read headers. + */ void LDM_readHeader(const void *src, size_t *compressSize, size_t *decompressSize); diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index f8ae54698..fbfd789bc 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -13,12 +13,9 @@ #include #include "ldm.h" -// #define BUF_SIZE 16*1024 // Block size #define DEBUG //#define TEST -//#define ZSTD - /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. */ @@ -75,28 +72,25 @@ static int compress(const char *fname, const char *oname) { return 1; } +/* #ifdef TEST LDM_test(src, statbuf.st_size, dst + LDM_HEADER_SIZE, statbuf.st_size); #endif +*/ -#ifdef ZSTD - compressSize = ZSTD_compress(dst, statbuf.st_size, - src, statbuf.st_size, 1); -#else compressSize = LDM_HEADER_SIZE + LDM_compress(src, statbuf.st_size, dst + LDM_HEADER_SIZE, statbuf.st_size); - // Write compress and decompress size to header - // TODO: should depend on LDM_DECOMPRESS_SIZE write32 + // Write compress and decompress size to header + // TODO: should depend on LDM_DECOMPRESS_SIZE write32 memcpy(dst, &compressSize, 4); memcpy(dst + 4, &(statbuf.st_size), 4); #ifdef DEBUG printf("Compressed size: %zu\n", compressSize); printf("Decompressed size: %zu\n", (size_t)statbuf.st_size); -#endif #endif // Truncate file to compressSize. @@ -169,17 +163,11 @@ static int decompress(const char *fname, const char *oname) { return 1; } -#ifdef ZSTD - outSize = ZSTD_decompress(dst, decomrpessed_size, - src + LDM_HEADER_SIZE, - statbuf.st_size - LDM_HEADER_SIZE); -#else outSize = LDM_decompress( src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, dst, decompressSize); printf("Ret size out: %zu\n", outSize); - #endif ftruncate(fdout, outSize); close(fdin); diff --git a/contrib/long_distance_matching/versions/v0.5/Makefile b/contrib/long_distance_matching/versions/v0.5/Makefile new file mode 100644 index 000000000..5ffd4eafe --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.5/Makefile @@ -0,0 +1,40 @@ +# ################################################################ +# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. +# ################################################################ + +# This Makefile presumes libzstd is installed, using `sudo make install` + +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: main-ldm + + +#main : ldm.c main.c +# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +main-ldm : util.c ldm.c main-ldm.c + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +clean: + @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ + main main-ldm + @echo Cleaning completed + diff --git a/contrib/long_distance_matching/versions/v0.5/ldm.c b/contrib/long_distance_matching/versions/v0.5/ldm.c new file mode 100644 index 000000000..325c50406 --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.5/ldm.c @@ -0,0 +1,659 @@ +#include +#include +#include +#include + +#include "ldm.h" +#include "util.h" + +// Insert every (HASH_ONLY_EVERY + 1) into the hash table. +#define HASH_ONLY_EVERY 0 + +#define LDM_MEMORY_USAGE 20 +#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) + +#define LDM_OFFSET_SIZE 4 + +#define WINDOW_SIZE (1 << 20) + +//These should be multiples of four. +#define LDM_HASH_LENGTH 100 +#define MINMATCH 100 + +#define ML_BITS 4 +#define ML_MASK ((1U<stats); +#ifdef COMPUTE_STATS + printf("=====================\n"); + printf("Compression statistics\n"); + printf("Total number of matches: %u\n", stats->numMatches); + printf("Average match length: %.1f\n", ((double)stats->totalMatchLength) / + (double)stats->numMatches); + printf("Average literal length: %.1f\n", + ((double)stats->totalLiteralLength) / (double)stats->numMatches); + printf("Average offset length: %.1f\n", + ((double)stats->totalOffset) / (double)stats->numMatches); + printf("Num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", + stats->numCollisions, stats->numHashInserts, + stats->numHashInserts == 0 ? + 1.0 : (100.0 * (double)stats->numCollisions) / + (double)stats->numHashInserts); + + // Output occupancy of hash table. + { + U32 i = 0; + U32 ctr = 0; + for (; i < LDM_HASHTABLESIZE_U32; i++) { + if ((cctx->hashTable)[i].offset == 0) { + ctr++; + } + } + printf("Hash table size, empty slots, %% empty: %u %u %.3f\n", + LDM_HASHTABLESIZE_U32, ctr, + 100.0 * (double)(ctr) / (double)LDM_HASHTABLESIZE_U32); + } + + printf("=====================\n"); +#endif +} + +/** + * Checks whether the MINMATCH bytes from p are the same as the MINMATCH + * bytes from match. + * + * This assumes MINMATCH is a multiple of four. + * + * Return 1 if valid, 0 otherwise. + */ +static int LDM_isValidMatch(const BYTE *p, const BYTE *match) { + /* + if (memcmp(p, match, MINMATCH) == 0) { + return 1; + } + return 0; + */ + + //TODO: This seems to be faster for some reason? + U16 lengthLeft = MINMATCH; + const BYTE *curP = p; + const BYTE *curMatch = match; + + for (; lengthLeft >= 8; lengthLeft -= 8) { + if (LDM_read64(curP) != LDM_read64(curMatch)) { + return 0; + } + curP += 8; + curMatch += 8; + } + if (lengthLeft > 0) { + return (LDM_read32(curP) == LDM_read32(curMatch)); + } + return 1; +} + +/** + * Convert a sum computed from getChecksum to a hash value in the range + * of the hash table. + */ +static hash_t checksumToHash(U32 sum) { + return ((sum * 2654435761U) >> ((32)-LDM_HASHLOG)); +} + +/** + * Computes a checksum based on rsync's checksum. + * + * a(k,l) = \sum_{i = k}^l x_i (mod M) + * b(k,l) = \sum_{i = k}^l ((l - i + 1) * x_i) (mod M) + * checksum(k,l) = a(k,l) + 2^{16} * b(k,l) + */ +static U32 getChecksum(const char *data, U32 len) { + U32 i; + U32 s1, s2; + const schar *buf = (const schar *)data; + + s1 = s2 = 0; + for (i = 0; i < (len - 4); i += 4) { + s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + + (2 * buf[i + 2]) + (buf[i + 3]); + s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; + } + for(; i < len; i++) { + s1 += buf[i]; + s2 += s1; + } + return (s1 & 0xffff) + (s2 << 16); +} + +/** + * Update a checksum computed from getChecksum(data, len). + * + * The checksum can be updated along its ends as follows: + * a(k+1, l+1) = (a(k,l) - x_k + x_{l+1}) (mod M) + * b(k+1, l+1) = (b(k,l) - (l-k+1)*x_k + (a(k+1,l+1)) (mod M) + * + * Thus toRemove should correspond to data[0]. + */ +static U32 updateChecksum(U32 sum, U32 len, + schar toRemove, schar toAdd) { + U32 s1 = (sum & 0xffff) - toRemove + toAdd; + U32 s2 = (sum >> 16) - (toRemove * len) + s1; + + return (s1 & 0xffff) + (s2 << 16); +} + +/** + * Update cctx->nextSum, cctx->nextHash, and cctx->nextPosHashed + * based on cctx->lastSum 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) { +#ifdef RUN_CHECKS + U32 check; + if ((cctx->nextIp - cctx->ibase != 1) && + (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { + printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, + cctx->DEBUG_setNextHash - cctx->ibase); + } + + cctx->DEBUG_setNextHash = cctx->nextIp; +#endif + +// cctx->nextSum = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); + cctx->nextSum = updateChecksum( + cctx->lastSum, LDM_HASH_LENGTH, + (schar)((cctx->lastPosHashed)[0]), + (schar)((cctx->lastPosHashed)[LDM_HASH_LENGTH])); + cctx->nextPosHashed = cctx->nextIp; + cctx->nextHash = checksumToHash(cctx->nextSum); + +#ifdef RUN_CHECKS + check = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); + + if (check != cctx->nextSum) { + printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); + } + + if ((cctx->nextIp - cctx->lastPosHashed) != 1) { + printf("setNextHash: nextIp != lastPosHashed + 1. %zu %zu %zu\n", + cctx->nextIp - cctx->ibase, cctx->lastPosHashed - cctx->ibase, + cctx->ip - cctx->ibase); + } +#endif +} + +static void putHashOfCurrentPositionFromHash( + LDM_CCtx *cctx, hash_t hash, U32 sum) { +#ifdef COMPUTE_STATS + if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { + offset_t offset = (cctx->hashTable)[hash].offset; + cctx->stats.numHashInserts++; + if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { + cctx->stats.numCollisions++; + } + } +#endif + + // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. + // Note: this works only when cctx->step is 1. + if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { + (cctx->hashTable)[hash] = (hashEntry){ (offset_t)(cctx->ip - cctx->ibase) }; + } + + cctx->lastPosHashed = cctx->ip; + cctx->lastHash = hash; + cctx->lastSum = sum; +} + +/** + * Copy over the cctx->lastHash, cctx->lastSum, and cctx->lastPosHashed + * fields from the "next" fields. + * + * This requires that cctx->ip == cctx->nextPosHashed. + */ +static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { +#ifdef RUN_CHECKS + if (cctx->ip != cctx->nextPosHashed) { + printf("CHECK failed: updateLastHashFromNextHash %zu\n", + cctx->ip - cctx->ibase); + } +#endif + putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); +} + +/** + * Insert hash of the current position into the hash table. + */ +static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { + U32 sum = getChecksum((const char *)cctx->ip, LDM_HASH_LENGTH); + hash_t hash = checksumToHash(sum); + +#ifdef RUN_CHECKS + if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { + printf("CHECK failed: putHashOfCurrentPosition %zu\n", + cctx->ip - cctx->ibase); + } +#endif + + putHashOfCurrentPositionFromHash(cctx, hash, sum); +} + +/** + * Returns the position of the entry at hashTable[hash]. + */ +static const BYTE *getPositionOnHash(LDM_CCtx *cctx, hash_t hash) { + return cctx->hashTable[hash].offset + cctx->ibase; +} + +/** + * Counts the number of bytes that match from pIn and pMatch, + * up to pInLimit. + * + * TODO: make more efficient. + */ +static unsigned countMatchLength(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { + const BYTE * const pStart = pIn; + while (pIn < pInLimit - 1) { + BYTE const diff = LDM_readByte(pMatch) ^ LDM_readByte(pIn); + if (!diff) { + pIn++; + pMatch++; + continue; + } + return (unsigned)(pIn - pStart); + } + return (unsigned)(pIn - pStart); +} + +void LDM_readHeader(const void *src, size_t *compressSize, + size_t *decompressSize) { + const U32 *ip = (const U32 *)src; + *compressSize = *ip++; + *decompressSize = *ip; +} + +/** + * Initialize a compression context. + */ +static void 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 - MINMATCH; + + cctx->obase = (BYTE *)dst; + cctx->op = (BYTE *)dst; + + cctx->anchor = cctx->ibase; + + memset(&(cctx->stats), 0, sizeof(cctx->stats)); + memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); + + 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; + + cctx->DEBUG_setNextHash = 0; +} + +/** + * 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). + * + */ +static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { + cctx->nextIp = cctx->ip + cctx->step; + + do { + hash_t h; + U32 sum; + setNextHash(cctx); + h = cctx->nextHash; + sum = cctx->nextSum; + cctx->ip = cctx->nextIp; + cctx->nextIp += cctx->step; + + if (cctx->ip > cctx->imatchLimit) { + return 1; + } + + *match = getPositionOnHash(cctx, h); + putHashOfCurrentPositionFromHash(cctx, h, sum); + + } while (cctx->ip - *match > WINDOW_SIZE || + !LDM_isValidMatch(cctx->ip, *match)); + setNextHash(cctx); + return 0; +} + +/** + * Write current block (literals, literal length, match offset, + * match length). + * + * Update input pointer, inserting hashes into hash table along the way. + */ +static void outputBlock(LDM_CCtx *cctx, + unsigned const literalLength, + unsigned const offset, + unsigned const matchLength) { + BYTE *token = cctx->op++; + + /* Encode the literal length. */ + if (literalLength >= RUN_MASK) { + int len = (int)literalLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) { + *(cctx->op)++ = 255; + } + *(cctx->op)++ = (BYTE)len; + } else { + *token = (BYTE)(literalLength << ML_BITS); + } + + /* Encode the literals. */ + memcpy(cctx->op, cctx->anchor, literalLength); + cctx->op += literalLength; + + /* Encode the offset. */ + LDM_write32(cctx->op, offset); + cctx->op += LDM_OFFSET_SIZE; + + /* Encode the match length. */ + if (matchLength >= ML_MASK) { + unsigned matchLengthRemaining = matchLength; + *token += ML_MASK; + matchLengthRemaining -= ML_MASK; + LDM_write32(cctx->op, 0xFFFFFFFF); + while (matchLengthRemaining >= 4*0xFF) { + cctx->op += 4; + LDM_write32(cctx->op, 0xffffffff); + matchLengthRemaining -= 4*0xFF; + } + cctx->op += matchLengthRemaining / 255; + *(cctx->op)++ = (BYTE)(matchLengthRemaining % 255); + } else { + *token += (BYTE)(matchLength); + } +} + +// TODO: srcSize and maxDstSize is unused +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { + LDM_CCtx cctx; + initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); + + /* Hash the first position and put it into the hash table. */ + LDM_putHashOfCurrentPosition(&cctx); + + // TODO: loop condition is not accurate. + while (1) { + const BYTE *match; + + /** + * 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. + */ + if (LDM_findBestMatch(&cctx, &match) != 0) { + goto _last_literals; + } +#ifdef COMPUTE_STATS + cctx.stats.numMatches++; +#endif + + /** + * Catch up: look back to extend the match backwards from the found match. + */ + while (cctx.ip > cctx.anchor && match > cctx.ibase && + cctx.ip[-1] == match[-1]) { + cctx.ip--; + match--; + } + + /** + * Write current block (literals, literal length, match offset, match + * length) and update pointers and hashes. + */ + { + unsigned const literalLength = (unsigned)(cctx.ip - cctx.anchor); + unsigned const offset = cctx.ip - match; + unsigned const matchLength = countMatchLength( + cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); + +#ifdef COMPUTE_STATS + cctx.stats.totalLiteralLength += literalLength; + cctx.stats.totalOffset += offset; + cctx.stats.totalMatchLength += matchLength + MINMATCH; +#endif + outputBlock(&cctx, literalLength, offset, matchLength); + + // Move ip to end of block, inserting hashes at each position. + cctx.nextIp = cctx.ip + cctx.step; + while (cctx.ip < cctx.anchor + MINMATCH + 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); + } +_last_literals: + /* Encode the last literals (no more matches). */ + { + size_t const lastRun = (size_t)(cctx.iend - cctx.anchor); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *(cctx.op)++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255; accumulator -= 255) { + *(cctx.op)++ = 255; + } + *(cctx.op)++ = (BYTE)accumulator; + } else { + *(cctx.op)++ = (BYTE)(lastRun << ML_BITS); + } + memcpy(cctx.op, cctx.anchor, lastRun); + cctx.op += lastRun; + } + +#ifdef COMPUTE_STATS + printCompressStats(&cctx); +#endif + + return (cctx.op - (const BYTE *)cctx.obase); +} + +typedef struct LDM_DCtx { + size_t compressSize; + size_t maxDecompressSize; + + 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 */ +} LDM_DCtx; + +static void LDM_initializeDCtx(LDM_DCtx *dctx, + const void *src, size_t compressSize, + void *dst, size_t maxDecompressSize) { + dctx->compressSize = compressSize; + dctx->maxDecompressSize = maxDecompressSize; + + dctx->ibase = src; + dctx->ip = (const BYTE *)src; + dctx->iend = dctx->ip + dctx->compressSize; + dctx->op = dst; + dctx->oend = dctx->op + dctx->maxDecompressSize; + +} + +size_t LDM_decompress(const void *src, size_t compressSize, + void *dst, size_t maxDecompressSize) { + LDM_DCtx dctx; + LDM_initializeDCtx(&dctx, src, compressSize, dst, maxDecompressSize); + + while (dctx.ip < dctx.iend) { + BYTE *cpy; + const BYTE *match; + size_t length, offset; + + /* Get the literal length. */ + unsigned const 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 + offset = LDM_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 += MINMATCH; + + /* Copy match. */ + cpy = dctx.op + length; + + // Inefficient for now. + while (match < cpy - offset && dctx.op < dctx.oend) { + *(dctx.op)++ = *match++; + } + } + return dctx.op - (BYTE *)dst; +} + +/* +void LDM_test(const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { + const BYTE *ip = (const BYTE *)src + 1125; + U32 sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); + U32 sum2; + ++ip; + for (; ip < (const BYTE *)src + 1125 + 100; ip++) { + sum2 = updateChecksum(sum, LDM_HASH_LENGTH, + ip[-1], ip[LDM_HASH_LENGTH - 1]); + sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); + printf("TEST HASH: %zu %u %u\n", ip - (const BYTE *)src, sum, sum2); + } +} +*/ + + diff --git a/contrib/long_distance_matching/versions/v0.5/ldm.h b/contrib/long_distance_matching/versions/v0.5/ldm.h new file mode 100644 index 000000000..1bd19745c --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.5/ldm.h @@ -0,0 +1,26 @@ +#ifndef LDM_H +#define LDM_H + +#include /* size_t */ + +#define LDM_COMPRESS_SIZE 4 +#define LDM_DECOMPRESS_SIZE 4 +#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) + +size_t LDM_compress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +size_t LDM_decompress(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +/** + * Reads the header from src and writes the compressed size and + * decompressed size into compressSize and decompressSize respectively. + */ +void LDM_readHeader(const void *src, size_t *compressSize, + size_t *decompressSize); + +void LDM_test(const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v0.5/main-ldm.c b/contrib/long_distance_matching/versions/v0.5/main-ldm.c new file mode 100644 index 000000000..fbfd789bc --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.5/main-ldm.c @@ -0,0 +1,468 @@ +// TODO: file size must fit into a U32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ldm.h" + +#define DEBUG +//#define TEST + +/* Compress file given by fname and output to oname. + * Returns 0 if successful, error code otherwise. + */ +static int compress(const char *fname, const char *oname) { + int fdin, fdout; + struct stat statbuf; + char *src, *dst; + size_t maxCompressSize, compressSize; + + /* 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; + } + + maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; + + /* Go to the location corresponding to the last byte. */ + /* TODO: fallocate? */ + if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* Write a dummy byte at the last location. */ + if (write(fdout, "", 1) != 1) { + perror("write 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; + } + + /* mmap the output file */ + if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { + perror("mmap error for output"); + return 1; + } + +/* +#ifdef TEST + LDM_test(src, statbuf.st_size, + dst + LDM_HEADER_SIZE, statbuf.st_size); +#endif +*/ + + compressSize = LDM_HEADER_SIZE + + LDM_compress(src, statbuf.st_size, + dst + LDM_HEADER_SIZE, statbuf.st_size); + + // Write compress and decompress size to header + // TODO: should depend on LDM_DECOMPRESS_SIZE write32 + memcpy(dst, &compressSize, 4); + memcpy(dst + 4, &(statbuf.st_size), 4); + +#ifdef DEBUG + printf("Compressed size: %zu\n", compressSize); + printf("Decompressed size: %zu\n", (size_t)statbuf.st_size); +#endif + + // Truncate file to compressSize. + ftruncate(fdout, compressSize); + + printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, + (unsigned)statbuf.st_size, (unsigned)compressSize, oname, + (double)compressSize / (statbuf.st_size) * 100); + + // Close files. + close(fdin); + close(fdout); + return 0; +} + +/* 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; + size_t compressSize, decompressSize, 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, &compressSize, &decompressSize); + + /* Go to the location corresponding to the last byte. */ + if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { + perror("lseek error"); + return 1; + } + + /* write a dummy byte at the last location */ + if (write(fdout, "", 1) != 1) { + perror("write error"); + return 1; + } + + /* mmap the output file */ + if ((dst = mmap(0, decompressSize, 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, decompressSize); + + printf("Ret size out: %zu\n", outSize); + ftruncate(fdout, 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 void verify(const char *inpFilename, const char *decFilename) { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + } + + fclose(decFp); + fclose(inpFp); +} + +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 */ + { + struct timeval tv1, tv2; + gettimeofday(&tv1, NULL); + if (compress(inpFilename, ldmFilename)) { + printf("Compress error"); + return 1; + } + gettimeofday(&tv2, NULL); + printf("Total compress time = %f seconds\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec)); + } + + /* Decompress */ + { + struct timeval tv1, tv2; + gettimeofday(&tv1, NULL); + if (decompress(ldmFilename, decFilename)) { + printf("Decompress error"); + 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 */ + verify(inpFilename, decFilename); + return 0; +} + + +#if 0 +static size_t compress_file(FILE *in, FILE *out, size_t *size_in, + size_t *size_out) { + char *src, *buf = NULL; + size_t r = 1; + size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; + + src = malloc(BUF_SIZE); + if (!src) { + printf("Not enough memory\n"); + goto cleanup; + } + + size = BUF_SIZE + LDM_HEADER_SIZE; + buf = malloc(size); + if (!buf) { + printf("Not enough memory\n"); + goto cleanup; + } + + + for (;;) { + k = fread(src, 1, BUF_SIZE, in); + if (k == 0) + break; + count_in += k; + + n = LDM_compress(src, buf, k, BUF_SIZE); + + // n = k; + // offset += n; + offset = k; + count_out += k; + +// k = fwrite(src, 1, offset, out); + + k = fwrite(buf, 1, offset, out); + if (k < offset) { + if (ferror(out)) + printf("Write failed\n"); + else + printf("Short write\n"); + goto cleanup; + } + + } + *size_in = count_in; + *size_out = count_out; + r = 0; + cleanup: + free(src); + free(buf); + return r; +} + +static size_t decompress_file(FILE *in, FILE *out) { + void *src = malloc(BUF_SIZE); + void *dst = NULL; + size_t dst_capacity = BUF_SIZE; + size_t ret = 1; + size_t bytes_written = 0; + + if (!src) { + perror("decompress_file(src)"); + goto cleanup; + } + + while (ret != 0) { + /* Load more input */ + size_t src_size = fread(src, 1, BUF_SIZE, in); + void *src_ptr = src; + void *src_end = src_ptr + src_size; + if (src_size == 0 || ferror(in)) { + printf("(TODO): Decompress: not enough input or error reading file\n"); + //TODO + ret = 0; + goto cleanup; + } + + /* Allocate destination buffer if it hasn't been allocated already */ + if (!dst) { + dst = malloc(dst_capacity); + if (!dst) { + perror("decompress_file(dst)"); + goto cleanup; + } + } + + // TODO + + /* Decompress: + * Continue while there is more input to read. + */ + while (src_ptr != src_end && ret != 0) { + // size_t dst_size = src_size; + size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); + size_t written = fwrite(dst, 1, dst_size, out); +// printf("Writing %zu bytes\n", dst_size); + bytes_written += dst_size; + if (written != dst_size) { + printf("Decompress: Failed to write to file\n"); + goto cleanup; + } + src_ptr += src_size; + src_size = src_end - src_ptr; + } + + /* Update input */ + + } + + printf("Wrote %zu bytes\n", bytes_written); + + cleanup: + free(src); + free(dst); + + return ret; +} + +int main2(int argc, char *argv[]) { + char inpFilename[256] = { 0 }; + char ldmFilename[256] = { 0 }; + char decFilename[256] = { 0 }; + + if (argc < 2) { + printf("Please specify input filename\n"); + return 0; + } + 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 */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *outFp = fopen(ldmFilename, "wb"); + size_t sizeIn = 0; + size_t sizeOut = 0; + size_t ret; + printf("compress : %s -> %s\n", inpFilename, ldmFilename); + ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); + if (ret) { + printf("compress : failed with code %zu\n", ret); + return ret; + } + printf("%s: %zu → %zu bytes, %.1f%%\n", + inpFilename, sizeIn, sizeOut, + (double)sizeOut / sizeIn * 100); + printf("compress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* decompress */ + { + FILE *inpFp = fopen(ldmFilename, "rb"); + FILE *outFp = fopen(decFilename, "wb"); + size_t ret; + + printf("decompress : %s -> %s\n", ldmFilename, decFilename); + ret = decompress_file(inpFp, outFp); + if (ret) { + printf("decompress : failed with code %zu\n", ret); + return ret; + } + printf("decompress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* verify */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); + } + return 0; +} +#endif + diff --git a/contrib/long_distance_matching/versions/v0.5/util.c b/contrib/long_distance_matching/versions/v0.5/util.c new file mode 100644 index 000000000..70fcbc2ce --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.5/util.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include + +#include "util.h" + +typedef uint8_t BYTE; +typedef uint16_t U16; +typedef uint32_t U32; +typedef int32_t S32; +typedef uint64_t U64; + +unsigned LDM_isLittleEndian(void) { + const union { U32 u; BYTE c[4]; } one = { 1 }; + return one.c[0]; +} + +U16 LDM_read16(const void *memPtr) { + U16 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +U16 LDM_readLE16(const void *memPtr) { + if (LDM_isLittleEndian()) { + return LDM_read16(memPtr); + } else { + const BYTE *p = (const BYTE *)memPtr; + return (U16)((U16)p[0] + (p[1] << 8)); + } +} + +void LDM_write16(void *memPtr, U16 value){ + memcpy(memPtr, &value, sizeof(value)); +} + +void LDM_write32(void *memPtr, U32 value) { + memcpy(memPtr, &value, sizeof(value)); +} + +void LDM_writeLE16(void *memPtr, U16 value) { + if (LDM_isLittleEndian()) { + LDM_write16(memPtr, value); + } else { + BYTE* p = (BYTE *)memPtr; + p[0] = (BYTE) value; + p[1] = (BYTE)(value>>8); + } +} + +U32 LDM_read32(const void *ptr) { + return *(const U32 *)ptr; +} + +U64 LDM_read64(const void *ptr) { + return *(const U64 *)ptr; +} + +void LDM_copy8(void *dst, const void *src) { + memcpy(dst, src, 8); +} + +BYTE LDM_readByte(const void *memPtr) { + BYTE val; + memcpy(&val, memPtr, 1); + return val; +} + diff --git a/contrib/long_distance_matching/versions/v0.5/util.h b/contrib/long_distance_matching/versions/v0.5/util.h new file mode 100644 index 000000000..d1c3c999b --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.5/util.h @@ -0,0 +1,25 @@ +#ifndef LDM_UTIL_H +#define LDM_UTIL_H + +unsigned LDM_isLittleEndian(void); + +uint16_t LDM_read16(const void *memPtr); + +uint16_t LDM_readLE16(const void *memPtr); + +void LDM_write16(void *memPtr, uint16_t value); + +void LDM_write32(void *memPtr, uint32_t value); + +void LDM_writeLE16(void *memPtr, uint16_t value); + +uint32_t LDM_read32(const void *ptr); + +uint64_t LDM_read64(const void *ptr); + +void LDM_copy8(void *dst, const void *src); + +uint8_t LDM_readByte(const void *ptr); + + +#endif /* LDM_UTIL_H */ From 5353d350aea592e87825129c86d57edeaf0a082e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 16:02:20 -0700 Subject: [PATCH 120/318] working with fixed compression level and fixed dictionary size --- contrib/adaptive-compression/adapt.c | 29 +++++++++++----------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index b841cefaf..eb1e24a79 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -49,11 +49,11 @@ typedef struct { typedef struct { buffer_t src; buffer_t dst; - buffer_t dict; unsigned compressionLevel; unsigned jobID; unsigned lastJob; size_t compressedSize; + size_t dictSize; } jobDescription; typedef struct { @@ -91,7 +91,6 @@ static void freeCompressionJobs(adaptCCtx* ctx) jobDescription job = ctx->jobs[u]; free(job.dst.start); free(job.src.start); - free(job.dict.start); } } @@ -139,7 +138,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobReadyID = 0; ctx->jobCompressedID = 0; ctx->jobWriteID = 0; - ctx->targetDictSize = FILE_CHUNK_SIZE >> 1; + ctx->targetDictSize = FILE_CHUNK_SIZE >> 15; ctx->lastDictSize = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); /* initializing jobs */ @@ -147,18 +146,16 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) unsigned jobNum; for (jobNum=0; jobNumjobs[jobNum]; - job->src.start = malloc(FILE_CHUNK_SIZE); + job->src.start = malloc(2 * FILE_CHUNK_SIZE); job->dst.start = malloc(ZSTD_compressBound(FILE_CHUNK_SIZE)); - job->dict.start = malloc(FILE_CHUNK_SIZE); job->lastJob = 0; - if (!job->src.start || !job->dst.start || !job->dict.start) { + if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); freeCCtx(ctx); return NULL; } job->src.capacity = FILE_CHUNK_SIZE; job->dst.capacity = ZSTD_compressBound(FILE_CHUNK_SIZE); - job->dict.capacity = FILE_CHUNK_SIZE; } } ctx->nextJobID = 0; @@ -257,19 +254,17 @@ static void* compressionThread(void* arg) } pthread_mutex_unlock(&ctx->jobReady_mutex); DEBUG(3, "compressionThread(): continuing after job ready\n"); - DEBUG(3, "%.*s\n", (int)job->dict.size, (char*)job->dict.start); DEBUG(3, "DICTIONARY ENDED\n"); - DEBUG(2, "%.*s", (int)job->src.size, (char*)job->src.start); + DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); DEBUG(3, "cLevel used: %u\n", cLevel); - DEBUG(3, "dictSize: %zu, srcSize: %zu\n", job->dict.size, job->src.size); DEBUG(3, "compression level used: %u\n", cLevel); /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->dict.start, job->dict.size, 6); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start, job->dictSize, 6); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); @@ -280,7 +275,7 @@ static void* compressionThread(void* arg) /* continue compression */ if (currJob != 0) { /* not first job flush/overwrite the frame header */ - size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size); + size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, 0); if (ZSTD_isError(hSize)) { DISPLAY("Error: something went wrong while continuing compression\n"); job->compressedSize = hSize; @@ -290,8 +285,8 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } job->compressedSize = (job->lastJob) ? - ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size) : - ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size); + ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, job->src.size) : + ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, job->src.size); if (ZSTD_isError(job->compressedSize)) { DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(job->compressedSize)); ctx->threadError = 1; @@ -411,10 +406,8 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) job->src.size = srcSize; job->jobID = nextJob; job->lastJob = last; - memcpy(job->src.start, ctx->input.buffer.start + ctx->lastDictSize, srcSize); - job->dict.size = ctx->lastDictSize; - DEBUG(3, "copied %zu bytes\n", ctx->lastDictSize); - memcpy(job->dict.start, ctx->input.buffer.start, ctx->lastDictSize); + memcpy(job->src.start, ctx->input.buffer.start, ctx->lastDictSize + srcSize); + job->dictSize = ctx->lastDictSize; pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); From 74d3a6f5aefaf7d35a7af87e66b3046c1ead2593 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 16:18:41 -0700 Subject: [PATCH 121/318] passes tests with adaptive compression level --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index eb1e24a79..e9230ddb1 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -264,7 +264,7 @@ static void* compressionThread(void* arg) /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start, job->dictSize, 6); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start, job->dictSize, cLevel); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); From 8de82b6eb045d66dc8f0c4d54a7bbe7f144a05cc Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 12 Jul 2017 16:31:31 -0700 Subject: [PATCH 122/318] [ldm] Clean up versions --- contrib/long_distance_matching/ldm.c | 5 +- contrib/long_distance_matching/util.c | 1 - .../versions/v0.1/ldm.c | 394 ---------- .../versions/v0.1/ldm.h | 19 - .../versions/v0.1/main-ldm.c | 459 ----------- .../versions/v0.2/Makefile | 32 - .../versions/v0.2/ldm.c | 436 ----------- .../versions/v0.2/ldm.h | 19 - .../versions/v0.2/main-ldm.c | 474 ------------ .../versions/v0.3/Makefile | 10 - .../versions/v0.3/README | 3 + .../versions/v0.4/Makefile | 40 - .../versions/v0.4/ldm.c | 729 ------------------ .../versions/v0.4/ldm.h | 22 - .../versions/v0.4/main-ldm.c | 480 ------------ .../versions/v0.4/util.c | 69 -- .../versions/v0.4/util.h | 25 - .../versions/v0.5/Makefile | 15 +- .../versions/v0.5/README | 5 + .../versions/v0.5/ldm.c | 8 +- 20 files changed, 15 insertions(+), 3230 deletions(-) delete mode 100644 contrib/long_distance_matching/versions/v0.1/ldm.c delete mode 100644 contrib/long_distance_matching/versions/v0.1/ldm.h delete mode 100644 contrib/long_distance_matching/versions/v0.1/main-ldm.c delete mode 100644 contrib/long_distance_matching/versions/v0.2/Makefile delete mode 100644 contrib/long_distance_matching/versions/v0.2/ldm.c delete mode 100644 contrib/long_distance_matching/versions/v0.2/ldm.h delete mode 100644 contrib/long_distance_matching/versions/v0.2/main-ldm.c create mode 100644 contrib/long_distance_matching/versions/v0.3/README delete mode 100644 contrib/long_distance_matching/versions/v0.4/Makefile delete mode 100644 contrib/long_distance_matching/versions/v0.4/ldm.c delete mode 100644 contrib/long_distance_matching/versions/v0.4/ldm.h delete mode 100644 contrib/long_distance_matching/versions/v0.4/main-ldm.c delete mode 100644 contrib/long_distance_matching/versions/v0.4/util.c delete mode 100644 contrib/long_distance_matching/versions/v0.4/util.h create mode 100644 contrib/long_distance_matching/versions/v0.5/README diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index b17a0f156..87645b76d 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -19,8 +19,8 @@ #define WINDOW_SIZE (1 << 20) //These should be multiples of four. -#define LDM_HASH_LENGTH 100 -#define MINMATCH 100 +#define LDM_HASH_LENGTH 4 +#define MINMATCH 4 #define ML_BITS 4 #define ML_MASK ((1U<iend = dctx->ip + dctx->compressSize; dctx->op = dst; dctx->oend = dctx->op + dctx->maxDecompressSize; - } size_t LDM_decompress(const void *src, size_t compressSize, diff --git a/contrib/long_distance_matching/util.c b/contrib/long_distance_matching/util.c index 70fcbc2ce..47ac8a126 100644 --- a/contrib/long_distance_matching/util.c +++ b/contrib/long_distance_matching/util.c @@ -66,4 +66,3 @@ BYTE LDM_readByte(const void *memPtr) { memcpy(&val, memPtr, 1); return val; } - diff --git a/contrib/long_distance_matching/versions/v0.1/ldm.c b/contrib/long_distance_matching/versions/v0.1/ldm.c deleted file mode 100644 index 266425f8a..000000000 --- a/contrib/long_distance_matching/versions/v0.1/ldm.c +++ /dev/null @@ -1,394 +0,0 @@ -#include -#include -#include -#include - -#include "ldm.h" - -#define LDM_MEMORY_USAGE 14 -#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) -#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) - -#define WINDOW_SIZE (1 << 20) -#define MAX_WINDOW_SIZE 31 -#define HASH_SIZE 4 -#define MINMATCH 4 - -#define ML_BITS 4 -#define ML_MASK ((1U<>8); - } -} - -static U32 LDM_read32(const void *ptr) { - return *(const U32 *)ptr; -} - -static U64 LDM_read64(const void *ptr) { - return *(const U64 *)ptr; -} - - -static void LDM_copy8(void *dst, const void *src) { - memcpy(dst, src, 8); -} - -static void LDM_wild_copy(void *dstPtr, const void *srcPtr, void *dstEnd) { - BYTE *d = (BYTE *)dstPtr; - const BYTE *s = (const BYTE *)srcPtr; - BYTE * const e = (BYTE *)dstEnd; - - do { - LDM_copy8(d, s); - d += 8; - s += 8; - } while (d < e); - -} - -struct hash_entry { - U64 offset; - tag t; -}; - -static U32 LDM_hash(U32 sequence) { - return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); -} - -static U32 LDM_hash5(U64 sequence) { - static const U64 prime5bytes = 889523592379ULL; - static const U64 prime8bytes = 11400714785074694791ULL; - const U32 hashLog = LDM_HASHLOG; - if (LDM_isLittleEndian()) - return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); - else - return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); -} - -static U32 LDM_hash_position(const void * const p) { - return LDM_hash(LDM_read32(p)); -} - -static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, - const BYTE *srcBase) { - U32 *hashTable = (U32 *) tableBase; - hashTable[h] = (U32)(p - srcBase); -} - -static void LDM_put_position(const BYTE *p, void *tableBase, - const BYTE *srcBase) { - U32 const h = LDM_hash_position(p); - LDM_put_position_on_hash(p, h, tableBase, srcBase); -} - -static const BYTE *LDM_get_position_on_hash( - U32 h, void *tableBase, const BYTE *srcBase) { - const U32 * const hashTable = (U32*)tableBase; - return hashTable[h] + srcBase; -} - -static BYTE LDM_read_byte(const void *memPtr) { - BYTE val; - memcpy(&val, memPtr, 1); - return val; -} - -static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { - const BYTE * const pStart = pIn; - while (pIn < pInLimit - 1) { - BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); - if (!diff) { - pIn++; - pMatch++; - continue; - } - return (unsigned)(pIn - pStart); - } - return (unsigned)(pIn - pStart); -} - -void LDM_read_header(void const *source, size_t *compressed_size, - size_t *decompressed_size) { - const U32 *ip = (const U32 *)source; - *compressed_size = *ip++; - *decompressed_size = *ip; -} - -size_t LDM_compress(void const *source, void *dest, size_t source_size, - size_t max_dest_size) { - const BYTE * const istart = (const BYTE*)source; - const BYTE *ip = istart; - const BYTE * const iend = istart + source_size; - const BYTE *ilimit = iend - HASH_SIZE; - const BYTE * const matchlimit = iend - HASH_SIZE; - const BYTE * const mflimit = iend - MINMATCH; - BYTE *op = (BYTE*) dest; - U32 hashTable[LDM_HASHTABLESIZE_U32]; - memset(hashTable, 0, sizeof(hashTable)); - - const BYTE *anchor = (const BYTE *)source; -// struct LDM_cctx cctx; - size_t output_size = 0; - - U32 forwardH; - - /* Hash first byte: put into hash table */ - - LDM_put_position(ip, hashTable, istart); - ip++; - forwardH = LDM_hash_position(ip); - - //TODO Loop terminates before ip>=ilimit. - while (ip < ilimit) { - const BYTE *match; - BYTE *token; - - /* Find a match */ - { - const BYTE *forwardIp = ip; - unsigned step = 1; - - do { - U32 const h = forwardH; - ip = forwardIp; - forwardIp += step; - - if (forwardIp > mflimit) { - goto _last_literals; - } - - match = LDM_get_position_on_hash(h, hashTable, istart); - - forwardH = LDM_hash_position(forwardIp); - LDM_put_position_on_hash(ip, h, hashTable, istart); - } while (ip - match > WINDOW_SIZE || - LDM_read64(match) != LDM_read64(ip)); - } - - // TODO catchup - while (ip > anchor && match > istart && ip[-1] == match[-1]) { - ip--; - match--; - } - - /* Encode literals */ - { - unsigned const litLength = (unsigned)(ip - anchor); - token = op++; - -#ifdef LDM_DEBUG - printf("Cur position: %zu\n", anchor - istart); - printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match); -#endif - /* - fwrite(match, 4, 1, stdout); - printf("\n"); - */ - - if (litLength >= RUN_MASK) { - int len = (int)litLength - RUN_MASK; - *token = (RUN_MASK << ML_BITS); - for (; len >= 255; len -= 255) { - *op++ = 255; - } - *op++ = (BYTE)len; - } else { - *token = (BYTE)(litLength << ML_BITS); - } -#ifdef LDM_DEBUG - printf("Literals "); - fwrite(anchor, litLength, 1, stdout); - printf("\n"); -#endif - memcpy(op, anchor, litLength); - //LDM_wild_copy(op, anchor, op + litLength); - op += litLength; - } -_next_match: - /* Encode offset */ - { - LDM_write32(op, ip - match); - op += 4; - } - - /* Encode Match Length */ - { - unsigned matchCode; - matchCode = LDM_count(ip + MINMATCH, match + MINMATCH, - matchlimit); -#ifdef LDM_DEBUG - printf("Match length %zu\n", matchCode + MINMATCH); - fwrite(ip, MINMATCH + matchCode, 1, stdout); - printf("\n"); -#endif - ip += MINMATCH + matchCode; - if (matchCode >= ML_MASK) { - *token += ML_MASK; - matchCode -= ML_MASK; - LDM_write32(op, 0xFFFFFFFF); - while (matchCode >= 4*0xFF) { - op += 4; - LDM_write32(op, 0xffffffff); - matchCode -= 4*0xFF; - } - op += matchCode / 255; - *op++ = (BYTE)(matchCode % 255); - } else { - *token += (BYTE)(matchCode); - } -#ifdef LDM_DEBUG - printf("\n"); -#endif - } - - anchor = ip; - - LDM_put_position(ip, hashTable, istart); - forwardH = LDM_hash_position(++ip); - } -_last_literals: - /* Encode last literals */ - { - size_t const lastRun = (size_t)(iend - anchor); - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *op++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255; accumulator -= 255) { - *op++ = 255; - } - *op++ = (BYTE)accumulator; - } else { - *op++ = (BYTE)(lastRun << ML_BITS); - } - memcpy(op, anchor, lastRun); - op += lastRun; - } - return (op - (BYTE *)dest); -} - -size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, - size_t max_decompressed_size) { - const BYTE *ip = (const BYTE *)source; - const BYTE * const iend = ip + compressed_size; - BYTE *op = (BYTE *)dest; - BYTE * const oend = op + max_decompressed_size; - BYTE *cpy; - - while (ip < iend) { - size_t length; - const BYTE *match; - size_t offset; - - /* get literal length */ - unsigned const token = *ip++; - if ((length=(token >> ML_BITS)) == RUN_MASK) { - unsigned s; - do { - s = *ip++; - length += s; - } while (s == 255); - } -#ifdef LDM_DEBUG - printf("Literal length: %zu\n", length); -#endif - - /* copy literals */ - cpy = op + length; -#ifdef LDM_DEBUG - printf("Literals "); - fwrite(ip, length, 1, stdout); - printf("\n"); -#endif - memcpy(op, ip, length); -// LDM_wild_copy(op, ip, cpy); - ip += length; - op = cpy; - - /* get offset */ - offset = LDM_read32(ip); - -#ifdef LDM_DEBUG - printf("Offset: %zu\n", offset); -#endif - ip += 4; - match = op - offset; - // LDM_write32(op, (U32)offset); - - /* get matchlength */ - length = token & ML_MASK; - if (length == ML_MASK) { - unsigned s; - do { - s = *ip++; - length += s; - } while (s == 255); - } - length += MINMATCH; -#ifdef LDM_DEBUG - printf("Match length: %zu\n", length); -#endif - /* copy match */ - cpy = op + length; - - // Inefficient for now - - while (match < cpy - offset && op < oend) { - *op++ = *match++; - } - } -// memcpy(dest, source, compressed_size); - return op - (BYTE *)dest; -} - - diff --git a/contrib/long_distance_matching/versions/v0.1/ldm.h b/contrib/long_distance_matching/versions/v0.1/ldm.h deleted file mode 100644 index f4ca25a38..000000000 --- a/contrib/long_distance_matching/versions/v0.1/ldm.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LDM_H -#define LDM_H - -#include /* size_t */ - -#define LDM_COMPRESS_SIZE 4 -#define LDM_DECOMPRESS_SIZE 4 -#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) - -size_t LDM_compress(void const *source, void *dest, size_t source_size, - size_t max_dest_size); - -size_t LDM_decompress(void const *source, void *dest, size_t compressed_size, - size_t max_decompressed_size); - -void LDM_read_header(void const *source, size_t *compressed_size, - size_t *decompressed_size); - -#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v0.1/main-ldm.c b/contrib/long_distance_matching/versions/v0.1/main-ldm.c deleted file mode 100644 index 10869cce3..000000000 --- a/contrib/long_distance_matching/versions/v0.1/main-ldm.c +++ /dev/null @@ -1,459 +0,0 @@ -// TODO: file size must fit into a U32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "ldm.h" - -// #define BUF_SIZE 16*1024 // Block size -#define DEBUG - -//#define ZSTD - -#if 0 -static size_t compress_file(FILE *in, FILE *out, size_t *size_in, - size_t *size_out) { - char *src, *buf = NULL; - size_t r = 1; - size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; - - src = malloc(BUF_SIZE); - if (!src) { - printf("Not enough memory\n"); - goto cleanup; - } - - size = BUF_SIZE + LDM_HEADER_SIZE; - buf = malloc(size); - if (!buf) { - printf("Not enough memory\n"); - goto cleanup; - } - - - for (;;) { - k = fread(src, 1, BUF_SIZE, in); - if (k == 0) - break; - count_in += k; - - n = LDM_compress(src, buf, k, BUF_SIZE); - - // n = k; - // offset += n; - offset = k; - count_out += k; - -// k = fwrite(src, 1, offset, out); - - k = fwrite(buf, 1, offset, out); - if (k < offset) { - if (ferror(out)) - printf("Write failed\n"); - else - printf("Short write\n"); - goto cleanup; - } - - } - *size_in = count_in; - *size_out = count_out; - r = 0; - cleanup: - free(src); - free(buf); - return r; -} - -static size_t decompress_file(FILE *in, FILE *out) { - void *src = malloc(BUF_SIZE); - void *dst = NULL; - size_t dst_capacity = BUF_SIZE; - size_t ret = 1; - size_t bytes_written = 0; - - if (!src) { - perror("decompress_file(src)"); - goto cleanup; - } - - while (ret != 0) { - /* Load more input */ - size_t src_size = fread(src, 1, BUF_SIZE, in); - void *src_ptr = src; - void *src_end = src_ptr + src_size; - if (src_size == 0 || ferror(in)) { - printf("(TODO): Decompress: not enough input or error reading file\n"); - //TODO - ret = 0; - goto cleanup; - } - - /* Allocate destination buffer if it hasn't been allocated already */ - if (!dst) { - dst = malloc(dst_capacity); - if (!dst) { - perror("decompress_file(dst)"); - goto cleanup; - } - } - - // TODO - - /* Decompress: - * Continue while there is more input to read. - */ - while (src_ptr != src_end && ret != 0) { - // size_t dst_size = src_size; - size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); - size_t written = fwrite(dst, 1, dst_size, out); -// printf("Writing %zu bytes\n", dst_size); - bytes_written += dst_size; - if (written != dst_size) { - printf("Decompress: Failed to write to file\n"); - goto cleanup; - } - src_ptr += src_size; - src_size = src_end - src_ptr; - } - - /* Update input */ - - } - - printf("Wrote %zu bytes\n", bytes_written); - - cleanup: - free(src); - free(dst); - - return ret; -} -#endif - -static size_t compress(const char *fname, const char *oname) { - int fdin, fdout; - struct stat statbuf; - char *src, *dst; - - /* 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 size of input file */ - if (fstat (fdin, &statbuf) < 0) { - perror("Fstat error"); - return 1; - } - size_t size_in = statbuf.st_size; - - /* go to the location corresponding to the last byte */ - if (lseek(fdout, size_in + LDM_HEADER_SIZE - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write 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; - } - size_t out_size = statbuf.st_size + LDM_HEADER_SIZE; - - /* mmap the output file */ - if ((dst = mmap(0, out_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - - #ifdef ZSTD - size_t size_out = ZSTD_compress(dst, statbuf.st_size, - src, statbuf.st_size, 1); - #else - size_t size_out = LDM_compress(src, dst + LDM_HEADER_SIZE, statbuf.st_size, - statbuf.st_size); - size_out += LDM_HEADER_SIZE; - - // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &size_out, 4); - memcpy(dst + 4, &(statbuf.st_size), 4); - printf("Compressed size: %zu\n", size_out); - printf("Decompressed size: %zu\n", statbuf.st_size); - #endif - ftruncate(fdout, size_out); - - printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, - (unsigned)statbuf.st_size, (unsigned)size_out, oname, - (double)size_out / (statbuf.st_size) * 100); - - close(fdin); - close(fdout); - return 0; -} - -static size_t decompress(const char *fname, const char *oname) { - int fdin, fdout; - struct stat statbuf; - char *src, *dst; - - /* 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 size of 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 header */ - size_t compressed_size, decompressed_size; - LDM_read_header(src, &compressed_size, &decompressed_size); - - printf("Size, compressed_size, decompressed_size: %zu %zu %zu\n", - statbuf.st_size, compressed_size, decompressed_size); - - /* go to the location corresponding to the last byte */ - if (lseek(fdout, decompressed_size - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write error"); - return 1; - } - - /* mmap the output file */ - if ((dst = mmap(0, decompressed_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - - /* Copy input file to output file */ -// memcpy(dst, src, statbuf.st_size); - - #ifdef ZSTD - size_t size_out = ZSTD_decompress(dst, decomrpessed_size, - src + LDM_HEADER_SIZE, - statbuf.st_size - LDM_HEADER_SIZE); - #else - size_t size_out = LDM_decompress(src + LDM_HEADER_SIZE, dst, - statbuf.st_size - LDM_HEADER_SIZE, - decompressed_size); - printf("Ret size out: %zu\n", size_out); - #endif - ftruncate(fdout, size_out); - - close(fdin); - close(fdout); - return 0; -} - -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; -} - -static void verify(const char *inpFilename, const char *decFilename) { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); -} - -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); - - struct timeval tv1, tv2; - /* compress */ - { - gettimeofday(&tv1, NULL); - if (compress(inpFilename, ldmFilename)) { - printf("Compress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - } - - /* decompress */ - - gettimeofday(&tv1, NULL); - if (decompress(ldmFilename, decFilename)) { - printf("Decompress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - - /* verify */ - verify(inpFilename, decFilename); - return 0; -} - -#if 0 -int main2(int argc, char *argv[]) { - char inpFilename[256] = { 0 }; - char ldmFilename[256] = { 0 }; - char decFilename[256] = { 0 }; - - if (argc < 2) { - printf("Please specify input filename\n"); - return 0; - } - 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 */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *outFp = fopen(ldmFilename, "wb"); - size_t sizeIn = 0; - size_t sizeOut = 0; - size_t ret; - printf("compress : %s -> %s\n", inpFilename, ldmFilename); - ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); - if (ret) { - printf("compress : failed with code %zu\n", ret); - return ret; - } - printf("%s: %zu → %zu bytes, %.1f%%\n", - inpFilename, sizeIn, sizeOut, - (double)sizeOut / sizeIn * 100); - printf("compress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* decompress */ - { - FILE *inpFp = fopen(ldmFilename, "rb"); - FILE *outFp = fopen(decFilename, "wb"); - size_t ret; - - printf("decompress : %s -> %s\n", ldmFilename, decFilename); - ret = decompress_file(inpFp, outFp); - if (ret) { - printf("decompress : failed with code %zu\n", ret); - return ret; - } - printf("decompress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* verify */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); - } - return 0; -} -#endif - diff --git a/contrib/long_distance_matching/versions/v0.2/Makefile b/contrib/long_distance_matching/versions/v0.2/Makefile deleted file mode 100644 index 4e04fd6a2..000000000 --- a/contrib/long_distance_matching/versions/v0.2/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# ################################################################ -# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. -# All rights reserved. -# -# This source code is licensed under the BSD-style license found in the -# LICENSE file in the root directory of this source tree. An additional grant -# of patent rights can be found in the PATENTS file in the same directory. -# ################################################################ - -# This Makefile presumes libzstd is installed, using `sudo make install` - - -LDFLAGS += -lzstd - -.PHONY: default all clean - -default: all - -all: main-ldm - - -#main : ldm.c main.c -# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -main-ldm : ldm.c main-ldm.c - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -clean: - @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main main-ldm - @echo Cleaning completed - diff --git a/contrib/long_distance_matching/versions/v0.2/ldm.c b/contrib/long_distance_matching/versions/v0.2/ldm.c deleted file mode 100644 index 9081d1362..000000000 --- a/contrib/long_distance_matching/versions/v0.2/ldm.c +++ /dev/null @@ -1,436 +0,0 @@ -#include -#include -#include -#include - -#include "ldm.h" - -#define HASH_EVERY 7 - -#define LDM_MEMORY_USAGE 14 -#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) -#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) - -#define WINDOW_SIZE (1 << 20) -#define MAX_WINDOW_SIZE 31 -#define HASH_SIZE 8 -#define MINMATCH 8 - -#define ML_BITS 4 -#define ML_MASK ((1U<>8); - } -} - -static U32 LDM_read32(const void *ptr) { - return *(const U32 *)ptr; -} - -static U64 LDM_read64(const void *ptr) { - return *(const U64 *)ptr; -} - -static void LDM_copy8(void *dst, const void *src) { - memcpy(dst, src, 8); -} - -typedef struct compress_stats { - U32 num_matches; - U32 total_match_length; - U32 total_literal_length; - U64 total_offset; -} compress_stats; - -static void LDM_printCompressStats(const compress_stats *stats) { - printf("=====================\n"); - printf("Compression statistics\n"); - printf("Total number of matches: %u\n", stats->num_matches); - printf("Average match length: %.1f\n", ((double)stats->total_match_length) / - (double)stats->num_matches); - printf("Average literal length: %.1f\n", - ((double)stats->total_literal_length) / (double)stats->num_matches); - printf("Average offset length: %.1f\n", - ((double)stats->total_offset) / (double)stats->num_matches); - printf("=====================\n"); -} - -// TODO: unused. -struct hash_entry { - U64 offset; - tag t; -}; - -static U32 LDM_hash(U32 sequence) { - return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); -} - -static U32 LDM_hash5(U64 sequence) { - static const U64 prime5bytes = 889523592379ULL; - static const U64 prime8bytes = 11400714785074694791ULL; - const U32 hashLog = LDM_HASHLOG; - if (LDM_isLittleEndian()) - return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); - else - return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); -} - -static U32 LDM_hash_position(const void * const p) { - return LDM_hash(LDM_read32(p)); -} - -static void LDM_put_position_on_hash(const BYTE *p, U32 h, void *tableBase, - const BYTE *srcBase) { - if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { - return; - } - - U32 *hashTable = (U32 *) tableBase; - hashTable[h] = (U32)(p - srcBase); -} - -static void LDM_put_position(const BYTE *p, void *tableBase, - const BYTE *srcBase) { - if (((p - srcBase) & HASH_EVERY) != HASH_EVERY) { - return; - } - U32 const h = LDM_hash_position(p); - LDM_put_position_on_hash(p, h, tableBase, srcBase); -} - -static const BYTE *LDM_get_position_on_hash( - U32 h, void *tableBase, const BYTE *srcBase) { - const U32 * const hashTable = (U32*)tableBase; - return hashTable[h] + srcBase; -} - -static BYTE LDM_read_byte(const void *memPtr) { - BYTE val; - memcpy(&val, memPtr, 1); - return val; -} - -static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { - const BYTE * const pStart = pIn; - while (pIn < pInLimit - 1) { - BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); - if (!diff) { - pIn++; - pMatch++; - continue; - } - return (unsigned)(pIn - pStart); - } - return (unsigned)(pIn - pStart); -} - -void LDM_read_header(const void *src, size_t *compressSize, - size_t *decompressSize) { - const U32 *ip = (const U32 *)src; - *compressSize = *ip++; - *decompressSize = *ip; -} - -// TODO: maxDstSize is unused -size_t LDM_compress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { - const BYTE * const istart = (const BYTE*)src; - const BYTE *ip = istart; - const BYTE * const iend = istart + srcSize; - const BYTE *ilimit = iend - HASH_SIZE; - const BYTE * const matchlimit = iend - HASH_SIZE; - const BYTE * const mflimit = iend - MINMATCH; - BYTE *op = (BYTE*) dst; - - compress_stats compressStats = { 0 }; - - U32 hashTable[LDM_HASHTABLESIZE_U32]; - memset(hashTable, 0, sizeof(hashTable)); - - const BYTE *anchor = (const BYTE *)src; -// struct LDM_cctx cctx; - size_t output_size = 0; - - U32 forwardH; - - /* Hash first byte: put into hash table */ - - LDM_put_position(ip, hashTable, istart); - const BYTE *lastHash = ip; - ip++; - forwardH = LDM_hash_position(ip); - - //TODO Loop terminates before ip>=ilimit. - while (ip < ilimit) { - const BYTE *match; - BYTE *token; - - /* Find a match */ - { - const BYTE *forwardIp = ip; - unsigned step = 1; - - do { - U32 const h = forwardH; - ip = forwardIp; - forwardIp += step; - - if (forwardIp > mflimit) { - goto _last_literals; - } - - match = LDM_get_position_on_hash(h, hashTable, istart); - - forwardH = LDM_hash_position(forwardIp); - LDM_put_position_on_hash(ip, h, hashTable, istart); - lastHash = ip; - } while (ip - match > WINDOW_SIZE || - LDM_read64(match) != LDM_read64(ip)); - } - compressStats.num_matches++; - - /* Catchup: look back to extend match from found match */ - while (ip > anchor && match > istart && ip[-1] == match[-1]) { - ip--; - match--; - } - - /* Encode literals */ - { - unsigned const litLength = (unsigned)(ip - anchor); - token = op++; - - compressStats.total_literal_length += litLength; - -#ifdef LDM_DEBUG - printf("Cur position: %zu\n", anchor - istart); - printf("LitLength %zu. (Match offset). %zu\n", litLength, ip - match); -#endif - - if (litLength >= RUN_MASK) { - int len = (int)litLength - RUN_MASK; - *token = (RUN_MASK << ML_BITS); - for (; len >= 255; len -= 255) { - *op++ = 255; - } - *op++ = (BYTE)len; - } else { - *token = (BYTE)(litLength << ML_BITS); - } -#ifdef LDM_DEBUG - printf("Literals "); - fwrite(anchor, litLength, 1, stdout); - printf("\n"); -#endif - memcpy(op, anchor, litLength); - op += litLength; - } -_next_match: - /* Encode offset */ - { - /* - LDM_writeLE16(op, ip-match); - op += 2; - */ - LDM_write32(op, ip - match); - op += 4; - compressStats.total_offset += (ip - match); - } - - /* Encode Match Length */ - { - unsigned matchCode; - matchCode = LDM_count(ip + MINMATCH, match + MINMATCH, - matchlimit); -#ifdef LDM_DEBUG - printf("Match length %zu\n", matchCode + MINMATCH); - fwrite(ip, MINMATCH + matchCode, 1, stdout); - printf("\n"); -#endif - compressStats.total_match_length += matchCode + MINMATCH; - unsigned ctr = 1; - ip++; - for (; ctr < MINMATCH + matchCode; ip++, ctr++) { - LDM_put_position(ip, hashTable, istart); - } -// ip += MINMATCH + matchCode; - if (matchCode >= ML_MASK) { - *token += ML_MASK; - matchCode -= ML_MASK; - LDM_write32(op, 0xFFFFFFFF); - while (matchCode >= 4*0xFF) { - op += 4; - LDM_write32(op, 0xffffffff); - matchCode -= 4*0xFF; - } - op += matchCode / 255; - *op++ = (BYTE)(matchCode % 255); - } else { - *token += (BYTE)(matchCode); - } -#ifdef LDM_DEBUG - printf("\n"); - -#endif - } - - anchor = ip; - - LDM_put_position(ip, hashTable, istart); - forwardH = LDM_hash_position(++ip); - lastHash = ip; - } -_last_literals: - /* Encode last literals */ - { - size_t const lastRun = (size_t)(iend - anchor); - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *op++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255; accumulator -= 255) { - *op++ = 255; - } - *op++ = (BYTE)accumulator; - } else { - *op++ = (BYTE)(lastRun << ML_BITS); - } - memcpy(op, anchor, lastRun); - op += lastRun; - } - LDM_printCompressStats(&compressStats); - return (op - (BYTE *)dst); -} - -typedef struct LDM_DCtx { - const BYTE * const ibase; /* Pointer to base of input */ - const BYTE *ip; /* Pointer to current input position */ - const BYTE *iend; /* End of source */ - BYTE *op; /* Pointer to output */ - const BYTE * const oend; /* Pointer to end of output */ - -} LDM_DCtx; - -size_t LDM_decompress(const void *src, size_t compressed_size, - void *dst, size_t max_decompressed_size) { - const BYTE *ip = (const BYTE *)src; - const BYTE * const iend = ip + compressed_size; - BYTE *op = (BYTE *)dst; - BYTE * const oend = op + max_decompressed_size; - BYTE *cpy; - - while (ip < iend) { - size_t length; - const BYTE *match; - size_t offset; - - /* get literal length */ - unsigned const token = *ip++; - if ((length=(token >> ML_BITS)) == RUN_MASK) { - unsigned s; - do { - s = *ip++; - length += s; - } while (s == 255); - } -#ifdef LDM_DEBUG - printf("Literal length: %zu\n", length); -#endif - - /* copy literals */ - cpy = op + length; -#ifdef LDM_DEBUG - printf("Literals "); - fwrite(ip, length, 1, stdout); - printf("\n"); -#endif - memcpy(op, ip, length); - ip += length; - op = cpy; - - /* get offset */ - /* - offset = LDM_readLE16(ip); - ip += 2; - */ - offset = LDM_read32(ip); - ip += 4; -#ifdef LDM_DEBUG - printf("Offset: %zu\n", offset); -#endif - match = op - offset; - // LDM_write32(op, (U32)offset); - - /* get matchlength */ - length = token & ML_MASK; - if (length == ML_MASK) { - unsigned s; - do { - s = *ip++; - length += s; - } while (s == 255); - } - length += MINMATCH; -#ifdef LDM_DEBUG - printf("Match length: %zu\n", length); -#endif - /* copy match */ - cpy = op + length; - - // Inefficient for now - while (match < cpy - offset && op < oend) { - *op++ = *match++; - } - } - return op - (BYTE *)dst; -} - - diff --git a/contrib/long_distance_matching/versions/v0.2/ldm.h b/contrib/long_distance_matching/versions/v0.2/ldm.h deleted file mode 100644 index 0ac7b2ece..000000000 --- a/contrib/long_distance_matching/versions/v0.2/ldm.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LDM_H -#define LDM_H - -#include /* size_t */ - -#define LDM_COMPRESS_SIZE 4 -#define LDM_DECOMPRESS_SIZE 4 -#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) - -size_t LDM_compress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -size_t LDM_decompress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -void LDM_read_header(const void *src, size_t *compressSize, - size_t *decompressSize); - -#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v0.2/main-ldm.c b/contrib/long_distance_matching/versions/v0.2/main-ldm.c deleted file mode 100644 index 0017335b8..000000000 --- a/contrib/long_distance_matching/versions/v0.2/main-ldm.c +++ /dev/null @@ -1,474 +0,0 @@ -// TODO: file size must fit into a U32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "ldm.h" - -// #define BUF_SIZE 16*1024 // Block size -#define DEBUG - -//#define ZSTD - -/* Compress file given by fname and output to oname. - * Returns 0 if successful, error code otherwise. - */ -static int compress(const char *fname, const char *oname) { - int fdin, fdout; - struct stat statbuf; - char *src, *dst; - - /* 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; - } - - size_t maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; - - /* Go to the location corresponding to the last byte. */ - /* TODO: fallocate? */ - if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* Write a dummy byte at the last location. */ - if (write(fdout, "", 1) != 1) { - perror("write 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; - } - - /* mmap the output file */ - if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - -#ifdef ZSTD - size_t compressSize = ZSTD_compress(dst, statbuf.st_size, - src, statbuf.st_size, 1); -#else - size_t compressSize = LDM_HEADER_SIZE + - LDM_compress(src, statbuf.st_size, - dst + LDM_HEADER_SIZE, statbuf.st_size); - - // Write compress and decompress size to header - // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &compressSize, 4); - memcpy(dst + 4, &(statbuf.st_size), 4); - -#ifdef DEBUG - printf("Compressed size: %zu\n", compressSize); - printf("Decompressed size: %zu\n", statbuf.st_size); -#endif -#endif - - // Truncate file to compressSize. - ftruncate(fdout, compressSize); - - printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, - (unsigned)statbuf.st_size, (unsigned)compressSize, oname, - (double)compressSize / (statbuf.st_size) * 100); - - // Close files. - close(fdin); - close(fdout); - return 0; -} - -/* 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; - - /* 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. */ - size_t compressSize, decompressSize; - LDM_read_header(src, &compressSize, &decompressSize); - -#ifdef DEBUG - printf("Size, compressSize, decompressSize: %zu %zu %zu\n", - statbuf.st_size, compressSize, decompressSize); -#endif - - /* Go to the location corresponding to the last byte. */ - if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write error"); - return 1; - } - - /* mmap the output file */ - if ((dst = mmap(0, decompressSize, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - -#ifdef ZSTD - size_t outSize = ZSTD_decompress(dst, decomrpessed_size, - src + LDM_HEADER_SIZE, - statbuf.st_size - LDM_HEADER_SIZE); -#else - size_t outSize = LDM_decompress( - src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, - dst, decompressSize); - - printf("Ret size out: %zu\n", outSize); - #endif - ftruncate(fdout, 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 void verify(const char *inpFilename, const char *decFilename) { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); -} - -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); - - struct timeval tv1, tv2; - - /* Compress */ - - gettimeofday(&tv1, NULL); - if (compress(inpFilename, ldmFilename)) { - printf("Compress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - - /* Decompress */ - - gettimeofday(&tv1, NULL); - if (decompress(ldmFilename, decFilename)) { - printf("Decompress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - - /* verify */ - verify(inpFilename, decFilename); - return 0; -} - - -#if 0 -static size_t compress_file(FILE *in, FILE *out, size_t *size_in, - size_t *size_out) { - char *src, *buf = NULL; - size_t r = 1; - size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; - - src = malloc(BUF_SIZE); - if (!src) { - printf("Not enough memory\n"); - goto cleanup; - } - - size = BUF_SIZE + LDM_HEADER_SIZE; - buf = malloc(size); - if (!buf) { - printf("Not enough memory\n"); - goto cleanup; - } - - - for (;;) { - k = fread(src, 1, BUF_SIZE, in); - if (k == 0) - break; - count_in += k; - - n = LDM_compress(src, buf, k, BUF_SIZE); - - // n = k; - // offset += n; - offset = k; - count_out += k; - -// k = fwrite(src, 1, offset, out); - - k = fwrite(buf, 1, offset, out); - if (k < offset) { - if (ferror(out)) - printf("Write failed\n"); - else - printf("Short write\n"); - goto cleanup; - } - - } - *size_in = count_in; - *size_out = count_out; - r = 0; - cleanup: - free(src); - free(buf); - return r; -} - -static size_t decompress_file(FILE *in, FILE *out) { - void *src = malloc(BUF_SIZE); - void *dst = NULL; - size_t dst_capacity = BUF_SIZE; - size_t ret = 1; - size_t bytes_written = 0; - - if (!src) { - perror("decompress_file(src)"); - goto cleanup; - } - - while (ret != 0) { - /* Load more input */ - size_t src_size = fread(src, 1, BUF_SIZE, in); - void *src_ptr = src; - void *src_end = src_ptr + src_size; - if (src_size == 0 || ferror(in)) { - printf("(TODO): Decompress: not enough input or error reading file\n"); - //TODO - ret = 0; - goto cleanup; - } - - /* Allocate destination buffer if it hasn't been allocated already */ - if (!dst) { - dst = malloc(dst_capacity); - if (!dst) { - perror("decompress_file(dst)"); - goto cleanup; - } - } - - // TODO - - /* Decompress: - * Continue while there is more input to read. - */ - while (src_ptr != src_end && ret != 0) { - // size_t dst_size = src_size; - size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); - size_t written = fwrite(dst, 1, dst_size, out); -// printf("Writing %zu bytes\n", dst_size); - bytes_written += dst_size; - if (written != dst_size) { - printf("Decompress: Failed to write to file\n"); - goto cleanup; - } - src_ptr += src_size; - src_size = src_end - src_ptr; - } - - /* Update input */ - - } - - printf("Wrote %zu bytes\n", bytes_written); - - cleanup: - free(src); - free(dst); - - return ret; -} - -int main2(int argc, char *argv[]) { - char inpFilename[256] = { 0 }; - char ldmFilename[256] = { 0 }; - char decFilename[256] = { 0 }; - - if (argc < 2) { - printf("Please specify input filename\n"); - return 0; - } - 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 */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *outFp = fopen(ldmFilename, "wb"); - size_t sizeIn = 0; - size_t sizeOut = 0; - size_t ret; - printf("compress : %s -> %s\n", inpFilename, ldmFilename); - ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); - if (ret) { - printf("compress : failed with code %zu\n", ret); - return ret; - } - printf("%s: %zu → %zu bytes, %.1f%%\n", - inpFilename, sizeIn, sizeOut, - (double)sizeOut / sizeIn * 100); - printf("compress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* decompress */ - { - FILE *inpFp = fopen(ldmFilename, "rb"); - FILE *outFp = fopen(decFilename, "wb"); - size_t ret; - - printf("decompress : %s -> %s\n", ldmFilename, decFilename); - ret = decompress_file(inpFp, outFp); - if (ret) { - printf("decompress : failed with code %zu\n", ret); - return ret; - } - printf("decompress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* verify */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); - } - return 0; -} -#endif - diff --git a/contrib/long_distance_matching/versions/v0.3/Makefile b/contrib/long_distance_matching/versions/v0.3/Makefile index 5ffd4eafe..e5153970b 100644 --- a/contrib/long_distance_matching/versions/v0.3/Makefile +++ b/contrib/long_distance_matching/versions/v0.3/Makefile @@ -1,12 +1,3 @@ -# ################################################################ -# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. -# All rights reserved. -# -# This source code is licensed under the BSD-style license found in the -# LICENSE file in the root directory of this source tree. An additional grant -# of patent rights can be found in the PATENTS file in the same directory. -# ################################################################ - # This Makefile presumes libzstd is installed, using `sudo make install` CFLAGS ?= -O3 @@ -26,7 +17,6 @@ default: all all: main-ldm - #main : ldm.c main.c # $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ diff --git a/contrib/long_distance_matching/versions/v0.3/README b/contrib/long_distance_matching/versions/v0.3/README new file mode 100644 index 000000000..8699562e5 --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.3/README @@ -0,0 +1,3 @@ +This version uses simple lz4-style compression: +- A 4-byte hash is inserted into the hash table for every position. +- Hash table replacement policy: direct overwrite. diff --git a/contrib/long_distance_matching/versions/v0.4/Makefile b/contrib/long_distance_matching/versions/v0.4/Makefile deleted file mode 100644 index 5ffd4eafe..000000000 --- a/contrib/long_distance_matching/versions/v0.4/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -# ################################################################ -# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. -# All rights reserved. -# -# This source code is licensed under the BSD-style license found in the -# LICENSE file in the root directory of this source tree. An additional grant -# of patent rights can be found in the PATENTS file in the same directory. -# ################################################################ - -# This Makefile presumes libzstd is installed, using `sudo make install` - -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: main-ldm - - -#main : ldm.c main.c -# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -main-ldm : util.c ldm.c main-ldm.c - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -clean: - @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main main-ldm - @echo Cleaning completed - diff --git a/contrib/long_distance_matching/versions/v0.4/ldm.c b/contrib/long_distance_matching/versions/v0.4/ldm.c deleted file mode 100644 index 79648097a..000000000 --- a/contrib/long_distance_matching/versions/v0.4/ldm.c +++ /dev/null @@ -1,729 +0,0 @@ -#include -#include -#include -#include - - -#include "ldm.h" -#include "util.h" - -#define HASH_EVERY 1 - -#define LDM_MEMORY_USAGE 22 -#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) -#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) - -#define LDM_OFFSET_SIZE 4 - -#define WINDOW_SIZE (1 << 23) -#define MAX_WINDOW_SIZE 31 -#define HASH_SIZE 4 -#define LDM_HASH_LENGTH 4 - -// Should be multiple of four -#define MINMATCH 4 - -#define ML_BITS 4 -#define ML_MASK ((1U<numMatches); - printf("Average match length: %.1f\n", ((double)stats->totalMatchLength) / - (double)stats->numMatches); - printf("Average literal length: %.1f\n", - ((double)stats->totalLiteralLength) / (double)stats->numMatches); - printf("Average offset length: %.1f\n", - ((double)stats->totalOffset) / (double)stats->numMatches); - printf("Num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", - stats->numCollisions, stats->numHashInserts, - stats->numHashInserts == 0 ? - 1.0 : (100.0 * (double)stats->numCollisions) / - (double)stats->numHashInserts); - printf("=====================\n"); -} - -typedef 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_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; - - const BYTE *lastPosHashed; /* Last position hashed */ - hash_t lastHash; /* Hash corresponding to lastPosHashed */ - const BYTE *nextIp; - const BYTE *nextPosHashed; - hash_t nextHash; /* Hash corresponding to nextPosHashed */ - - // Members for rolling hash. - U32 lastSum; - U32 nextSum; - - unsigned step; - - // DEBUG - const BYTE *DEBUG_setNextHash; -} LDM_CCtx; - -static int LDM_isValidMatch(const BYTE *p, const BYTE *match) { - U16 lengthLeft = MINMATCH; - const BYTE *curP = p; - const BYTE *curMatch = match; - - for (; lengthLeft >= 8; lengthLeft -= 8) { - if (LDM_read64(curP) != LDM_read64(curMatch)) { - return 0; - } - curP += 8; - curMatch += 8; - } - if (lengthLeft > 0) { - return LDM_read32(curP) == LDM_read32(curMatch); - } - return 1; -} - - - -#ifdef LDM_ROLLING_HASH -/** - * Convert a sum computed from LDM_getRollingHash to a hash value in the range - * of the hash table. - */ -static hash_t LDM_sumToHash(U32 sum) { - return sum & (LDM_HASH_SIZE_U32 - 1); -} - -static U32 LDM_getRollingHash(const char *data, U32 len) { - U32 i; - U32 s1, s2; - const schar *buf = (const schar *)data; - - s1 = s2 = 0; - for (i = 0; i < (len - 4); i += 4) { - s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + - (2 * buf[i + 2]) + (buf[i + 3]); - s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; - } - for(; i < len; i++) { - s1 += buf[i]; - s2 += s1; - } - return (s1 & 0xffff) + (s2 << 16); -} - -typedef struct LDM_sumStruct { - U16 s1, s2; -} LDM_sumStruct; - -static U32 LDM_updateRollingHash(U32 sum, U32 len, - schar toRemove, schar toAdd) { - U32 s1 = (sum & 0xffff) - toRemove + toAdd; - U32 s2 = (sum >> 16) - (toRemove * len) + s1; - - return (s1 & 0xffff) + (s2 << 16); -} - - -/* -static hash_t LDM_hashPosition(const void * const p) { - return LDM_sumToHash(LDM_getRollingHash((const char *)p, LDM_HASH_LENGTH)); -} -*/ - -/* -static void LDM_getRollingHashParts(U32 sum, LDM_sumStruct *sumStruct) { - sumStruct->s1 = sum & 0xffff; - sumStruct->s2 = sum >> 16; -} -*/ - -static void LDM_setNextHash(LDM_CCtx *cctx) { - -#ifdef RUN_CHECKS - U32 check; - if ((cctx->nextIp - cctx->ibase != 1) && - (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { - printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, - cctx->DEBUG_setNextHash - cctx->ibase); - } - - cctx->DEBUG_setNextHash = cctx->nextIp; -#endif - -// cctx->nextSum = LDM_getRollingHash((const char *)cctx->nextIp, LDM_HASH_LENGTH); - cctx->nextSum = LDM_updateRollingHash( - cctx->lastSum, LDM_HASH_LENGTH, - (schar)((cctx->lastPosHashed)[0]), - (schar)((cctx->lastPosHashed)[LDM_HASH_LENGTH])); - -#ifdef RUN_CHECKS - check = LDM_getRollingHash((const char *)cctx->nextIp, LDM_HASH_LENGTH); - - if (check != cctx->nextSum) { - printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); -// printf("INFO: %u %u %u\n", LDM_read32(cctx->nextIp), - } -#endif - cctx->nextPosHashed = cctx->nextIp; - cctx->nextHash = LDM_sumToHash(cctx->nextSum); - -#ifdef RUN_CHECKS - if ((cctx->nextIp - cctx->lastPosHashed) != 1) { - printf("setNextHash: nextIp != lastPosHashed + 1. %zu %zu %zu\n", - cctx->nextIp - cctx->ibase, cctx->lastPosHashed - cctx->ibase, - cctx->ip - cctx->ibase); - } -#endif - -} - -static void LDM_putHashOfCurrentPositionFromHash( - LDM_CCtx *cctx, hash_t hash, U32 sum) { - /* - if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { - return; - } - */ -#ifdef COMPUTE_STATS - if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { - offset_t offset = (cctx->hashTable)[hash].offset; - cctx->stats.numHashInserts++; - if (offset == 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { - cctx->stats.numCollisions++; - } - } -#endif - (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; - cctx->lastPosHashed = cctx->ip; - cctx->lastHash = hash; - cctx->lastSum = sum; -} - -static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { -#ifdef RUN_CHECKS - if (cctx->ip != cctx->nextPosHashed) { - printf("CHECK failed: updateLastHashFromNextHash %zu\n", cctx->ip - cctx->ibase); - } -#endif - LDM_putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); -} - -static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - U32 sum = LDM_getRollingHash((const char *)cctx->ip, LDM_HASH_LENGTH); - hash_t hash = LDM_sumToHash(sum); -#ifdef RUN_CHECKS - if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { - printf("CHECK failed: putHashOfCurrentPosition %zu\n", cctx->ip - cctx->ibase); - } -#endif -// hash_t hash = LDM_hashPosition(cctx->ip); - LDM_putHashOfCurrentPositionFromHash(cctx, hash, sum); -// printf("Offset %zu\n", cctx->ip - cctx->ibase); -} - -#else -static hash_t LDM_hash(U32 sequence) { - return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); -} - -static hash_t LDM_hashPosition(const void * const p) { - return LDM_hash(LDM_read32(p)); -} - -static void LDM_putHashOfCurrentPositionFromHash( - LDM_CCtx *cctx, hash_t hash) { - /* - if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { - return; - } - */ -#ifdef COMPUTE_STATS - if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { - offset_t offset = (cctx->hashTable)[hash].offset; - cctx->stats.numHashInserts++; - if (offset == 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { - cctx->stats.numCollisions++; - } - } -#endif - - (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; -#ifdef RUN_CHECKS - if (cctx->ip - cctx->lastPosHashed != 1) { - printf("putHashError\n"); - } -#endif - cctx->lastPosHashed = cctx->ip; - cctx->lastHash = hash; -} - -static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - hash_t hash = LDM_hashPosition(cctx->ip); - LDM_putHashOfCurrentPositionFromHash(cctx, hash); -} - -#endif - -/* -static hash_t LDM_hash5(U64 sequence) { - static const U64 prime5bytes = 889523592379ULL; - static const U64 prime8bytes = 11400714785074694791ULL; - const U32 hashLog = LDM_HASHLOG; - if (LDM_isLittleEndian()) - return (((sequence << 24) * prime5bytes) >> (64 - hashLog)); - else - return (((sequence >> 24) * prime8bytes) >> (64 - hashLog)); -} -*/ - - -static const BYTE *LDM_getPositionOnHash( - hash_t h, void *tableBase, const BYTE *srcBase) { - const LDM_hashEntry * const hashTable = (LDM_hashEntry *)tableBase; - return hashTable[h].offset + srcBase; -} - - -static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { - const BYTE * const pStart = pIn; - while (pIn < pInLimit - 1) { - BYTE const diff = LDM_readByte(pMatch) ^ LDM_readByte(pIn); - if (!diff) { - pIn++; - pMatch++; - continue; - } - return (unsigned)(pIn - pStart); - } - return (unsigned)(pIn - pStart); -} - -void LDM_readHeader(const void *src, size_t *compressSize, - size_t *decompressSize) { - const U32 *ip = (const U32 *)src; - *compressSize = *ip++; - *decompressSize = *ip; -} - -static void 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; - -#ifdef LDM_ROLLING_HASH - cctx->ihashLimit = cctx->iend - LDM_HASH_LENGTH; -#else - cctx->ihashLimit = cctx->iend - HASH_SIZE; -#endif - cctx->imatchLimit = cctx->iend - MINMATCH; - - cctx->obase = (BYTE *)dst; - cctx->op = (BYTE *)dst; - - cctx->anchor = cctx->ibase; - - memset(&(cctx->stats), 0, sizeof(cctx->stats)); - memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); - - cctx->lastPosHashed = NULL; - - cctx->step = 1; - cctx->nextIp = cctx->ip + cctx->step; - cctx->nextPosHashed = 0; - - cctx->DEBUG_setNextHash = 0; -} - -#ifdef LDM_ROLLING_HASH -static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { - cctx->nextIp = cctx->ip + cctx->step; - - do { - hash_t h; - U32 sum; -// printf("Call A\n"); - LDM_setNextHash(cctx); -// printf("End call a\n"); - h = cctx->nextHash; - sum = cctx->nextSum; - cctx->ip = cctx->nextIp; - cctx->nextIp += cctx->step; - - if (cctx->ip > cctx->imatchLimit) { - return 1; - } - - *match = LDM_getPositionOnHash(h, cctx->hashTable, cctx->ibase); - -// // Compute cctx->nextSum and cctx->nextHash from cctx->nextIp. -// LDM_setNextHash(cctx); - LDM_putHashOfCurrentPositionFromHash(cctx, h, sum); - -// printf("%u %u\n", cctx->lastHash, cctx->nextHash); - } while (cctx->ip - *match > WINDOW_SIZE || - !LDM_isValidMatch(cctx->ip, *match)); -// LDM_read64(*match) != LDM_read64(cctx->ip)); - LDM_setNextHash(cctx); - return 0; -} -#else -static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { - cctx->nextIp = cctx->ip; - - do { - hash_t const h = cctx->nextHash; - cctx->ip = cctx->nextIp; - cctx->nextIp += cctx->step; - - if (cctx->ip > cctx->imatchLimit) { - return 1; - } - - *match = LDM_getPositionOnHash(h, cctx->hashTable, cctx->ibase); - - cctx->nextHash = LDM_hashPosition(cctx->nextIp); - LDM_putHashOfCurrentPositionFromHash(cctx, h); - - } while (cctx->ip - *match > WINDOW_SIZE || - !LDM_isValidMatch(cctx->ip, *match)); - return 0; -} - -#endif - -/** - * Write current block (literals, literal length, match offset, - * match length). - * - * Update input pointer, inserting hashes into hash table along the - * way. - */ -static void LDM_outputBlock(LDM_CCtx *cctx, const BYTE *match) { - unsigned const literalLength = (unsigned)(cctx->ip - cctx->anchor); - unsigned const offset = cctx->ip - match; - unsigned const matchLength = LDM_count( - cctx->ip + MINMATCH, match + MINMATCH, cctx->ihashLimit); - BYTE *token = cctx->op++; - - cctx->stats.totalLiteralLength += literalLength; - cctx->stats.totalOffset += offset; - cctx->stats.totalMatchLength += matchLength + MINMATCH; - - /* Encode the literal length. */ - if (literalLength >= RUN_MASK) { - int len = (int)literalLength - RUN_MASK; - *token = (RUN_MASK << ML_BITS); - for (; len >= 255; len -= 255) { - *(cctx->op)++ = 255; - } - *(cctx->op)++ = (BYTE)len; - } else { - *token = (BYTE)(literalLength << ML_BITS); - } - - /* Encode the literals. */ - memcpy(cctx->op, cctx->anchor, literalLength); - cctx->op += literalLength; - - /* Encode the offset. */ - LDM_write32(cctx->op, offset); - cctx->op += LDM_OFFSET_SIZE; - - /* Encode match length */ - if (matchLength >= ML_MASK) { - unsigned matchLengthRemaining = matchLength; - *token += ML_MASK; - matchLengthRemaining -= ML_MASK; - LDM_write32(cctx->op, 0xFFFFFFFF); - while (matchLengthRemaining >= 4*0xFF) { - cctx->op += 4; - LDM_write32(cctx->op, 0xffffffff); - matchLengthRemaining -= 4*0xFF; - } - cctx->op += matchLengthRemaining / 255; - *(cctx->op)++ = (BYTE)(matchLengthRemaining % 255); - } else { - *token += (BYTE)(matchLength); - } - -// LDM_setNextHash(cctx); -// cctx->ip = cctx->lastPosHashed + 1; -// cctx->nextIp = cctx->ip + cctx->step; -// printf("HERE: %zu %zu %zu\n", cctx->ip - cctx->ibase, -// cctx->lastPosHashed - cctx->ibase, cctx->nextIp - cctx->ibase); - - cctx->nextIp = cctx->ip + cctx->step; - - while (cctx->ip < cctx->anchor + MINMATCH + matchLength + literalLength) { -// printf("Loop\n"); - if (cctx->ip > cctx->lastPosHashed) { - LDM_updateLastHashFromNextHash(cctx); -// LDM_putHashOfCurrentPosition(cctx); -#ifdef LDM_ROLLING_HASH - LDM_setNextHash(cctx); -#endif - } - /* - printf("Call b %zu %zu %zu\n", - cctx->lastPosHashed - cctx->ibase, - cctx->nextIp - cctx->ibase, - cctx->ip - cctx->ibase); - */ -// printf("end call b\n"); - cctx->ip++; - cctx->nextIp++; - } - -// printf("There: %zu %zu\n", cctx->ip - cctx->ibase, cctx->lastPosHashed - cctx->ibase); -} - -// TODO: srcSize and maxDstSize is unused -size_t LDM_compress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { - LDM_CCtx cctx; - LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); - - /* Hash the first position and put it into the hash table. */ - LDM_putHashOfCurrentPosition(&cctx); -#ifdef LDM_ROLLING_HASH -// LDM_setNextHash(&cctx); -// tmp_hash = LDM_updateRollingHash(cctx.lastSum, LDM_HASH_LENGTH, -// cctx.ip[0], cctx.ip[LDM_HASH_LENGTH]); -// printf("Update test: %u %u\n", tmp_hash, cctx.nextSum); -// cctx.ip++; -#else - cctx.ip++; - cctx.nextHash = LDM_hashPosition(cctx.ip); -#endif - - // TODO: loop condition is not accurate. - while (1) { - const BYTE *match; -// printf("Start of loop\n"); - - /** - * 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. - */ - if (LDM_findBestMatch(&cctx, &match) != 0) { - goto _last_literals; - } -// printf("End of match finding\n"); - - cctx.stats.numMatches++; - - /** - * Catch up: look back to extend the match backwards from the found match. - */ - while (cctx.ip > cctx.anchor && match > cctx.ibase && - cctx.ip[-1] == match[-1]) { -// printf("Catch up\n"); - cctx.ip--; - match--; - } - - /** - * Write current block (literals, literal length, match offset, match - * length) and update pointers and hashes. - */ - LDM_outputBlock(&cctx, match); -// printf("End of loop\n"); - - // Set start of next block to current input pointer. - cctx.anchor = cctx.ip; - LDM_updateLastHashFromNextHash(&cctx); -// LDM_putHashOfCurrentPosition(&cctx); -#ifndef LDM_ROLLING_HASH - cctx.ip++; -#endif - - /* - LDM_putHashOfCurrentPosition(&cctx); - printf("Call c\n"); - LDM_setNextHash(&cctx); - printf("End call c\n"); - cctx.ip++; - cctx.nextIp++; - */ - } -_last_literals: - /* Encode the last literals (no more matches). */ - { - size_t const lastRun = (size_t)(cctx.iend - cctx.anchor); - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *(cctx.op)++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255; accumulator -= 255) { - *(cctx.op)++ = 255; - } - *(cctx.op)++ = (BYTE)accumulator; - } else { - *(cctx.op)++ = (BYTE)(lastRun << ML_BITS); - } - memcpy(cctx.op, cctx.anchor, lastRun); - cctx.op += lastRun; - } - LDM_printCompressStats(&cctx.stats); - return (cctx.op - (const BYTE *)cctx.obase); -} - -typedef struct LDM_DCtx { - size_t compressSize; - size_t maxDecompressSize; - - 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 */ -} LDM_DCtx; - -static void LDM_initializeDCtx(LDM_DCtx *dctx, - const void *src, size_t compressSize, - void *dst, size_t maxDecompressSize) { - dctx->compressSize = compressSize; - dctx->maxDecompressSize = maxDecompressSize; - - dctx->ibase = src; - dctx->ip = (const BYTE *)src; - dctx->iend = dctx->ip + dctx->compressSize; - dctx->op = dst; - dctx->oend = dctx->op + dctx->maxDecompressSize; - -} - -size_t LDM_decompress(const void *src, size_t compressSize, - void *dst, size_t maxDecompressSize) { - LDM_DCtx dctx; - LDM_initializeDCtx(&dctx, src, compressSize, dst, maxDecompressSize); - - while (dctx.ip < dctx.iend) { - BYTE *cpy; - const BYTE *match; - size_t length, offset; - - /* Get the literal length. */ - unsigned const token = *(dctx.ip)++; - if ((length = (token >> ML_BITS)) == RUN_MASK) { - unsigned s; - do { - s = *(dctx.ip)++; - length += s; - } while (s == 255); - } - - /* Copy literals. */ - cpy = dctx.op + length; - memcpy(dctx.op, dctx.ip, length); - dctx.ip += length; - dctx.op = cpy; - - //TODO : dynamic offset size - offset = LDM_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 += MINMATCH; - - /* Copy match. */ - cpy = dctx.op + length; - - // Inefficient for now. - while (match < cpy - offset && dctx.op < dctx.oend) { - *(dctx.op)++ = *match++; - } - } - return dctx.op - (BYTE *)dst; -} - -void LDM_test(const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { -#ifdef LDM_ROLLING_HASH - const BYTE *ip = (const BYTE *)src + 1125; - U32 sum = LDM_getRollingHash((const char *)ip, LDM_HASH_LENGTH); - U32 sum2; - ++ip; - for (; ip < (const BYTE *)src + 1125 + 100; ip++) { - sum2 = LDM_updateRollingHash(sum, LDM_HASH_LENGTH, - ip[-1], ip[LDM_HASH_LENGTH - 1]); - sum = LDM_getRollingHash((const char *)ip, LDM_HASH_LENGTH); - printf("TEST HASH: %zu %u %u\n", ip - (const BYTE *)src, sum, sum2); - } -#endif -} - - diff --git a/contrib/long_distance_matching/versions/v0.4/ldm.h b/contrib/long_distance_matching/versions/v0.4/ldm.h deleted file mode 100644 index a34faac4f..000000000 --- a/contrib/long_distance_matching/versions/v0.4/ldm.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef LDM_H -#define LDM_H - -#include /* size_t */ - -#define LDM_COMPRESS_SIZE 4 -#define LDM_DECOMPRESS_SIZE 4 -#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) - -size_t LDM_compress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -size_t LDM_decompress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -void LDM_readHeader(const void *src, size_t *compressSize, - size_t *decompressSize); - -void LDM_test(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v0.4/main-ldm.c b/contrib/long_distance_matching/versions/v0.4/main-ldm.c deleted file mode 100644 index f8ae54698..000000000 --- a/contrib/long_distance_matching/versions/v0.4/main-ldm.c +++ /dev/null @@ -1,480 +0,0 @@ -// TODO: file size must fit into a U32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "ldm.h" - -// #define BUF_SIZE 16*1024 // Block size -#define DEBUG -//#define TEST - -//#define ZSTD - -/* Compress file given by fname and output to oname. - * Returns 0 if successful, error code otherwise. - */ -static int compress(const char *fname, const char *oname) { - int fdin, fdout; - struct stat statbuf; - char *src, *dst; - size_t maxCompressSize, compressSize; - - /* 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; - } - - maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; - - /* Go to the location corresponding to the last byte. */ - /* TODO: fallocate? */ - if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* Write a dummy byte at the last location. */ - if (write(fdout, "", 1) != 1) { - perror("write 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; - } - - /* mmap the output file */ - if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - -#ifdef TEST - LDM_test(src, statbuf.st_size, - dst + LDM_HEADER_SIZE, statbuf.st_size); -#endif - -#ifdef ZSTD - compressSize = ZSTD_compress(dst, statbuf.st_size, - src, statbuf.st_size, 1); -#else - compressSize = LDM_HEADER_SIZE + - LDM_compress(src, statbuf.st_size, - dst + LDM_HEADER_SIZE, statbuf.st_size); - - // Write compress and decompress size to header - // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &compressSize, 4); - memcpy(dst + 4, &(statbuf.st_size), 4); - -#ifdef DEBUG - printf("Compressed size: %zu\n", compressSize); - printf("Decompressed size: %zu\n", (size_t)statbuf.st_size); -#endif -#endif - - // Truncate file to compressSize. - ftruncate(fdout, compressSize); - - printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, - (unsigned)statbuf.st_size, (unsigned)compressSize, oname, - (double)compressSize / (statbuf.st_size) * 100); - - // Close files. - close(fdin); - close(fdout); - return 0; -} - -/* 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; - size_t compressSize, decompressSize, 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, &compressSize, &decompressSize); - - /* Go to the location corresponding to the last byte. */ - if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write error"); - return 1; - } - - /* mmap the output file */ - if ((dst = mmap(0, decompressSize, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - -#ifdef ZSTD - outSize = ZSTD_decompress(dst, decomrpessed_size, - src + LDM_HEADER_SIZE, - statbuf.st_size - LDM_HEADER_SIZE); -#else - outSize = LDM_decompress( - src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, - dst, decompressSize); - - printf("Ret size out: %zu\n", outSize); - #endif - ftruncate(fdout, 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 void verify(const char *inpFilename, const char *decFilename) { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - } - - fclose(decFp); - fclose(inpFp); -} - -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 */ - { - struct timeval tv1, tv2; - gettimeofday(&tv1, NULL); - if (compress(inpFilename, ldmFilename)) { - printf("Compress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total compress time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - } - - /* Decompress */ - { - struct timeval tv1, tv2; - gettimeofday(&tv1, NULL); - if (decompress(ldmFilename, decFilename)) { - printf("Decompress error"); - 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 */ - verify(inpFilename, decFilename); - return 0; -} - - -#if 0 -static size_t compress_file(FILE *in, FILE *out, size_t *size_in, - size_t *size_out) { - char *src, *buf = NULL; - size_t r = 1; - size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; - - src = malloc(BUF_SIZE); - if (!src) { - printf("Not enough memory\n"); - goto cleanup; - } - - size = BUF_SIZE + LDM_HEADER_SIZE; - buf = malloc(size); - if (!buf) { - printf("Not enough memory\n"); - goto cleanup; - } - - - for (;;) { - k = fread(src, 1, BUF_SIZE, in); - if (k == 0) - break; - count_in += k; - - n = LDM_compress(src, buf, k, BUF_SIZE); - - // n = k; - // offset += n; - offset = k; - count_out += k; - -// k = fwrite(src, 1, offset, out); - - k = fwrite(buf, 1, offset, out); - if (k < offset) { - if (ferror(out)) - printf("Write failed\n"); - else - printf("Short write\n"); - goto cleanup; - } - - } - *size_in = count_in; - *size_out = count_out; - r = 0; - cleanup: - free(src); - free(buf); - return r; -} - -static size_t decompress_file(FILE *in, FILE *out) { - void *src = malloc(BUF_SIZE); - void *dst = NULL; - size_t dst_capacity = BUF_SIZE; - size_t ret = 1; - size_t bytes_written = 0; - - if (!src) { - perror("decompress_file(src)"); - goto cleanup; - } - - while (ret != 0) { - /* Load more input */ - size_t src_size = fread(src, 1, BUF_SIZE, in); - void *src_ptr = src; - void *src_end = src_ptr + src_size; - if (src_size == 0 || ferror(in)) { - printf("(TODO): Decompress: not enough input or error reading file\n"); - //TODO - ret = 0; - goto cleanup; - } - - /* Allocate destination buffer if it hasn't been allocated already */ - if (!dst) { - dst = malloc(dst_capacity); - if (!dst) { - perror("decompress_file(dst)"); - goto cleanup; - } - } - - // TODO - - /* Decompress: - * Continue while there is more input to read. - */ - while (src_ptr != src_end && ret != 0) { - // size_t dst_size = src_size; - size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); - size_t written = fwrite(dst, 1, dst_size, out); -// printf("Writing %zu bytes\n", dst_size); - bytes_written += dst_size; - if (written != dst_size) { - printf("Decompress: Failed to write to file\n"); - goto cleanup; - } - src_ptr += src_size; - src_size = src_end - src_ptr; - } - - /* Update input */ - - } - - printf("Wrote %zu bytes\n", bytes_written); - - cleanup: - free(src); - free(dst); - - return ret; -} - -int main2(int argc, char *argv[]) { - char inpFilename[256] = { 0 }; - char ldmFilename[256] = { 0 }; - char decFilename[256] = { 0 }; - - if (argc < 2) { - printf("Please specify input filename\n"); - return 0; - } - 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 */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *outFp = fopen(ldmFilename, "wb"); - size_t sizeIn = 0; - size_t sizeOut = 0; - size_t ret; - printf("compress : %s -> %s\n", inpFilename, ldmFilename); - ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); - if (ret) { - printf("compress : failed with code %zu\n", ret); - return ret; - } - printf("%s: %zu → %zu bytes, %.1f%%\n", - inpFilename, sizeIn, sizeOut, - (double)sizeOut / sizeIn * 100); - printf("compress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* decompress */ - { - FILE *inpFp = fopen(ldmFilename, "rb"); - FILE *outFp = fopen(decFilename, "wb"); - size_t ret; - - printf("decompress : %s -> %s\n", ldmFilename, decFilename); - ret = decompress_file(inpFp, outFp); - if (ret) { - printf("decompress : failed with code %zu\n", ret); - return ret; - } - printf("decompress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* verify */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); - } - return 0; -} -#endif - diff --git a/contrib/long_distance_matching/versions/v0.4/util.c b/contrib/long_distance_matching/versions/v0.4/util.c deleted file mode 100644 index 70fcbc2ce..000000000 --- a/contrib/long_distance_matching/versions/v0.4/util.c +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include -#include - -#include "util.h" - -typedef uint8_t BYTE; -typedef uint16_t U16; -typedef uint32_t U32; -typedef int32_t S32; -typedef uint64_t U64; - -unsigned LDM_isLittleEndian(void) { - const union { U32 u; BYTE c[4]; } one = { 1 }; - return one.c[0]; -} - -U16 LDM_read16(const void *memPtr) { - U16 val; - memcpy(&val, memPtr, sizeof(val)); - return val; -} - -U16 LDM_readLE16(const void *memPtr) { - if (LDM_isLittleEndian()) { - return LDM_read16(memPtr); - } else { - const BYTE *p = (const BYTE *)memPtr; - return (U16)((U16)p[0] + (p[1] << 8)); - } -} - -void LDM_write16(void *memPtr, U16 value){ - memcpy(memPtr, &value, sizeof(value)); -} - -void LDM_write32(void *memPtr, U32 value) { - memcpy(memPtr, &value, sizeof(value)); -} - -void LDM_writeLE16(void *memPtr, U16 value) { - if (LDM_isLittleEndian()) { - LDM_write16(memPtr, value); - } else { - BYTE* p = (BYTE *)memPtr; - p[0] = (BYTE) value; - p[1] = (BYTE)(value>>8); - } -} - -U32 LDM_read32(const void *ptr) { - return *(const U32 *)ptr; -} - -U64 LDM_read64(const void *ptr) { - return *(const U64 *)ptr; -} - -void LDM_copy8(void *dst, const void *src) { - memcpy(dst, src, 8); -} - -BYTE LDM_readByte(const void *memPtr) { - BYTE val; - memcpy(&val, memPtr, 1); - return val; -} - diff --git a/contrib/long_distance_matching/versions/v0.4/util.h b/contrib/long_distance_matching/versions/v0.4/util.h deleted file mode 100644 index d1c3c999b..000000000 --- a/contrib/long_distance_matching/versions/v0.4/util.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef LDM_UTIL_H -#define LDM_UTIL_H - -unsigned LDM_isLittleEndian(void); - -uint16_t LDM_read16(const void *memPtr); - -uint16_t LDM_readLE16(const void *memPtr); - -void LDM_write16(void *memPtr, uint16_t value); - -void LDM_write32(void *memPtr, uint32_t value); - -void LDM_writeLE16(void *memPtr, uint16_t value); - -uint32_t LDM_read32(const void *ptr); - -uint64_t LDM_read64(const void *ptr); - -void LDM_copy8(void *dst, const void *src); - -uint8_t LDM_readByte(const void *ptr); - - -#endif /* LDM_UTIL_H */ diff --git a/contrib/long_distance_matching/versions/v0.5/Makefile b/contrib/long_distance_matching/versions/v0.5/Makefile index 5ffd4eafe..fa4abce63 100644 --- a/contrib/long_distance_matching/versions/v0.5/Makefile +++ b/contrib/long_distance_matching/versions/v0.5/Makefile @@ -1,12 +1,3 @@ -# ################################################################ -# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. -# All rights reserved. -# -# This source code is licensed under the BSD-style license found in the -# LICENSE file in the root directory of this source tree. An additional grant -# of patent rights can be found in the PATENTS file in the same directory. -# ################################################################ - # This Makefile presumes libzstd is installed, using `sudo make install` CFLAGS ?= -O3 @@ -26,15 +17,11 @@ default: all all: main-ldm - -#main : ldm.c main.c -# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - main-ldm : util.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main main-ldm + main-ldm @echo Cleaning completed diff --git a/contrib/long_distance_matching/versions/v0.5/README b/contrib/long_distance_matching/versions/v0.5/README new file mode 100644 index 000000000..7901ae769 --- /dev/null +++ b/contrib/long_distance_matching/versions/v0.5/README @@ -0,0 +1,5 @@ +This version uses simple lz4-style compression with a rolling hash. +- A rolling checksum based on rsync's Adler-32 style checksum is used. +- The checksum is hashed using lz4's hash function. +- Hash table replacement policy: direct overwrite. +- The length of input to the hash function can be set with LDM_HASH_LENGTH. diff --git a/contrib/long_distance_matching/versions/v0.5/ldm.c b/contrib/long_distance_matching/versions/v0.5/ldm.c index 325c50406..5fa20c066 100644 --- a/contrib/long_distance_matching/versions/v0.5/ldm.c +++ b/contrib/long_distance_matching/versions/v0.5/ldm.c @@ -19,8 +19,8 @@ #define WINDOW_SIZE (1 << 20) //These should be multiples of four. -#define LDM_HASH_LENGTH 100 -#define MINMATCH 100 +#define LDM_HASH_LENGTH 4 +#define MINMATCH 4 #define ML_BITS 4 #define ML_MASK ((1U<stats); -#ifdef COMPUTE_STATS printf("=====================\n"); printf("Compression statistics\n"); printf("Total number of matches: %u\n", stats->numMatches); @@ -131,8 +131,8 @@ static void printCompressStats(const LDM_CCtx *cctx) { } printf("=====================\n"); -#endif } +#endif /** * Checks whether the MINMATCH bytes from p are the same as the MINMATCH From 3c16edd26ac531d358cad5031ac3f12bed12f45b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 16:40:24 -0700 Subject: [PATCH 123/318] added copyright header, removed clean from makefile --- contrib/adaptive-compression/Makefile | 2 +- contrib/adaptive-compression/adapt.c | 25 +++++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 38e3a7787..ed1a55ad4 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -19,7 +19,7 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -all: clean adapt +all: adapt adapt: $(ZSTD_FILES) adapt.c $(CC) $(FLAGS) $^ -o $@ diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index e9230ddb1..67fb75646 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -1,3 +1,19 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include /* fprintf */ +#include /* malloc, free */ +#include /* pthread functions */ +#include /* memset */ +#include "zstd_internal.h" +#include "util.h" + #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define PRINT(...) fprintf(stdout, __VA_ARGS__) #define DEBUG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } @@ -11,13 +27,6 @@ #define DEFAULT_ADAPT_PARAM 1 typedef unsigned char BYTE; -#include /* fprintf */ -#include /* malloc, free */ -#include /* pthread functions */ -#include /* memset */ -#include "zstd_internal.h" -#include "util.h" - static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; static unsigned g_displayStats = 0; @@ -138,7 +147,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobReadyID = 0; ctx->jobCompressedID = 0; ctx->jobWriteID = 0; - ctx->targetDictSize = FILE_CHUNK_SIZE >> 15; + ctx->targetDictSize = 1 << 12; ctx->lastDictSize = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); /* initializing jobs */ From 954d999abfbf7dc22cb4afe3e7a1cc19c8edd865 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 16:50:43 -0700 Subject: [PATCH 124/318] fixed up freeCCtx() removed BYTE since it wasn't being used --- contrib/adaptive-compression/adapt.c | 36 +++++++++++++++------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 67fb75646..3cafbca30 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -25,7 +25,6 @@ #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 1 -typedef unsigned char BYTE; static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -106,23 +105,26 @@ static void freeCompressionJobs(adaptCCtx* ctx) static int freeCCtx(adaptCCtx* ctx) { if (!ctx) return 0; - int const compressedMutexError = pthread_mutex_destroy(&ctx->jobCompressed_mutex); - int const compressedCondError = pthread_cond_destroy(&ctx->jobCompressed_cond); - int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); - int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); - int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); - int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); - int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex); - int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); - int const fileCloseError = (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; - int const cctxError = ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)) ? 1 : 0; - free(ctx->input.buffer.start); - if (ctx->jobs){ - freeCompressionJobs(ctx); - free(ctx->jobs); + { + int error = 0; + error |= pthread_mutex_destroy(&ctx->jobCompressed_mutex); + error |= pthread_cond_destroy(&ctx->jobCompressed_cond); + error |= pthread_mutex_destroy(&ctx->jobReady_mutex); + error |= pthread_cond_destroy(&ctx->jobReady_cond); + error |= pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); + error |= pthread_cond_destroy(&ctx->allJobsCompleted_cond); + error |= pthread_mutex_destroy(&ctx->jobWrite_mutex); + error |= pthread_cond_destroy(&ctx->jobWrite_cond); + error |= (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; + error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); + free(ctx->input.buffer.start); + if (ctx->jobs){ + freeCompressionJobs(ctx); + free(ctx->jobs); + } + free(ctx); + return error; } - free(ctx); - return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError | cctxError; } static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) From b5b18cf66477fecca9ba42982baa0bd9779c9957 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 17:10:58 -0700 Subject: [PATCH 125/318] changed to malloc, added comment about adaptive compression level, and changed ternary operators --- contrib/adaptive-compression/adapt.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 3cafbca30..bcac46de1 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -130,12 +130,11 @@ static int freeCCtx(adaptCCtx* ctx) static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) { - adaptCCtx* ctx = malloc(sizeof(adaptCCtx)); + adaptCCtx* ctx = calloc(1, sizeof(adaptCCtx)); if (ctx == NULL) { DISPLAY("Error: could not allocate space for context\n"); return NULL; } - memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = g_compressionLevel; pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); pthread_cond_init(&ctx->jobCompressed_cond, NULL); @@ -216,16 +215,24 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); } +/* + * Compression level is changed depending on which part of the compression process is lagging + * Currently, three theads exist for job creation, compression, and file writing respectively. + * adaptCompressionLevel() increments or decrements compression level based on which of the threads is lagging + * job creation or file writing lag => increased compression level + * compression thread lag => decreased compression level + * detecting which thread is lagging is done by keeping track of how many calls each thread makes to pthread_cond_wait + */ static unsigned adaptCompressionLevel(adaptCCtx* ctx) { unsigned reset = 0; - unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; - unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; - unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter ? 1 : 0; - unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter ? 1 : 0; - unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; - unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; - unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; + unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; + unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; + unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; + unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter; + unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)); + unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)); + unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)); DEBUG(3, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; From 7c886db0a8ff8ef8941c2e4a24ea75a3e2756849 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 17:28:53 -0700 Subject: [PATCH 126/318] changed to stderr --- contrib/adaptive-compression/adapt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index bcac46de1..9c6986446 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -336,12 +336,12 @@ static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0); double const sizeMB = (double)g_streamedSize / (1 << 20); double const avgCompRate = sizeMB * 1000 / timeElapsed; - fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); + fprintf(stderr, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); if (last) { - fprintf(stdout, "\n"); + fprintf(stderr, "\n"); } else { - fflush(stdout); + fflush(stderr); } } From 92bed4a7e0051c06dc6ceca058b5559615fcc2a8 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 12 Jul 2017 18:47:26 -0700 Subject: [PATCH 127/318] [ldm] Add CHAR_OFFSET in hash function and extend header size --- contrib/long_distance_matching/ldm.c | 18 +- contrib/long_distance_matching/ldm.h | 4 +- contrib/long_distance_matching/main-ldm.c | 203 +--------------------- contrib/long_distance_matching/util.c | 5 + contrib/long_distance_matching/util.h | 2 + 5 files changed, 22 insertions(+), 210 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 87645b76d..e64d28656 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -28,6 +28,7 @@ #define RUN_MASK ((1U<> 16) - (toRemove * len) + s1; + U32 s2 = (sum >> 16) - ((toRemove + CHECKSUM_CHAR_OFFSET) * len) + s1; return (s1 & 0xffff) + (s2 << 16); } @@ -344,9 +348,9 @@ static unsigned countMatchLength(const BYTE *pIn, const BYTE *pMatch, return (unsigned)(pIn - pStart); } -void LDM_readHeader(const void *src, size_t *compressSize, - size_t *decompressSize) { - const U32 *ip = (const U32 *)src; +void LDM_readHeader(const void *src, U64 *compressSize, + U64 *decompressSize) { + const U64 *ip = (const U64 *)src; *compressSize = *ip++; *decompressSize = *ip; } diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index d7f977d9b..f04b6e958 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -3,8 +3,8 @@ #include /* size_t */ -#define LDM_COMPRESS_SIZE 4 -#define LDM_DECOMPRESS_SIZE 4 +#define LDM_COMPRESS_SIZE 8 +#define LDM_DECOMPRESS_SIZE 8 #define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) /** diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index fbfd789bc..8354b795a 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -85,8 +85,8 @@ static int compress(const char *fname, const char *oname) { // Write compress and decompress size to header // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &compressSize, 4); - memcpy(dst + 4, &(statbuf.st_size), 4); + memcpy(dst, &compressSize, 8); + memcpy(dst + 8, &(statbuf.st_size), 8); #ifdef DEBUG printf("Compressed size: %zu\n", compressSize); @@ -267,202 +267,3 @@ int main(int argc, const char *argv[]) { verify(inpFilename, decFilename); return 0; } - - -#if 0 -static size_t compress_file(FILE *in, FILE *out, size_t *size_in, - size_t *size_out) { - char *src, *buf = NULL; - size_t r = 1; - size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; - - src = malloc(BUF_SIZE); - if (!src) { - printf("Not enough memory\n"); - goto cleanup; - } - - size = BUF_SIZE + LDM_HEADER_SIZE; - buf = malloc(size); - if (!buf) { - printf("Not enough memory\n"); - goto cleanup; - } - - - for (;;) { - k = fread(src, 1, BUF_SIZE, in); - if (k == 0) - break; - count_in += k; - - n = LDM_compress(src, buf, k, BUF_SIZE); - - // n = k; - // offset += n; - offset = k; - count_out += k; - -// k = fwrite(src, 1, offset, out); - - k = fwrite(buf, 1, offset, out); - if (k < offset) { - if (ferror(out)) - printf("Write failed\n"); - else - printf("Short write\n"); - goto cleanup; - } - - } - *size_in = count_in; - *size_out = count_out; - r = 0; - cleanup: - free(src); - free(buf); - return r; -} - -static size_t decompress_file(FILE *in, FILE *out) { - void *src = malloc(BUF_SIZE); - void *dst = NULL; - size_t dst_capacity = BUF_SIZE; - size_t ret = 1; - size_t bytes_written = 0; - - if (!src) { - perror("decompress_file(src)"); - goto cleanup; - } - - while (ret != 0) { - /* Load more input */ - size_t src_size = fread(src, 1, BUF_SIZE, in); - void *src_ptr = src; - void *src_end = src_ptr + src_size; - if (src_size == 0 || ferror(in)) { - printf("(TODO): Decompress: not enough input or error reading file\n"); - //TODO - ret = 0; - goto cleanup; - } - - /* Allocate destination buffer if it hasn't been allocated already */ - if (!dst) { - dst = malloc(dst_capacity); - if (!dst) { - perror("decompress_file(dst)"); - goto cleanup; - } - } - - // TODO - - /* Decompress: - * Continue while there is more input to read. - */ - while (src_ptr != src_end && ret != 0) { - // size_t dst_size = src_size; - size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); - size_t written = fwrite(dst, 1, dst_size, out); -// printf("Writing %zu bytes\n", dst_size); - bytes_written += dst_size; - if (written != dst_size) { - printf("Decompress: Failed to write to file\n"); - goto cleanup; - } - src_ptr += src_size; - src_size = src_end - src_ptr; - } - - /* Update input */ - - } - - printf("Wrote %zu bytes\n", bytes_written); - - cleanup: - free(src); - free(dst); - - return ret; -} - -int main2(int argc, char *argv[]) { - char inpFilename[256] = { 0 }; - char ldmFilename[256] = { 0 }; - char decFilename[256] = { 0 }; - - if (argc < 2) { - printf("Please specify input filename\n"); - return 0; - } - 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 */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *outFp = fopen(ldmFilename, "wb"); - size_t sizeIn = 0; - size_t sizeOut = 0; - size_t ret; - printf("compress : %s -> %s\n", inpFilename, ldmFilename); - ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); - if (ret) { - printf("compress : failed with code %zu\n", ret); - return ret; - } - printf("%s: %zu → %zu bytes, %.1f%%\n", - inpFilename, sizeIn, sizeOut, - (double)sizeOut / sizeIn * 100); - printf("compress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* decompress */ - { - FILE *inpFp = fopen(ldmFilename, "rb"); - FILE *outFp = fopen(decFilename, "wb"); - size_t ret; - - printf("decompress : %s -> %s\n", ldmFilename, decFilename); - ret = decompress_file(inpFp, outFp); - if (ret) { - printf("decompress : failed with code %zu\n", ret); - return ret; - } - printf("decompress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* verify */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); - } - return 0; -} -#endif - diff --git a/contrib/long_distance_matching/util.c b/contrib/long_distance_matching/util.c index 47ac8a126..627492159 100644 --- a/contrib/long_distance_matching/util.c +++ b/contrib/long_distance_matching/util.c @@ -53,6 +53,11 @@ U32 LDM_read32(const void *ptr) { return *(const U32 *)ptr; } +//TODO: endianness? +void LDM_write64(void *memPtr, U64 value) { + memcpy(memPtr, &value, sizeof(value)); +} + U64 LDM_read64(const void *ptr) { return *(const U64 *)ptr; } diff --git a/contrib/long_distance_matching/util.h b/contrib/long_distance_matching/util.h index d1c3c999b..dbf55cbc2 100644 --- a/contrib/long_distance_matching/util.h +++ b/contrib/long_distance_matching/util.h @@ -21,5 +21,7 @@ void LDM_copy8(void *dst, const void *src); uint8_t LDM_readByte(const void *ptr); +void LDM_write64(void *memPtr, uint64_t value); + #endif /* LDM_UTIL_H */ From de0414b7365246ffb7848db72ed08ff6c07d8a91 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 12 Jul 2017 19:08:24 -0700 Subject: [PATCH 128/318] [libzstd] Pull CTables into sub-structure --- lib/common/fse.h | 11 +-- lib/common/huf.h | 11 ++- lib/common/zstd_internal.h | 14 ++++ lib/compress/zstd_compress.c | 130 ++++++++++--------------------- lib/decompress/zstd_decompress.c | 8 +- 5 files changed, 70 insertions(+), 104 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 6d5d41def..54ac98b1c 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -31,13 +31,14 @@ You can contact the author at : - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ****************************************************************** */ -#ifndef FSE_H -#define FSE_H #if defined (__cplusplus) extern "C" { #endif +#ifndef FSE_H +#define FSE_H + /*-***************************************** * Dependencies @@ -297,8 +298,10 @@ FSE_decompress_usingDTable() result will tell how many bytes were regenerated (< If there is an error, the function will return an error code, which can be tested using FSE_isError(). (ex: dst buffer too small) */ +#endif /* FSE_H */ -#ifdef FSE_STATIC_LINKING_ONLY +#if defined(FSE_STATIC_LINKING_ONLY) && !defined(FSE_H_FSE_STATIC_LINKING_ONLY) +#define FSE_H_FSE_STATIC_LINKING_ONLY /* *** Dependency *** */ #include "bitstream.h" @@ -694,5 +697,3 @@ MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr) #if defined (__cplusplus) } #endif - -#endif /* FSE_H */ diff --git a/lib/common/huf.h b/lib/common/huf.h index dabd35991..2b3015a84 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -31,13 +31,13 @@ You can contact the author at : - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ****************************************************************** */ -#ifndef HUF_H_298734234 -#define HUF_H_298734234 #if defined (__cplusplus) extern "C" { #endif +#ifndef HUF_H_298734234 +#define HUF_H_298734234 /* *** Dependencies *** */ #include /* size_t */ @@ -124,6 +124,7 @@ HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const #define HUF_DECOMPRESS_WORKSPACE_SIZE (2 << 10) #define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32)) +#endif /* HUF_H_298734234 */ /* ****************************************************************** * WARNING !! @@ -132,7 +133,8 @@ HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const * because they are not guaranteed to remain stable in the future. * Only consider them in association with static linking. *******************************************************************/ -#ifdef HUF_STATIC_LINKING_ONLY +#if defined(HUF_STATIC_LINKING_ONLY) && !defined(HUF_H_HUF_STATIC_LINKING_ONLY) +#define HUF_H_HUF_STATIC_LINKING_ONLY /* *** Dependencies *** */ #include "mem.h" /* U32 */ @@ -295,9 +297,6 @@ size_t HUF_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void* c #endif /* HUF_STATIC_LINKING_ONLY */ - #if defined (__cplusplus) } #endif - -#endif /* HUF_H_298734234 */ diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index f49f6a13c..42e5e7b5d 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -50,6 +50,10 @@ #include "error_private.h" #define ZSTD_STATIC_LINKING_ONLY #include "zstd.h" +#define FSE_STATIC_LINKING_ONLY +#include "fse.h" +#define HUF_STATIC_LINKING_ONLY +#include "huf.h" #ifndef XXH_STATIC_LINKING_ONLY # define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ #endif @@ -266,6 +270,16 @@ typedef struct { const BYTE* cachedLiterals; } seqStore_t; +typedef struct { + HUF_repeat hufCTable_repeatMode; + U32 hufCTable[HUF_CTABLE_SIZE_U32(255)]; + U32 fseCTables_ready; + 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]; +} ZSTD_entropyCTables_t; + const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); void ZSTD_seqToCodes(const seqStore_t* seqStorePtr); diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 9300357f2..64da67d5c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -36,13 +36,6 @@ static const U32 g_searchStrength = 8; /* control skip over incompressible dat #define HASH_READ_SIZE 8 typedef enum { ZSTDcs_created=0, ZSTDcs_init, ZSTDcs_ongoing, ZSTDcs_ending } ZSTD_compressionStage_e; -/* entropy tables always have same size */ -static size_t const hufCTable_size = HUF_CTABLE_SIZE(255); -static size_t const litlengthCTable_size = FSE_CTABLE_SIZE(LLFSELog, MaxLL); -static size_t const offcodeCTable_size = FSE_CTABLE_SIZE(OffFSELog, MaxOff); -static size_t const matchlengthCTable_size = FSE_CTABLE_SIZE(MLFSELog, MaxML); -static size_t const entropyScratchSpace_size = HUF_WORKSPACE_SIZE; - /*-************************************* * Helper functions @@ -108,13 +101,7 @@ struct ZSTD_CCtx_s { U32* hashTable; U32* hashTable3; U32* chainTable; - HUF_repeat hufCTable_repeatMode; - HUF_CElt* hufCTable; - U32 fseCTables_ready; - FSE_CTable* offcodeCTable; - FSE_CTable* matchlengthCTable; - FSE_CTable* litlengthCTable; - unsigned* entropyScratchSpace; + ZSTD_entropyCTables_t* entropy; /* streaming */ char* inBuff; @@ -174,19 +161,9 @@ ZSTD_CCtx* ZSTD_initStaticCCtx(void *workspace, size_t workspaceSize) cctx->workSpaceSize = workspaceSize - sizeof(ZSTD_CCtx); /* entropy space (never moves) */ - /* note : this code should be shared with resetCCtx, rather than copy/pasted */ - { void* ptr = cctx->workSpace; - cctx->hufCTable = (HUF_CElt*)ptr; - ptr = (char*)cctx->hufCTable + hufCTable_size; - cctx->offcodeCTable = (FSE_CTable*) ptr; - ptr = (char*)ptr + offcodeCTable_size; - cctx->matchlengthCTable = (FSE_CTable*) ptr; - ptr = (char*)ptr + matchlengthCTable_size; - cctx->litlengthCTable = (FSE_CTable*) ptr; - ptr = (char*)ptr + litlengthCTable_size; - assert(((size_t)ptr & 3) == 0); /* ensure correct alignment */ - cctx->entropyScratchSpace = (unsigned*) ptr; - } + if (cctx->workSpaceSize < sizeof(ZSTD_entropyCTables_t)) return NULL; + assert(((size_t)cctx->workSpace & 7) == 0); /* ensure correct alignment */ + cctx->entropy = (ZSTD_entropyCTables_t*)cctx->workSpace; return cctx; } @@ -551,9 +528,7 @@ size_t ZSTD_estimateCCtxSize_advanced(ZSTD_compressionParameters cParams) 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 = hufCTable_size + litlengthCTable_size - + offcodeCTable_size + matchlengthCTable_size - + entropyScratchSpace_size; + size_t const entropySpace = sizeof(ZSTD_entropyCTables_t); size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); size_t const optBudget = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<appliedParams.cParams)) { DEBUGLOG(5, "ZSTD_equivalentParams()==1"); - zc->fseCTables_ready = 0; - zc->hufCTable_repeatMode = HUF_repeat_none; + zc->entropy->fseCTables_ready = 0; + zc->entropy->hufCTable_repeatMode = HUF_repeat_none; return ZSTD_continueCCtx(zc, params, pledgedSrcSize); } } @@ -662,9 +637,7 @@ 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 = hufCTable_size + litlengthCTable_size - + offcodeCTable_size + matchlengthCTable_size - + entropyScratchSpace_size; + { size_t const entropySpace = sizeof(ZSTD_entropyCTables_t); size_t const optPotentialSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<workSpace; /* entropy space */ - zc->hufCTable = (HUF_CElt*)ptr; - ptr = (char*)zc->hufCTable + hufCTable_size; /* note : HUF_CElt* is incomplete type, size is estimated via macro */ - zc->offcodeCTable = (FSE_CTable*) ptr; - ptr = (char*)ptr + offcodeCTable_size; - zc->matchlengthCTable = (FSE_CTable*) ptr; - ptr = (char*)ptr + matchlengthCTable_size; - zc->litlengthCTable = (FSE_CTable*) ptr; - ptr = (char*)ptr + litlengthCTable_size; - assert(((size_t)ptr & 3) == 0); /* ensure correct alignment */ - zc->entropyScratchSpace = (unsigned*) ptr; + assert(((size_t)zc->workSpace & 3) == 0); /* ensure correct alignment */ + assert(zc->workSpaceSize >= sizeof(ZSTD_entropyCTables_t)); + zc->entropy = (ZSTD_entropyCTables_t*)zc->workSpace; } } /* init params */ @@ -715,8 +681,8 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->stage = ZSTDcs_init; zc->dictID = 0; zc->loadedDictEnd = 0; - zc->fseCTables_ready = 0; - zc->hufCTable_repeatMode = HUF_repeat_none; + zc->entropy->fseCTables_ready = 0; + zc->entropy->hufCTable_repeatMode = HUF_repeat_none; zc->nextToUpdate = 1; zc->nextSrc = NULL; zc->base = NULL; @@ -727,13 +693,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->hashLog3 = hashLog3; zc->seqStore.litLengthSum = 0; - /* ensure entropy tables are close together at the beginning */ - assert((void*)zc->hufCTable == zc->workSpace); - assert((char*)zc->offcodeCTable == (char*)zc->hufCTable + hufCTable_size); - assert((char*)zc->matchlengthCTable == (char*)zc->offcodeCTable + offcodeCTable_size); - assert((char*)zc->litlengthCTable == (char*)zc->matchlengthCTable + matchlengthCTable_size); - assert((char*)zc->entropyScratchSpace == (char*)zc->litlengthCTable + litlengthCTable_size); - ptr = (char*)zc->entropyScratchSpace + entropyScratchSpace_size; + ptr = zc->entropy + 1; /* opt parser space */ if ((params.cParams.strategy == ZSTD_btopt) || (params.cParams.strategy == ZSTD_btultra)) { @@ -830,16 +790,7 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, dstCCtx->dictID = srcCCtx->dictID; /* copy entropy tables */ - dstCCtx->fseCTables_ready = srcCCtx->fseCTables_ready; - if (srcCCtx->fseCTables_ready) { - memcpy(dstCCtx->litlengthCTable, srcCCtx->litlengthCTable, litlengthCTable_size); - memcpy(dstCCtx->matchlengthCTable, srcCCtx->matchlengthCTable, matchlengthCTable_size); - memcpy(dstCCtx->offcodeCTable, srcCCtx->offcodeCTable, offcodeCTable_size); - } - dstCCtx->hufCTable_repeatMode = srcCCtx->hufCTable_repeatMode; - if (srcCCtx->hufCTable_repeatMode) { - memcpy(dstCCtx->hufCTable, srcCCtx->hufCTable, hufCTable_size); - } + memcpy(dstCCtx->entropy, srcCCtx->entropy, sizeof(ZSTD_entropyCTables_t)); return 0; } @@ -970,28 +921,28 @@ static size_t ZSTD_compressLiterals (ZSTD_CCtx* zc, /* small ? don't even attempt compression (speed opt) */ # define LITERAL_NOENTROPY 63 - { size_t const minLitSize = zc->hufCTable_repeatMode == HUF_repeat_valid ? 6 : LITERAL_NOENTROPY; + { size_t const minLitSize = zc->entropy->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 = zc->hufCTable_repeatMode; + { HUF_repeat repeat = zc->entropy->hufCTable_repeatMode; int const preferRepeat = zc->appliedParams.cParams.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, - zc->entropyScratchSpace, entropyScratchSpace_size, zc->hufCTable, &repeat, preferRepeat) + zc->entropy->workspace, sizeof(zc->entropy->workspace), (HUF_CElt*)zc->entropy->hufCTable, &repeat, preferRepeat) : HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, - zc->entropyScratchSpace, entropyScratchSpace_size, zc->hufCTable, &repeat, preferRepeat); + zc->entropy->workspace, sizeof(zc->entropy->workspace), (HUF_CElt*)zc->entropy->hufCTable, &repeat, preferRepeat); if (repeat != HUF_repeat_none) { hType = set_repeat; } /* reused the existing table */ - else { zc->hufCTable_repeatMode = HUF_repeat_check; } /* now have a table to reuse */ + else { zc->entropy->hufCTable_repeatMode = HUF_repeat_check; } /* now have a table to reuse */ } if ((cLitSize==0) | (cLitSize >= srcSize - minGain)) { - zc->hufCTable_repeatMode = HUF_repeat_none; + zc->entropy->hufCTable_repeatMode = HUF_repeat_none; return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } if (cLitSize==1) { - zc->hufCTable_repeatMode = HUF_repeat_none; + zc->entropy->hufCTable_repeatMode = HUF_repeat_none; return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); } @@ -1070,9 +1021,9 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, const seqStore_t* seqStorePtr = &(zc->seqStore); U32 count[MaxSeq+1]; S16 norm[MaxSeq+1]; - FSE_CTable* CTable_LitLength = zc->litlengthCTable; - FSE_CTable* CTable_OffsetBits = zc->offcodeCTable; - FSE_CTable* CTable_MatchLength = zc->matchlengthCTable; + FSE_CTable* CTable_LitLength = zc->entropy->litlengthCTable; + FSE_CTable* CTable_OffsetBits = zc->entropy->offcodeCTable; + FSE_CTable* CTable_MatchLength = zc->entropy->matchlengthCTable; U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */ const seqDef* const sequences = seqStorePtr->sequencesStart; const BYTE* const ofCodeTable = seqStorePtr->ofCode; @@ -1111,12 +1062,12 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, /* CTable for Literal Lengths */ { U32 max = MaxLL; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, zc->entropyScratchSpace); + size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, zc->entropy->workspace); if ((mostFrequent == nbSeq) && (nbSeq > 2)) { *op++ = llCodeTable[0]; FSE_buildCTable_rle(CTable_LitLength, (BYTE)max); LLtype = set_rle; - } else if ((zc->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + } else if ((zc->entropy->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { LLtype = set_repeat; } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (LL_defaultNormLog-1)))) { FSE_buildCTable_wksp(CTable_LitLength, LL_defaultNorm, MaxLL, LL_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); @@ -1135,12 +1086,12 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, /* CTable for Offsets */ { U32 max = MaxOff; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, zc->entropyScratchSpace); + size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, zc->entropy->workspace); if ((mostFrequent == nbSeq) && (nbSeq > 2)) { *op++ = ofCodeTable[0]; FSE_buildCTable_rle(CTable_OffsetBits, (BYTE)max); Offtype = set_rle; - } else if ((zc->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + } else if ((zc->entropy->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { Offtype = set_repeat; } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (OF_defaultNormLog-1)))) { FSE_buildCTable_wksp(CTable_OffsetBits, OF_defaultNorm, MaxOff, OF_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); @@ -1159,12 +1110,12 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, /* CTable for MatchLengths */ { U32 max = MaxML; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, zc->entropyScratchSpace); + size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, zc->entropy->workspace); if ((mostFrequent == nbSeq) && (nbSeq > 2)) { *op++ = *mlCodeTable; FSE_buildCTable_rle(CTable_MatchLength, (BYTE)max); MLtype = set_rle; - } else if ((zc->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + } else if ((zc->entropy->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { MLtype = set_repeat; } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (ML_defaultNormLog-1)))) { FSE_buildCTable_wksp(CTable_MatchLength, ML_defaultNorm, MaxML, ML_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); @@ -1182,7 +1133,7 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, } } *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2)); - zc->fseCTables_ready = 0; + zc->entropy->fseCTables_ready = 0; /* Encoding Sequences */ { BIT_CStream_t blockStream; @@ -1261,7 +1212,7 @@ _check_compressibility: { size_t const minGain = ZSTD_minGain(srcSize); size_t const maxCSize = srcSize - minGain; if ((size_t)(op-ostart) >= maxCSize) { - zc->hufCTable_repeatMode = HUF_repeat_none; + zc->entropy->hufCTable_repeatMode = HUF_repeat_none; return 0; } } @@ -3106,13 +3057,14 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t const BYTE* const dictEnd = dictPtr + dictSize; short offcodeNCount[MaxOff+1]; unsigned offcodeMaxValue = MaxOff; - BYTE scratchBuffer[1<entropy->workspace) >= (1<dictID = cctx->appliedParams.fParams.noDictIDFlag ? 0 : MEM_readLE32(dictPtr); dictPtr += 4; - { size_t const hufHeaderSize = HUF_readCTable(cctx->hufCTable, 255, dictPtr, dictEnd-dictPtr); + { size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)cctx->entropy->hufCTable, 255, dictPtr, dictEnd-dictPtr); if (HUF_isError(hufHeaderSize)) return ERROR(dictionary_corrupted); dictPtr += hufHeaderSize; } @@ -3122,7 +3074,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->offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog, scratchBuffer, sizeof(scratchBuffer)), + CHECK_E( FSE_buildCTable_wksp(cctx->entropy->offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)), dictionary_corrupted); dictPtr += offcodeHeaderSize; } @@ -3134,7 +3086,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->matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, scratchBuffer, sizeof(scratchBuffer)), + CHECK_E( FSE_buildCTable_wksp(cctx->entropy->matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)), dictionary_corrupted); dictPtr += matchlengthHeaderSize; } @@ -3146,7 +3098,7 @@ 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->litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, scratchBuffer, sizeof(scratchBuffer)), + CHECK_E( FSE_buildCTable_wksp(cctx->entropy->litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)), dictionary_corrupted); dictPtr += litlengthHeaderSize; } @@ -3172,8 +3124,8 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t if (cctx->rep[u] > dictContentSize) return ERROR(dictionary_corrupted); } } - cctx->fseCTables_ready = 1; - cctx->hufCTable_repeatMode = HUF_repeat_valid; + cctx->entropy->fseCTables_ready = 1; + cctx->entropy->hufCTable_repeatMode = HUF_repeat_valid; return ZSTD_loadDictionaryContent(cctx, dictPtr, dictContentSize); } } diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index a145dbf86..4e96504ef 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -95,7 +95,7 @@ typedef struct { HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)]; /* can accommodate HUF_decompress4X */ U32 workspace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; U32 rep[ZSTD_REP_NUM]; -} ZSTD_entropyTables_t; +} ZSTD_entropyDTables_t; struct ZSTD_DCtx_s { @@ -103,7 +103,7 @@ struct ZSTD_DCtx_s const FSE_DTable* MLTptr; const FSE_DTable* OFTptr; const HUF_DTable* HUFptr; - ZSTD_entropyTables_t entropy; + ZSTD_entropyDTables_t entropy; const void* previousDstEnd; /* detect continuity */ const void* base; /* start of current segment */ const void* vBase; /* virtual start of previous segment if it was just before current one */ @@ -1842,7 +1842,7 @@ static size_t ZSTD_refDictContent(ZSTD_DCtx* dctx, const void* dict, size_t dict /* ZSTD_loadEntropy() : * dict : must point at beginning of a valid zstd dictionary * @return : size of entropy tables read */ -static size_t ZSTD_loadEntropy(ZSTD_entropyTables_t* entropy, const void* const dict, size_t const dictSize) +static size_t ZSTD_loadEntropy(ZSTD_entropyDTables_t* entropy, const void* const dict, size_t const dictSize) { const BYTE* dictPtr = (const BYTE*)dict; const BYTE* const dictEnd = dictPtr + dictSize; @@ -1933,7 +1933,7 @@ struct ZSTD_DDict_s { void* dictBuffer; const void* dictContent; size_t dictSize; - ZSTD_entropyTables_t entropy; + ZSTD_entropyDTables_t entropy; U32 dictID; U32 entropyPresent; ZSTD_customMem cMem; From 4e77f7761da1b4b35222ac75bd10b1c8c8dcea0d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 13 Jul 2017 02:09:07 -0700 Subject: [PATCH 129/318] clarified comment on ZSTD_p_contentSizeFlag --- lib/zstd.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/zstd.h b/lib/zstd.h index 291c6df25..a2a756dfc 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -955,7 +955,9 @@ typedef enum { * Special: value 0 means "do not change strategy". */ /* frame parameters */ - ZSTD_p_contentSizeFlag=200, /* Content size is written into frame header _whenever known_ (default:1) */ + ZSTD_p_contentSizeFlag=200, /* Content size is written into frame header _whenever known_ (default:1) + * note that content size must be known at the beginning, + * it is sent using ZSTD_CCtx_setPledgedSrcSize() */ ZSTD_p_checksumFlag, /* A 32-bits checksum of content is written at end of frame (default:0) */ ZSTD_p_dictIDFlag, /* When applicable, dictID of dictionary is provided in frame header (default:1) */ From 132e6efd760b97403f1e84c6c4e4ee6569fad605 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 13 Jul 2017 02:22:58 -0700 Subject: [PATCH 130/318] switched ZSTDMT_compress_advanced() last argument to overlapLog overlapRLog (== 9 - overlapLog) was a bit "strange" as all other public entry points use overlapLog --- doc/zstd_manual.html | 4 +++- lib/compress/zstdmt_compress.c | 22 ++++++++++++---------- lib/compress/zstdmt_compress.h | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 1058503f8..c166e7258 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -813,7 +813,9 @@ void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx); * Special: value 0 means "do not change strategy". */ /* frame parameters */ - ZSTD_p_contentSizeFlag=200, /* Content size is written into frame header _whenever known_ (default:1) */ + ZSTD_p_contentSizeFlag=200, /* Content size is written into frame header _whenever known_ (default:1) + * note that content size must be known at the beginning, + * it is sent using ZSTD_CCtx_setPledgedSrcSize() */ ZSTD_p_checksumFlag, /* A 32-bits checksum of content is written at end of frame (default:0) */ ZSTD_p_dictIDFlag, /* When applicable, dictID of dictionary is provided in frame header (default:1) */ diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 677e96f08..ed4b9117a 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -10,6 +10,7 @@ /* ====== Tuning parameters ====== */ #define ZSTDMT_NBTHREADS_MAX 128 +#define ZSTDMT_OVERLAPLOG_DEFAULT 6 /* ====== Compiler specifics ====== */ @@ -383,7 +384,7 @@ struct ZSTDMT_CCtx_s { unsigned nextJobID; unsigned frameEnded; unsigned allJobsCompleted; - unsigned overlapRLog; + unsigned overlapLog; unsigned long long frameContentSize; size_t sectionSize; ZSTD_customMem cMem; @@ -417,7 +418,7 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem) mtctx->nbThreads = nbThreads; mtctx->allJobsCompleted = 1; mtctx->sectionSize = 0; - mtctx->overlapRLog = 3; + mtctx->overlapLog = ZSTDMT_OVERLAPLOG_DEFAULT; mtctx->factory = POOL_create(nbThreads, 1); mtctx->jobs = ZSTDMT_allocJobsTable(&nbJobs, cMem); mtctx->jobIDMask = nbJobs - 1; @@ -491,7 +492,7 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, return 0; case ZSTDMT_p_overlapSectionLog : DEBUGLOG(5, "ZSTDMT_p_overlapSectionLog : %u", value); - mtctx->overlapRLog = (value >= 9) ? 0 : 9 - value; + mtctx->overlapLog = (value >= 9) ? 9 : value; return 0; default : return ERROR(compressionParameter_unsupported); @@ -520,8 +521,9 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, const void* src, size_t srcSize, const ZSTD_CDict* cdict, ZSTD_parameters const params, - unsigned overlapRLog) + unsigned overlapLog) { + unsigned const overlapRLog = (overlapLog>9) ? 0 : 9-overlapLog; size_t const overlapSize = (overlapRLog>=9) ? 0 : (size_t)1 << (params.cParams.windowLog - overlapRLog); unsigned nbChunks = computeNbChunks(srcSize, params.cParams.windowLog, mtctx->nbThreads); size_t const proposedChunkSize = (srcSize + (nbChunks-1)) / nbChunks; @@ -538,7 +540,7 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, params.fParams); return ZSTD_compress_advanced(cctx, dst, dstCapacity, src, srcSize, NULL, 0, params); } - assert(avgChunkSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), which is useful to avoid allocating extra buffers */ + assert(avgChunkSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), which is required for compressWithinDst */ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgChunkSize) ); XXH64_reset(&xxh64, 0); @@ -642,10 +644,10 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, const void* src, size_t srcSize, int compressionLevel) { - U32 const overlapRLog = (compressionLevel >= ZSTD_maxCLevel()) ? 0 : 3; + U32 const overlapLog = (compressionLevel >= ZSTD_maxCLevel()) ? 9 : ZSTDMT_OVERLAPLOG_DEFAULT; ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize, 0); params.fParams.contentSizeFlag = 1; - return ZSTDMT_compress_advanced(mtctx, dst, dstCapacity, src, srcSize, NULL, params, overlapRLog); + return ZSTDMT_compress_advanced(mtctx, dst, dstCapacity, src, srcSize, NULL, params, overlapLog); } @@ -710,8 +712,8 @@ size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, zcs->cdict = cdict; } - zcs->targetDictSize = (zcs->overlapRLog>=9) ? 0 : (size_t)1 << (zcs->params.cParams.windowLog - zcs->overlapRLog); - DEBUGLOG(4, "overlapRLog : %u ", zcs->overlapRLog); + zcs->targetDictSize = (zcs->overlapLog==0) ? 0 : (size_t)1 << (zcs->params.cParams.windowLog - (9 - zcs->overlapLog)); + DEBUGLOG(4, "overlapLog : %u ", zcs->overlapLog); DEBUGLOG(4, "overlap Size : %u KB", (U32)(zcs->targetDictSize>>10)); zcs->targetSectionSize = zcs->sectionSize ? zcs->sectionSize : (size_t)1 << (zcs->params.cParams.windowLog + 2); zcs->targetSectionSize = MAX(ZSTDMT_SECTION_SIZE_MIN, zcs->targetSectionSize); @@ -918,7 +920,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, size_t const cSize = ZSTDMT_compress_advanced(mtctx, (char*)output->dst + output->pos, output->size - output->pos, (const char*)input->src + input->pos, input->size - input->pos, - mtctx->cdict, mtctx->params, mtctx->overlapRLog); + mtctx->cdict, mtctx->params, mtctx->overlapLog); if (ZSTD_isError(cSize)) return cSize; input->pos = input->size; output->pos += cSize; diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 7584007f1..843a240aa 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -68,7 +68,7 @@ ZSTDLIB_API size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, const void* src, size_t srcSize, const ZSTD_CDict* cdict, ZSTD_parameters const params, - unsigned overlapRLog); /* overlapRLog = 9 - overlapLog */ + unsigned overlapLog); ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, const void* dict, size_t dictSize, /* dict can be released after init, a local copy is preserved within zcs */ From 766663f1f12bb8d7db9740114c7c33ce52f85d9c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 13 Jul 2017 10:15:27 -0700 Subject: [PATCH 131/318] added altering dictionary size depending on compression level --- contrib/adaptive-compression/adapt.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 9c6986446..81027666b 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -84,7 +84,6 @@ typedef struct { pthread_mutex_t jobWrite_mutex; pthread_cond_t jobWrite_cond; size_t lastDictSize; - size_t targetDictSize; inBuff_t input; cStat_t stats; jobDescription* jobs; @@ -148,7 +147,6 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobReadyID = 0; ctx->jobCompressedID = 0; ctx->jobWriteID = 0; - ctx->targetDictSize = 1 << 12; ctx->lastDictSize = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); /* initializing jobs */ @@ -255,6 +253,14 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) return ctx->compressionLevel; } +static size_t getUseableDictSize(unsigned compressionLevel) +{ + ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, 0); + unsigned overlapLog = compressionLevel >= (unsigned)ZSTD_maxCLevel() ? 0 : 3; + size_t overlapSize = 1 << (params.cParams.windowLog - overlapLog); + return overlapSize; +} + static void* compressionThread(void* arg) { adaptCCtx* ctx = (adaptCCtx*)arg; @@ -281,8 +287,10 @@ static void* compressionThread(void* arg) DEBUG(3, "compression level used: %u\n", cLevel); /* begin compression */ { + size_t useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); + DEBUG(2, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start, job->dictSize, cLevel); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); @@ -435,12 +443,11 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); /* if not on the last job, reuse data as dictionary in next job */ if (!last) { - size_t const newDictSize = ctx->targetDictSize; size_t const oldDictSize = ctx->lastDictSize; - DEBUG(3, "newDictSize %zu oldDictSize %zu\n", newDictSize, oldDictSize); - memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize + srcSize - newDictSize, newDictSize); - ctx->lastDictSize = newDictSize; - ctx->input.filled = newDictSize; + DEBUG(3, "oldDictSize %zu\n", oldDictSize); + memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize, srcSize); + ctx->lastDictSize = srcSize; + ctx->input.filled = srcSize; } return 0; } From 3a60efd3a92ce2e3b222e69ff0965a223d26978e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 13 Jul 2017 10:10:13 -0700 Subject: [PATCH 132/318] policy change : ZSTDMT automatically caps nbThreads to ZSTDMT_NBTHREADS_MAX (#760) Previously, ZSTDMT would refuse to create the compressor. Also : increased ZSTDMT_NBTHREADS_MAX to 256, updated doc, and added relevant test --- lib/compress/zstdmt_compress.c | 5 +++-- programs/zstd.1 | 4 ++-- programs/zstd.1.md | 1 + tests/playTests.sh | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index ed4b9117a..3be850c6d 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -9,7 +9,7 @@ /* ====== Tuning parameters ====== */ -#define ZSTDMT_NBTHREADS_MAX 128 +#define ZSTDMT_NBTHREADS_MAX 256 #define ZSTDMT_OVERLAPLOG_DEFAULT 6 @@ -407,7 +407,8 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem) U32 nbJobs = nbThreads + 2; DEBUGLOG(3, "ZSTDMT_createCCtx_advanced"); - if ((nbThreads < 1) | (nbThreads > ZSTDMT_NBTHREADS_MAX)) return NULL; + if (nbThreads < 1) return NULL; + nbThreads = MIN(nbThreads , ZSTDMT_NBTHREADS_MAX); if ((cMem.customAlloc!=NULL) ^ (cMem.customFree!=NULL)) /* invalid custom allocator */ return NULL; diff --git a/programs/zstd.1 b/programs/zstd.1 index 5df45db21..2b80659cc 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -1,5 +1,5 @@ . -.TH "ZSTD" "1" "June 2017" "zstd 1.3.0" "User Commands" +.TH "ZSTD" "1" "July 2017" "zstd 1.3.1" "User Commands" . .SH "NAME" \fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files @@ -105,7 +105,7 @@ unlocks high compression levels 20+ (maximum 22), using a lot more memory\. Note . .TP \fB\-T#\fR, \fB\-\-threads=#\fR -Compress using \fB#\fR threads (default: 1)\. If \fB#\fR is 0, attempt to detect and use the number of physical CPU cores\. This modifier does nothing if \fBzstd\fR is compiled without multithread support\. +Compress using \fB#\fR threads (default: 1)\. If \fB#\fR 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\. This modifier does nothing if \fBzstd\fR is compiled without multithread support\. . .TP \fB\-D file\fR diff --git a/programs/zstd.1.md b/programs/zstd.1.md index 24e25a2f3..ba51c8435 100644 --- a/programs/zstd.1.md +++ b/programs/zstd.1.md @@ -108,6 +108,7 @@ the last one takes effect. * `-T#`, `--threads=#`: Compress using `#` 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. This modifier does nothing if `zstd` is compiled without multithread support. * `-D file`: use `file` as Dictionary to compress or decompress FILE(s) diff --git a/tests/playTests.sh b/tests/playTests.sh index 88a1c2ab4..dd0f2dbfe 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -643,7 +643,8 @@ then $ECHO "\n**** zstdmt long round-trip tests **** " roundTripTest -g99000000 -P99 "20 -T2" roundTripTest -g6000000000 -P99 "1 -T2" - fileRoundTripTest -g4193M -P98 " -T0" + roundTripTest -g1500000000 -P97 "1 -T999" + fileRoundTripTest -g4195M -P98 " -T0" else $ECHO "\n**** no multithreading, skipping zstdmt tests **** " fi From 68c4560701cca29837dfa5a57a730a56be0199fb Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 13 Jul 2017 10:38:19 -0700 Subject: [PATCH 133/318] [ldm] Add TODO and comment for segfaulting in compress function --- contrib/long_distance_matching/ldm.c | 7 +++++-- contrib/long_distance_matching/main-ldm.c | 10 ++++++---- contrib/long_distance_matching/util.c | 5 ----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index e64d28656..7a5450732 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -28,7 +28,7 @@ #define RUN_MASK ((1U< #include #include @@ -12,12 +10,17 @@ #include #include "ldm.h" +#include "zstd.h" #define DEBUG //#define TEST /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. + * + * TODO: This currently seg faults 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; @@ -78,10 +81,9 @@ static int compress(const char *fname, const char *oname) { dst + LDM_HEADER_SIZE, statbuf.st_size); #endif */ - compressSize = LDM_HEADER_SIZE + LDM_compress(src, statbuf.st_size, - dst + LDM_HEADER_SIZE, statbuf.st_size); + dst + LDM_HEADER_SIZE, maxCompressSize); // Write compress and decompress size to header // TODO: should depend on LDM_DECOMPRESS_SIZE write32 diff --git a/contrib/long_distance_matching/util.c b/contrib/long_distance_matching/util.c index 627492159..47ac8a126 100644 --- a/contrib/long_distance_matching/util.c +++ b/contrib/long_distance_matching/util.c @@ -53,11 +53,6 @@ U32 LDM_read32(const void *ptr) { return *(const U32 *)ptr; } -//TODO: endianness? -void LDM_write64(void *memPtr, U64 value) { - memcpy(memPtr, &value, sizeof(value)); -} - U64 LDM_read64(const void *ptr) { return *(const U64 *)ptr; } From 50421d9474710b44618d843fb000b08b3a96df65 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 13 Jul 2017 11:45:00 -0700 Subject: [PATCH 134/318] [ldm] Remove old main files --- contrib/long_distance_matching/main.c | 240 -------------------------- contrib/long_distance_matching/main.h | 7 - 2 files changed, 247 deletions(-) delete mode 100644 contrib/long_distance_matching/main.c delete mode 100644 contrib/long_distance_matching/main.h diff --git a/contrib/long_distance_matching/main.c b/contrib/long_distance_matching/main.c deleted file mode 100644 index 67144166b..000000000 --- a/contrib/long_distance_matching/main.c +++ /dev/null @@ -1,240 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "ldm.h" - -#define BUF_SIZE 16*1024 // Block size -#define LDM_HEADER_SIZE 8 - -/* -static size_t compress_file_mmap(FILE *in, FILE *out, size_t *size_in, - size_t *size_out) { - char *src, *dst; - struct stat statbuf; - - if (fstat(in, &statbuf) < 0) { - printf("fstat error\n"); - return 1; - } - - - return 0; -} -*/ - -static size_t compress_file(FILE *in, FILE *out, size_t *size_in, - size_t *size_out) { - char *src, *buf = NULL; - size_t r = 1; - size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; - - src = malloc(BUF_SIZE); - if (!src) { - printf("Not enough memory\n"); - goto cleanup; - } - - size = BUF_SIZE + LDM_HEADER_SIZE; - buf = malloc(size); - if (!buf) { - printf("Not enough memory\n"); - goto cleanup; - } - - for (;;) { - k = fread(src, 1, BUF_SIZE, in); - if (k == 0) - break; - count_in += k; - - n = LDM_compress(src, buf, k, BUF_SIZE); - - // n = k; - // offset += n; - offset = n; - count_out += n; - - k = fwrite(buf, 1, offset, out); - if (k < offset) { - if (ferror(out)) - printf("Write failed\n"); - else - printf("Short write\n"); - goto cleanup; - } - - } - *size_in = count_in; - *size_out = count_out; - r = 0; - cleanup: - free(src); - free(buf); - return r; -} - -static size_t decompress_file(FILE *in, FILE *out) { - void *src = malloc(BUF_SIZE); - void *dst = NULL; - size_t dst_capacity = BUF_SIZE; - size_t ret = 1; - size_t bytes_written = 0; - - if (!src) { - perror("decompress_file(src)"); - goto cleanup; - } - - while (ret != 0) { - /* Load more input */ - size_t src_size = fread(src, 1, BUF_SIZE, in); - void *src_ptr = src; - void *src_end = src_ptr + src_size; - if (src_size == 0 || ferror(in)) { - printf("(TODO): Decompress: not enough input or error reading file\n"); - //TODO - ret = 0; - goto cleanup; - } - - /* Allocate destination buffer if it hasn't been allocated already */ - if (!dst) { - dst = malloc(dst_capacity); - if (!dst) { - perror("decompress_file(dst)"); - goto cleanup; - } - } - - /* Decompress: - * Continue while there is more input to read. - */ - while (src_ptr != src_end && ret != 0) { - // size_t dst_size = src_size; - size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); - size_t written = fwrite(dst, 1, dst_size, out); -// printf("Writing %zu bytes\n", dst_size); - bytes_written += dst_size; - if (written != dst_size) { - printf("Decompress: Failed to write to file\n"); - goto cleanup; - } - src_ptr += src_size; - src_size = src_end - src_ptr; - } - - /* Update input */ - - } - - printf("Wrote %zu bytes\n", bytes_written); - - cleanup: - free(src); - free(dst); - - return ret; -} - -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; -} - -int main(int argc, char *argv[]) { - char inpFilename[256] = { 0 }; - char ldmFilename[256] = { 0 }; - char decFilename[256] = { 0 }; - - if (argc < 2) { - printf("Please specify input filename\n"); - return 0; - } - 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 */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *outFp = fopen(ldmFilename, "wb"); - size_t sizeIn = 0; - size_t sizeOut = 0; - size_t ret; - printf("compress : %s -> %s\n", inpFilename, ldmFilename); - ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); - if (ret) { - printf("compress : failed with code %zu\n", ret); - return ret; - } - printf("%s: %zu → %zu bytes, %.1f%%\n", - inpFilename, sizeIn, sizeOut, - (double)sizeOut / sizeIn * 100); - printf("compress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* decompress */ - { - FILE *inpFp = fopen(ldmFilename, "rb"); - FILE *outFp = fopen(decFilename, "wb"); - size_t ret; - - printf("decompress : %s -> %s\n", ldmFilename, decFilename); - ret = decompress_file(inpFp, outFp); - if (ret) { - printf("decompress : failed with code %zu\n", ret); - return ret; - } - printf("decompress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* verify */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); - } - - return 0; -} - - diff --git a/contrib/long_distance_matching/main.h b/contrib/long_distance_matching/main.h deleted file mode 100644 index a0b030121..000000000 --- a/contrib/long_distance_matching/main.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _MAIN_H -#define _MAIN_H - -void compress_file(FILE *in, FILE *out, int argc, char *argv[]); -void decompress_file(FILE *in, FILE *out, int argc, char *argv[]); - -#endif /* _MAIN_H */ From 830ef4152a8b7568916ecb600a0aac1a5212c08f Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 13 Jul 2017 12:45:39 -0700 Subject: [PATCH 135/318] [libzstd] Increase granularity of FSECTable repeat mode --- lib/common/fse.h | 5 +++++ lib/common/zstd_internal.h | 6 ++++-- lib/compress/zstd_compress.c | 31 ++++++++++++++++++++++++------- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 54ac98b1c..1c44f8375 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -384,6 +384,11 @@ size_t FSE_buildDTable_rle (FSE_DTable* dt, unsigned char symbolValue); size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, FSE_DTable* workSpace, unsigned maxLog); /**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DTABLE_SIZE_U32(maxLog)` */ +typedef enum { + FSE_repeat_none, /**< Cannot use the previous table */ + FSE_repeat_check, /**< Can use the previous table but it must be checked */ + FSE_repeat_valid /**< Can use the previous table and it is asumed to be valid */ + } FSE_repeat; /* ***************************************** * FSE symbol compression API diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 42e5e7b5d..f3779e844 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -271,13 +271,15 @@ typedef struct { } seqStore_t; typedef struct { - HUF_repeat hufCTable_repeatMode; U32 hufCTable[HUF_CTABLE_SIZE_U32(255)]; - U32 fseCTables_ready; 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; const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b362a1919..dfcb02661 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -616,8 +616,10 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, if (crp == ZSTDcrp_continue) { if (ZSTD_equivalentParams(params.cParams, zc->appliedParams.cParams)) { DEBUGLOG(5, "ZSTD_equivalentParams()==1"); - zc->entropy->fseCTables_ready = 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; return ZSTD_continueCCtx(zc, params, pledgedSrcSize); } } @@ -681,8 +683,10 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->stage = ZSTDcs_init; zc->dictID = 0; zc->loadedDictEnd = 0; - zc->entropy->fseCTables_ready = 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->nextToUpdate = 1; zc->nextSrc = NULL; zc->base = NULL; @@ -1067,11 +1071,13 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, *op++ = llCodeTable[0]; FSE_buildCTable_rle(CTable_LitLength, (BYTE)max); LLtype = set_rle; - } else if ((zc->entropy->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + zc->entropy->litlength_repeatMode = FSE_repeat_check; + } else if ((zc->entropy->litlength_repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { LLtype = set_repeat; } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (LL_defaultNormLog-1)))) { FSE_buildCTable_wksp(CTable_LitLength, LL_defaultNorm, MaxLL, LL_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); LLtype = set_basic; + zc->entropy->litlength_repeatMode = FSE_repeat_valid; } else { size_t nbSeq_1 = nbSeq; const U32 tableLog = FSE_optimalTableLog(LLFSELog, nbSeq, max); @@ -1082,6 +1088,7 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, op += NCountSize; } FSE_buildCTable_wksp(CTable_LitLength, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer)); LLtype = set_compressed; + zc->entropy->litlength_repeatMode = FSE_repeat_check; } } /* CTable for Offsets */ @@ -1091,11 +1098,13 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, *op++ = ofCodeTable[0]; FSE_buildCTable_rle(CTable_OffsetBits, (BYTE)max); Offtype = set_rle; - } else if ((zc->entropy->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + zc->entropy->offcode_repeatMode = FSE_repeat_check; + } else if ((zc->entropy->offcode_repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { Offtype = set_repeat; } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (OF_defaultNormLog-1)))) { FSE_buildCTable_wksp(CTable_OffsetBits, OF_defaultNorm, MaxOff, OF_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); Offtype = set_basic; + zc->entropy->offcode_repeatMode = FSE_repeat_valid; } else { size_t nbSeq_1 = nbSeq; const U32 tableLog = FSE_optimalTableLog(OffFSELog, nbSeq, max); @@ -1106,6 +1115,7 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, op += NCountSize; } FSE_buildCTable_wksp(CTable_OffsetBits, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer)); Offtype = set_compressed; + zc->entropy->offcode_repeatMode = FSE_repeat_check; } } /* CTable for MatchLengths */ @@ -1115,11 +1125,13 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, *op++ = *mlCodeTable; FSE_buildCTable_rle(CTable_MatchLength, (BYTE)max); MLtype = set_rle; - } else if ((zc->entropy->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + zc->entropy->matchlength_repeatMode = FSE_repeat_check; + } else if ((zc->entropy->matchlength_repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { MLtype = set_repeat; } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (ML_defaultNormLog-1)))) { FSE_buildCTable_wksp(CTable_MatchLength, ML_defaultNorm, MaxML, ML_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); MLtype = set_basic; + zc->entropy->matchlength_repeatMode = FSE_repeat_valid; } else { size_t nbSeq_1 = nbSeq; const U32 tableLog = FSE_optimalTableLog(MLFSELog, nbSeq, max); @@ -1130,10 +1142,10 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, op += NCountSize; } FSE_buildCTable_wksp(CTable_MatchLength, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer)); MLtype = set_compressed; + zc->entropy->matchlength_repeatMode = FSE_repeat_check; } } *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2)); - zc->entropy->fseCTables_ready = 0; /* Encoding Sequences */ { BIT_CStream_t blockStream; @@ -1213,6 +1225,9 @@ _check_compressibility: size_t const maxCSize = srcSize - minGain; if ((size_t)(op-ostart) >= maxCSize) { 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 0; } } @@ -3124,8 +3139,10 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t if (cctx->rep[u] > dictContentSize) return ERROR(dictionary_corrupted); } } - cctx->entropy->fseCTables_ready = 1; 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); } } From 9306feb8fabdbaa1e4c4ccee90eb3cf8848556e6 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 13 Jul 2017 13:44:48 -0700 Subject: [PATCH 136/318] [ldm] Switch to using lib/common/mem.h and move typedefs to ldm.h Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags: Blame Revision: --- contrib/long_distance_matching/Makefile | 7 +- contrib/long_distance_matching/ldm.c | 82 +++++++++++------------ contrib/long_distance_matching/ldm.h | 15 +++-- contrib/long_distance_matching/main-ldm.c | 13 ++-- contrib/long_distance_matching/util.c | 68 ------------------- contrib/long_distance_matching/util.h | 27 -------- 6 files changed, 57 insertions(+), 155 deletions(-) delete mode 100644 contrib/long_distance_matching/util.c delete mode 100644 contrib/long_distance_matching/util.h diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 5ffd4eafe..8ba16d03d 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -9,6 +9,7 @@ # 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 \ @@ -26,11 +27,7 @@ default: all all: main-ldm - -#main : ldm.c main.c -# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -main-ldm : util.c ldm.c main-ldm.c +main-ldm : ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 7a5450732..00099fbeb 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -4,23 +4,22 @@ #include #include "ldm.h" -#include "util.h" // Insert every (HASH_ONLY_EVERY + 1) into the hash table. #define HASH_ONLY_EVERY 0 -#define LDM_MEMORY_USAGE 20 +#define LDM_MEMORY_USAGE 22 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) #define LDM_OFFSET_SIZE 4 -#define WINDOW_SIZE (1 << 20) +#define WINDOW_SIZE (1 << 29) //These should be multiples of four. -#define LDM_HASH_LENGTH 4 -#define MINMATCH 4 +#define LDM_HASH_LENGTH 8 +#define MINMATCH 8 #define ML_BITS 4 #define ML_MASK ((1U<= 8; lengthLeft -= 8) { - if (LDM_read64(curP) != LDM_read64(curMatch)) { + if (MEM_read64(curP) != MEM_read64(curMatch)) { return 0; } curP += 8; curMatch += 8; } if (lengthLeft > 0) { - return (LDM_read32(curP) == LDM_read32(curMatch)); + return (MEM_read32(curP) == MEM_read32(curMatch)); } return 1; } @@ -184,10 +173,9 @@ static hash_t checksumToHash(U32 sum) { * b(k,l) = \sum_{i = k}^l ((l - i + 1) * x_i) (mod M) * checksum(k,l) = a(k,l) + 2^{16} * b(k,l) */ -static U32 getChecksum(const char *data, U32 len) { +static U32 getChecksum(const BYTE *buf, U32 len) { U32 i; U32 s1, s2; - const schar *buf = (const schar *)data; s1 = s2 = 0; for (i = 0; i < (len - 4); i += 4) { @@ -215,7 +203,7 @@ static U32 getChecksum(const char *data, U32 len) { * Thus toRemove should correspond to data[0]. */ static U32 updateChecksum(U32 sum, U32 len, - schar toRemove, schar toAdd) { + BYTE toRemove, BYTE toAdd) { U32 s1 = (sum & 0xffff) - toRemove + toAdd; U32 s2 = (sum >> 16) - ((toRemove + CHECKSUM_CHAR_OFFSET) * len) + s1; @@ -244,13 +232,13 @@ static void setNextHash(LDM_CCtx *cctx) { // cctx->nextSum = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); cctx->nextSum = updateChecksum( cctx->lastSum, LDM_HASH_LENGTH, - (schar)((cctx->lastPosHashed)[0]), - (schar)((cctx->lastPosHashed)[LDM_HASH_LENGTH])); + (cctx->lastPosHashed)[0], + (cctx->lastPosHashed)[LDM_HASH_LENGTH]); cctx->nextPosHashed = cctx->nextIp; cctx->nextHash = checksumToHash(cctx->nextSum); #ifdef RUN_CHECKS - check = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); + check = getChecksum(cctx->nextIp, LDM_HASH_LENGTH); if (check != cctx->nextSum) { printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); @@ -279,7 +267,8 @@ static void putHashOfCurrentPositionFromHash( // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. // Note: this works only when cctx->step is 1. if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { - (cctx->hashTable)[hash] = (hashEntry){ (offset_t)(cctx->ip - cctx->ibase) }; + (cctx->hashTable)[hash] = + (LDM_hashEntry){ (offset_t)(cctx->ip - cctx->ibase) }; } cctx->lastPosHashed = cctx->ip; @@ -307,7 +296,7 @@ static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { * Insert hash of the current position into the hash table. */ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - U32 sum = getChecksum((const char *)cctx->ip, LDM_HASH_LENGTH); + U32 sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); hash_t hash = checksumToHash(sum); #ifdef RUN_CHECKS @@ -337,7 +326,7 @@ static unsigned countMatchLength(const BYTE *pIn, const BYTE *pMatch, const BYTE *pInLimit) { const BYTE * const pStart = pIn; while (pIn < pInLimit - 1) { - BYTE const diff = LDM_readByte(pMatch) ^ LDM_readByte(pIn); + BYTE const diff = (*pMatch) ^ *(pIn); if (!diff) { pIn++; pMatch++; @@ -427,9 +416,9 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { * Update input pointer, inserting hashes into hash table along the way. */ static void outputBlock(LDM_CCtx *cctx, - unsigned const literalLength, - unsigned const offset, - unsigned const matchLength) { + const unsigned literalLength, + const unsigned offset, + const unsigned matchLength) { BYTE *token = cctx->op++; /* Encode the literal length. */ @@ -449,7 +438,7 @@ static void outputBlock(LDM_CCtx *cctx, cctx->op += literalLength; /* Encode the offset. */ - LDM_write32(cctx->op, offset); + MEM_write32(cctx->op, offset); cctx->op += LDM_OFFSET_SIZE; /* Encode the match length. */ @@ -457,10 +446,10 @@ static void outputBlock(LDM_CCtx *cctx, unsigned matchLengthRemaining = matchLength; *token += ML_MASK; matchLengthRemaining -= ML_MASK; - LDM_write32(cctx->op, 0xFFFFFFFF); + MEM_write32(cctx->op, 0xFFFFFFFF); while (matchLengthRemaining >= 4*0xFF) { cctx->op += 4; - LDM_write32(cctx->op, 0xffffffff); + MEM_write32(cctx->op, 0xffffffff); matchLengthRemaining -= 4*0xFF; } cctx->op += matchLengthRemaining / 255; @@ -514,9 +503,9 @@ size_t LDM_compress(const void *src, size_t srcSize, * length) and update pointers and hashes. */ { - unsigned const literalLength = (unsigned)(cctx.ip - cctx.anchor); - unsigned const offset = cctx.ip - match; - unsigned const matchLength = countMatchLength( + const unsigned literalLength = (unsigned)(cctx.ip - cctx.anchor); + const unsigned offset = cctx.ip - match; + const unsigned matchLength = countMatchLength( cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); #ifdef COMPUTE_STATS @@ -605,7 +594,7 @@ size_t LDM_decompress(const void *src, size_t compressSize, size_t length, offset; /* Get the literal length. */ - unsigned const token = *(dctx.ip)++; + const unsigned token = *(dctx.ip)++; if ((length = (token >> ML_BITS)) == RUN_MASK) { unsigned s; do { @@ -621,7 +610,7 @@ size_t LDM_decompress(const void *src, size_t compressSize, dctx.op = cpy; //TODO : dynamic offset size - offset = LDM_read32(dctx.ip); + offset = MEM_read32(dctx.ip); dctx.ip += LDM_OFFSET_SIZE; match = dctx.op - offset; @@ -647,6 +636,11 @@ size_t LDM_decompress(const void *src, size_t compressSize, return dctx.op - (BYTE *)dst; } +// TODO: implement and test hash function +void LDM_test(void) { + +} + /* void LDM_test(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index f04b6e958..fd8c2ab88 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -3,10 +3,18 @@ #include /* size_t */ +#include "mem.h" // from /lib/common/mem.h + #define LDM_COMPRESS_SIZE 8 #define LDM_DECOMPRESS_SIZE 8 #define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) +typedef U32 offset_t; +typedef U32 hash_t; +typedef struct LDM_hashEntry LDM_hashEntry; +typedef struct LDM_compressStats LDM_compressStats; +typedef struct LDM_CCtx LDM_CCtx; + /** * Compresses src into dst. * @@ -46,10 +54,9 @@ size_t LDM_decompress(const void *src, size_t srcSize, * * NB: LDM_compress and LDM_decompress currently do not add/read headers. */ -void LDM_readHeader(const void *src, size_t *compressSize, - size_t *decompressSize); +void LDM_readHeader(const void *src, U64 *compressSize, + U64 *decompressSize); -void LDM_test(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); +void LDM_test(void); #endif /* LDM_H */ diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 9e7d45264..2017cf4ef 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -75,12 +75,6 @@ static int compress(const char *fname, const char *oname) { return 1; } -/* -#ifdef TEST - LDM_test(src, statbuf.st_size, - dst + LDM_HEADER_SIZE, statbuf.st_size); -#endif -*/ compressSize = LDM_HEADER_SIZE + LDM_compress(src, statbuf.st_size, dst + LDM_HEADER_SIZE, maxCompressSize); @@ -116,7 +110,8 @@ static int decompress(const char *fname, const char *oname) { int fdin, fdout; struct stat statbuf; char *src, *dst; - size_t compressSize, decompressSize, outSize; + U64 compressSize, decompressSize; + size_t outSize; /* Open the input file. */ if ((fdin = open(fname, O_RDONLY)) < 0) { @@ -267,5 +262,9 @@ int main(int argc, const char *argv[]) { } /* verify */ verify(inpFilename, decFilename); + +#ifdef TEST + LDM_test(); +#endif return 0; } diff --git a/contrib/long_distance_matching/util.c b/contrib/long_distance_matching/util.c deleted file mode 100644 index 47ac8a126..000000000 --- a/contrib/long_distance_matching/util.c +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include -#include -#include - -#include "util.h" - -typedef uint8_t BYTE; -typedef uint16_t U16; -typedef uint32_t U32; -typedef int32_t S32; -typedef uint64_t U64; - -unsigned LDM_isLittleEndian(void) { - const union { U32 u; BYTE c[4]; } one = { 1 }; - return one.c[0]; -} - -U16 LDM_read16(const void *memPtr) { - U16 val; - memcpy(&val, memPtr, sizeof(val)); - return val; -} - -U16 LDM_readLE16(const void *memPtr) { - if (LDM_isLittleEndian()) { - return LDM_read16(memPtr); - } else { - const BYTE *p = (const BYTE *)memPtr; - return (U16)((U16)p[0] + (p[1] << 8)); - } -} - -void LDM_write16(void *memPtr, U16 value){ - memcpy(memPtr, &value, sizeof(value)); -} - -void LDM_write32(void *memPtr, U32 value) { - memcpy(memPtr, &value, sizeof(value)); -} - -void LDM_writeLE16(void *memPtr, U16 value) { - if (LDM_isLittleEndian()) { - LDM_write16(memPtr, value); - } else { - BYTE* p = (BYTE *)memPtr; - p[0] = (BYTE) value; - p[1] = (BYTE)(value>>8); - } -} - -U32 LDM_read32(const void *ptr) { - return *(const U32 *)ptr; -} - -U64 LDM_read64(const void *ptr) { - return *(const U64 *)ptr; -} - -void LDM_copy8(void *dst, const void *src) { - memcpy(dst, src, 8); -} - -BYTE LDM_readByte(const void *memPtr) { - BYTE val; - memcpy(&val, memPtr, 1); - return val; -} diff --git a/contrib/long_distance_matching/util.h b/contrib/long_distance_matching/util.h deleted file mode 100644 index dbf55cbc2..000000000 --- a/contrib/long_distance_matching/util.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef LDM_UTIL_H -#define LDM_UTIL_H - -unsigned LDM_isLittleEndian(void); - -uint16_t LDM_read16(const void *memPtr); - -uint16_t LDM_readLE16(const void *memPtr); - -void LDM_write16(void *memPtr, uint16_t value); - -void LDM_write32(void *memPtr, uint32_t value); - -void LDM_writeLE16(void *memPtr, uint16_t value); - -uint32_t LDM_read32(const void *ptr); - -uint64_t LDM_read64(const void *ptr); - -void LDM_copy8(void *dst, const void *src); - -uint8_t LDM_readByte(const void *ptr); - -void LDM_write64(void *memPtr, uint64_t value); - - -#endif /* LDM_UTIL_H */ From 9165e97fc69d6a6f0476575b696d5f93f5070ea6 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 13 Jul 2017 13:50:23 -0700 Subject: [PATCH 137/318] added some tests for correctness, time, and compression ratio --- contrib/adaptive-compression/Makefile | 15 +- contrib/adaptive-compression/datagencli.c | 129 +++++++++++ .../adaptive-compression/test-correctness.sh | 205 ++++++++++++++++++ .../adaptive-compression/test-performance.sh | 34 +++ 4 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 contrib/adaptive-compression/datagencli.c create mode 100755 contrib/adaptive-compression/test-correctness.sh create mode 100755 contrib/adaptive-compression/test-performance.sh diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index ed1a55ad4..f2059a193 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -19,13 +19,24 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -all: adapt +all: adapt datagen adapt: $(ZSTD_FILES) adapt.c $(CC) $(FLAGS) $^ -o $@ +datagen : $(PRGDIR)/datagen.c datagencli.c + $(CC) $(FLAGS) $^ -o $@$(EXT) + +test-adapt-correctness: datagen adapt + @./test-correctness.sh + @echo "test correctness complete" + +test-adapt-performance: datagen adapt + @./test-performance.sh + @echo "test performance complete" + clean: - @$(RM) -f adapt + @$(RM) -f adapt datagen @$(RM) -rf *.dSYM @$(RM) -f tmp* @$(RM) -f tests/*.zst diff --git a/contrib/adaptive-compression/datagencli.c b/contrib/adaptive-compression/datagencli.c new file mode 100644 index 000000000..8a81939d1 --- /dev/null +++ b/contrib/adaptive-compression/datagencli.c @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + + +/*-************************************ +* Dependencies +**************************************/ +#include "util.h" /* Compiler options */ +#include /* fprintf, stderr */ +#include "datagen.h" /* RDG_generate */ + + +/*-************************************ +* Constants +**************************************/ +#define KB *(1 <<10) +#define MB *(1 <<20) +#define GB *(1U<<30) + +#define SIZE_DEFAULT ((64 KB) + 1) +#define SEED_DEFAULT 0 +#define COMPRESSIBILITY_DEFAULT 50 + + +/*-************************************ +* Macros +**************************************/ +#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define DISPLAYLEVEL(l, ...) if (displayLevel>=l) { DISPLAY(__VA_ARGS__); } +static unsigned displayLevel = 2; + + +/*-******************************************************* +* Command line +*********************************************************/ +static int usage(const char* programName) +{ + DISPLAY( "Compressible data generator\n"); + DISPLAY( "Usage :\n"); + DISPLAY( " %s [args]\n", programName); + DISPLAY( "\n"); + DISPLAY( "Arguments :\n"); + DISPLAY( " -g# : generate # data (default:%i)\n", SIZE_DEFAULT); + DISPLAY( " -s# : Select seed (default:%i)\n", SEED_DEFAULT); + DISPLAY( " -P# : Select compressibility in %% (default:%i%%)\n", + COMPRESSIBILITY_DEFAULT); + DISPLAY( " -h : display help and exit\n"); + return 0; +} + + +int main(int argc, const char** argv) +{ + unsigned probaU32 = COMPRESSIBILITY_DEFAULT; + double litProba = 0.0; + U64 size = SIZE_DEFAULT; + U32 seed = SEED_DEFAULT; + const char* const programName = argv[0]; + + int argNb; + for(argNb=1; argNb='0') && (*argument<='9')) + size *= 10, size += *argument++ - '0'; + if (*argument=='K') { size <<= 10; argument++; } + if (*argument=='M') { size <<= 20; argument++; } + if (*argument=='G') { size <<= 30; argument++; } + if (*argument=='B') { argument++; } + break; + case 's': + argument++; + seed=0; + while ((*argument>='0') && (*argument<='9')) + seed *= 10, seed += *argument++ - '0'; + break; + case 'P': + argument++; + probaU32 = 0; + while ((*argument>='0') && (*argument<='9')) + probaU32 *= 10, probaU32 += *argument++ - '0'; + if (probaU32>100) probaU32 = 100; + break; + case 'L': /* hidden argument : Literal distribution probability */ + argument++; + litProba=0.; + while ((*argument>='0') && (*argument<='9')) + litProba *= 10, litProba += *argument++ - '0'; + if (litProba>100.) litProba=100.; + litProba /= 100.; + break; + case 'v': + displayLevel = 4; + argument++; + break; + default: + return usage(programName); + } + } } } /* for(argNb=1; argNb tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g500MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g250MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g125MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g50MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g25MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g5MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g500KB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +echo -e "\ncorrectness tests -- streaming" +./datagen -g1GB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100MB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g1MB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100KB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10KB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +echo -e "\ncorrectness tests -- read limit" +./datagen -g1GB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100MB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g1MB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100KB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10KB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +echo -e "\ncorrectness tests -- write limit" +./datagen -g1GB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100MB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g1MB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100KB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10KB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +echo -e "\ncorrectness tests -- read and write limits" +./datagen -g1GB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100MB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g1MB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100KB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10KB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + + +make clean diff --git a/contrib/adaptive-compression/test-performance.sh b/contrib/adaptive-compression/test-performance.sh new file mode 100755 index 000000000..6a88325d5 --- /dev/null +++ b/contrib/adaptive-compression/test-performance.sh @@ -0,0 +1,34 @@ +echo "testing time" +./datagen -g1GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +rm tmp* + +./datagen -g2GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +rm tmp* + +./datagen -g4GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +rm tmp* + +echo -e "\ntesting compression ratio" +./datagen -g1GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +ls -l tmp1.zst tmp2.zst +rm tmp* + +./datagen -g2GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +ls -l tmp1.zst tmp2.zst +rm tmp* + +./datagen -g4GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +ls -l tmp1.zst tmp2.zst +rm tmp* From 2b3c7e4199842a7ae2038e31adb36be69bb0725d Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 13 Jul 2017 14:39:35 -0700 Subject: [PATCH 138/318] [ldm] Make some functions shared --- contrib/long_distance_matching/ldm.c | 178 +++++++++------------- contrib/long_distance_matching/ldm.h | 71 ++++++++- contrib/long_distance_matching/main-ldm.c | 34 ++--- 3 files changed, 160 insertions(+), 123 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 00099fbeb..a5057aece 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -19,7 +19,6 @@ //These should be multiples of four. #define LDM_HASH_LENGTH 8 -#define MINMATCH 8 #define ML_BITS 4 #define ML_MASK ((1U<stats); -#ifdef COMPUTE_STATS +void LDM_printCompressStats(const LDM_compressStats *stats, + const LDM_hashEntry *hashTable, + U32 hashTableSize) { printf("=====================\n"); printf("Compression statistics\n"); printf("Total number of matches: %u\n", stats->numMatches); @@ -110,50 +106,41 @@ static void printCompressStats(const LDM_CCtx *cctx) { { U32 i = 0; U32 ctr = 0; - for (; i < LDM_HASHTABLESIZE_U32; i++) { - if ((cctx->hashTable)[i].offset == 0) { + for (; i < hashTableSize; i++) { + if (hashTable[i].offset == 0) { ctr++; } } printf("Hash table size, empty slots, %% empty: %u %u %.3f\n", - LDM_HASHTABLESIZE_U32, ctr, - 100.0 * (double)(ctr) / (double)LDM_HASHTABLESIZE_U32); + hashTableSize, ctr, + 100.0 * (double)(ctr) / (double)hashTableSize); } printf("=====================\n"); -#endif } -/** - * Checks whether the MINMATCH bytes from p are the same as the MINMATCH - * bytes from match. - * - * This assumes MINMATCH is a multiple of four. - * - * Return 1 if valid, 0 otherwise. - */ -static int LDM_isValidMatch(const BYTE *p, const BYTE *match) { +int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { /* - if (memcmp(p, match, MINMATCH) == 0) { + if (memcmp(pIn, pMatch, LDM_MIN_MATCH_LENGTH) == 0) { return 1; } return 0; */ //TODO: This seems to be faster for some reason? - U16 lengthLeft = MINMATCH; - const BYTE *curP = p; - const BYTE *curMatch = match; + U32 lengthLeft = LDM_MIN_MATCH_LENGTH; + const BYTE *curIn = pIn; + const BYTE *curMatch = pMatch; for (; lengthLeft >= 8; lengthLeft -= 8) { - if (MEM_read64(curP) != MEM_read64(curMatch)) { + if (MEM_read64(curIn) != MEM_read64(curMatch)) { return 0; } - curP += 8; + curIn += 8; curMatch += 8; } if (lengthLeft > 0) { - return (MEM_read32(curP) == MEM_read32(curMatch)); + return (MEM_read32(curIn) == MEM_read32(curMatch)); } return 1; } @@ -316,14 +303,8 @@ static const BYTE *getPositionOnHash(LDM_CCtx *cctx, hash_t hash) { return cctx->hashTable[hash].offset + cctx->ibase; } -/** - * Counts the number of bytes that match from pIn and pMatch, - * up to pInLimit. - * - * TODO: make more efficient. - */ -static unsigned countMatchLength(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { +U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { const BYTE * const pStart = pIn; while (pIn < pInLimit - 1) { BYTE const diff = (*pMatch) ^ *(pIn); @@ -332,24 +313,23 @@ static unsigned countMatchLength(const BYTE *pIn, const BYTE *pMatch, pMatch++; continue; } - return (unsigned)(pIn - pStart); + return (U32)(pIn - pStart); } - return (unsigned)(pIn - pStart); + return (U32)(pIn - pStart); } -void LDM_readHeader(const void *src, U64 *compressSize, - U64 *decompressSize) { - const U64 *ip = (const U64 *)src; - *compressSize = *ip++; - *decompressSize = *ip; +void LDM_readHeader(const void *src, U64 *compressedSize, + U64 *decompressedSize) { + const BYTE *ip = (const BYTE *)src; + *compressedSize = MEM_readLE64(ip); + ip += sizeof(U64); + *decompressedSize = MEM_readLE64(ip); + // ip += sizeof(U64); } -/** - * Initialize a compression context. - */ -static void initializeCCtx(LDM_CCtx *cctx, - const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { +void LDM_initializeCCtx(LDM_CCtx *cctx, + const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { cctx->isize = srcSize; cctx->maxOSize = maxDstSize; @@ -358,7 +338,7 @@ static void initializeCCtx(LDM_CCtx *cctx, cctx->iend = cctx->ibase + srcSize; cctx->ihashLimit = cctx->iend - LDM_HASH_LENGTH; - cctx->imatchLimit = cctx->iend - MINMATCH; + cctx->imatchLimit = cctx->iend - LDM_MIN_MATCH_LENGTH; cctx->obase = (BYTE *)dst; cctx->op = (BYTE *)dst; @@ -409,33 +389,33 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { return 0; } -/** - * Write current block (literals, literal length, match offset, - * match length). - * - * Update input pointer, inserting hashes into hash table along the way. - */ -static void outputBlock(LDM_CCtx *cctx, - const unsigned literalLength, - const unsigned offset, - const unsigned matchLength) { - BYTE *token = cctx->op++; - +void LDM_encodeLiteralLengthAndLiterals( + LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength) { /* Encode the literal length. */ if (literalLength >= RUN_MASK) { int len = (int)literalLength - RUN_MASK; - *token = (RUN_MASK << ML_BITS); + *pToken = (RUN_MASK << ML_BITS); for (; len >= 255; len -= 255) { *(cctx->op)++ = 255; } *(cctx->op)++ = (BYTE)len; } else { - *token = (BYTE)(literalLength << ML_BITS); + *pToken = (BYTE)(literalLength << ML_BITS); } /* Encode the literals. */ memcpy(cctx->op, cctx->anchor, literalLength); cctx->op += literalLength; +} + +void LDM_outputBlock(LDM_CCtx *cctx, + const U32 literalLength, + const U32 offset, + const U32 matchLength) { + BYTE *pToken = cctx->op++; + + /* Encode the literal length and literals. */ + LDM_encodeLiteralLengthAndLiterals(cctx, pToken, literalLength); /* Encode the offset. */ MEM_write32(cctx->op, offset); @@ -444,7 +424,7 @@ static void outputBlock(LDM_CCtx *cctx, /* Encode the match length. */ if (matchLength >= ML_MASK) { unsigned matchLengthRemaining = matchLength; - *token += ML_MASK; + *pToken += ML_MASK; matchLengthRemaining -= ML_MASK; MEM_write32(cctx->op, 0xFFFFFFFF); while (matchLengthRemaining >= 4*0xFF) { @@ -455,7 +435,7 @@ static void outputBlock(LDM_CCtx *cctx, cctx->op += matchLengthRemaining / 255; *(cctx->op)++ = (BYTE)(matchLengthRemaining % 255); } else { - *token += (BYTE)(matchLength); + *pToken += (BYTE)(matchLength); } } @@ -467,7 +447,7 @@ static void outputBlock(LDM_CCtx *cctx, size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; - initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); + LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); @@ -503,21 +483,23 @@ size_t LDM_compress(const void *src, size_t srcSize, * length) and update pointers and hashes. */ { - const unsigned literalLength = (unsigned)(cctx.ip - cctx.anchor); - const unsigned offset = cctx.ip - match; - const unsigned matchLength = countMatchLength( - cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); + const U32 literalLength = cctx.ip - cctx.anchor; + const U32 offset = cctx.ip - match; + const U32 matchLength = LDM_countMatchLength( + cctx.ip + LDM_MIN_MATCH_LENGTH, match + LDM_MIN_MATCH_LENGTH, + cctx.ihashLimit); #ifdef COMPUTE_STATS cctx.stats.totalLiteralLength += literalLength; cctx.stats.totalOffset += offset; - cctx.stats.totalMatchLength += matchLength + MINMATCH; + cctx.stats.totalMatchLength += matchLength + LDM_MIN_MATCH_LENGTH; #endif - outputBlock(&cctx, literalLength, offset, matchLength); + LDM_outputBlock(&cctx, literalLength, offset, matchLength); // Move ip to end of block, inserting hashes at each position. cctx.nextIp = cctx.ip + cctx.step; - while (cctx.ip < cctx.anchor + MINMATCH + matchLength + literalLength) { + while (cctx.ip < cctx.anchor + LDM_MIN_MATCH_LENGTH + + matchLength + literalLength) { if (cctx.ip > cctx.lastPosHashed) { // TODO: Simplify. LDM_updateLastHashFromNextHash(&cctx); @@ -535,31 +517,21 @@ size_t LDM_compress(const void *src, size_t srcSize, _last_literals: /* Encode the last literals (no more matches). */ { - size_t const lastRun = (size_t)(cctx.iend - cctx.anchor); - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *(cctx.op)++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255; accumulator -= 255) { - *(cctx.op)++ = 255; - } - *(cctx.op)++ = (BYTE)accumulator; - } else { - *(cctx.op)++ = (BYTE)(lastRun << ML_BITS); - } - memcpy(cctx.op, cctx.anchor, lastRun); - cctx.op += lastRun; + const size_t lastRun = (size_t)(cctx.iend - cctx.anchor); + BYTE *pToken = cctx.op++; + LDM_encodeLiteralLengthAndLiterals(&cctx, pToken, lastRun); } #ifdef COMPUTE_STATS - printCompressStats(&cctx); + LDM_printCompressStats(&cctx.stats, cctx.hashTable, LDM_HASHTABLESIZE_U32); #endif return (cctx.op - (const BYTE *)cctx.obase); } -typedef struct LDM_DCtx { - size_t compressSize; - size_t maxDecompressSize; +struct LDM_DCtx { + size_t compressedSize; + size_t maxDecompressedSize; const BYTE *ibase; /* Base of input */ const BYTE *ip; /* Current input position */ @@ -568,25 +540,25 @@ typedef struct LDM_DCtx { const BYTE *obase; /* Base of output */ BYTE *op; /* Current output position */ const BYTE *oend; /* End of output */ -} LDM_DCtx; +}; -static void LDM_initializeDCtx(LDM_DCtx *dctx, - const void *src, size_t compressSize, - void *dst, size_t maxDecompressSize) { - dctx->compressSize = compressSize; - dctx->maxDecompressSize = maxDecompressSize; +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->compressSize; + dctx->iend = dctx->ip + dctx->compressedSize; dctx->op = dst; - dctx->oend = dctx->op + dctx->maxDecompressSize; + dctx->oend = dctx->op + dctx->maxDecompressedSize; } -size_t LDM_decompress(const void *src, size_t compressSize, - void *dst, size_t maxDecompressSize) { +size_t LDM_decompress(const void *src, size_t compressedSize, + void *dst, size_t maxDecompressedSize) { LDM_DCtx dctx; - LDM_initializeDCtx(&dctx, src, compressSize, dst, maxDecompressSize); + LDM_initializeDCtx(&dctx, src, compressedSize, dst, maxDecompressedSize); while (dctx.ip < dctx.iend) { BYTE *cpy; @@ -623,7 +595,7 @@ size_t LDM_decompress(const void *src, size_t compressSize, length += s; } while (s == 255); } - length += MINMATCH; + length += LDM_MIN_MATCH_LENGTH; /* Copy match. */ cpy = dctx.op + length; diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index fd8c2ab88..19d475dcc 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -9,11 +9,15 @@ #define LDM_DECOMPRESS_SIZE 8 #define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) +// This should be a multiple of four. +#define LDM_MIN_MATCH_LENGTH 8 + typedef U32 offset_t; typedef U32 hash_t; typedef struct LDM_hashEntry LDM_hashEntry; typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; +typedef struct LDM_DCtx LDM_DCtx; /** * Compresses src into dst. @@ -45,17 +49,78 @@ typedef struct LDM_CCtx LDM_CCtx; size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize); +/** + * Initialize the compression context. + */ +void LDM_initializeCCtx(LDM_CCtx *cctx, + const void *src, size_t srcSize, + void *dst, size_t maxDstSize); +/** + * Outputs compression statistics to stdout. + */ +void LDM_printCompressStats(const LDM_compressStats *stats, + const LDM_hashEntry *hashTable, + U32 hashTableSize); +/** + * Checks whether the LDM_MIN_MATCH_LENGTH bytes from p are the same as the + * LDM_MIN_MATCH_LENGTH bytes from match. + * + * This assumes LDM_MIN_MATCH_LENGTH is a multiple of four. + * + * Return 1 if valid, 0 otherwise. + */ +int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch); + +/** + * Counts the number of bytes that match from pIn and pMatch, + * up to pInLimit. + */ +U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit); + +/** + * Encode the literal length followed by the literals. + * + * The literal length is written to the upper four bits of pToken, with + * additional bytes written to the output as needed (see lz4). + * + * This is followed by literalLength bytes corresponding to the literals. + */ +void LDM_encodeLiteralLengthAndLiterals( + LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength); + +/** + * Write current block (literals, literal length, match offset, + * match length). + */ +void LDM_outputBlock(LDM_CCtx *cctx, + const U32 literalLength, + const U32 offset, + const U32 matchLength); + +/** + * Decompresses src into dst. + * + * Note: assumes src does not have a header. + */ size_t LDM_decompress(const void *src, size_t srcSize, void *dst, size_t maxDstSize); +/** + * Initialize the decompression context. + */ +void LDM_initializeDCtx(LDM_DCtx *dctx, + const void *src, size_t compressedSize, + void *dst, size_t maxDecompressedSize); + /** * Reads the header from src and writes the compressed size and - * decompressed size into compressSize and decompressSize respectively. + * decompressed size into compressedSize and decompressedSize respectively. * * NB: LDM_compress and LDM_decompress currently do not add/read headers. */ -void LDM_readHeader(const void *src, U64 *compressSize, - U64 *decompressSize); +void LDM_readHeader(const void *src, U64 *compressedSize, + U64 *decompressedSize); void LDM_test(void); diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 2017cf4ef..40afef8c2 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -26,7 +26,7 @@ static int compress(const char *fname, const char *oname) { int fdin, fdout; struct stat statbuf; char *src, *dst; - size_t maxCompressSize, compressSize; + size_t maxCompressedSize, compressedSize; /* Open the input file. */ if ((fdin = open(fname, O_RDONLY)) < 0) { @@ -46,11 +46,11 @@ static int compress(const char *fname, const char *oname) { return 1; } - maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; + maxCompressedSize = statbuf.st_size + LDM_HEADER_SIZE; /* Go to the location corresponding to the last byte. */ /* TODO: fallocate? */ - if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { + if (lseek(fdout, maxCompressedSize - 1, SEEK_SET) == -1) { perror("lseek error"); return 1; } @@ -69,32 +69,32 @@ static int compress(const char *fname, const char *oname) { } /* mmap the output file */ - if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, + if ((dst = mmap(0, maxCompressedSize, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { perror("mmap error for output"); return 1; } - compressSize = LDM_HEADER_SIZE + + compressedSize = LDM_HEADER_SIZE + LDM_compress(src, statbuf.st_size, - dst + LDM_HEADER_SIZE, maxCompressSize); + dst + LDM_HEADER_SIZE, maxCompressedSize); // Write compress and decompress size to header // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &compressSize, 8); + memcpy(dst, &compressedSize, 8); memcpy(dst + 8, &(statbuf.st_size), 8); #ifdef DEBUG - printf("Compressed size: %zu\n", compressSize); + printf("Compressed size: %zu\n", compressedSize); printf("Decompressed size: %zu\n", (size_t)statbuf.st_size); #endif - // Truncate file to compressSize. - ftruncate(fdout, compressSize); + // Truncate file to compressedSize. + ftruncate(fdout, compressedSize); printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, - (unsigned)statbuf.st_size, (unsigned)compressSize, oname, - (double)compressSize / (statbuf.st_size) * 100); + (unsigned)statbuf.st_size, (unsigned)compressedSize, oname, + (double)compressedSize / (statbuf.st_size) * 100); // Close files. close(fdin); @@ -110,7 +110,7 @@ static int decompress(const char *fname, const char *oname) { int fdin, fdout; struct stat statbuf; char *src, *dst; - U64 compressSize, decompressSize; + U64 compressedSize, decompressedSize; size_t outSize; /* Open the input file. */ @@ -139,10 +139,10 @@ static int decompress(const char *fname, const char *oname) { } /* Read the header. */ - LDM_readHeader(src, &compressSize, &decompressSize); + LDM_readHeader(src, &compressedSize, &decompressedSize); /* Go to the location corresponding to the last byte. */ - if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { + if (lseek(fdout, decompressedSize - 1, SEEK_SET) == -1) { perror("lseek error"); return 1; } @@ -154,7 +154,7 @@ static int decompress(const char *fname, const char *oname) { } /* mmap the output file */ - if ((dst = mmap(0, decompressSize, PROT_READ | PROT_WRITE, + if ((dst = mmap(0, decompressedSize, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { perror("mmap error for output"); return 1; @@ -162,7 +162,7 @@ static int decompress(const char *fname, const char *oname) { outSize = LDM_decompress( src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, - dst, decompressSize); + dst, decompressedSize); printf("Ret size out: %zu\n", outSize); ftruncate(fdout, outSize); From 0d9665cef5fb39a55acfe2069ae443501d7079cf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 13 Jul 2017 14:46:54 -0700 Subject: [PATCH 139/318] added additional tests for performance, allowed force compression level for testing purposes --- contrib/adaptive-compression/adapt.c | 61 +++++++++++-------- .../adaptive-compression/test-performance.sh | 29 ++++++++- 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 81027666b..c2160714d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -33,6 +33,7 @@ static UTIL_time_t g_startTime; static size_t g_streamedSize = 0; static unsigned g_useProgressBar = 0; static UTIL_freq_t g_ticksPerSecond; +static unsigned g_forceCompressionLevel = 0; typedef struct { void* start; @@ -223,34 +224,39 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) */ static unsigned adaptCompressionLevel(adaptCCtx* ctx) { - unsigned reset = 0; - unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; - unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; - unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; - unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter; - unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)); - unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)); - unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)); - DEBUG(3, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); - if (allSlow) { - reset = 1; + if (g_forceCompressionLevel) { + return g_compressionLevel; } - else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); - ctx->compressionLevel++; - reset = 1; + else { + unsigned reset = 0; + unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; + unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; + unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; + unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter; + unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)); + unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)); + unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)); + DEBUG(3, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); + if (allSlow) { + reset = 1; + } + else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { + DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); + ctx->compressionLevel++; + reset = 1; + } + else if (compressSlow && ctx->compressionLevel > 1) { + DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); + ctx->compressionLevel--; + reset = 1; + } + if (reset) { + ctx->stats.readyCounter = 0; + ctx->stats.writeCounter = 0; + ctx->stats.compressedCounter = 0; + } + return ctx->compressionLevel; } - else if (compressSlow && ctx->compressionLevel > 1) { - DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - ctx->compressionLevel--; - reset = 1; - } - if (reset) { - ctx->stats.readyCounter = 0; - ctx->stats.writeCounter = 0; - ctx->stats.compressedCounter = 0; - } - return ctx->compressionLevel; } static size_t getUseableDictSize(unsigned compressionLevel) @@ -649,6 +655,9 @@ int main(int argCount, const char* argv[]) forceStdout = 1; outFilename = stdoutmark; break; + case 'f': + g_forceCompressionLevel = 1; + break; default: DISPLAY("Error: invalid argument provided\n"); ret = 1; diff --git a/contrib/adaptive-compression/test-performance.sh b/contrib/adaptive-compression/test-performance.sh index 6a88325d5..6c4991c48 100755 --- a/contrib/adaptive-compression/test-performance.sh +++ b/contrib/adaptive-compression/test-performance.sh @@ -1,4 +1,4 @@ -echo "testing time" +echo "testing time -- no limits set" ./datagen -g1GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp @@ -14,7 +14,7 @@ time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp rm tmp* -echo -e "\ntesting compression ratio" +echo -e "\ntesting compression ratio -- no limits set" ./datagen -g1GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp @@ -32,3 +32,28 @@ time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp ls -l tmp1.zst tmp2.zst rm tmp* + +echo e "\ntesting performance at various compression levels -- no limits set" +./datagen -g1GB > tmp +echo "adapt" +time ./adapt -i5 -f tmp -otmp1.zst +echo "zstdcli" +time zstd -5 tmp -o tmp2.zst +ls -l tmp1.zst tmp2.zst +rm tmp* + +./datagen -g1GB > tmp +echo "adapt" +time ./adapt -i10 -f tmp -otmp1.zst +echo "zstdcli" +time zstd -10 tmp -o tmp2.zst +ls -l tmp1.zst tmp2.zst +rm tmp* + +./datagen -g1GB > tmp +echo "adapt" +time ./adapt -i15 -f tmp -otmp1.zst +echo "zstdcli" +time zstd -15 tmp -o tmp2.zst +ls -l tmp1.zst tmp2.zst +rm tmp* From 65a4ce2635f519f1db3e9f8027f1596429f789cf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 13 Jul 2017 14:57:24 -0700 Subject: [PATCH 140/318] added tests for forced compression level --- .../adaptive-compression/test-correctness.sh | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/contrib/adaptive-compression/test-correctness.sh b/contrib/adaptive-compression/test-correctness.sh index 6277fafba..e6ac6dfbb 100755 --- a/contrib/adaptive-compression/test-correctness.sh +++ b/contrib/adaptive-compression/test-correctness.sh @@ -201,5 +201,40 @@ zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* +echo -e "\ncorrectness tests -- forced compression level" +./datagen -g1GB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* +./datagen -g100MB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* + +./datagen -g1MB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* + +./datagen -g100KB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* + +./datagen -g10KB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* make clean From 361c06df75d154b793cfb02b7bd279e9a6846cbd Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 13 Jul 2017 15:29:41 -0700 Subject: [PATCH 141/318] Add min/max offset to stats --- contrib/long_distance_matching/ldm.c | 97 +++--- contrib/long_distance_matching/ldm.h | 24 +- .../versions/v0.5/Makefile | 14 +- .../versions/v0.5/ldm.c | 328 ++++++++---------- .../versions/v0.5/ldm.h | 135 ++++++- .../versions/v0.5/main-ldm.c | 254 ++------------ .../versions/v0.5/util.c | 69 ---- .../versions/v0.5/util.h | 25 -- 8 files changed, 386 insertions(+), 560 deletions(-) delete mode 100644 contrib/long_distance_matching/versions/v0.5/util.c delete mode 100644 contrib/long_distance_matching/versions/v0.5/util.h diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index a5057aece..b8e8c63b2 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -1,25 +1,14 @@ -#include -#include +#include #include #include +#include +#include #include "ldm.h" // Insert every (HASH_ONLY_EVERY + 1) into the hash table. #define HASH_ONLY_EVERY 0 -#define LDM_MEMORY_USAGE 22 -#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) - -#define LDM_OFFSET_SIZE 4 - -#define WINDOW_SIZE (1 << 29) - -//These should be multiples of four. -#define LDM_HASH_LENGTH 8 - #define ML_BITS 4 #define ML_MASK ((1U<numMatches); - printf("Average match length: %.1f\n", ((double)stats->totalMatchLength) / + //TODO: compute percentage matched? + printf("num matches, total match length: %u, %llu\n", + stats->numMatches, + stats->totalMatchLength); + printf("avg match length: %.1f\n", ((double)stats->totalMatchLength) / (double)stats->numMatches); - printf("Average literal length: %.1f\n", + printf("avg literal length: %.1f\n", ((double)stats->totalLiteralLength) / (double)stats->numMatches); - printf("Average offset length: %.1f\n", + printf("avg offset length: %.1f\n", ((double)stats->totalOffset) / (double)stats->numMatches); - printf("Num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", + printf("min offset, max offset: %u %u\n", + stats->minOffset, stats->maxOffset); + printf("num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", stats->numCollisions, stats->numHashInserts, stats->numHashInserts == 0 ? 1.0 : (100.0 * (double)stats->numCollisions) / (double)stats->numHashInserts); - - // Output occupancy of hash table. - { - U32 i = 0; - U32 ctr = 0; - for (; i < hashTableSize; i++) { - if (hashTable[i].offset == 0) { - ctr++; - } - } - printf("Hash table size, empty slots, %% empty: %u %u %.3f\n", - hashTableSize, ctr, - 100.0 * (double)(ctr) / (double)hashTableSize); - } - - printf("=====================\n"); } int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { @@ -219,8 +214,8 @@ static void setNextHash(LDM_CCtx *cctx) { // cctx->nextSum = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); cctx->nextSum = updateChecksum( cctx->lastSum, LDM_HASH_LENGTH, - (cctx->lastPosHashed)[0], - (cctx->lastPosHashed)[LDM_HASH_LENGTH]); + cctx->lastPosHashed[0], + cctx->lastPosHashed[LDM_HASH_LENGTH]); cctx->nextPosHashed = cctx->nextIp; cctx->nextHash = checksumToHash(cctx->nextSum); @@ -243,7 +238,7 @@ static void putHashOfCurrentPositionFromHash( LDM_CCtx *cctx, hash_t hash, U32 sum) { #ifdef COMPUTE_STATS if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { - offset_t offset = (cctx->hashTable)[hash].offset; + offset_t offset = cctx->hashTable[hash].offset; cctx->stats.numHashInserts++; if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { cctx->stats.numCollisions++; @@ -254,8 +249,8 @@ static void putHashOfCurrentPositionFromHash( // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. // Note: this works only when cctx->step is 1. if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { - (cctx->hashTable)[hash] = - (LDM_hashEntry){ (offset_t)(cctx->ip - cctx->ibase) }; + const LDM_hashEntry entry = { cctx->ip - cctx->ibase }; + cctx->hashTable[hash] = entry; } cctx->lastPosHashed = cctx->ip; @@ -347,6 +342,7 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, memset(&(cctx->stats), 0, sizeof(cctx->stats)); memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); + cctx->stats.minOffset = UINT_MAX; cctx->lastPosHashed = NULL; @@ -493,6 +489,10 @@ size_t LDM_compress(const void *src, size_t srcSize, 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; #endif LDM_outputBlock(&cctx, literalLength, offset, matchLength); @@ -523,7 +523,8 @@ _last_literals: } #ifdef COMPUTE_STATS - LDM_printCompressStats(&cctx.stats, cctx.hashTable, LDM_HASHTABLESIZE_U32); + LDM_printCompressStats(&cctx.stats); + LDM_outputHashtableOccupancy(cctx.hashTable, LDM_HASHTABLESIZE_U32); #endif return (cctx.op - (const BYTE *)cctx.obase); diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 19d475dcc..5da3c3b99 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -8,9 +8,19 @@ #define LDM_COMPRESS_SIZE 8 #define LDM_DECOMPRESS_SIZE 8 #define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) +#define LDM_OFFSET_SIZE 4 -// This should be a multiple of four. +// Defines the size of the hash table. +#define LDM_MEMORY_USAGE 22 +#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) + +#define WINDOW_SIZE (1 << 25) + +//These should be multiples of four. #define LDM_MIN_MATCH_LENGTH 8 +#define LDM_HASH_LENGTH 8 typedef U32 offset_t; typedef U32 hash_t; @@ -55,12 +65,18 @@ size_t LDM_compress(const void *src, size_t srcSize, void LDM_initializeCCtx(LDM_CCtx *cctx, const void *src, size_t srcSize, void *dst, size_t maxDstSize); + +/** + * Prints the percentage of the hash table occupied (where occupied is defined + * as the entry being non-zero). + */ +void LDM_outputHashtableOccupancy(const LDM_hashEntry *hashTable, + U32 hashTableSize); + /** * Outputs compression statistics to stdout. */ -void LDM_printCompressStats(const LDM_compressStats *stats, - const LDM_hashEntry *hashTable, - U32 hashTableSize); +void LDM_printCompressStats(const LDM_compressStats *stats); /** * Checks whether the LDM_MIN_MATCH_LENGTH bytes from p are the same as the * LDM_MIN_MATCH_LENGTH bytes from match. diff --git a/contrib/long_distance_matching/versions/v0.5/Makefile b/contrib/long_distance_matching/versions/v0.5/Makefile index fa4abce63..dee686bca 100644 --- a/contrib/long_distance_matching/versions/v0.5/Makefile +++ b/contrib/long_distance_matching/versions/v0.5/Makefile @@ -1,5 +1,15 @@ +# ################################################################ +# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. +# ################################################################ + # 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 \ @@ -17,11 +27,11 @@ default: all all: main-ldm -main-ldm : util.c ldm.c main-ldm.c +main-ldm : ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main-ldm + main main-ldm @echo Cleaning completed diff --git a/contrib/long_distance_matching/versions/v0.5/ldm.c b/contrib/long_distance_matching/versions/v0.5/ldm.c index 5fa20c066..b8e8c63b2 100644 --- a/contrib/long_distance_matching/versions/v0.5/ldm.c +++ b/contrib/long_distance_matching/versions/v0.5/ldm.c @@ -1,63 +1,46 @@ -#include -#include +#include #include #include +#include +#include #include "ldm.h" -#include "util.h" // Insert every (HASH_ONLY_EVERY + 1) into the hash table. #define HASH_ONLY_EVERY 0 -#define LDM_MEMORY_USAGE 20 -#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) - -#define LDM_OFFSET_SIZE 4 - -#define WINDOW_SIZE (1 << 20) - -//These should be multiples of four. -#define LDM_HASH_LENGTH 4 -#define MINMATCH 4 - #define ML_BITS 4 #define ML_MASK ((1U<stats); +void LDM_outputHashtableOccupancy( + const LDM_hashEntry *hashTable, U32 hashTableSize) { + U32 i = 0; + U32 ctr = 0; + for (; i < hashTableSize; i++) { + if (hashTable[i].offset == 0) { + ctr++; + } + } + printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", + hashTableSize, ctr, + 100.0 * (double)(ctr) / (double)hashTableSize); +} + +void LDM_printCompressStats(const LDM_compressStats *stats) { printf("=====================\n"); printf("Compression statistics\n"); - printf("Total number of matches: %u\n", stats->numMatches); - printf("Average match length: %.1f\n", ((double)stats->totalMatchLength) / + //TODO: compute percentage matched? + printf("num matches, total match length: %u, %llu\n", + stats->numMatches, + stats->totalMatchLength); + printf("avg match length: %.1f\n", ((double)stats->totalMatchLength) / (double)stats->numMatches); - printf("Average literal length: %.1f\n", + printf("avg literal length: %.1f\n", ((double)stats->totalLiteralLength) / (double)stats->numMatches); - printf("Average offset length: %.1f\n", + printf("avg offset length: %.1f\n", ((double)stats->totalOffset) / (double)stats->numMatches); - printf("Num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", + printf("min offset, max offset: %u %u\n", + stats->minOffset, stats->maxOffset); + printf("num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", stats->numCollisions, stats->numHashInserts, stats->numHashInserts == 0 ? 1.0 : (100.0 * (double)stats->numCollisions) / (double)stats->numHashInserts); - - // Output occupancy of hash table. - { - U32 i = 0; - U32 ctr = 0; - for (; i < LDM_HASHTABLESIZE_U32; i++) { - if ((cctx->hashTable)[i].offset == 0) { - ctr++; - } - } - printf("Hash table size, empty slots, %% empty: %u %u %.3f\n", - LDM_HASHTABLESIZE_U32, ctr, - 100.0 * (double)(ctr) / (double)LDM_HASHTABLESIZE_U32); - } - - printf("=====================\n"); } -#endif -/** - * Checks whether the MINMATCH bytes from p are the same as the MINMATCH - * bytes from match. - * - * This assumes MINMATCH is a multiple of four. - * - * Return 1 if valid, 0 otherwise. - */ -static int LDM_isValidMatch(const BYTE *p, const BYTE *match) { +int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { /* - if (memcmp(p, match, MINMATCH) == 0) { + if (memcmp(pIn, pMatch, LDM_MIN_MATCH_LENGTH) == 0) { return 1; } return 0; */ //TODO: This seems to be faster for some reason? - U16 lengthLeft = MINMATCH; - const BYTE *curP = p; - const BYTE *curMatch = match; + U32 lengthLeft = LDM_MIN_MATCH_LENGTH; + const BYTE *curIn = pIn; + const BYTE *curMatch = pMatch; for (; lengthLeft >= 8; lengthLeft -= 8) { - if (LDM_read64(curP) != LDM_read64(curMatch)) { + if (MEM_read64(curIn) != MEM_read64(curMatch)) { return 0; } - curP += 8; + curIn += 8; curMatch += 8; } if (lengthLeft > 0) { - return (LDM_read32(curP) == LDM_read32(curMatch)); + return (MEM_read32(curIn) == MEM_read32(curMatch)); } return 1; } @@ -183,19 +155,21 @@ static hash_t checksumToHash(U32 sum) { * b(k,l) = \sum_{i = k}^l ((l - i + 1) * x_i) (mod M) * checksum(k,l) = a(k,l) + 2^{16} * b(k,l) */ -static U32 getChecksum(const char *data, U32 len) { +static U32 getChecksum(const BYTE *buf, U32 len) { U32 i; U32 s1, s2; - const schar *buf = (const schar *)data; s1 = s2 = 0; for (i = 0; i < (len - 4); i += 4) { s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + - (2 * buf[i + 2]) + (buf[i + 3]); - s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; + (2 * buf[i + 2]) + (buf[i + 3]) + + (10 * CHECKSUM_CHAR_OFFSET); + s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3] + + + (4 * CHECKSUM_CHAR_OFFSET); + } for(; i < len; i++) { - s1 += buf[i]; + s1 += buf[i] + CHECKSUM_CHAR_OFFSET; s2 += s1; } return (s1 & 0xffff) + (s2 << 16); @@ -211,9 +185,9 @@ static U32 getChecksum(const char *data, U32 len) { * Thus toRemove should correspond to data[0]. */ static U32 updateChecksum(U32 sum, U32 len, - schar toRemove, schar toAdd) { + BYTE toRemove, BYTE toAdd) { U32 s1 = (sum & 0xffff) - toRemove + toAdd; - U32 s2 = (sum >> 16) - (toRemove * len) + s1; + U32 s2 = (sum >> 16) - ((toRemove + CHECKSUM_CHAR_OFFSET) * len) + s1; return (s1 & 0xffff) + (s2 << 16); } @@ -240,13 +214,13 @@ static void setNextHash(LDM_CCtx *cctx) { // cctx->nextSum = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); cctx->nextSum = updateChecksum( cctx->lastSum, LDM_HASH_LENGTH, - (schar)((cctx->lastPosHashed)[0]), - (schar)((cctx->lastPosHashed)[LDM_HASH_LENGTH])); + cctx->lastPosHashed[0], + cctx->lastPosHashed[LDM_HASH_LENGTH]); cctx->nextPosHashed = cctx->nextIp; cctx->nextHash = checksumToHash(cctx->nextSum); #ifdef RUN_CHECKS - check = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); + check = getChecksum(cctx->nextIp, LDM_HASH_LENGTH); if (check != cctx->nextSum) { printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); @@ -264,7 +238,7 @@ static void putHashOfCurrentPositionFromHash( LDM_CCtx *cctx, hash_t hash, U32 sum) { #ifdef COMPUTE_STATS if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { - offset_t offset = (cctx->hashTable)[hash].offset; + offset_t offset = cctx->hashTable[hash].offset; cctx->stats.numHashInserts++; if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { cctx->stats.numCollisions++; @@ -275,7 +249,8 @@ static void putHashOfCurrentPositionFromHash( // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. // Note: this works only when cctx->step is 1. if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { - (cctx->hashTable)[hash] = (hashEntry){ (offset_t)(cctx->ip - cctx->ibase) }; + const LDM_hashEntry entry = { cctx->ip - cctx->ibase }; + cctx->hashTable[hash] = entry; } cctx->lastPosHashed = cctx->ip; @@ -303,7 +278,7 @@ static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { * Insert hash of the current position into the hash table. */ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - U32 sum = getChecksum((const char *)cctx->ip, LDM_HASH_LENGTH); + U32 sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); hash_t hash = checksumToHash(sum); #ifdef RUN_CHECKS @@ -323,40 +298,33 @@ static const BYTE *getPositionOnHash(LDM_CCtx *cctx, hash_t hash) { return cctx->hashTable[hash].offset + cctx->ibase; } -/** - * Counts the number of bytes that match from pIn and pMatch, - * up to pInLimit. - * - * TODO: make more efficient. - */ -static unsigned countMatchLength(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { +U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { const BYTE * const pStart = pIn; while (pIn < pInLimit - 1) { - BYTE const diff = LDM_readByte(pMatch) ^ LDM_readByte(pIn); + BYTE const diff = (*pMatch) ^ *(pIn); if (!diff) { pIn++; pMatch++; continue; } - return (unsigned)(pIn - pStart); + return (U32)(pIn - pStart); } - return (unsigned)(pIn - pStart); + return (U32)(pIn - pStart); } -void LDM_readHeader(const void *src, size_t *compressSize, - size_t *decompressSize) { - const U32 *ip = (const U32 *)src; - *compressSize = *ip++; - *decompressSize = *ip; +void LDM_readHeader(const void *src, U64 *compressedSize, + U64 *decompressedSize) { + const BYTE *ip = (const BYTE *)src; + *compressedSize = MEM_readLE64(ip); + ip += sizeof(U64); + *decompressedSize = MEM_readLE64(ip); + // ip += sizeof(U64); } -/** - * Initialize a compression context. - */ -static void initializeCCtx(LDM_CCtx *cctx, - const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { +void LDM_initializeCCtx(LDM_CCtx *cctx, + const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { cctx->isize = srcSize; cctx->maxOSize = maxDstSize; @@ -365,7 +333,7 @@ static void initializeCCtx(LDM_CCtx *cctx, cctx->iend = cctx->ibase + srcSize; cctx->ihashLimit = cctx->iend - LDM_HASH_LENGTH; - cctx->imatchLimit = cctx->iend - MINMATCH; + cctx->imatchLimit = cctx->iend - LDM_MIN_MATCH_LENGTH; cctx->obase = (BYTE *)dst; cctx->op = (BYTE *)dst; @@ -374,6 +342,7 @@ static void initializeCCtx(LDM_CCtx *cctx, memset(&(cctx->stats), 0, sizeof(cctx->stats)); memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); + cctx->stats.minOffset = UINT_MAX; cctx->lastPosHashed = NULL; @@ -416,61 +385,65 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { return 0; } -/** - * Write current block (literals, literal length, match offset, - * match length). - * - * Update input pointer, inserting hashes into hash table along the way. - */ -static void outputBlock(LDM_CCtx *cctx, - unsigned const literalLength, - unsigned const offset, - unsigned const matchLength) { - BYTE *token = cctx->op++; - +void LDM_encodeLiteralLengthAndLiterals( + LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength) { /* Encode the literal length. */ if (literalLength >= RUN_MASK) { int len = (int)literalLength - RUN_MASK; - *token = (RUN_MASK << ML_BITS); + *pToken = (RUN_MASK << ML_BITS); for (; len >= 255; len -= 255) { *(cctx->op)++ = 255; } *(cctx->op)++ = (BYTE)len; } else { - *token = (BYTE)(literalLength << ML_BITS); + *pToken = (BYTE)(literalLength << ML_BITS); } /* Encode the literals. */ memcpy(cctx->op, cctx->anchor, literalLength); cctx->op += literalLength; +} + +void LDM_outputBlock(LDM_CCtx *cctx, + const U32 literalLength, + const U32 offset, + const U32 matchLength) { + BYTE *pToken = cctx->op++; + + /* Encode the literal length and literals. */ + LDM_encodeLiteralLengthAndLiterals(cctx, pToken, literalLength); /* Encode the offset. */ - LDM_write32(cctx->op, offset); + MEM_write32(cctx->op, offset); cctx->op += LDM_OFFSET_SIZE; /* Encode the match length. */ if (matchLength >= ML_MASK) { unsigned matchLengthRemaining = matchLength; - *token += ML_MASK; + *pToken += ML_MASK; matchLengthRemaining -= ML_MASK; - LDM_write32(cctx->op, 0xFFFFFFFF); + MEM_write32(cctx->op, 0xFFFFFFFF); while (matchLengthRemaining >= 4*0xFF) { cctx->op += 4; - LDM_write32(cctx->op, 0xffffffff); + MEM_write32(cctx->op, 0xffffffff); matchLengthRemaining -= 4*0xFF; } cctx->op += matchLengthRemaining / 255; *(cctx->op)++ = (BYTE)(matchLengthRemaining % 255); } else { - *token += (BYTE)(matchLength); + *pToken += (BYTE)(matchLength); } } -// TODO: srcSize and maxDstSize is unused +// 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; - initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); + LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); @@ -506,21 +479,27 @@ size_t LDM_compress(const void *src, size_t srcSize, * length) and update pointers and hashes. */ { - unsigned const literalLength = (unsigned)(cctx.ip - cctx.anchor); - unsigned const offset = cctx.ip - match; - unsigned const matchLength = countMatchLength( - cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); + const U32 literalLength = cctx.ip - cctx.anchor; + const U32 offset = cctx.ip - match; + const U32 matchLength = LDM_countMatchLength( + cctx.ip + LDM_MIN_MATCH_LENGTH, match + LDM_MIN_MATCH_LENGTH, + cctx.ihashLimit); #ifdef COMPUTE_STATS cctx.stats.totalLiteralLength += literalLength; cctx.stats.totalOffset += offset; - cctx.stats.totalMatchLength += matchLength + MINMATCH; + 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; #endif - outputBlock(&cctx, literalLength, offset, matchLength); + LDM_outputBlock(&cctx, literalLength, offset, matchLength); // Move ip to end of block, inserting hashes at each position. cctx.nextIp = cctx.ip + cctx.step; - while (cctx.ip < cctx.anchor + MINMATCH + matchLength + literalLength) { + while (cctx.ip < cctx.anchor + LDM_MIN_MATCH_LENGTH + + matchLength + literalLength) { if (cctx.ip > cctx.lastPosHashed) { // TODO: Simplify. LDM_updateLastHashFromNextHash(&cctx); @@ -538,31 +517,22 @@ size_t LDM_compress(const void *src, size_t srcSize, _last_literals: /* Encode the last literals (no more matches). */ { - size_t const lastRun = (size_t)(cctx.iend - cctx.anchor); - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *(cctx.op)++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255; accumulator -= 255) { - *(cctx.op)++ = 255; - } - *(cctx.op)++ = (BYTE)accumulator; - } else { - *(cctx.op)++ = (BYTE)(lastRun << ML_BITS); - } - memcpy(cctx.op, cctx.anchor, lastRun); - cctx.op += lastRun; + const size_t lastRun = (size_t)(cctx.iend - cctx.anchor); + BYTE *pToken = cctx.op++; + LDM_encodeLiteralLengthAndLiterals(&cctx, pToken, lastRun); } #ifdef COMPUTE_STATS - printCompressStats(&cctx); + LDM_printCompressStats(&cctx.stats); + LDM_outputHashtableOccupancy(cctx.hashTable, LDM_HASHTABLESIZE_U32); #endif return (cctx.op - (const BYTE *)cctx.obase); } -typedef struct LDM_DCtx { - size_t compressSize; - size_t maxDecompressSize; +struct LDM_DCtx { + size_t compressedSize; + size_t maxDecompressedSize; const BYTE *ibase; /* Base of input */ const BYTE *ip; /* Current input position */ @@ -571,26 +541,25 @@ typedef struct LDM_DCtx { const BYTE *obase; /* Base of output */ BYTE *op; /* Current output position */ const BYTE *oend; /* End of output */ -} LDM_DCtx; +}; -static void LDM_initializeDCtx(LDM_DCtx *dctx, - const void *src, size_t compressSize, - void *dst, size_t maxDecompressSize) { - dctx->compressSize = compressSize; - dctx->maxDecompressSize = maxDecompressSize; +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->compressSize; + dctx->iend = dctx->ip + dctx->compressedSize; dctx->op = dst; - dctx->oend = dctx->op + dctx->maxDecompressSize; - + dctx->oend = dctx->op + dctx->maxDecompressedSize; } -size_t LDM_decompress(const void *src, size_t compressSize, - void *dst, size_t maxDecompressSize) { +size_t LDM_decompress(const void *src, size_t compressedSize, + void *dst, size_t maxDecompressedSize) { LDM_DCtx dctx; - LDM_initializeDCtx(&dctx, src, compressSize, dst, maxDecompressSize); + LDM_initializeDCtx(&dctx, src, compressedSize, dst, maxDecompressedSize); while (dctx.ip < dctx.iend) { BYTE *cpy; @@ -598,7 +567,7 @@ size_t LDM_decompress(const void *src, size_t compressSize, size_t length, offset; /* Get the literal length. */ - unsigned const token = *(dctx.ip)++; + const unsigned token = *(dctx.ip)++; if ((length = (token >> ML_BITS)) == RUN_MASK) { unsigned s; do { @@ -614,7 +583,7 @@ size_t LDM_decompress(const void *src, size_t compressSize, dctx.op = cpy; //TODO : dynamic offset size - offset = LDM_read32(dctx.ip); + offset = MEM_read32(dctx.ip); dctx.ip += LDM_OFFSET_SIZE; match = dctx.op - offset; @@ -627,7 +596,7 @@ size_t LDM_decompress(const void *src, size_t compressSize, length += s; } while (s == 255); } - length += MINMATCH; + length += LDM_MIN_MATCH_LENGTH; /* Copy match. */ cpy = dctx.op + length; @@ -640,6 +609,11 @@ size_t LDM_decompress(const void *src, size_t compressSize, return dctx.op - (BYTE *)dst; } +// TODO: implement and test hash function +void LDM_test(void) { + +} + /* void LDM_test(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { diff --git a/contrib/long_distance_matching/versions/v0.5/ldm.h b/contrib/long_distance_matching/versions/v0.5/ldm.h index 1bd19745c..5da3c3b99 100644 --- a/contrib/long_distance_matching/versions/v0.5/ldm.h +++ b/contrib/long_distance_matching/versions/v0.5/ldm.h @@ -3,24 +3,141 @@ #include /* size_t */ -#define LDM_COMPRESS_SIZE 4 -#define LDM_DECOMPRESS_SIZE 4 -#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) +#include "mem.h" // from /lib/common/mem.h +#define LDM_COMPRESS_SIZE 8 +#define LDM_DECOMPRESS_SIZE 8 +#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) +#define LDM_OFFSET_SIZE 4 + +// Defines the size of the hash table. +#define LDM_MEMORY_USAGE 22 +#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) + +#define WINDOW_SIZE (1 << 25) + +//These should be multiples of four. +#define LDM_MIN_MATCH_LENGTH 8 +#define LDM_HASH_LENGTH 8 + +typedef U32 offset_t; +typedef U32 hash_t; +typedef struct LDM_hashEntry LDM_hashEntry; +typedef struct LDM_compressStats LDM_compressStats; +typedef struct LDM_CCtx LDM_CCtx; +typedef struct LDM_DCtx LDM_DCtx; + +/** + * Compresses src into dst. + * + * NB: This currently ignores maxDstSize and assumes enough space is available. + * + * Block format (see lz4 documentation for more information): + * github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md + * + * A block is composed of sequences. Each sequence begins with a token, which + * is a one-byte value separated into two 4-bit fields. + * + * The first field uses the four high bits of the token and encodes the literal + * length. If the field value is 0, there is no literal. If it is 15, + * additional bytes are added (each ranging from 0 to 255) to the previous + * value to produce a total length. + * + * Following the token and optional length bytes are the literals. + * + * Next are the 4 bytes representing the offset of the match (2 in lz4), + * representing the position to copy the literals. + * + * The lower four bits of the token encode the match length. With additional + * bytes added similarly to the additional literal length bytes after the offset. + * + * The last sequence is incomplete and stops right after the lieterals. + * + */ size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize); +/** + * Initialize the compression context. + */ +void LDM_initializeCCtx(LDM_CCtx *cctx, + const void *src, size_t srcSize, + void *dst, size_t maxDstSize); + +/** + * Prints the percentage of the hash table occupied (where occupied is defined + * as the entry being non-zero). + */ +void LDM_outputHashtableOccupancy(const LDM_hashEntry *hashTable, + U32 hashTableSize); + +/** + * Outputs compression statistics to stdout. + */ +void LDM_printCompressStats(const LDM_compressStats *stats); +/** + * Checks whether the LDM_MIN_MATCH_LENGTH bytes from p are the same as the + * LDM_MIN_MATCH_LENGTH bytes from match. + * + * This assumes LDM_MIN_MATCH_LENGTH is a multiple of four. + * + * Return 1 if valid, 0 otherwise. + */ +int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch); + +/** + * Counts the number of bytes that match from pIn and pMatch, + * up to pInLimit. + */ +U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit); + +/** + * Encode the literal length followed by the literals. + * + * The literal length is written to the upper four bits of pToken, with + * additional bytes written to the output as needed (see lz4). + * + * This is followed by literalLength bytes corresponding to the literals. + */ +void LDM_encodeLiteralLengthAndLiterals( + LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength); + +/** + * Write current block (literals, literal length, match offset, + * match length). + */ +void LDM_outputBlock(LDM_CCtx *cctx, + const U32 literalLength, + const U32 offset, + const U32 matchLength); + +/** + * Decompresses src into dst. + * + * Note: assumes src does not have a header. + */ size_t LDM_decompress(const void *src, size_t srcSize, void *dst, size_t maxDstSize); /** - * Reads the header from src and writes the compressed size and - * decompressed size into compressSize and decompressSize respectively. + * Initialize the decompression context. */ -void LDM_readHeader(const void *src, size_t *compressSize, - size_t *decompressSize); +void LDM_initializeDCtx(LDM_DCtx *dctx, + const void *src, size_t compressedSize, + void *dst, size_t maxDecompressedSize); -void LDM_test(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); +/** + * Reads the header from src and writes the compressed size and + * decompressed size into compressedSize and decompressedSize respectively. + * + * NB: LDM_compress and LDM_decompress currently do not add/read headers. + */ +void LDM_readHeader(const void *src, U64 *compressedSize, + U64 *decompressedSize); + +void LDM_test(void); #endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v0.5/main-ldm.c b/contrib/long_distance_matching/versions/v0.5/main-ldm.c index fbfd789bc..40afef8c2 100644 --- a/contrib/long_distance_matching/versions/v0.5/main-ldm.c +++ b/contrib/long_distance_matching/versions/v0.5/main-ldm.c @@ -1,5 +1,3 @@ -// TODO: file size must fit into a U32 - #include #include #include @@ -12,18 +10,23 @@ #include #include "ldm.h" +#include "zstd.h" #define DEBUG //#define TEST /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. + * + * TODO: This currently seg faults 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 maxCompressSize, compressSize; + size_t maxCompressedSize, compressedSize; /* Open the input file. */ if ((fdin = open(fname, O_RDONLY)) < 0) { @@ -43,11 +46,11 @@ static int compress(const char *fname, const char *oname) { return 1; } - maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; + maxCompressedSize = statbuf.st_size + LDM_HEADER_SIZE; /* Go to the location corresponding to the last byte. */ /* TODO: fallocate? */ - if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { + if (lseek(fdout, maxCompressedSize - 1, SEEK_SET) == -1) { perror("lseek error"); return 1; } @@ -66,39 +69,32 @@ static int compress(const char *fname, const char *oname) { } /* mmap the output file */ - if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, + if ((dst = mmap(0, maxCompressedSize, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { perror("mmap error for output"); return 1; } -/* -#ifdef TEST - LDM_test(src, statbuf.st_size, - dst + LDM_HEADER_SIZE, statbuf.st_size); -#endif -*/ - - compressSize = LDM_HEADER_SIZE + + compressedSize = LDM_HEADER_SIZE + LDM_compress(src, statbuf.st_size, - dst + LDM_HEADER_SIZE, statbuf.st_size); + dst + LDM_HEADER_SIZE, maxCompressedSize); // Write compress and decompress size to header // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &compressSize, 4); - memcpy(dst + 4, &(statbuf.st_size), 4); + memcpy(dst, &compressedSize, 8); + memcpy(dst + 8, &(statbuf.st_size), 8); #ifdef DEBUG - printf("Compressed size: %zu\n", compressSize); + printf("Compressed size: %zu\n", compressedSize); printf("Decompressed size: %zu\n", (size_t)statbuf.st_size); #endif - // Truncate file to compressSize. - ftruncate(fdout, compressSize); + // Truncate file to compressedSize. + ftruncate(fdout, compressedSize); printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, - (unsigned)statbuf.st_size, (unsigned)compressSize, oname, - (double)compressSize / (statbuf.st_size) * 100); + (unsigned)statbuf.st_size, (unsigned)compressedSize, oname, + (double)compressedSize / (statbuf.st_size) * 100); // Close files. close(fdin); @@ -114,7 +110,8 @@ static int decompress(const char *fname, const char *oname) { int fdin, fdout; struct stat statbuf; char *src, *dst; - size_t compressSize, decompressSize, outSize; + U64 compressedSize, decompressedSize; + size_t outSize; /* Open the input file. */ if ((fdin = open(fname, O_RDONLY)) < 0) { @@ -142,10 +139,10 @@ static int decompress(const char *fname, const char *oname) { } /* Read the header. */ - LDM_readHeader(src, &compressSize, &decompressSize); + LDM_readHeader(src, &compressedSize, &decompressedSize); /* Go to the location corresponding to the last byte. */ - if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { + if (lseek(fdout, decompressedSize - 1, SEEK_SET) == -1) { perror("lseek error"); return 1; } @@ -157,7 +154,7 @@ static int decompress(const char *fname, const char *oname) { } /* mmap the output file */ - if ((dst = mmap(0, decompressSize, PROT_READ | PROT_WRITE, + if ((dst = mmap(0, decompressedSize, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { perror("mmap error for output"); return 1; @@ -165,7 +162,7 @@ static int decompress(const char *fname, const char *oname) { outSize = LDM_decompress( src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, - dst, decompressSize); + dst, decompressedSize); printf("Ret size out: %zu\n", outSize); ftruncate(fdout, outSize); @@ -265,204 +262,9 @@ int main(int argc, const char *argv[]) { } /* verify */ verify(inpFilename, decFilename); - return 0; -} - -#if 0 -static size_t compress_file(FILE *in, FILE *out, size_t *size_in, - size_t *size_out) { - char *src, *buf = NULL; - size_t r = 1; - size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; - - src = malloc(BUF_SIZE); - if (!src) { - printf("Not enough memory\n"); - goto cleanup; - } - - size = BUF_SIZE + LDM_HEADER_SIZE; - buf = malloc(size); - if (!buf) { - printf("Not enough memory\n"); - goto cleanup; - } - - - for (;;) { - k = fread(src, 1, BUF_SIZE, in); - if (k == 0) - break; - count_in += k; - - n = LDM_compress(src, buf, k, BUF_SIZE); - - // n = k; - // offset += n; - offset = k; - count_out += k; - -// k = fwrite(src, 1, offset, out); - - k = fwrite(buf, 1, offset, out); - if (k < offset) { - if (ferror(out)) - printf("Write failed\n"); - else - printf("Short write\n"); - goto cleanup; - } - - } - *size_in = count_in; - *size_out = count_out; - r = 0; - cleanup: - free(src); - free(buf); - return r; -} - -static size_t decompress_file(FILE *in, FILE *out) { - void *src = malloc(BUF_SIZE); - void *dst = NULL; - size_t dst_capacity = BUF_SIZE; - size_t ret = 1; - size_t bytes_written = 0; - - if (!src) { - perror("decompress_file(src)"); - goto cleanup; - } - - while (ret != 0) { - /* Load more input */ - size_t src_size = fread(src, 1, BUF_SIZE, in); - void *src_ptr = src; - void *src_end = src_ptr + src_size; - if (src_size == 0 || ferror(in)) { - printf("(TODO): Decompress: not enough input or error reading file\n"); - //TODO - ret = 0; - goto cleanup; - } - - /* Allocate destination buffer if it hasn't been allocated already */ - if (!dst) { - dst = malloc(dst_capacity); - if (!dst) { - perror("decompress_file(dst)"); - goto cleanup; - } - } - - // TODO - - /* Decompress: - * Continue while there is more input to read. - */ - while (src_ptr != src_end && ret != 0) { - // size_t dst_size = src_size; - size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); - size_t written = fwrite(dst, 1, dst_size, out); -// printf("Writing %zu bytes\n", dst_size); - bytes_written += dst_size; - if (written != dst_size) { - printf("Decompress: Failed to write to file\n"); - goto cleanup; - } - src_ptr += src_size; - src_size = src_end - src_ptr; - } - - /* Update input */ - - } - - printf("Wrote %zu bytes\n", bytes_written); - - cleanup: - free(src); - free(dst); - - return ret; -} - -int main2(int argc, char *argv[]) { - char inpFilename[256] = { 0 }; - char ldmFilename[256] = { 0 }; - char decFilename[256] = { 0 }; - - if (argc < 2) { - printf("Please specify input filename\n"); - return 0; - } - 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 */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *outFp = fopen(ldmFilename, "wb"); - size_t sizeIn = 0; - size_t sizeOut = 0; - size_t ret; - printf("compress : %s -> %s\n", inpFilename, ldmFilename); - ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); - if (ret) { - printf("compress : failed with code %zu\n", ret); - return ret; - } - printf("%s: %zu → %zu bytes, %.1f%%\n", - inpFilename, sizeIn, sizeOut, - (double)sizeOut / sizeIn * 100); - printf("compress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* decompress */ - { - FILE *inpFp = fopen(ldmFilename, "rb"); - FILE *outFp = fopen(decFilename, "wb"); - size_t ret; - - printf("decompress : %s -> %s\n", ldmFilename, decFilename); - ret = decompress_file(inpFp, outFp); - if (ret) { - printf("decompress : failed with code %zu\n", ret); - return ret; - } - printf("decompress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* verify */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); - } - return 0; -} +#ifdef TEST + LDM_test(); #endif - + return 0; +} diff --git a/contrib/long_distance_matching/versions/v0.5/util.c b/contrib/long_distance_matching/versions/v0.5/util.c deleted file mode 100644 index 70fcbc2ce..000000000 --- a/contrib/long_distance_matching/versions/v0.5/util.c +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include -#include - -#include "util.h" - -typedef uint8_t BYTE; -typedef uint16_t U16; -typedef uint32_t U32; -typedef int32_t S32; -typedef uint64_t U64; - -unsigned LDM_isLittleEndian(void) { - const union { U32 u; BYTE c[4]; } one = { 1 }; - return one.c[0]; -} - -U16 LDM_read16(const void *memPtr) { - U16 val; - memcpy(&val, memPtr, sizeof(val)); - return val; -} - -U16 LDM_readLE16(const void *memPtr) { - if (LDM_isLittleEndian()) { - return LDM_read16(memPtr); - } else { - const BYTE *p = (const BYTE *)memPtr; - return (U16)((U16)p[0] + (p[1] << 8)); - } -} - -void LDM_write16(void *memPtr, U16 value){ - memcpy(memPtr, &value, sizeof(value)); -} - -void LDM_write32(void *memPtr, U32 value) { - memcpy(memPtr, &value, sizeof(value)); -} - -void LDM_writeLE16(void *memPtr, U16 value) { - if (LDM_isLittleEndian()) { - LDM_write16(memPtr, value); - } else { - BYTE* p = (BYTE *)memPtr; - p[0] = (BYTE) value; - p[1] = (BYTE)(value>>8); - } -} - -U32 LDM_read32(const void *ptr) { - return *(const U32 *)ptr; -} - -U64 LDM_read64(const void *ptr) { - return *(const U64 *)ptr; -} - -void LDM_copy8(void *dst, const void *src) { - memcpy(dst, src, 8); -} - -BYTE LDM_readByte(const void *memPtr) { - BYTE val; - memcpy(&val, memPtr, 1); - return val; -} - diff --git a/contrib/long_distance_matching/versions/v0.5/util.h b/contrib/long_distance_matching/versions/v0.5/util.h deleted file mode 100644 index d1c3c999b..000000000 --- a/contrib/long_distance_matching/versions/v0.5/util.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef LDM_UTIL_H -#define LDM_UTIL_H - -unsigned LDM_isLittleEndian(void); - -uint16_t LDM_read16(const void *memPtr); - -uint16_t LDM_readLE16(const void *memPtr); - -void LDM_write16(void *memPtr, uint16_t value); - -void LDM_write32(void *memPtr, uint32_t value); - -void LDM_writeLE16(void *memPtr, uint16_t value); - -uint32_t LDM_read32(const void *ptr); - -uint64_t LDM_read64(const void *ptr); - -void LDM_copy8(void *dst, const void *src); - -uint8_t LDM_readByte(const void *ptr); - - -#endif /* LDM_UTIL_H */ From 6733c0777ccf277cd5b41b0f0dac78a9cdb24c3e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 13 Jul 2017 15:34:44 -0700 Subject: [PATCH 142/318] updated NEWS regarding #760 --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 457105070..68539d759 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,6 @@ v1.3.1 perf: substantially decreased memory usage in Multi-threading mode, thanks to reports by Tino Reichardt +perf: Multi-threading supports up to 256 threads. Cap at 256 when more are requested (#760) build: fix Visual compilation for non x86/x64 targets, reported by Greg Slazinski (#718) API exp : breaking change : ZSTD_getframeHeader() provides more information From 175a6c602928cd1277d961271b7d782d496520eb Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 13 Jul 2017 16:16:31 -0700 Subject: [PATCH 143/318] [ldm] Minor refactoring --- contrib/long_distance_matching/ldm.c | 30 ++++++++++++---------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index b8e8c63b2..437feb1cf 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -443,24 +443,19 @@ void LDM_outputBlock(LDM_CCtx *cctx, size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; + const BYTE *match; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); - // TODO: loop condition is not accurate. - while (1) { - const BYTE *match; - - /** - * 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. - */ - if (LDM_findBestMatch(&cctx, &match) != 0) { - goto _last_literals; - } + /** + * 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) == 0) { #ifdef COMPUTE_STATS cctx.stats.numMatches++; #endif @@ -485,6 +480,8 @@ size_t LDM_compress(const void *src, size_t srcSize, cctx.ip + LDM_MIN_MATCH_LENGTH, match + LDM_MIN_MATCH_LENGTH, cctx.ihashLimit); + LDM_outputBlock(&cctx, literalLength, offset, matchLength); + #ifdef COMPUTE_STATS cctx.stats.totalLiteralLength += literalLength; cctx.stats.totalOffset += offset; @@ -494,7 +491,6 @@ size_t LDM_compress(const void *src, size_t srcSize, cctx.stats.maxOffset = offset > cctx.stats.maxOffset ? offset : cctx.stats.maxOffset; #endif - LDM_outputBlock(&cctx, literalLength, offset, matchLength); // Move ip to end of block, inserting hashes at each position. cctx.nextIp = cctx.ip + cctx.step; @@ -514,10 +510,10 @@ size_t LDM_compress(const void *src, size_t srcSize, cctx.anchor = cctx.ip; LDM_updateLastHashFromNextHash(&cctx); } -_last_literals: + /* Encode the last literals (no more matches). */ { - const size_t lastRun = (size_t)(cctx.iend - cctx.anchor); + const size_t lastRun = cctx.iend - cctx.anchor; BYTE *pToken = cctx.op++; LDM_encodeLiteralLengthAndLiterals(&cctx, pToken, lastRun); } @@ -527,7 +523,7 @@ _last_literals: LDM_outputHashtableOccupancy(cctx.hashTable, LDM_HASHTABLESIZE_U32); #endif - return (cctx.op - (const BYTE *)cctx.obase); + return cctx.op - cctx.obase; } struct LDM_DCtx { From 0c8b9436b78e5192650ba9d50e1c52e2f0110aec Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 13 Jul 2017 16:38:20 -0700 Subject: [PATCH 144/318] removed goto statements for the most part --- contrib/adaptive-compression/adapt.c | 116 ++++++++++++++++----------- 1 file changed, 69 insertions(+), 47 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index c2160714d..addd0c59d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -92,6 +92,11 @@ typedef struct { ZSTD_CCtx* cctx; } adaptCCtx; +typedef struct { + FILE* srcFile; + adaptCCtx* ctx; +} fcResources; + static void freeCompressionJobs(adaptCCtx* ctx) { unsigned u; @@ -207,6 +212,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) static void waitUntilAllJobsCompleted(adaptCCtx* ctx) { + if (!ctx) return; pthread_mutex_lock(&ctx->allJobsCompleted_mutex); while (ctx->allJobsCompleted == 0) { pthread_cond_wait(&ctx->allJobsCompleted_cond, &ctx->allJobsCompleted_mutex); @@ -466,40 +472,10 @@ static void printStats(cStat_t stats) DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite); } -static int compressFilename(const char* const srcFilename, const char* const dstFilenameOrNull) +static int performCompression(adaptCCtx* ctx, FILE* const srcFile) { - unsigned const stdinUsed = !strcmp(srcFilename, stdinmark); - FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb"); - const char* const outFilenameIntermediate = (stdinUsed && !dstFilenameOrNull) ? stdoutmark : dstFilenameOrNull; - const char* outFilename = outFilenameIntermediate; - char fileAndSuffix[MAX_PATH]; - size_t const numJobs = MAX_NUM_JOBS; - int ret = 0; - adaptCCtx* ctx = NULL; - UTIL_getTime(&g_startTime); - g_streamedSize = 0; - - if (!outFilenameIntermediate) { - if (snprintf(fileAndSuffix, MAX_PATH, "%s.zst", srcFilename) + 1 > MAX_PATH) { - DISPLAY("Error: output filename is too long\n"); - ret = 1; - goto cleanup; - } - outFilename = fileAndSuffix; - } - - /* checking for errors */ - if (!srcFilename || !outFilename || !srcFile) { - DISPLAY("Error: initial variables could not be allocated\n"); - ret = 1; - goto cleanup; - } - - /* creating context */ - ctx = createCCtx(numJobs, outFilename); - if (ctx == NULL) { - ret = 1; - goto cleanup; + if (!ctx || !srcFile) { + return 1; } /* create output thread */ @@ -507,8 +483,8 @@ static int compressFilename(const char* const srcFilename, const char* const dst pthread_t out; if (pthread_create(&out, NULL, &outputThread, ctx)) { DISPLAY("Error: could not create output thread\n"); - ret = 1; - goto cleanup; + ctx->threadError = 1; + return 1; } } @@ -517,8 +493,8 @@ static int compressFilename(const char* const srcFilename, const char* const dst pthread_t compression; if (pthread_create(&compression, NULL, &compressionThread, ctx)) { DISPLAY("Error: could not create compression thread\n"); - ret = 1; - goto cleanup; + ctx->threadError = 1; + return 1; } } @@ -528,8 +504,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); ctx->threadError = 1; - ret = 1; - goto cleanup; + return 1; } g_streamedSize += readSize; /* reading was fine, now create the compression job */ @@ -537,9 +512,8 @@ static int compressFilename(const char* const srcFilename, const char* const dst int const last = feof(srcFile); int const error = createCompressionJob(ctx, readSize, last); if (error != 0) { - ret = error; ctx->threadError = 1; - goto cleanup; + return error; } } if (feof(srcFile)) { @@ -548,12 +522,60 @@ static int compressFilename(const char* const srcFilename, const char* const dst } } -cleanup: - waitUntilAllJobsCompleted(ctx); - if (g_displayStats) printStats(ctx->stats); - /* file compression completed */ - ret |= (srcFile != NULL) ? fclose(srcFile) : 0; - ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; + /* success -- created all jobs */ + return 0; +} + +static fcResources createFileCompressionResources(const char* const srcFilename, const char* const dstFilenameOrNull) +{ + fcResources fcr; + unsigned const stdinUsed = !strcmp(srcFilename, stdinmark); + FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb"); + const char* const outFilenameIntermediate = (stdinUsed && !dstFilenameOrNull) ? stdoutmark : dstFilenameOrNull; + const char* outFilename = outFilenameIntermediate; + char fileAndSuffix[MAX_PATH]; + size_t const numJobs = MAX_NUM_JOBS; + + memset(&fcr, 0, sizeof(fcr)); + + if (!outFilenameIntermediate) { + if (snprintf(fileAndSuffix, MAX_PATH, "%s.zst", srcFilename) + 1 > MAX_PATH) { + DISPLAY("Error: output filename is too long\n"); + return fcr; + } + outFilename = fileAndSuffix; + } + + /* checking for errors */ + if (!outFilename || !srcFile) { + DISPLAY("Error: initial variables could not be allocated\n"); + return fcr; + } + + /* creating context */ + fcr.ctx = createCCtx(numJobs, outFilename); + fcr.srcFile = srcFile; + return fcr; +} + +static int freeFileCompressionResources(fcResources* fcr) +{ + int ret = 0; + waitUntilAllJobsCompleted(fcr->ctx); + if (g_displayStats) printStats(fcr->ctx->stats); + ret |= (fcr->srcFile != NULL) ? fclose(fcr->srcFile) : 0; + ret |= (fcr->ctx != NULL) ? freeCCtx(fcr->ctx) : 0; + return ret; +} + +static int compressFilename(const char* const srcFilename, const char* const dstFilenameOrNull) +{ + int ret = 0; + UTIL_getTime(&g_startTime); + g_streamedSize = 0; + fcResources fcr = createFileCompressionResources(srcFilename, dstFilenameOrNull); + ret |= performCompression(fcr.ctx, fcr.srcFile); + ret |= freeFileCompressionResources(&fcr); return ret; } From 2bd6440be0b36304722b20a6fcc7052c197fd33d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 13 Jul 2017 17:12:16 -0700 Subject: [PATCH 145/318] pinned down error code enum values Note : all error codes are changed by this new version, but it's expected to be the last change for existing codes. Codes are now grouped by category, and receive a manually attributed value. The objective is to guarantee that error code values will not change in the future when introducing new codes. Intentionnal empty spaces and ranges are defined in order to keep room for potential new codes. --- NEWS | 1 + lib/common/error_private.c | 10 +++--- lib/common/zstd_errors.h | 54 +++++++++++++++----------------- lib/compress/zstd_compress.c | 23 +++++++------- lib/compress/zstdmt_compress.c | 2 +- lib/decompress/zstd_decompress.c | 2 +- lib/dictBuilder/zdict.c | 2 +- lib/legacy/zstd_v04.c | 2 +- lib/legacy/zstd_v05.c | 2 +- lib/legacy/zstd_v06.c | 2 +- 10 files changed, 49 insertions(+), 51 deletions(-) diff --git a/NEWS b/NEWS index 68539d759..b3c2613fe 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ perf: substantially decreased memory usage in Multi-threading mode, thanks to re perf: Multi-threading supports up to 256 threads. Cap at 256 when more are requested (#760) build: fix Visual compilation for non x86/x64 targets, reported by Greg Slazinski (#718) API exp : breaking change : ZSTD_getframeHeader() provides more information +API exp : breaking change : pinned down values of error codes v1.3.0 cli : new : `--list` command, by Paul Cruz diff --git a/lib/common/error_private.c b/lib/common/error_private.c index 2d752cd23..c3a386214 100644 --- a/lib/common/error_private.c +++ b/lib/common/error_private.c @@ -20,19 +20,17 @@ const char* ERR_getErrorString(ERR_enum code) case PREFIX(GENERIC): return "Error (generic)"; case PREFIX(prefix_unknown): return "Unknown frame descriptor"; case PREFIX(version_unsupported): return "Version not supported"; - case PREFIX(parameter_unknown): return "Unknown parameter type"; case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter"; - case PREFIX(frameParameter_unsupportedBy32bits): return "Frame parameter unsupported in 32-bits mode"; case PREFIX(frameParameter_windowTooLarge): return "Frame requires too much memory for decoding"; - case PREFIX(compressionParameter_unsupported): return "Compression parameter is not supported"; - case PREFIX(compressionParameter_outOfBound): return "Compression parameter is out of bound"; + case PREFIX(corruption_detected): return "Corrupted block detected"; + case PREFIX(checksum_wrong): return "Restored data doesn't match checksum"; + case PREFIX(parameter_unsupported): return "Unsupported parameter"; + 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(stage_wrong): return "Operation not authorized at current processing stage"; case PREFIX(dstSize_tooSmall): return "Destination buffer is too small"; case PREFIX(srcSize_wrong): return "Src size is incorrect"; - case PREFIX(corruption_detected): return "Corrupted block detected"; - case PREFIX(checksum_wrong): return "Restored data doesn't match checksum"; case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported"; case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large"; case PREFIX(maxSymbolValue_tooSmall): return "Specified maxSymbolValue is too small"; diff --git a/lib/common/zstd_errors.h b/lib/common/zstd_errors.h index 19f1597aa..fbc13d19f 100644 --- a/lib/common/zstd_errors.h +++ b/lib/common/zstd_errors.h @@ -37,43 +37,41 @@ extern "C" { /*-**************************************** * error codes list * note : this API is still considered unstable - * it should not be used with a dynamic library + * and shall not be used with a dynamic library. * only static linking is allowed ******************************************/ typedef enum { - ZSTD_error_no_error, - ZSTD_error_GENERIC, - ZSTD_error_prefix_unknown, - ZSTD_error_version_unsupported, - ZSTD_error_parameter_unknown, - ZSTD_error_frameParameter_unsupported, - ZSTD_error_frameParameter_unsupportedBy32bits, - ZSTD_error_frameParameter_windowTooLarge, - ZSTD_error_compressionParameter_unsupported, - ZSTD_error_compressionParameter_outOfBound, - ZSTD_error_init_missing, - ZSTD_error_memory_allocation, - ZSTD_error_stage_wrong, - ZSTD_error_dstSize_tooSmall, - ZSTD_error_srcSize_wrong, - ZSTD_error_corruption_detected, - ZSTD_error_checksum_wrong, - ZSTD_error_tableLog_tooLarge, - ZSTD_error_maxSymbolValue_tooLarge, - ZSTD_error_maxSymbolValue_tooSmall, - ZSTD_error_dictionary_corrupted, - ZSTD_error_dictionary_wrong, - ZSTD_error_dictionaryCreation_failed, - ZSTD_error_frameIndex_tooLarge, - ZSTD_error_seekableIO, - ZSTD_error_maxCode + ZSTD_error_no_error = 0, + ZSTD_error_GENERIC = 1, + ZSTD_error_prefix_unknown = 10, + ZSTD_error_version_unsupported = 12, + ZSTD_error_frameParameter_unsupported = 14, + ZSTD_error_frameParameter_windowTooLarge = 16, + ZSTD_error_corruption_detected = 20, + ZSTD_error_checksum_wrong = 22, + ZSTD_error_dictionary_corrupted = 30, + ZSTD_error_dictionary_wrong = 32, + ZSTD_error_dictionaryCreation_failed = 34, + ZSTD_error_parameter_unsupported = 40, + ZSTD_error_parameter_outOfBound = 42, + ZSTD_error_tableLog_tooLarge = 44, + ZSTD_error_maxSymbolValue_tooLarge = 46, + ZSTD_error_maxSymbolValue_tooSmall = 48, + ZSTD_error_stage_wrong = 60, + ZSTD_error_init_missing = 62, + ZSTD_error_memory_allocation = 64, + ZSTD_error_dstSize_tooSmall = 70, + ZSTD_error_srcSize_wrong = 72, + ZSTD_error_frameIndex_tooLarge = 100, + ZSTD_error_seekableIO = 102, + ZSTD_error_maxCode = 120 /* never EVER use this value directly, it may change in future versions! Use ZSTD_isError() instead */ } ZSTD_ErrorCode; /*! ZSTD_getErrorCode() : convert a `size_t` function result into a `ZSTD_ErrorCode` enum type, which can be used to compare with enum list published above */ ZSTDERRORLIB_API ZSTD_ErrorCode ZSTD_getErrorCode(size_t functionResult); -ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); +ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); /*< Same as ZSTD_getErrorName, but using a `ZSTD_ErrorCode` enum argument */ #if defined (__cplusplus) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b362a1919..4ccb7ce6b 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -214,7 +214,7 @@ size_t ZSTD_setCCtxParameter(ZSTD_CCtx* cctx, ZSTD_CCtxParameter param, unsigned ZSTD_STATIC_ASSERT(ZSTD_dm_auto==0); ZSTD_STATIC_ASSERT(ZSTD_dm_rawContent==1); case ZSTD_p_forceRawDict : cctx->dictMode = (ZSTD_dictMode_e)(value>0); return 0; - default: return ERROR(parameter_unknown); + default: return ERROR(parameter_unsupported); } } @@ -228,9 +228,9 @@ static void ZSTD_cLevelToCParams(ZSTD_CCtx* cctx) cctx->compressionLevel = ZSTD_CLEVEL_CUSTOM; } -#define CLAMPCHECK(val,min,max) { \ - if (((val)<(min)) | ((val)>(max))) { \ - return ERROR(compressionParameter_outOfBound); \ +#define CLAMPCHECK(val,min,max) { \ + if (((val)<(min)) | ((val)>(max))) { \ + return ERROR(parameter_outOfBound); \ } } size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned value) @@ -326,7 +326,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v /* restrict dictionary mode, to "rawContent" or "fullDict" only */ ZSTD_STATIC_ASSERT((U32)ZSTD_dm_fullDict > (U32)ZSTD_dm_rawContent); if (value > (unsigned)ZSTD_dm_fullDict) - return ERROR(compressionParameter_outOfBound); + return ERROR(parameter_outOfBound); cctx->dictMode = (ZSTD_dictMode_e)value; return 0; @@ -347,11 +347,11 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v if (value==0) return 0; DEBUGLOG(5, " setting nbThreads : %u", value); #ifndef ZSTD_MULTITHREAD - if (value > 1) return ERROR(compressionParameter_unsupported); + if (value > 1) return ERROR(parameter_unsupported); #endif if ((value>1) && (cctx->nbThreads != value)) { if (cctx->staticSize) /* MT not compatible with static alloc */ - return ERROR(compressionParameter_unsupported); + return ERROR(parameter_unsupported); ZSTDMT_freeCCtx(cctx->mtctx); cctx->nbThreads = 1; cctx->mtctx = ZSTDMT_createCCtx_advanced(value, cctx->customMem); @@ -361,17 +361,17 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v return 0; case ZSTD_p_jobSize: - if (cctx->nbThreads <= 1) return ERROR(compressionParameter_unsupported); + if (cctx->nbThreads <= 1) return ERROR(parameter_unsupported); assert(cctx->mtctx != NULL); return ZSTDMT_setMTCtxParameter(cctx->mtctx, ZSTDMT_p_sectionSize, value); case ZSTD_p_overlapSizeLog: DEBUGLOG(5, " setting overlap with nbThreads == %u", cctx->nbThreads); - if (cctx->nbThreads <= 1) return ERROR(compressionParameter_unsupported); + if (cctx->nbThreads <= 1) return ERROR(parameter_unsupported); assert(cctx->mtctx != NULL); return ZSTDMT_setMTCtxParameter(cctx->mtctx, ZSTDMT_p_overlapSectionLog, value); - default: return ERROR(parameter_unknown); + default: return ERROR(parameter_unsupported); } } @@ -451,7 +451,8 @@ size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams) 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.strategy) > (U32)ZSTD_btultra) return ERROR(compressionParameter_unsupported); + if ((U32)(cParams.strategy) > (U32)ZSTD_btultra) + return ERROR(parameter_unsupported); return 0; } diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 3be850c6d..ccc20c78a 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -496,7 +496,7 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, mtctx->overlapLog = (value >= 9) ? 9 : value; return 0; default : - return ERROR(compressionParameter_unsupported); + return ERROR(parameter_unsupported); } } diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 4e96504ef..92e80c1ac 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2227,7 +2227,7 @@ size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, { switch(paramType) { - default : return ERROR(parameter_unknown); + default : return ERROR(parameter_unsupported); case DStream_p_maxWindowSize : zds->maxWindowSize = paramValue ? paramValue : (U32)(-1); break; } return 0; diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 742586eac..113d205fc 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -695,7 +695,7 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, DISPLAYLEVEL(1, "Not enough memory \n"); goto _cleanup; } - if (offcodeMax>OFFCODE_MAX) { eSize = ERROR(dictionary_wrong); goto _cleanup; } /* too large dictionary */ + if (offcodeMax>OFFCODE_MAX) { eSize = ERROR(dictionaryCreation_failed); goto _cleanup; } /* too large dictionary */ for (u=0; u<256; u++) countLit[u] = 1; /* any character must be described */ for (u=0; u<=offcodeMax; u++) offcodeCount[u] = 1; for (u=0; u<=MaxML; u++) matchLengthCount[u] = 1; diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c index 8b8e23cb0..2ba75a875 100644 --- a/lib/legacy/zstd_v04.c +++ b/lib/legacy/zstd_v04.c @@ -2776,7 +2776,7 @@ static size_t ZSTD_decodeFrameHeader_Part2(ZSTD_DCtx* zc, const void* src, size_ size_t result; if (srcSize != zc->headerSize) return ERROR(srcSize_wrong); result = ZSTD_getFrameParams(&(zc->params), src, srcSize); - if ((MEM_32bits()) && (zc->params.windowLog > 25)) return ERROR(frameParameter_unsupportedBy32bits); + if ((MEM_32bits()) && (zc->params.windowLog > 25)) return ERROR(frameParameter_unsupported); return result; } diff --git a/lib/legacy/zstd_v05.c b/lib/legacy/zstd_v05.c index e929618a3..bd3e8c113 100644 --- a/lib/legacy/zstd_v05.c +++ b/lib/legacy/zstd_v05.c @@ -2888,7 +2888,7 @@ static size_t ZSTDv05_decodeFrameHeader_Part2(ZSTDv05_DCtx* zc, const void* src, if (srcSize != zc->headerSize) return ERROR(srcSize_wrong); result = ZSTDv05_getFrameParams(&(zc->params), src, srcSize); - if ((MEM_32bits()) && (zc->params.windowLog > 25)) return ERROR(frameParameter_unsupportedBy32bits); + if ((MEM_32bits()) && (zc->params.windowLog > 25)) return ERROR(frameParameter_unsupported); return result; } diff --git a/lib/legacy/zstd_v06.c b/lib/legacy/zstd_v06.c index 26f0929da..3fb47c2bf 100644 --- a/lib/legacy/zstd_v06.c +++ b/lib/legacy/zstd_v06.c @@ -3084,7 +3084,7 @@ size_t ZSTDv06_getFrameParams(ZSTDv06_frameParams* fparamsPtr, const void* src, static size_t ZSTDv06_decodeFrameHeader(ZSTDv06_DCtx* zc, const void* src, size_t srcSize) { size_t const result = ZSTDv06_getFrameParams(&(zc->fParams), src, srcSize); - if ((MEM_32bits()) && (zc->fParams.windowLog > 25)) return ERROR(frameParameter_unsupportedBy32bits); + if ((MEM_32bits()) && (zc->fParams.windowLog > 25)) return ERROR(frameParameter_unsupported); return result; } From 3b0cff3c33724c9dd538a2c1a3f326a879a438f8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 13 Jul 2017 18:58:30 -0700 Subject: [PATCH 146/318] fixed clang's -Wdocumentation --- lib/common/zstd_errors.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/zstd_errors.h b/lib/common/zstd_errors.h index fbc13d19f..a645a9e8e 100644 --- a/lib/common/zstd_errors.h +++ b/lib/common/zstd_errors.h @@ -71,7 +71,7 @@ typedef enum { convert a `size_t` function result into a `ZSTD_ErrorCode` enum type, which can be used to compare with enum list published above */ ZSTDERRORLIB_API ZSTD_ErrorCode ZSTD_getErrorCode(size_t functionResult); -ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); /*< Same as ZSTD_getErrorName, but using a `ZSTD_ErrorCode` enum argument */ +ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); /**< Same as ZSTD_getErrorName, but using a `ZSTD_ErrorCode` enum argument */ #if defined (__cplusplus) From 4db7f12ef3a3466684a786de4f61b8ad8ef6950e Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Fri, 14 Jul 2017 10:52:03 -0700 Subject: [PATCH 147/318] Add offset histogram --- contrib/long_distance_matching/Makefile | 2 +- contrib/long_distance_matching/ldm.c | 41 +++++++++++++++++++++-- contrib/long_distance_matching/ldm.h | 6 ++-- contrib/long_distance_matching/main-ldm.c | 2 +- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 8ba16d03d..cff786442 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -27,7 +27,7 @@ default: all all: main-ldm -main-ldm : ldm.c main-ldm.c +main-ldm : ldm.h ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 437feb1cf..186fa08eb 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -15,7 +15,7 @@ #define RUN_MASK ((1U<>= 1) { + ret++; + } + return ret; +} + void LDM_printCompressStats(const LDM_compressStats *stats) { + int i = 0; printf("=====================\n"); printf("Compression statistics\n"); //TODO: compute percentage matched? @@ -107,11 +131,22 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { ((double)stats->totalOffset) / (double)stats->numMatches); printf("min offset, max offset: %u %u\n", stats->minOffset, stats->maxOffset); + + printf("\n"); + printf("offset histogram\n"); + for (; i <= intLog2(stats->maxOffset); i++) { + printf("2^%*d: %10u\n", 2, i, stats->offsetHistogram[i]); + } + printf("\n"); + + printf("num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", stats->numCollisions, stats->numHashInserts, stats->numHashInserts == 0 ? 1.0 : (100.0 * (double)stats->numCollisions) / (double)stats->numHashInserts); + printf("=====================\n"); + } int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { @@ -145,7 +180,7 @@ int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { * of the hash table. */ static hash_t checksumToHash(U32 sum) { - return ((sum * 2654435761U) >> ((32)-LDM_HASHLOG)); + return ((sum * 2654435761U) >> (32 - LDM_HASHLOG)); } /** @@ -490,6 +525,7 @@ size_t LDM_compress(const void *src, size_t srcSize, 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)]++; #endif // Move ip to end of block, inserting hashes at each position. @@ -607,7 +643,6 @@ size_t LDM_decompress(const void *src, size_t compressedSize, // TODO: implement and test hash function void LDM_test(void) { - } /* diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 5da3c3b99..0e54faa70 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -11,7 +11,7 @@ #define LDM_OFFSET_SIZE 4 // Defines the size of the hash table. -#define LDM_MEMORY_USAGE 22 +#define LDM_MEMORY_USAGE 20 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) @@ -19,8 +19,8 @@ #define WINDOW_SIZE (1 << 25) //These should be multiples of four. -#define LDM_MIN_MATCH_LENGTH 8 -#define LDM_HASH_LENGTH 8 +#define LDM_MIN_MATCH_LENGTH 4 +#define LDM_HASH_LENGTH 4 typedef U32 offset_t; typedef U32 hash_t; diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 40afef8c2..ea6375ba7 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -13,7 +13,7 @@ #include "zstd.h" #define DEBUG -//#define TEST +#define TEST /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. From 55f960e8db9e3df9322ae9c973c699f671f51c90 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Fri, 14 Jul 2017 11:00:20 -0700 Subject: [PATCH 148/318] Add percentages to offset histogram --- contrib/long_distance_matching/ldm.c | 5 ++++- contrib/long_distance_matching/ldm.h | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 186fa08eb..d935a2bdf 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -135,7 +135,10 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { printf("\n"); printf("offset histogram\n"); for (; i <= intLog2(stats->maxOffset); i++) { - printf("2^%*d: %10u\n", 2, i, stats->offsetHistogram[i]); + printf("2^%*d: %10u %6.3f%%\n", 2, i, + stats->offsetHistogram[i], + 100.0 * (double) stats->offsetHistogram[i] / + (double)stats->numMatches); } printf("\n"); diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 0e54faa70..3c8c04ecc 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -19,8 +19,8 @@ #define WINDOW_SIZE (1 << 25) //These should be multiples of four. -#define LDM_MIN_MATCH_LENGTH 4 -#define LDM_HASH_LENGTH 4 +#define LDM_MIN_MATCH_LENGTH 1024 +#define LDM_HASH_LENGTH 1024 typedef U32 offset_t; typedef U32 hash_t; From 2d8e6c6608bc0de2d24a40ad6fe7a2fb8e8377bf Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Fri, 14 Jul 2017 12:31:01 -0700 Subject: [PATCH 149/318] Add more statistics --- contrib/long_distance_matching/ldm.c | 61 ++++++++++++++++++++++------ contrib/long_distance_matching/ldm.h | 19 ++++++--- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index d935a2bdf..c9c6a7096 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -37,6 +37,7 @@ typedef struct LDM_hashTable { // TODO: Scanning speed // TODO: Memory usage struct LDM_compressStats { + U32 windowSizeLog, hashTableSizeLog; U32 numMatches; U64 totalMatchLength; U64 totalLiteralLength; @@ -73,7 +74,9 @@ struct LDM_CCtx { LDM_compressStats stats; /* Compression statistics */ - LDM_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; + LDM_hashEntry *hashTable; + +// LDM_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; const BYTE *lastPosHashed; /* Last position hashed */ hash_t lastHash; /* Hash corresponding to lastPosHashed */ @@ -90,7 +93,7 @@ struct LDM_CCtx { const BYTE *DEBUG_setNextHash; }; -void LDM_outputHashtableOccupancy( +void LDM_outputHashTableOccupancy( const LDM_hashEntry *hashTable, U32 hashTableSize) { U32 i = 0; U32 ctr = 0; @@ -104,9 +107,8 @@ void LDM_outputHashtableOccupancy( 100.0 * (double)(ctr) / (double)hashTableSize); } -// TODO: This can be done more efficienctly but is not that important as it -// is only used for computing stats. -// +// TODO: This can be done more efficiently (but it is not that important as it +// is only used for computing stats). static int intLog2(U32 x) { int ret = 0; while (x >>= 1) { @@ -115,30 +117,57 @@ static int intLog2(U32 x) { return ret; } +// TODO: Maybe we would eventually prefer to have linear rather than +// exponential buckets. +void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx) { + int i = 0; + int buckets[32] = { 0 }; + + printf("\n"); + printf("Hash table histogram\n"); + for (; i < LDM_HASHTABLESIZE_U32; i++) { + int offset = (cctx->ip - cctx->ibase) - cctx->hashTable[i].offset; + buckets[intLog2(offset)]++; + } + + i = 0; + for (; i < 32; i++) { + printf("2^%*d: %10u %6.3f%%\n", 2, i, + buckets[i], + 100.0 * (double) buckets[i] / + (double) LDM_HASHTABLESIZE_U32); + } + printf("\n"); +} + void LDM_printCompressStats(const LDM_compressStats *stats) { int i = 0; printf("=====================\n"); printf("Compression statistics\n"); //TODO: compute percentage matched? + printf("Window size, hash table size (bytes): 2^%u, 2^%u\n", + stats->windowSizeLog, stats->hashTableSizeLog); printf("num matches, total match length: %u, %llu\n", stats->numMatches, stats->totalMatchLength); printf("avg match length: %.1f\n", ((double)stats->totalMatchLength) / (double)stats->numMatches); - printf("avg literal length: %.1f\n", - ((double)stats->totalLiteralLength) / (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", + printf("min offset, max offset: %u, %u\n", stats->minOffset, stats->maxOffset); printf("\n"); - printf("offset histogram\n"); + printf("offset histogram: offset, num matches, %% of matches\n"); + for (; i <= intLog2(stats->maxOffset); i++) { printf("2^%*d: %10u %6.3f%%\n", 2, i, stats->offsetHistogram[i], 100.0 * (double) stats->offsetHistogram[i] / - (double)stats->numMatches); + (double) stats->numMatches); } printf("\n"); @@ -379,8 +408,12 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->anchor = cctx->ibase; memset(&(cctx->stats), 0, sizeof(cctx->stats)); - memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); + cctx->hashTable = calloc(LDM_HASHTABLESIZE_U32, sizeof(LDM_hashEntry)); +// memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); cctx->stats.minOffset = UINT_MAX; + cctx->stats.windowSizeLog = LDM_WINDOW_SIZE_LOG; + cctx->stats.hashTableSizeLog = LDM_MEMORY_USAGE; + cctx->lastPosHashed = NULL; @@ -417,7 +450,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { *match = getPositionOnHash(cctx, h); putHashOfCurrentPositionFromHash(cctx, h, sum); - } while (cctx->ip - *match > WINDOW_SIZE || + } while (cctx->ip - *match > LDM_WINDOW_SIZE || !LDM_isValidMatch(cctx->ip, *match)); setNextHash(cctx); return 0; @@ -550,6 +583,8 @@ size_t LDM_compress(const void *src, size_t srcSize, LDM_updateLastHashFromNextHash(&cctx); } + // LDM_outputHashTableOffsetHistogram(&cctx); + /* Encode the last literals (no more matches). */ { const size_t lastRun = cctx.iend - cctx.anchor; @@ -559,7 +594,7 @@ size_t LDM_compress(const void *src, size_t srcSize, #ifdef COMPUTE_STATS LDM_printCompressStats(&cctx.stats); - LDM_outputHashtableOccupancy(cctx.hashTable, LDM_HASHTABLESIZE_U32); + LDM_outputHashTableOccupancy(cctx.hashTable, LDM_HASHTABLESIZE_U32); #endif return cctx.op - cctx.obase; diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 3c8c04ecc..874443593 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -11,16 +11,17 @@ #define LDM_OFFSET_SIZE 4 // Defines the size of the hash table. -#define LDM_MEMORY_USAGE 20 +#define LDM_MEMORY_USAGE 16 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) -#define WINDOW_SIZE (1 << 25) +#define LDM_WINDOW_SIZE_LOG 25 +#define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) //These should be multiples of four. -#define LDM_MIN_MATCH_LENGTH 1024 -#define LDM_HASH_LENGTH 1024 +#define LDM_MIN_MATCH_LENGTH 4 +#define LDM_HASH_LENGTH 4 typedef U32 offset_t; typedef U32 hash_t; @@ -70,9 +71,17 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, * Prints the percentage of the hash table occupied (where occupied is defined * as the entry being non-zero). */ -void LDM_outputHashtableOccupancy(const LDM_hashEntry *hashTable, +void LDM_outputHashTableOccupancy(const LDM_hashEntry *hashTable, U32 hashTableSize); +/** + * Prints the distribution of offsets in the hash table. + * + * The offsets are defined as the distance of the hash table entry from the + * current input position of the cctx. + */ +void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx); + /** * Outputs compression statistics to stdout. */ From 6e443b4960091fb7b885b83372b1edf79f873564 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Fri, 14 Jul 2017 14:27:55 -0700 Subject: [PATCH 150/318] Move hash table access for own functions --- contrib/long_distance_matching/ldm.c | 77 ++++++---- contrib/long_distance_matching/ldm.h | 11 +- .../versions/v0.5/Makefile | 4 +- .../versions/v0.5/ldm.c | 133 ++++++++++++++---- .../versions/v0.5/ldm.h | 26 +++- .../versions/v0.5/main-ldm.c | 2 +- 6 files changed, 189 insertions(+), 64 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index c9c6a7096..08cb856c7 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -23,17 +23,23 @@ struct LDM_hashEntry { offset_t offset; }; -typedef struct LDM_hashTable { - U32 numEntries; - U32 minimumTagMask; // TODO: what if tag == offset? - - // Maximum number of elements in the table. - U32 limit; - +// TODO: move to its own file. +struct LDM_hashTable { + U32 size; LDM_hashEntry *entries; -} LDM_hashTable; +}; + +LDM_hashEntry *HASH_getHash( + const LDM_hashTable *table, const hash_t hash) { + return &(table->entries[hash]); +} + +void HASH_insert(LDM_hashTable *table, + const hash_t hash, const LDM_hashEntry entry) { + *HASH_getHash(table, hash) = entry; +} + -// TODO: Add offset histogram by powers of two // TODO: Scanning speed // TODO: Memory usage struct LDM_compressStats { @@ -74,7 +80,9 @@ struct LDM_CCtx { LDM_compressStats stats; /* Compression statistics */ - LDM_hashEntry *hashTable; + LDM_hashTable hashTable; + +// LDM_hashEntry *hashTable; // LDM_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; @@ -93,18 +101,19 @@ struct LDM_CCtx { const BYTE *DEBUG_setNextHash; }; -void LDM_outputHashTableOccupancy( - const LDM_hashEntry *hashTable, U32 hashTableSize) { + + +void LDM_outputHashTableOccupancy(const LDM_hashTable *hashTable) { U32 i = 0; U32 ctr = 0; - for (; i < hashTableSize; i++) { - if (hashTable[i].offset == 0) { + for (; i < hashTable->size; i++) { + if (HASH_getHash(hashTable, i)->offset == 0) { ctr++; } } printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", - hashTableSize, ctr, - 100.0 * (double)(ctr) / (double)hashTableSize); + hashTable->size, ctr, + 100.0 * (double)(ctr) / (double)hashTable->size); } // TODO: This can be done more efficiently (but it is not that important as it @@ -120,13 +129,14 @@ static int intLog2(U32 x) { // TODO: Maybe we would eventually prefer to have linear rather than // exponential buckets. void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx) { - int i = 0; + U32 i = 0; int buckets[32] = { 0 }; printf("\n"); printf("Hash table histogram\n"); - for (; i < LDM_HASHTABLESIZE_U32; i++) { - int offset = (cctx->ip - cctx->ibase) - cctx->hashTable[i].offset; + for (; i < cctx->hashTable.size; i++) { + int offset = (cctx->ip - cctx->ibase) - + HASH_getHash(&cctx->hashTable, i)->offset; buckets[intLog2(offset)]++; } @@ -135,7 +145,7 @@ void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx) { printf("2^%*d: %10u %6.3f%%\n", 2, i, buckets[i], 100.0 * (double) buckets[i] / - (double) LDM_HASHTABLESIZE_U32); + (double) cctx->hashTable.size); } printf("\n"); } @@ -305,7 +315,7 @@ static void putHashOfCurrentPositionFromHash( LDM_CCtx *cctx, hash_t hash, U32 sum) { #ifdef COMPUTE_STATS if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { - offset_t offset = cctx->hashTable[hash].offset; + offset_t offset = HASH_getHash(&cctx->hashTable, hash)->offset; cctx->stats.numHashInserts++; if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { cctx->stats.numCollisions++; @@ -317,7 +327,7 @@ static void putHashOfCurrentPositionFromHash( // Note: this works only when cctx->step is 1. if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { const LDM_hashEntry entry = { cctx->ip - cctx->ibase }; - cctx->hashTable[hash] = entry; + HASH_insert(&cctx->hashTable, hash, entry); } cctx->lastPosHashed = cctx->ip; @@ -362,7 +372,7 @@ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { * Returns the position of the entry at hashTable[hash]. */ static const BYTE *getPositionOnHash(LDM_CCtx *cctx, hash_t hash) { - return cctx->hashTable[hash].offset + cctx->ibase; + return HASH_getHash(&cctx->hashTable, hash)->offset + cctx->ibase; } U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, @@ -389,6 +399,11 @@ void LDM_readHeader(const void *src, U64 *compressedSize, // ip += sizeof(U64); } +static void LDM_initializeHashTable(LDM_hashTable *table) { + table->size = LDM_HASHTABLESIZE_U32; + table->entries = calloc(LDM_HASHTABLESIZE_U32, sizeof(LDM_hashEntry)); +} + void LDM_initializeCCtx(LDM_CCtx *cctx, const void *src, size_t srcSize, void *dst, size_t maxDstSize) { @@ -408,7 +423,9 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->anchor = cctx->ibase; memset(&(cctx->stats), 0, sizeof(cctx->stats)); - cctx->hashTable = calloc(LDM_HASHTABLESIZE_U32, sizeof(LDM_hashEntry)); + + LDM_initializeHashTable(&cctx->hashTable); +// calloc(LDM_HASHTABLESIZE_U32, sizeof(LDM_hashEntry)); // memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); cctx->stats.minOffset = UINT_MAX; cctx->stats.windowSizeLog = LDM_WINDOW_SIZE_LOG; @@ -424,6 +441,10 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->DEBUG_setNextHash = 0; } +void LDM_destroyCCtx(LDM_CCtx *cctx) { + free((cctx->hashTable).entries); +} + /** * Finds the "best" match. * @@ -594,10 +615,14 @@ size_t LDM_compress(const void *src, size_t srcSize, #ifdef COMPUTE_STATS LDM_printCompressStats(&cctx.stats); - LDM_outputHashTableOccupancy(cctx.hashTable, LDM_HASHTABLESIZE_U32); + LDM_outputHashTableOccupancy(&cctx.hashTable); #endif - return cctx.op - cctx.obase; + { + const size_t ret = cctx.op - cctx.obase; + LDM_destroyCCtx(&cctx); + return ret; + } } struct LDM_DCtx { diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 874443593..8c3aa4e61 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -26,6 +26,7 @@ typedef U32 offset_t; typedef U32 hash_t; typedef struct LDM_hashEntry LDM_hashEntry; +typedef struct LDM_hashTable LDM_hashTable; typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; typedef struct LDM_DCtx LDM_DCtx; @@ -62,17 +63,23 @@ size_t LDM_compress(const void *src, size_t srcSize, /** * Initialize the compression context. + * + * Allocates memory for the hash table. */ void LDM_initializeCCtx(LDM_CCtx *cctx, const void *src, size_t srcSize, void *dst, size_t maxDstSize); +/** + * Frees up memory allocating in initializeCCtx + */ +void LDM_destroyCCtx(LDM_CCtx *cctx); + /** * Prints the percentage of the hash table occupied (where occupied is defined * as the entry being non-zero). */ -void LDM_outputHashTableOccupancy(const LDM_hashEntry *hashTable, - U32 hashTableSize); +void LDM_outputHashTableOccupancy(const LDM_hashTable *hashTable); /** * Prints the distribution of offsets in the hash table. diff --git a/contrib/long_distance_matching/versions/v0.5/Makefile b/contrib/long_distance_matching/versions/v0.5/Makefile index dee686bca..cff786442 100644 --- a/contrib/long_distance_matching/versions/v0.5/Makefile +++ b/contrib/long_distance_matching/versions/v0.5/Makefile @@ -9,7 +9,7 @@ # This Makefile presumes libzstd is installed, using `sudo make install` -CPPFLAGS+= -I../../../../lib/common +CPPFLAGS+= -I../../lib/common CFLAGS ?= -O3 DEBUGFLAGS = -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ @@ -27,7 +27,7 @@ default: all all: main-ldm -main-ldm : ldm.c main-ldm.c +main-ldm : ldm.h ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: diff --git a/contrib/long_distance_matching/versions/v0.5/ldm.c b/contrib/long_distance_matching/versions/v0.5/ldm.c index b8e8c63b2..06c97bc48 100644 --- a/contrib/long_distance_matching/versions/v0.5/ldm.c +++ b/contrib/long_distance_matching/versions/v0.5/ldm.c @@ -15,7 +15,7 @@ #define RUN_MASK ((1U<>= 1) { + ret++; + } + return ret; +} + +// TODO: Maybe we would eventually prefer to have linear rather than +// exponential buckets. +void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx) { + int i = 0; + int buckets[32] = { 0 }; + + printf("\n"); + printf("Hash table histogram\n"); + for (; i < LDM_HASHTABLESIZE_U32; i++) { + int offset = (cctx->ip - cctx->ibase) - cctx->hashTable[i].offset; + buckets[intLog2(offset)]++; + } + + i = 0; + for (; i < 32; i++) { + printf("2^%*d: %10u %6.3f%%\n", 2, i, + buckets[i], + 100.0 * (double) buckets[i] / + (double) LDM_HASHTABLESIZE_U32); + } + printf("\n"); +} + void LDM_printCompressStats(const LDM_compressStats *stats) { + int i = 0; printf("=====================\n"); printf("Compression statistics\n"); //TODO: compute percentage matched? + printf("Window size, hash table size (bytes): 2^%u, 2^%u\n", + stats->windowSizeLog, stats->hashTableSizeLog); printf("num matches, total match length: %u, %llu\n", stats->numMatches, stats->totalMatchLength); printf("avg match length: %.1f\n", ((double)stats->totalMatchLength) / (double)stats->numMatches); - printf("avg literal length: %.1f\n", - ((double)stats->totalLiteralLength) / (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", + printf("min offset, max offset: %u, %u\n", stats->minOffset, stats->maxOffset); + + printf("\n"); + printf("offset histogram: offset, num matches, %% of matches\n"); + + for (; i <= intLog2(stats->maxOffset); i++) { + printf("2^%*d: %10u %6.3f%%\n", 2, i, + stats->offsetHistogram[i], + 100.0 * (double) stats->offsetHistogram[i] / + (double) stats->numMatches); + } + printf("\n"); + + printf("num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", stats->numCollisions, stats->numHashInserts, stats->numHashInserts == 0 ? 1.0 : (100.0 * (double)stats->numCollisions) / (double)stats->numHashInserts); + printf("=====================\n"); + } int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { @@ -145,7 +212,7 @@ int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { * of the hash table. */ static hash_t checksumToHash(U32 sum) { - return ((sum * 2654435761U) >> ((32)-LDM_HASHLOG)); + return ((sum * 2654435761U) >> (32 - LDM_HASHLOG)); } /** @@ -341,8 +408,12 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->anchor = cctx->ibase; memset(&(cctx->stats), 0, sizeof(cctx->stats)); - memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); + cctx->hashTable = calloc(LDM_HASHTABLESIZE_U32, sizeof(LDM_hashEntry)); +// memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); cctx->stats.minOffset = UINT_MAX; + cctx->stats.windowSizeLog = LDM_WINDOW_SIZE_LOG; + cctx->stats.hashTableSizeLog = LDM_MEMORY_USAGE; + cctx->lastPosHashed = NULL; @@ -353,6 +424,10 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->DEBUG_setNextHash = 0; } +void LDM_destroyCCtx(LDM_CCtx *cctx) { + free(cctx->hashTable); +} + /** * Finds the "best" match. * @@ -379,7 +454,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { *match = getPositionOnHash(cctx, h); putHashOfCurrentPositionFromHash(cctx, h, sum); - } while (cctx->ip - *match > WINDOW_SIZE || + } while (cctx->ip - *match > LDM_WINDOW_SIZE || !LDM_isValidMatch(cctx->ip, *match)); setNextHash(cctx); return 0; @@ -443,24 +518,19 @@ void LDM_outputBlock(LDM_CCtx *cctx, size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; + const BYTE *match; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); - // TODO: loop condition is not accurate. - while (1) { - const BYTE *match; - - /** - * 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. - */ - if (LDM_findBestMatch(&cctx, &match) != 0) { - goto _last_literals; - } + /** + * 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) == 0) { #ifdef COMPUTE_STATS cctx.stats.numMatches++; #endif @@ -485,6 +555,8 @@ size_t LDM_compress(const void *src, size_t srcSize, cctx.ip + LDM_MIN_MATCH_LENGTH, match + LDM_MIN_MATCH_LENGTH, cctx.ihashLimit); + LDM_outputBlock(&cctx, literalLength, offset, matchLength); + #ifdef COMPUTE_STATS cctx.stats.totalLiteralLength += literalLength; cctx.stats.totalOffset += offset; @@ -493,8 +565,8 @@ size_t LDM_compress(const void *src, size_t srcSize, 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)]++; #endif - LDM_outputBlock(&cctx, literalLength, offset, matchLength); // Move ip to end of block, inserting hashes at each position. cctx.nextIp = cctx.ip + cctx.step; @@ -514,20 +586,26 @@ size_t LDM_compress(const void *src, size_t srcSize, cctx.anchor = cctx.ip; LDM_updateLastHashFromNextHash(&cctx); } -_last_literals: + + // LDM_outputHashTableOffsetHistogram(&cctx); + /* Encode the last literals (no more matches). */ { - const size_t lastRun = (size_t)(cctx.iend - cctx.anchor); + const size_t lastRun = cctx.iend - cctx.anchor; BYTE *pToken = cctx.op++; LDM_encodeLiteralLengthAndLiterals(&cctx, pToken, lastRun); } #ifdef COMPUTE_STATS LDM_printCompressStats(&cctx.stats); - LDM_outputHashtableOccupancy(cctx.hashTable, LDM_HASHTABLESIZE_U32); + LDM_outputHashTableOccupancy(cctx.hashTable, LDM_HASHTABLESIZE_U32); #endif - return (cctx.op - (const BYTE *)cctx.obase); + { + const size_t ret = cctx.op - cctx.obase; + LDM_destroyCCtx(&cctx); + return ret; + } } struct LDM_DCtx { @@ -611,7 +689,6 @@ size_t LDM_decompress(const void *src, size_t compressedSize, // TODO: implement and test hash function void LDM_test(void) { - } /* diff --git a/contrib/long_distance_matching/versions/v0.5/ldm.h b/contrib/long_distance_matching/versions/v0.5/ldm.h index 5da3c3b99..70cda8b84 100644 --- a/contrib/long_distance_matching/versions/v0.5/ldm.h +++ b/contrib/long_distance_matching/versions/v0.5/ldm.h @@ -11,16 +11,17 @@ #define LDM_OFFSET_SIZE 4 // Defines the size of the hash table. -#define LDM_MEMORY_USAGE 22 +#define LDM_MEMORY_USAGE 16 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) -#define WINDOW_SIZE (1 << 25) +#define LDM_WINDOW_SIZE_LOG 25 +#define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) //These should be multiples of four. -#define LDM_MIN_MATCH_LENGTH 8 -#define LDM_HASH_LENGTH 8 +#define LDM_MIN_MATCH_LENGTH 4 +#define LDM_HASH_LENGTH 4 typedef U32 offset_t; typedef U32 hash_t; @@ -61,18 +62,33 @@ size_t LDM_compress(const void *src, size_t srcSize, /** * Initialize the compression context. + * + * Allocates memory for the hash table. */ void LDM_initializeCCtx(LDM_CCtx *cctx, const void *src, size_t srcSize, void *dst, size_t maxDstSize); +/** + * Frees up memory allocating in initializeCCtx + */ +void LDM_destroyCCtx(LDM_CCtx *cctx); + /** * Prints the percentage of the hash table occupied (where occupied is defined * as the entry being non-zero). */ -void LDM_outputHashtableOccupancy(const LDM_hashEntry *hashTable, +void LDM_outputHashTableOccupancy(const LDM_hashEntry *hashTable, U32 hashTableSize); +/** + * Prints the distribution of offsets in the hash table. + * + * The offsets are defined as the distance of the hash table entry from the + * current input position of the cctx. + */ +void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx); + /** * Outputs compression statistics to stdout. */ diff --git a/contrib/long_distance_matching/versions/v0.5/main-ldm.c b/contrib/long_distance_matching/versions/v0.5/main-ldm.c index 40afef8c2..ea6375ba7 100644 --- a/contrib/long_distance_matching/versions/v0.5/main-ldm.c +++ b/contrib/long_distance_matching/versions/v0.5/main-ldm.c @@ -13,7 +13,7 @@ #include "zstd.h" #define DEBUG -//#define TEST +#define TEST /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. From 1ab3f06f0041b5a29eadaa2da81112424999f554 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 14 Jul 2017 16:29:29 -0700 Subject: [PATCH 151/318] updated tests to use different seeds when executing different tests --- .../adaptive-compression/test-correctness.sh | 78 +++++++++---------- .../adaptive-compression/test-performance.sh | 18 ++--- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/contrib/adaptive-compression/test-correctness.sh b/contrib/adaptive-compression/test-correctness.sh index e6ac6dfbb..b057cbd64 100755 --- a/contrib/adaptive-compression/test-correctness.sh +++ b/contrib/adaptive-compression/test-correctness.sh @@ -1,238 +1,238 @@ echo "correctness tests -- general" -./datagen -g1GB > tmp +./datagen -s1 -g1GB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g500MB > tmp +./datagen -s2 -g500MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g250MB > tmp +./datagen -s3 -g250MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g125MB > tmp +./datagen -s4 -g125MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g50MB > tmp +./datagen -s5 -g50MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g25MB > tmp +./datagen -s6 -g25MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s7 -g10MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g5MB > tmp +./datagen -s8 -g5MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g500KB > tmp +./datagen -s9 -g500KB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- streaming" -./datagen -g1GB > tmp +./datagen -s10 -g1GB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100MB > tmp +./datagen -s11 -g100MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s12 -g10MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g1MB > tmp +./datagen -s13 -g1MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100KB > tmp +./datagen -s14 -g100KB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10KB > tmp +./datagen -s15 -g10KB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- read limit" -./datagen -g1GB > tmp +./datagen -s16 -g1GB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100MB > tmp +./datagen -s17 -g100MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s18 -g10MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g1MB > tmp +./datagen -s19 -g1MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100KB > tmp +./datagen -s20 -g100KB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10KB > tmp +./datagen -s21 -g10KB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- write limit" -./datagen -g1GB > tmp +./datagen -s22 -g1GB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100MB > tmp +./datagen -s23 -g100MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s24 -g10MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g1MB > tmp +./datagen -s25 -g1MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100KB > tmp +./datagen -s26 -g100KB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10KB > tmp +./datagen -s27 -g10KB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- read and write limits" -./datagen -g1GB > tmp +./datagen -s28 -g1GB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100MB > tmp +./datagen -s29 -g100MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s30 -g10MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g1MB > tmp +./datagen -s31 -g1MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100KB > tmp +./datagen -s32 -g100KB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10KB > tmp +./datagen -s33 -g10KB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- forced compression level" -./datagen -g1GB > tmp +./datagen -s34 -g1GB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 rm tmp* -./datagen -g100MB > tmp +./datagen -s35 -g100MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s36 -g10MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 rm tmp* -./datagen -g1MB > tmp +./datagen -s37 -g1MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 rm tmp* -./datagen -g100KB > tmp +./datagen -s38 -g100KB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 rm tmp* -./datagen -g10KB > tmp +./datagen -s39 -g10KB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 diff --git a/contrib/adaptive-compression/test-performance.sh b/contrib/adaptive-compression/test-performance.sh index 6c4991c48..958cb3cc8 100755 --- a/contrib/adaptive-compression/test-performance.sh +++ b/contrib/adaptive-compression/test-performance.sh @@ -1,40 +1,40 @@ echo "testing time -- no limits set" -./datagen -g1GB > tmp +./datagen -s1 -g1GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp rm tmp* -./datagen -g2GB > tmp +./datagen -s2 -g2GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp rm tmp* -./datagen -g4GB > tmp +./datagen -s3 -g4GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp rm tmp* echo -e "\ntesting compression ratio -- no limits set" -./datagen -g1GB > tmp +./datagen -s4 -g1GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp ls -l tmp1.zst tmp2.zst rm tmp* -./datagen -g2GB > tmp +./datagen -s5 -g2GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp ls -l tmp1.zst tmp2.zst rm tmp* -./datagen -g4GB > tmp +./datagen -s6 -g4GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp ls -l tmp1.zst tmp2.zst rm tmp* echo e "\ntesting performance at various compression levels -- no limits set" -./datagen -g1GB > tmp +./datagen -s7 -g1GB > tmp echo "adapt" time ./adapt -i5 -f tmp -otmp1.zst echo "zstdcli" @@ -42,7 +42,7 @@ time zstd -5 tmp -o tmp2.zst ls -l tmp1.zst tmp2.zst rm tmp* -./datagen -g1GB > tmp +./datagen -s8 -g1GB > tmp echo "adapt" time ./adapt -i10 -f tmp -otmp1.zst echo "zstdcli" @@ -50,7 +50,7 @@ time zstd -10 tmp -o tmp2.zst ls -l tmp1.zst tmp2.zst rm tmp* -./datagen -g1GB > tmp +./datagen -s9 -g1GB > tmp echo "adapt" time ./adapt -i15 -f tmp -otmp1.zst echo "zstdcli" From ca300ce6e0004a447327d19a2ed879923e8c8baa Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Fri, 14 Jul 2017 17:17:00 -0700 Subject: [PATCH 152/318] Decouple hash table from compression function --- contrib/long_distance_matching/Makefile | 2 +- contrib/long_distance_matching/basic_table.c | 56 +++++++++++ contrib/long_distance_matching/ldm.c | 93 +++++++------------ contrib/long_distance_matching/ldm.h | 13 --- .../long_distance_matching/ldm_hashtable.h | 36 +++++++ 5 files changed, 127 insertions(+), 73 deletions(-) create mode 100644 contrib/long_distance_matching/basic_table.c create mode 100644 contrib/long_distance_matching/ldm_hashtable.h diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index cff786442..0d4dea069 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -27,7 +27,7 @@ default: all all: main-ldm -main-ldm : ldm.h ldm.c main-ldm.c +main-ldm : basic_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: diff --git a/contrib/long_distance_matching/basic_table.c b/contrib/long_distance_matching/basic_table.c new file mode 100644 index 000000000..007086fee --- /dev/null +++ b/contrib/long_distance_matching/basic_table.c @@ -0,0 +1,56 @@ +#include +#include + +#include "ldm_hashtable.h" + +struct LDM_hashTable { + U32 size; + LDM_hashEntry *entries; +}; + +LDM_hashTable *HASH_createTable(U32 size) { + LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); + table->size = size; + table->entries = calloc(size, sizeof(LDM_hashEntry)); + return table; +} + +void HASH_initializeTable(LDM_hashTable *table, U32 size) { + table->size = size; + table->entries = calloc(size, sizeof(LDM_hashEntry)); +} + + +LDM_hashEntry *HASH_getEntryFromHash( + const LDM_hashTable *table, const hash_t hash) { + return &(table->entries[hash]); +} + +void HASH_insert(LDM_hashTable *table, + const hash_t hash, const LDM_hashEntry entry) { + *HASH_getEntryFromHash(table, hash) = entry; +} + +U32 HASH_getSize(const LDM_hashTable *table) { + return table->size; +} + +void HASH_destroyTable(LDM_hashTable *table) { + free(table->entries); + free(table); +} + +void HASH_outputTableOccupancy(const LDM_hashTable *hashTable) { + U32 i = 0; + U32 ctr = 0; + for (; i < HASH_getSize(hashTable); i++) { + if (HASH_getEntryFromHash(hashTable, i)->offset == 0) { + ctr++; + } + } + printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", + HASH_getSize(hashTable), ctr, + 100.0 * (double)(ctr) / (double)HASH_getSize(hashTable)); +} + + diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 08cb856c7..32da40f82 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -4,11 +4,13 @@ #include #include -#include "ldm.h" - // Insert every (HASH_ONLY_EVERY + 1) into the hash table. #define HASH_ONLY_EVERY 0 +#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) + #define ML_BITS 4 #define ML_MASK ((1U<entries[hash]); -} - -void HASH_insert(LDM_hashTable *table, - const hash_t hash, const LDM_hashEntry entry) { - *HASH_getHash(table, hash) = entry; -} - +#include "ldm_hashtable.h" // TODO: Scanning speed // TODO: Memory usage @@ -54,6 +39,8 @@ struct LDM_compressStats { U32 numCollisions; U32 numHashInserts; +// U64 numInvalidHashes, numValidHashes; // tmp + U32 offsetHistogram[32]; }; @@ -80,9 +67,7 @@ struct LDM_CCtx { LDM_compressStats stats; /* Compression statistics */ - LDM_hashTable hashTable; - -// LDM_hashEntry *hashTable; + LDM_hashTable *hashTable; // LDM_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; @@ -101,21 +86,6 @@ struct LDM_CCtx { const BYTE *DEBUG_setNextHash; }; - - -void LDM_outputHashTableOccupancy(const LDM_hashTable *hashTable) { - U32 i = 0; - U32 ctr = 0; - for (; i < hashTable->size; i++) { - if (HASH_getHash(hashTable, i)->offset == 0) { - ctr++; - } - } - printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", - hashTable->size, ctr, - 100.0 * (double)(ctr) / (double)hashTable->size); -} - // TODO: This can be done more efficiently (but it is not that important as it // is only used for computing stats). static int intLog2(U32 x) { @@ -128,15 +98,15 @@ static int intLog2(U32 x) { // TODO: Maybe we would eventually prefer to have linear rather than // exponential buckets. -void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx) { +void HASH_outputTableOffsetHistogram(const LDM_CCtx *cctx) { U32 i = 0; int buckets[32] = { 0 }; printf("\n"); printf("Hash table histogram\n"); - for (; i < cctx->hashTable.size; i++) { + for (; i < HASH_getSize(cctx->hashTable); i++) { int offset = (cctx->ip - cctx->ibase) - - HASH_getHash(&cctx->hashTable, i)->offset; + HASH_getEntryFromHash(cctx->hashTable, i)->offset; buckets[intLog2(offset)]++; } @@ -145,7 +115,7 @@ void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx) { printf("2^%*d: %10u %6.3f%%\n", 2, i, buckets[i], 100.0 * (double) buckets[i] / - (double) cctx->hashTable.size); + (double) HASH_getSize(cctx->hashTable)); } printf("\n"); } @@ -181,7 +151,10 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { } printf("\n"); - + /* + printf("Num invalid hashes, num valid hashes, %llu %llu\n", + stats->numInvalidHashes, stats->numValidHashes); + */ printf("num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", stats->numCollisions, stats->numHashInserts, stats->numHashInserts == 0 ? @@ -315,7 +288,7 @@ static void putHashOfCurrentPositionFromHash( LDM_CCtx *cctx, hash_t hash, U32 sum) { #ifdef COMPUTE_STATS if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { - offset_t offset = HASH_getHash(&cctx->hashTable, hash)->offset; + U32 offset = HASH_getEntryFromHash(cctx->hashTable, hash)->offset; cctx->stats.numHashInserts++; if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { cctx->stats.numCollisions++; @@ -327,7 +300,7 @@ static void putHashOfCurrentPositionFromHash( // Note: this works only when cctx->step is 1. if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { const LDM_hashEntry entry = { cctx->ip - cctx->ibase }; - HASH_insert(&cctx->hashTable, hash, entry); + HASH_insert(cctx->hashTable, hash, entry); } cctx->lastPosHashed = cctx->ip; @@ -371,9 +344,11 @@ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { /** * Returns the position of the entry at hashTable[hash]. */ -static const BYTE *getPositionOnHash(LDM_CCtx *cctx, hash_t hash) { - return HASH_getHash(&cctx->hashTable, hash)->offset + cctx->ibase; +/* +static const BYTE *getPositionOnHash(const LDM_CCtx *cctx, const hash_t hash) { + return HASH_getEntryFromHash(cctx->hashTable, hash)->offset + cctx->ibase; } +*/ U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, const BYTE *pInLimit) { @@ -399,11 +374,6 @@ void LDM_readHeader(const void *src, U64 *compressedSize, // ip += sizeof(U64); } -static void LDM_initializeHashTable(LDM_hashTable *table) { - table->size = LDM_HASHTABLESIZE_U32; - table->entries = calloc(LDM_HASHTABLESIZE_U32, sizeof(LDM_hashEntry)); -} - void LDM_initializeCCtx(LDM_CCtx *cctx, const void *src, size_t srcSize, void *dst, size_t maxDstSize) { @@ -423,8 +393,10 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->anchor = cctx->ibase; memset(&(cctx->stats), 0, sizeof(cctx->stats)); + cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U32); + + //HASH_initializeTable(cctx->hashTable, LDM_HASHTABLESIZE_U32); - LDM_initializeHashTable(&cctx->hashTable); // calloc(LDM_HASHTABLESIZE_U32, sizeof(LDM_hashEntry)); // memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); cctx->stats.minOffset = UINT_MAX; @@ -442,7 +414,7 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, } void LDM_destroyCCtx(LDM_CCtx *cctx) { - free((cctx->hashTable).entries); + HASH_destroyTable(cctx->hashTable); } /** @@ -458,6 +430,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { do { hash_t h; U32 sum; + LDM_hashEntry *entry; setNextHash(cctx); h = cctx->nextHash; sum = cctx->nextSum; @@ -468,7 +441,9 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { return 1; } - *match = getPositionOnHash(cctx, h); + entry = HASH_getEntryFromHash(cctx->hashTable, h); + *match = entry->offset + cctx->ibase; + putHashOfCurrentPositionFromHash(cctx, h, sum); } while (cctx->ip - *match > LDM_WINDOW_SIZE || @@ -604,7 +579,7 @@ size_t LDM_compress(const void *src, size_t srcSize, LDM_updateLastHashFromNextHash(&cctx); } - // LDM_outputHashTableOffsetHistogram(&cctx); + // HASH_outputTableOffsetHistogram(&cctx); /* Encode the last literals (no more matches). */ { @@ -615,7 +590,7 @@ size_t LDM_compress(const void *src, size_t srcSize, #ifdef COMPUTE_STATS LDM_printCompressStats(&cctx.stats); - LDM_outputHashTableOccupancy(&cctx.hashTable); + HASH_outputTableOccupancy(cctx.hashTable); #endif { diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 8c3aa4e61..18b64e378 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -12,9 +12,6 @@ // Defines the size of the hash table. #define LDM_MEMORY_USAGE 16 -#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) #define LDM_WINDOW_SIZE_LOG 25 #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) @@ -23,10 +20,6 @@ #define LDM_MIN_MATCH_LENGTH 4 #define LDM_HASH_LENGTH 4 -typedef U32 offset_t; -typedef U32 hash_t; -typedef struct LDM_hashEntry LDM_hashEntry; -typedef struct LDM_hashTable LDM_hashTable; typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; typedef struct LDM_DCtx LDM_DCtx; @@ -75,12 +68,6 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, */ void LDM_destroyCCtx(LDM_CCtx *cctx); -/** - * Prints the percentage of the hash table occupied (where occupied is defined - * as the entry being non-zero). - */ -void LDM_outputHashTableOccupancy(const LDM_hashTable *hashTable); - /** * Prints the distribution of offsets in the hash table. * diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h new file mode 100644 index 000000000..690c47a15 --- /dev/null +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -0,0 +1,36 @@ +#ifndef LDM_HASHTABLE_H +#define LDM_HASHTABLE_H + +#include "mem.h" + +typedef U32 hash_t; + +typedef struct LDM_hashEntry { + U32 offset; +} LDM_hashEntry; + +typedef struct LDM_hashTable LDM_hashTable; + +// TODO: rename functions +// TODO: comments + +LDM_hashTable *HASH_createTable(U32 size); + +LDM_hashEntry *HASH_getEntryFromHash(const LDM_hashTable *table, + const hash_t hash); + +void HASH_insert(LDM_hashTable *table, const hash_t hash, + const LDM_hashEntry entry); + +U32 HASH_getSize(const LDM_hashTable *table); + +void HASH_destroyTable(LDM_hashTable *table); + +/** + * Prints the percentage of the hash table occupied (where occupied is defined + * as the entry being non-zero). + */ +void HASH_outputTableOccupancy(const LDM_hashTable *hashTable); + + +#endif /* LDM_HASHTABLE_H */ From 50ce4eaeb65fcdd521c5be710cfb0dff7c02e1bf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 10:12:44 -0700 Subject: [PATCH 153/318] added error detection for pthread initialization, added compression completion measurement, fixed const values --- contrib/adaptive-compression/adapt.c | 169 ++++++++++++++++++--------- lib/compress/zstd_compress.c | 9 ++ lib/zstd.h | 6 +- 3 files changed, 131 insertions(+), 53 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index addd0c59d..d45bdf85b 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -25,6 +25,7 @@ #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 1 +#define MAX_COMPRESSION_LEVEL_CHANGE 10 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -65,6 +66,16 @@ typedef struct { size_t dictSize; } jobDescription; +typedef struct { + pthread_mutex_t pMutex; + int noError; +} mutex_t; + +typedef struct { + pthread_cond_t pCond; + int noError; +} cond_t; + typedef struct { unsigned compressionLevel; unsigned numActiveThreads; @@ -76,14 +87,16 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - pthread_mutex_t jobCompressed_mutex; - pthread_cond_t jobCompressed_cond; - pthread_mutex_t jobReady_mutex; - pthread_cond_t jobReady_cond; - pthread_mutex_t allJobsCompleted_mutex; - pthread_cond_t allJobsCompleted_cond; - pthread_mutex_t jobWrite_mutex; - pthread_cond_t jobWrite_cond; + unsigned completionMeasured; + double completion; + mutex_t jobCompressed_mutex; + cond_t jobCompressed_cond; + mutex_t jobReady_mutex; + cond_t jobReady_cond; + mutex_t allJobsCompleted_mutex; + cond_t allJobsCompleted_cond; + mutex_t jobWrite_mutex; + cond_t jobWrite_cond; size_t lastDictSize; inBuff_t input; cStat_t stats; @@ -107,20 +120,38 @@ static void freeCompressionJobs(adaptCCtx* ctx) } } +static int destroyMutex(mutex_t* mutex) +{ + if (mutex->noError) { + int const ret = pthread_mutex_destroy(&mutex->pMutex); + return ret; + } + return 0; +} + +static int destroyCond(cond_t* cond) +{ + if (cond->noError) { + int const ret = pthread_cond_destroy(&cond->pCond); + return ret; + } + return 0; +} + static int freeCCtx(adaptCCtx* ctx) { if (!ctx) return 0; { int error = 0; - error |= pthread_mutex_destroy(&ctx->jobCompressed_mutex); - error |= pthread_cond_destroy(&ctx->jobCompressed_cond); - error |= pthread_mutex_destroy(&ctx->jobReady_mutex); - error |= pthread_cond_destroy(&ctx->jobReady_cond); - error |= pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); - error |= pthread_cond_destroy(&ctx->allJobsCompleted_cond); - error |= pthread_mutex_destroy(&ctx->jobWrite_mutex); - error |= pthread_cond_destroy(&ctx->jobWrite_cond); - error |= (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; + error |= destroyMutex(&ctx->jobCompressed_mutex); + error |= destroyCond(&ctx->jobCompressed_cond); + error |= destroyMutex(&ctx->jobReady_mutex); + error |= destroyCond(&ctx->jobReady_cond); + error |= destroyMutex(&ctx->allJobsCompleted_mutex); + error |= destroyCond(&ctx->allJobsCompleted_cond); + error |= destroyMutex(&ctx->jobWrite_mutex); + error |= destroyCond(&ctx->jobWrite_cond); + error |= (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); free(ctx->input.buffer.start); if (ctx->jobs){ @@ -132,23 +163,41 @@ static int freeCCtx(adaptCCtx* ctx) } } +static int initMutex(mutex_t* mutex) +{ + int const ret = pthread_mutex_init(&mutex->pMutex, NULL); + mutex->noError = !ret; + return ret; +} + +static int initCond(cond_t* cond) +{ + int const ret = pthread_cond_init(&cond->pCond, NULL); + cond->noError = !ret; + return ret; +} + static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) { - adaptCCtx* ctx = calloc(1, sizeof(adaptCCtx)); + adaptCCtx* const ctx = calloc(1, sizeof(adaptCCtx)); if (ctx == NULL) { DISPLAY("Error: could not allocate space for context\n"); return NULL; } ctx->compressionLevel = g_compressionLevel; - pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); - pthread_cond_init(&ctx->jobCompressed_cond, NULL); - pthread_mutex_init(&ctx->jobReady_mutex, NULL); - pthread_cond_init(&ctx->jobReady_cond, NULL); - pthread_mutex_init(&ctx->allJobsCompleted_mutex, NULL); - pthread_cond_init(&ctx->allJobsCompleted_cond, NULL); - pthread_mutex_init(&ctx->jobWrite_mutex, NULL); - pthread_cond_init(&ctx->jobWrite_cond, NULL); + { + int pthreadError = 0; + pthreadError |= initMutex(&ctx->jobCompressed_mutex); + pthreadError |= initCond(&ctx->jobCompressed_cond); + pthreadError |= initMutex(&ctx->jobReady_mutex); + pthreadError |= initCond(&ctx->jobReady_cond); + pthreadError |= initMutex(&ctx->allJobsCompleted_mutex); + pthreadError |= initCond(&ctx->allJobsCompleted_cond); + pthreadError |= initMutex(&ctx->jobWrite_mutex); + pthreadError |= initCond(&ctx->jobWrite_cond); + if (pthreadError) return NULL; + } ctx->numJobs = numJobs; ctx->jobReadyID = 0; ctx->jobCompressedID = 0; @@ -213,11 +262,11 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) static void waitUntilAllJobsCompleted(adaptCCtx* ctx) { if (!ctx) return; - pthread_mutex_lock(&ctx->allJobsCompleted_mutex); + pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); while (ctx->allJobsCompleted == 0) { - pthread_cond_wait(&ctx->allJobsCompleted_cond, &ctx->allJobsCompleted_mutex); + pthread_cond_wait(&ctx->allJobsCompleted_cond.pCond, &ctx->allJobsCompleted_mutex.pMutex); } - pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); + pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); } /* @@ -252,14 +301,20 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) reset = 1; } else if (compressSlow && ctx->compressionLevel > 1) { + double const completion = ctx->completion; + unsigned const maxChange = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - ctx->compressionLevel--; + DEBUG(2, "completion: %f\n", completion); + ctx->compressionLevel -= change; reset = 1; } if (reset) { ctx->stats.readyCounter = 0; ctx->stats.writeCounter = 0; ctx->stats.compressedCounter = 0; + ctx->completion = 1; + ctx->completionMeasured = 0; } return ctx->compressionLevel; } @@ -281,14 +336,14 @@ static void* compressionThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); - pthread_mutex_lock(&ctx->jobReady_mutex); + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); while(currJob + 1 > ctx->jobReadyID) { ctx->stats.waitReady++; ctx->stats.readyCounter++; DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); - pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); + pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } - pthread_mutex_unlock(&ctx->jobReady_mutex); + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); DEBUG(3, "compressionThread(): continuing after job ready\n"); DEBUG(3, "DICTIONARY ENDED\n"); DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); @@ -299,7 +354,7 @@ static void* compressionThread(void* arg) DEBUG(3, "compression level used: %u\n", cLevel); /* begin compression */ { - size_t useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); + size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); DEBUG(2, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); @@ -332,11 +387,11 @@ static void* compressionThread(void* arg) } job->dst.size = job->compressedSize; } - pthread_mutex_lock(&ctx->jobCompressed_mutex); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); ctx->jobCompressedID++; DEBUG(3, "signaling for job %u\n", currJob); - pthread_cond_signal(&ctx->jobCompressed_cond); - pthread_mutex_unlock(&ctx->jobCompressed_mutex); + pthread_cond_signal(&ctx->jobCompressed_cond.pCond); + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); DEBUG(3, "finished job compression %u\n", currJob); currJob++; if (job->lastJob || ctx->threadError) { @@ -374,14 +429,19 @@ static void* outputThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "outputThread(): waiting on job compressed\n"); - pthread_mutex_lock(&ctx->jobCompressed_mutex); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; + if (!ctx->completionMeasured) { + ctx->completion = ZSTD_getCompletion(ctx->cctx); + ctx->completionMeasured = 1; + } + DEBUG(2, "output detected completion: %f\n", ctx->completion); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); - pthread_cond_wait(&ctx->jobCompressed_cond, &ctx->jobCompressed_mutex); + pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } - pthread_mutex_unlock(&ctx->jobCompressed_mutex); + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); DEBUG(3, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; @@ -403,19 +463,19 @@ static void* outputThread(void* arg) currJob++; displayProgress(currJob, ctx->compressionLevel, job->lastJob); DEBUG(3, "locking job write mutex\n"); - pthread_mutex_lock(&ctx->jobWrite_mutex); + pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); ctx->jobWriteID++; - pthread_cond_signal(&ctx->jobWrite_cond); - pthread_mutex_unlock(&ctx->jobWrite_mutex); + pthread_cond_signal(&ctx->jobWrite_cond.pCond); + pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "unlocking job write mutex\n"); if (job->lastJob || ctx->threadError) { /* finished with all jobs */ DEBUG(3, "all jobs finished writing\n"); - pthread_mutex_lock(&ctx->allJobsCompleted_mutex); + pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); ctx->allJobsCompleted = 1; - pthread_cond_signal(&ctx->allJobsCompleted_cond); - pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); + pthread_cond_signal(&ctx->allJobsCompleted_cond.pCond); + pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); break; } } @@ -428,15 +488,20 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; DEBUG(3, "createCompressionJob(): wait for job write\n"); - pthread_mutex_lock(&ctx->jobWrite_mutex); + pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; + if (!ctx->completionMeasured) { + ctx->completion = ZSTD_getCompletion(ctx->cctx); + ctx->completionMeasured = 1; + } + DEBUG(2, "job creation detected completion %f\n", ctx->completion); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); - pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); + pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } - pthread_mutex_unlock(&ctx->jobWrite_mutex); + pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "createCompressionJob(): continuing after job write\n"); DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); @@ -446,10 +511,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) job->lastJob = last; memcpy(job->src.start, ctx->input.buffer.start, ctx->lastDictSize + srcSize); job->dictSize = ctx->lastDictSize; - pthread_mutex_lock(&ctx->jobReady_mutex); + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); ctx->jobReadyID++; - pthread_cond_signal(&ctx->jobReady_cond); - pthread_mutex_unlock(&ctx->jobReady_mutex); + pthread_cond_signal(&ctx->jobReady_cond.pCond); + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); DEBUG(3, "finished job creation %u\n", nextJob); ctx->nextJobID++; DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index f492d92bd..0c8edecec 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -140,6 +140,9 @@ struct ZSTD_CCtx_s { /* Multi-threading */ U32 nbThreads; ZSTDMT_CCtx* mtctx; + + /* adaptive compression */ + double completion; }; @@ -2845,6 +2848,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, BYTE* op = ostart; U32 const maxDist = 1 << cctx->appliedParams.cParams.windowLog; + cctx->completion = 0; if (cctx->appliedParams.fParams.checksumFlag && srcSize) XXH64_update(&cctx->xxhState, src, srcSize); @@ -2895,6 +2899,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, } remaining -= blockSize; + cctx->completion = 1 - (double)remaining/srcSize; dstCapacity -= cSize; ip += blockSize; op += cSize; @@ -2997,6 +3002,10 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, return fhSize; } +ZSTDLIB_API double ZSTD_getCompletion(ZSTD_CCtx* cctx) +{ + return cctx->completion; +} size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, diff --git a/lib/zstd.h b/lib/zstd.h index 58e9a5606..e835ad3a7 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -808,7 +808,11 @@ ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); - +/*! ZSTD_getCompletion: get a double representing how much of a file/buffer has been compressed + * using ZSTD_compressContinue() + * return: a double value in the range of 0 to 1 representing how much a compression job has finished + */ +ZSTDLIB_API double ZSTD_getCompletion(ZSTD_CCtx* cctx); /*- Buffer-less streaming decompression (synchronous mode) From 044e40db5a97eabfc7d67e44faa751114767ac20 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 11:19:23 -0700 Subject: [PATCH 154/318] removed freeCCtx() calls from createCCtx() so that it is not called twice during errors --- contrib/adaptive-compression/adapt.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index d45bdf85b..a6ba47242 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -214,7 +214,6 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) job->lastJob = 0; if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); - freeCCtx(ctx); return NULL; } job->src.capacity = FILE_CHUNK_SIZE; @@ -231,17 +230,14 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->input.buffer.start = malloc(ctx->input.buffer.capacity); if (!ctx->input.buffer.start) { DISPLAY("Error: could not allocate input buffer\n"); - freeCCtx(ctx); return NULL; } if (!ctx->cctx) { DISPLAY("Error: could not allocate ZSTD_CCtx\n"); - freeCCtx(ctx); return NULL; } if (!ctx->jobs) { DISPLAY("Error: could not allocate space for jobs during context creation\n"); - freeCCtx(ctx); return NULL; } { @@ -249,7 +245,6 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) FILE* dstFile = stdoutUsed ? stdout : fopen(outFilename, "wb"); if (dstFile == NULL) { DISPLAY("Error: could not open output file\n"); - freeCCtx(ctx); return NULL; } ctx->dstFile = dstFile; From 634f01242053966e4e30ac383d6edb914179376f Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 14 Jul 2017 19:11:58 -0700 Subject: [PATCH 155/318] [libzstd] Refactor ZSTD_compressSequences() --- lib/compress/zstd_compress.c | 310 +++++++++++++++++++---------------- 1 file changed, 166 insertions(+), 144 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index dfcb02661..3c792a851 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1017,6 +1017,144 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) mlCodeTable[seqStorePtr->longLengthPos] = MaxML; } +MEM_STATIC symbolEncodingType_e ZSTD_selectEncodingType(FSE_repeat* repeatMode, + size_t const mostFrequent, size_t nbSeq, U32 defaultNormLog) +{ +#define MIN_SEQ_FOR_DYNAMIC_FSE 64 +#define MAX_SEQ_FOR_STATIC_FSE 1000 + + if ((mostFrequent == nbSeq) && (nbSeq > 2)) { + *repeatMode = FSE_repeat_check; + return set_rle; + } + if ((*repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + return set_repeat; + } + if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) { + *repeatMode = FSE_repeat_valid; + return set_basic; + } + *repeatMode = FSE_repeat_check; + return set_compressed; +} + +MEM_STATIC size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, + FSE_CTable* CTable, U32 FSELog, symbolEncodingType_e type, + U32* count, U32 max, + BYTE const* codeTable, size_t nbSeq, + S16 const* defaultNorm, U32 defaultNormLog, U32 defaultMax, + void* workspace, size_t workspaceSize) +{ + BYTE* op = (BYTE*)dst; + BYTE const* const oend = op + dstCapacity; + + switch (type) { + case set_rle: + *op = codeTable[0]; + CHECK_F(FSE_buildCTable_rle(CTable, (BYTE)max)); + return 1; + case set_repeat: + return 0; + case set_basic: + CHECK_F(FSE_buildCTable_wksp(CTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize)); + return 0; + case set_compressed: { + S16 norm[MaxSeq + 1]; + size_t nbSeq_1 = nbSeq; + const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max); + if (count[codeTable[nbSeq-1]] > 1) { + count[codeTable[nbSeq-1]]--; + nbSeq_1--; + } + 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)); + return NCountSize; + } + } + default: return assert(0), ERROR(GENERIC); + } +} + +MEM_STATIC 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) +{ + 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()) BIT_flushBits(&blockStream); /* (7)*/ + 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)*/ + } } + + FSE_flushCState(&blockStream, &stateMatchLength); + FSE_flushCState(&blockStream, &stateOffsetBits); + FSE_flushCState(&blockStream, &stateLitLength); + + { size_t const streamSize = BIT_closeCStream(&blockStream); + if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */ + return streamSize; + } +} + MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, void* dst, size_t dstCapacity, size_t srcSize) @@ -1024,7 +1162,6 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, const int longOffsets = zc->appliedParams.cParams.windowLog > STREAM_ACCUMULATOR_MIN; const seqStore_t* seqStorePtr = &(zc->seqStore); U32 count[MaxSeq+1]; - S16 norm[MaxSeq+1]; FSE_CTable* CTable_LitLength = zc->entropy->litlengthCTable; FSE_CTable* CTable_OffsetBits = zc->entropy->offcodeCTable; FSE_CTable* CTable_MatchLength = zc->entropy->matchlengthCTable; @@ -1038,7 +1175,8 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, BYTE* op = ostart; size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; BYTE* seqHead; - BYTE scratchBuffer[1<entropy->workspace) >= (1<litStart; @@ -1058,166 +1196,50 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, /* seqHead : flags for FSE encoding type */ seqHead = op++; -#define MIN_SEQ_FOR_DYNAMIC_FSE 64 -#define MAX_SEQ_FOR_STATIC_FSE 1000 - /* convert length/distances into codes */ ZSTD_seqToCodes(seqStorePtr); - /* CTable for Literal Lengths */ { U32 max = MaxLL; size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, zc->entropy->workspace); - if ((mostFrequent == nbSeq) && (nbSeq > 2)) { - *op++ = llCodeTable[0]; - FSE_buildCTable_rle(CTable_LitLength, (BYTE)max); - LLtype = set_rle; - zc->entropy->litlength_repeatMode = FSE_repeat_check; - } else if ((zc->entropy->litlength_repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { - LLtype = set_repeat; - } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (LL_defaultNormLog-1)))) { - FSE_buildCTable_wksp(CTable_LitLength, LL_defaultNorm, MaxLL, LL_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); - LLtype = set_basic; - zc->entropy->litlength_repeatMode = FSE_repeat_valid; - } else { - size_t nbSeq_1 = nbSeq; - const U32 tableLog = FSE_optimalTableLog(LLFSELog, nbSeq, max); - if (count[llCodeTable[nbSeq-1]]>1) { count[llCodeTable[nbSeq-1]]--; nbSeq_1--; } - 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; - op += NCountSize; } - FSE_buildCTable_wksp(CTable_LitLength, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer)); - LLtype = set_compressed; - zc->entropy->litlength_repeatMode = FSE_repeat_check; + LLtype = ZSTD_selectEncodingType(&zc->entropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog); + { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype, + count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, + zc->entropy->workspace, sizeof(zc->entropy->workspace)); + if (ZSTD_isError(countSize)) return countSize; + op += countSize; } } - /* CTable for Offsets */ { U32 max = MaxOff; size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, zc->entropy->workspace); - if ((mostFrequent == nbSeq) && (nbSeq > 2)) { - *op++ = ofCodeTable[0]; - FSE_buildCTable_rle(CTable_OffsetBits, (BYTE)max); - Offtype = set_rle; - zc->entropy->offcode_repeatMode = FSE_repeat_check; - } else if ((zc->entropy->offcode_repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { - Offtype = set_repeat; - } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (OF_defaultNormLog-1)))) { - FSE_buildCTable_wksp(CTable_OffsetBits, OF_defaultNorm, MaxOff, OF_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); - Offtype = set_basic; - zc->entropy->offcode_repeatMode = FSE_repeat_valid; - } else { - size_t nbSeq_1 = nbSeq; - const U32 tableLog = FSE_optimalTableLog(OffFSELog, nbSeq, max); - if (count[ofCodeTable[nbSeq-1]]>1) { count[ofCodeTable[nbSeq-1]]--; nbSeq_1--; } - 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; - op += NCountSize; } - FSE_buildCTable_wksp(CTable_OffsetBits, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer)); - Offtype = set_compressed; - zc->entropy->offcode_repeatMode = FSE_repeat_check; + Offtype = ZSTD_selectEncodingType(&zc->entropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog); + { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, + count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, MaxOff, + zc->entropy->workspace, sizeof(zc->entropy->workspace)); + if (ZSTD_isError(countSize)) return countSize; + op += countSize; } } - /* CTable for MatchLengths */ { U32 max = MaxML; size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, zc->entropy->workspace); - if ((mostFrequent == nbSeq) && (nbSeq > 2)) { - *op++ = *mlCodeTable; - FSE_buildCTable_rle(CTable_MatchLength, (BYTE)max); - MLtype = set_rle; - zc->entropy->matchlength_repeatMode = FSE_repeat_check; - } else if ((zc->entropy->matchlength_repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { - MLtype = set_repeat; - } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (ML_defaultNormLog-1)))) { - FSE_buildCTable_wksp(CTable_MatchLength, ML_defaultNorm, MaxML, ML_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); - MLtype = set_basic; - zc->entropy->matchlength_repeatMode = FSE_repeat_valid; - } else { - size_t nbSeq_1 = nbSeq; - const U32 tableLog = FSE_optimalTableLog(MLFSELog, nbSeq, max); - if (count[mlCodeTable[nbSeq-1]]>1) { count[mlCodeTable[nbSeq-1]]--; nbSeq_1--; } - 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; - op += NCountSize; } - FSE_buildCTable_wksp(CTable_MatchLength, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer)); - MLtype = set_compressed; - zc->entropy->matchlength_repeatMode = FSE_repeat_check; + MLtype = ZSTD_selectEncodingType(&zc->entropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog); + { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype, + count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, + zc->entropy->workspace, sizeof(zc->entropy->workspace)); + if (ZSTD_isError(countSize)) return countSize; + op += countSize; } } *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2)); - /* Encoding Sequences */ - { BIT_CStream_t blockStream; - FSE_CState_t stateMatchLength; - FSE_CState_t stateOffsetBits; - FSE_CState_t stateLitLength; + { size_t const streamSize = ZSTD_encodeSequences(op, oend - op, + CTable_MatchLength, mlCodeTable, + CTable_OffsetBits, ofCodeTable, + CTable_LitLength, llCodeTable, + sequences, nbSeq, longOffsets); + if (ZSTD_isError(streamSize)) return streamSize; + op += streamSize; + } - CHECK_E(BIT_initCStream(&blockStream, op, oend-op), 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()) BIT_flushBits(&blockStream); /* (7)*/ - 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)*/ - } } - - FSE_flushCState(&blockStream, &stateMatchLength); - FSE_flushCState(&blockStream, &stateOffsetBits); - FSE_flushCState(&blockStream, &stateLitLength); - - { size_t const streamSize = BIT_closeCStream(&blockStream); - if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */ - op += streamSize; - } } /* check compressibility */ _check_compressibility: From 4bb42b02c190c606176332ee2a1f7cb540b194ab Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 17 Jul 2017 11:53:54 -0700 Subject: [PATCH 156/318] Add basic chaining table --- contrib/long_distance_matching/Makefile | 10 +- contrib/long_distance_matching/basic_table.c | 19 ++-- .../long_distance_matching/chaining_table.c | 92 +++++++++++++++++++ contrib/long_distance_matching/ldm.c | 40 +++++--- contrib/long_distance_matching/ldm.h | 4 +- .../long_distance_matching/ldm_hashtable.h | 6 +- 6 files changed, 144 insertions(+), 27 deletions(-) create mode 100644 contrib/long_distance_matching/chaining_table.c diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 0d4dea069..3159df756 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -25,13 +25,17 @@ LDFLAGS += -lzstd default: all -all: main-ldm +all: main-basic main-chaining -main-ldm : basic_table.c ldm.c main-ldm.c +main-basic : basic_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ +main-chaining : chaining_table.c ldm.c main-ldm.c + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + + clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main main-ldm + main-basic main-chaining @echo Cleaning completed diff --git a/contrib/long_distance_matching/basic_table.c b/contrib/long_distance_matching/basic_table.c index 007086fee..c6a5040ee 100644 --- a/contrib/long_distance_matching/basic_table.c +++ b/contrib/long_distance_matching/basic_table.c @@ -2,16 +2,19 @@ #include #include "ldm_hashtable.h" +#include "mem.h" struct LDM_hashTable { U32 size; LDM_hashEntry *entries; + const BYTE *offsetBase; }; -LDM_hashTable *HASH_createTable(U32 size) { +LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase) { LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); table->size = size; table->entries = calloc(size, sizeof(LDM_hashEntry)); + table->offsetBase = offsetBase; return table; } @@ -20,15 +23,19 @@ void HASH_initializeTable(LDM_hashTable *table, U32 size) { table->entries = calloc(size, sizeof(LDM_hashEntry)); } +LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { + return table->entries + hash; +} LDM_hashEntry *HASH_getEntryFromHash( - const LDM_hashTable *table, const hash_t hash) { - return &(table->entries[hash]); + const LDM_hashTable *table, const hash_t hash, const U32 checksum) { + (void)checksum; + return getBucket(table, hash); } void HASH_insert(LDM_hashTable *table, const hash_t hash, const LDM_hashEntry entry) { - *HASH_getEntryFromHash(table, hash) = entry; + *getBucket(table, hash) = entry; } U32 HASH_getSize(const LDM_hashTable *table) { @@ -44,7 +51,7 @@ void HASH_outputTableOccupancy(const LDM_hashTable *hashTable) { U32 i = 0; U32 ctr = 0; for (; i < HASH_getSize(hashTable); i++) { - if (HASH_getEntryFromHash(hashTable, i)->offset == 0) { + if (getBucket(hashTable, i)->offset == 0) { ctr++; } } @@ -52,5 +59,3 @@ void HASH_outputTableOccupancy(const LDM_hashTable *hashTable) { HASH_getSize(hashTable), ctr, 100.0 * (double)(ctr) / (double)HASH_getSize(hashTable)); } - - diff --git a/contrib/long_distance_matching/chaining_table.c b/contrib/long_distance_matching/chaining_table.c new file mode 100644 index 000000000..226f78225 --- /dev/null +++ b/contrib/long_distance_matching/chaining_table.c @@ -0,0 +1,92 @@ +#include +#include + +#include "ldm_hashtable.h" +#include "mem.h" + +//TODO: move def somewhere else. +//TODO: memory usage is currently no longer LDM_MEMORY_USAGE. +// refactor code to scale the number of elements appropriately. + +// Number of elements per hash bucket. +#define HASH_BUCKET_SIZE_LOG 2 // MAX is 4 for now +#define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) + +struct LDM_hashTable { + U32 size; + LDM_hashEntry *entries; // 1-D array for now. + + // Position corresponding to offset=0 in LDM_hashEntry. + const BYTE *offsetBase; + BYTE *bucketOffsets; // Pointer to current insert position. + // Last insert was at bucketOffsets - 1? +}; + +LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase) { + LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); + table->size = size; + table->entries = calloc(size * HASH_BUCKET_SIZE, sizeof(LDM_hashEntry)); + table->bucketOffsets = calloc(size, sizeof(BYTE)); + table->offsetBase = offsetBase; + return table; +} + +static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { + return table->entries + (hash << HASH_BUCKET_SIZE_LOG); +} + +/* +static LDM_hashEntry *getLastInsertFromHash(const LDM_hashTable *table, + const hash_t hash) { + LDM_hashEntry *bucket = getBucket(table, hash); + BYTE offset = (table->bucketOffsets[hash] - 1) & (HASH_BUCKET_SIZE - 1); + return bucket + offset; +} +*/ + +LDM_hashEntry *HASH_getEntryFromHash(const LDM_hashTable *table, + const hash_t hash, + const U32 checksum) { + // Loop through bucket. + // TODO: in order of recency??? + LDM_hashEntry *bucket = getBucket(table, hash); + LDM_hashEntry *cur = bucket; + for(; cur < bucket + HASH_BUCKET_SIZE; ++cur) { + if (cur->checksum == checksum) { + return cur; + } + } + return NULL; +} + +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; +} + +U32 HASH_getSize(const LDM_hashTable *table) { + return table->size * HASH_BUCKET_SIZE; +} + +void HASH_destroyTable(LDM_hashTable *table) { + free(table->entries); + free(table->bucketOffsets); + free(table); +} + +void HASH_outputTableOccupancy(const LDM_hashTable *table) { + U32 ctr = 0; + LDM_hashEntry *cur = table->entries; + LDM_hashEntry *end = table->entries + (table->size * HASH_BUCKET_SIZE); + for (; cur < end; ++cur) { + if (cur->offset == 0) { + ctr++; + } + } + + printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", + HASH_getSize(table), ctr, + 100.0 * (double)(ctr) / (double)HASH_getSize(table)); +} diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 32da40f82..3cb82ea68 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -20,9 +20,8 @@ #define CHECKSUM_CHAR_OFFSET 10 //#define RUN_CHECKS //#define LDM_DEBUG -// -#include "ldm.h" +#include "ldm.h" #include "ldm_hashtable.h" // TODO: Scanning speed @@ -98,6 +97,7 @@ static int intLog2(U32 x) { // TODO: Maybe we would eventually prefer to have linear rather than // exponential buckets. +/** void HASH_outputTableOffsetHistogram(const LDM_CCtx *cctx) { U32 i = 0; int buckets[32] = { 0 }; @@ -119,6 +119,7 @@ void HASH_outputTableOffsetHistogram(const LDM_CCtx *cctx) { } printf("\n"); } +*/ void LDM_printCompressStats(const LDM_compressStats *stats) { int i = 0; @@ -127,9 +128,11 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { //TODO: compute percentage matched? printf("Window size, hash table size (bytes): 2^%u, 2^%u\n", stats->windowSizeLog, stats->hashTableSizeLog); - printf("num matches, total match length: %u, %llu\n", + printf("num matches, total match length, %% matched: %u, %llu, %.3f\n", stats->numMatches, - stats->totalMatchLength); + 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", @@ -155,11 +158,13 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { printf("Num invalid hashes, num valid hashes, %llu %llu\n", stats->numInvalidHashes, stats->numValidHashes); */ + /* printf("num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", stats->numCollisions, stats->numHashInserts, stats->numHashInserts == 0 ? 1.0 : (100.0 * (double)stats->numCollisions) / (double)stats->numHashInserts); + */ printf("=====================\n"); } @@ -173,6 +178,7 @@ int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { */ //TODO: This seems to be faster for some reason? + U32 lengthLeft = LDM_MIN_MATCH_LENGTH; const BYTE *curIn = pIn; const BYTE *curMatch = pMatch; @@ -286,8 +292,9 @@ static void setNextHash(LDM_CCtx *cctx) { static void putHashOfCurrentPositionFromHash( LDM_CCtx *cctx, hash_t hash, U32 sum) { + /* #ifdef COMPUTE_STATS - if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { + if (cctx->stats.numHashInserts < HASH_getSize(cctx->hashTable)) { U32 offset = HASH_getEntryFromHash(cctx->hashTable, hash)->offset; cctx->stats.numHashInserts++; if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { @@ -295,11 +302,13 @@ static void putHashOfCurrentPositionFromHash( } } #endif +*/ // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. // Note: this works only when cctx->step is 1. if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { - const LDM_hashEntry entry = { cctx->ip - cctx->ibase }; + const LDM_hashEntry entry = { cctx->ip - cctx->ibase , + MEM_read32(cctx->ip) }; HASH_insert(cctx->hashTable, hash, entry); } @@ -393,7 +402,7 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->anchor = cctx->ibase; memset(&(cctx->stats), 0, sizeof(cctx->stats)); - cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U32); + cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U32, cctx->ibase); //HASH_initializeTable(cctx->hashTable, LDM_HASHTABLESIZE_U32); @@ -425,12 +434,13 @@ void LDM_destroyCCtx(LDM_CCtx *cctx) { * */ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { + + LDM_hashEntry *entry = NULL; cctx->nextIp = cctx->ip + cctx->step; do { hash_t h; U32 sum; - LDM_hashEntry *entry; setNextHash(cctx); h = cctx->nextHash; sum = cctx->nextSum; @@ -441,13 +451,17 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { return 1; } - entry = HASH_getEntryFromHash(cctx->hashTable, h); - *match = entry->offset + cctx->ibase; + entry = HASH_getEntryFromHash(cctx->hashTable, h, MEM_read32(cctx->ip)); + + if (entry != NULL) { + *match = entry->offset + cctx->ibase; + } putHashOfCurrentPositionFromHash(cctx, h, sum); - } while (cctx->ip - *match > LDM_WINDOW_SIZE || - !LDM_isValidMatch(cctx->ip, *match)); + } while (entry == NULL || + (cctx->ip - *match > LDM_WINDOW_SIZE || + !LDM_isValidMatch(cctx->ip, *match))); setNextHash(cctx); return 0; } @@ -510,7 +524,7 @@ void LDM_outputBlock(LDM_CCtx *cctx, size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; - const BYTE *match; + const BYTE *match = NULL; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); /* Hash the first position and put it into the hash table. */ diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 18b64e378..6325d1b19 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -17,8 +17,8 @@ #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) //These should be multiples of four. -#define LDM_MIN_MATCH_LENGTH 4 -#define LDM_HASH_LENGTH 4 +#define LDM_MIN_MATCH_LENGTH 1024 +#define LDM_HASH_LENGTH 1024 typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h index 690c47a15..92add96f9 100644 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -7,6 +7,7 @@ typedef U32 hash_t; typedef struct LDM_hashEntry { U32 offset; + U32 checksum; // Not needed? } LDM_hashEntry; typedef struct LDM_hashTable LDM_hashTable; @@ -14,10 +15,11 @@ typedef struct LDM_hashTable LDM_hashTable; // TODO: rename functions // TODO: comments -LDM_hashTable *HASH_createTable(U32 size); +LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase); LDM_hashEntry *HASH_getEntryFromHash(const LDM_hashTable *table, - const hash_t hash); + const hash_t hash, + const U32 checksum); void HASH_insert(LDM_hashTable *table, const hash_t hash, const LDM_hashEntry entry); From e198230645b01678dde9599eb5a8be6eb3127bd3 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 17 Jul 2017 12:27:24 -0700 Subject: [PATCH 157/318] [libzstd] Remove ZSTD_CCtx* argument of ZSTD_compressSequences() --- lib/common/zstd_internal.h | 2 + lib/compress/zstd_compress.c | 118 ++++++++++++++++++----------------- lib/compress/zstd_opt.h | 8 +-- 3 files changed, 66 insertions(+), 62 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index f3779e844..7f183c8c8 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -246,6 +246,8 @@ 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]; /* opt */ ZSTD_optimal_t* priceTable; ZSTD_match_t* matchTable; diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 3c792a851..64f783c71 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -82,8 +82,6 @@ struct ZSTD_CCtx_s { U32 loadedDictEnd; /* index of end of dictionary */ U32 forceWindow; /* force back-references to respect limit of 1<stage = ZSTDcs_init; cctx->dictID = 0; cctx->loadedDictEnd = 0; - { int i; for (i=0; irep[i] = repStartValue[i]; } + { int i; for (i=0; iseqStore.rep[i] = repStartValue[i]; } cctx->seqStore.litLengthSum = 0; /* force reset of btopt stats */ XXH64_reset(&cctx->xxhState, 0); return 0; @@ -693,7 +691,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->dictBase = NULL; zc->dictLimit = 0; zc->lowLimit = 0; - { int i; for (i=0; irep[i] = repStartValue[i]; } + { int i; for (i=0; iseqStore.rep[i] = repStartValue[i]; } zc->hashLog3 = hashLog3; zc->seqStore.litLengthSum = 0; @@ -747,7 +745,7 @@ 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; irep[i] = 0; + for (i=0; iseqStore.rep[i] = 0; } @@ -911,7 +909,8 @@ 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_CCtx* zc, +static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t * entropy, + ZSTD_strategy strategy, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { @@ -925,28 +924,28 @@ static size_t ZSTD_compressLiterals (ZSTD_CCtx* zc, /* small ? don't even attempt compression (speed opt) */ # define LITERAL_NOENTROPY 63 - { size_t const minLitSize = zc->entropy->hufCTable_repeatMode == HUF_repeat_valid ? 6 : LITERAL_NOENTROPY; + { size_t const minLitSize = entropy->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 = zc->entropy->hufCTable_repeatMode; - int const preferRepeat = zc->appliedParams.cParams.strategy < ZSTD_lazy ? srcSize <= 1024 : 0; + { HUF_repeat repeat = entropy->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, - zc->entropy->workspace, sizeof(zc->entropy->workspace), (HUF_CElt*)zc->entropy->hufCTable, &repeat, preferRepeat) + entropy->workspace, sizeof(entropy->workspace), (HUF_CElt*)entropy->hufCTable, &repeat, preferRepeat) : HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, - zc->entropy->workspace, sizeof(zc->entropy->workspace), (HUF_CElt*)zc->entropy->hufCTable, &repeat, preferRepeat); + entropy->workspace, sizeof(entropy->workspace), (HUF_CElt*)entropy->hufCTable, &repeat, preferRepeat); if (repeat != HUF_repeat_none) { hType = set_repeat; } /* reused the existing table */ - else { zc->entropy->hufCTable_repeatMode = HUF_repeat_check; } /* now have a table to reuse */ + else { entropy->hufCTable_repeatMode = HUF_repeat_check; } /* now have a table to reuse */ } if ((cLitSize==0) | (cLitSize >= srcSize - minGain)) { - zc->entropy->hufCTable_repeatMode = HUF_repeat_none; + entropy->hufCTable_repeatMode = HUF_repeat_none; return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } if (cLitSize==1) { - zc->entropy->hufCTable_repeatMode = HUF_repeat_none; + entropy->hufCTable_repeatMode = HUF_repeat_none; return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); } @@ -1155,16 +1154,17 @@ MEM_STATIC size_t ZSTD_encodeSequences(void* dst, size_t dstCapacity, } } -MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, +MEM_STATIC size_t ZSTD_compressSequences (seqStore_t* seqStorePtr, + ZSTD_entropyCTables_t* entropy, + ZSTD_compressionParameters const* cParams, void* dst, size_t dstCapacity, size_t srcSize) { - const int longOffsets = zc->appliedParams.cParams.windowLog > STREAM_ACCUMULATOR_MIN; - const seqStore_t* seqStorePtr = &(zc->seqStore); + const int longOffsets = cParams->windowLog > STREAM_ACCUMULATOR_MIN; U32 count[MaxSeq+1]; - FSE_CTable* CTable_LitLength = zc->entropy->litlengthCTable; - FSE_CTable* CTable_OffsetBits = zc->entropy->offcodeCTable; - FSE_CTable* CTable_MatchLength = zc->entropy->matchlengthCTable; + FSE_CTable* CTable_LitLength = entropy->litlengthCTable; + FSE_CTable* CTable_OffsetBits = entropy->offcodeCTable; + FSE_CTable* CTable_MatchLength = entropy->matchlengthCTable; U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */ const seqDef* const sequences = seqStorePtr->sequencesStart; const BYTE* const ofCodeTable = seqStorePtr->ofCode; @@ -1176,13 +1176,15 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; BYTE* seqHead; - ZSTD_STATIC_ASSERT(sizeof(zc->entropy->workspace) >= (1<workspace) >= (1<litStart; size_t const litSize = seqStorePtr->lit - literals; - size_t const cSize = ZSTD_compressLiterals(zc, op, dstCapacity, literals, litSize); - if (ZSTD_isError(cSize)) return cSize; + size_t const cSize = ZSTD_compressLiterals( + entropy, cParams->strategy, op, dstCapacity, literals, litSize); + if (ZSTD_isError(cSize)) + return cSize; op += cSize; } @@ -1200,31 +1202,31 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, ZSTD_seqToCodes(seqStorePtr); /* CTable for Literal Lengths */ { U32 max = MaxLL; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, zc->entropy->workspace); - LLtype = ZSTD_selectEncodingType(&zc->entropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog); + size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, entropy->workspace); + LLtype = ZSTD_selectEncodingType(&entropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype, count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, - zc->entropy->workspace, sizeof(zc->entropy->workspace)); + entropy->workspace, sizeof(entropy->workspace)); if (ZSTD_isError(countSize)) return countSize; op += countSize; } } /* CTable for Offsets */ { U32 max = MaxOff; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, zc->entropy->workspace); - Offtype = ZSTD_selectEncodingType(&zc->entropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog); + size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, entropy->workspace); + Offtype = ZSTD_selectEncodingType(&entropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, MaxOff, - zc->entropy->workspace, sizeof(zc->entropy->workspace)); + entropy->workspace, sizeof(entropy->workspace)); if (ZSTD_isError(countSize)) return countSize; op += countSize; } } /* CTable for MatchLengths */ { U32 max = MaxML; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, zc->entropy->workspace); - MLtype = ZSTD_selectEncodingType(&zc->entropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog); + size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, entropy->workspace); + MLtype = ZSTD_selectEncodingType(&entropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype, count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, - zc->entropy->workspace, sizeof(zc->entropy->workspace)); + entropy->workspace, sizeof(entropy->workspace)); if (ZSTD_isError(countSize)) return countSize; op += countSize; } } @@ -1246,15 +1248,15 @@ _check_compressibility: { size_t const minGain = ZSTD_minGain(srcSize); size_t const maxCSize = srcSize - minGain; if ((size_t)(op-ostart) >= maxCSize) { - 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; + 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; } } /* confirm repcodes */ - { int i; for (i=0; irep[i] = zc->repToConfirm[i]; } + { int i; for (i=0; irep[i] = seqStorePtr->repToConfirm[i]; } return op - ostart; } @@ -1479,7 +1481,7 @@ void ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, const BYTE* const lowest = base + lowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; - U32 offset_1=cctx->rep[0], offset_2=cctx->rep[1]; + U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1]; U32 offsetSaved = 0; /* init */ @@ -1540,8 +1542,8 @@ void ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, } } } /* save reps for next block */ - cctx->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved; - cctx->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved; + seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved; + seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved; /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -1589,7 +1591,7 @@ static void ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx, const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; - U32 offset_1=ctx->rep[0], offset_2=ctx->rep[1]; + U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1]; /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ @@ -1655,7 +1657,7 @@ static void ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx, } } } /* save reps for next block */ - ctx->repToConfirm[0] = offset_1; ctx->repToConfirm[1] = offset_2; + seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2; /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -1724,7 +1726,7 @@ void ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx, const BYTE* const lowest = base + lowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; - U32 offset_1=cctx->rep[0], offset_2=cctx->rep[1]; + U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1]; U32 offsetSaved = 0; /* init */ @@ -1811,8 +1813,8 @@ void ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx, } } } /* save reps for next block */ - cctx->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved; - cctx->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved; + seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved; + seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved; /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -1861,7 +1863,7 @@ static void ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; - U32 offset_1=ctx->rep[0], offset_2=ctx->rep[1]; + U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1]; /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ @@ -1961,7 +1963,7 @@ static void ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, } } } /* save reps for next block */ - ctx->repToConfirm[0] = offset_1; ctx->repToConfirm[1] = offset_2; + seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2; /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -2397,7 +2399,7 @@ void ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx, size_t* offsetPtr, U32 maxNbAttempts, U32 matchLengthSearch); searchMax_f const searchMax = searchMethod ? ZSTD_BtFindBestMatch_selectMLS : ZSTD_HcFindBestMatch_selectMLS; - U32 offset_1 = ctx->rep[0], offset_2 = ctx->rep[1], savedOffset=0; + U32 offset_1 = seqStorePtr->rep[0], offset_2 = seqStorePtr->rep[1], savedOffset=0; /* init */ ip += (ip==base); @@ -2507,8 +2509,8 @@ _storeSequence: } } /* Save reps for next block */ - ctx->repToConfirm[0] = offset_1 ? offset_1 : savedOffset; - ctx->repToConfirm[1] = offset_2 ? offset_2 : savedOffset; + seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : savedOffset; + seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : savedOffset; /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -2566,7 +2568,7 @@ void ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx, U32 maxNbAttempts, U32 matchLengthSearch); searchMax_f searchMax = searchMethod ? ZSTD_BtFindBestMatch_selectMLS_extDict : ZSTD_HcFindBestMatch_extDict_selectMLS; - U32 offset_1 = ctx->rep[0], offset_2 = ctx->rep[1]; + U32 offset_1 = seqStorePtr->rep[0], offset_2 = seqStorePtr->rep[1]; /* init */ ctx->nextToUpdate3 = ctx->nextToUpdate; @@ -2702,7 +2704,7 @@ _storeSequence: } } /* Save reps for next block */ - ctx->repToConfirm[0] = offset_1; ctx->repToConfirm[1] = offset_2; + seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2; /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -2811,7 +2813,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCa if (current > zc->nextToUpdate + 384) zc->nextToUpdate = current - MIN(192, (U32)(current - zc->nextToUpdate - 384)); /* limited update after finding a very long match */ blockCompressor(zc, src, srcSize); - return ZSTD_compressSequences(zc, dst, dstCapacity, srcSize); + return ZSTD_compressSequences(&zc->seqStore, zc->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize); } @@ -3141,9 +3143,9 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t } if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted); - cctx->rep[0] = MEM_readLE32(dictPtr+0); - cctx->rep[1] = MEM_readLE32(dictPtr+4); - cctx->rep[2] = MEM_readLE32(dictPtr+8); + cctx->seqStore.rep[0] = MEM_readLE32(dictPtr+0); + cctx->seqStore.rep[1] = MEM_readLE32(dictPtr+4); + cctx->seqStore.rep[2] = MEM_readLE32(dictPtr+8); dictPtr += 12; { size_t const dictContentSize = (size_t)(dictEnd - dictPtr); @@ -3157,8 +3159,8 @@ 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->rep[u] == 0) return ERROR(dictionary_corrupted); - if (cctx->rep[u] > dictContentSize) return ERROR(dictionary_corrupted); + if (cctx->seqStore.rep[u] == 0) return ERROR(dictionary_corrupted); + if (cctx->seqStore.rep[u] > dictContentSize) return ERROR(dictionary_corrupted); } } cctx->entropy->hufCTable_repeatMode = HUF_repeat_valid; diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index e8e98915e..d6e3449a7 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -439,7 +439,7 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, ctx->nextToUpdate3 = ctx->nextToUpdate; ZSTD_rescaleFreqs(seqStorePtr, (const BYTE*)src, srcSize); ip += (ip==prefixStart); - { U32 i; for (i=0; irep[i]; } + { U32 i; for (i=0; irep[i]; } /* Match Loop */ while (ip < ilimit) { @@ -651,7 +651,7 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ } } /* for (cur=0; cur < last_pos; ) */ /* Save reps for next block */ - { int i; for (i=0; irepToConfirm[i] = rep[i]; } + { int i; for (i=0; irepToConfirm[i] = rep[i]; } /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -689,7 +689,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, /* init */ U32 offset, rep[ZSTD_REP_NUM]; - { U32 i; for (i=0; irep[i]; } + { U32 i; for (i=0; irep[i]; } ctx->nextToUpdate3 = ctx->nextToUpdate; ZSTD_rescaleFreqs(seqStorePtr, (const BYTE*)src, srcSize); @@ -924,7 +924,7 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ } } /* for (cur=0; cur < last_pos; ) */ /* Save reps for next block */ - { int i; for (i=0; irepToConfirm[i] = rep[i]; } + { int i; for (i=0; irepToConfirm[i] = rep[i]; } /* Last Literals */ { size_t lastLLSize = iend - anchor; From 708238e07e28569ff946c05b9eef3f98b32af124 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 14:01:13 -0700 Subject: [PATCH 158/318] open file outside of adaptCCtx, pass to the output thread --- contrib/adaptive-compression/adapt.c | 55 +++++++++++++++++----------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index a6ba47242..67d7fcc92 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -101,13 +101,18 @@ typedef struct { inBuff_t input; cStat_t stats; jobDescription* jobs; - FILE* dstFile; ZSTD_CCtx* cctx; } adaptCCtx; +typedef struct { + adaptCCtx* ctx; + FILE* dstFile; +} outputThreadArg; + typedef struct { FILE* srcFile; adaptCCtx* ctx; + outputThreadArg* otArg; } fcResources; static void freeCompressionJobs(adaptCCtx* ctx) @@ -151,7 +156,6 @@ static int freeCCtx(adaptCCtx* ctx) error |= destroyCond(&ctx->allJobsCompleted_cond); error |= destroyMutex(&ctx->jobWrite_mutex); error |= destroyCond(&ctx->jobWrite_cond); - error |= (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); free(ctx->input.buffer.start); if (ctx->jobs){ @@ -177,7 +181,7 @@ static int initCond(cond_t* cond) return ret; } -static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) +static adaptCCtx* createCCtx(unsigned numJobs) { adaptCCtx* const ctx = calloc(1, sizeof(adaptCCtx)); @@ -240,15 +244,6 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) DISPLAY("Error: could not allocate space for jobs during context creation\n"); return NULL; } - { - unsigned const stdoutUsed = !strcmp(outFilename, stdoutmark); - FILE* dstFile = stdoutUsed ? stdout : fopen(outFilename, "wb"); - if (dstFile == NULL) { - DISPLAY("Error: could not open output file\n"); - return NULL; - } - ctx->dstFile = dstFile; - } return ctx; } @@ -417,7 +412,9 @@ static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) static void* outputThread(void* arg) { - adaptCCtx* ctx = (adaptCCtx*)arg; + outputThreadArg* const otArg = (outputThreadArg*)arg; + adaptCCtx* const ctx = otArg->ctx; + FILE* const dstFile = otArg->dstFile; unsigned currJob = 0; for ( ; ; ) { @@ -446,7 +443,7 @@ static void* outputThread(void* arg) return arg; } { - size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, ctx->dstFile); + size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); if (writeSize != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); ctx->threadError = 1; @@ -532,16 +529,16 @@ static void printStats(cStat_t stats) DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite); } -static int performCompression(adaptCCtx* ctx, FILE* const srcFile) +static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadArg* otArg) { - if (!ctx || !srcFile) { + if (!ctx || !srcFile || !otArg) { return 1; } /* create output thread */ { pthread_t out; - if (pthread_create(&out, NULL, &outputThread, ctx)) { + if (pthread_create(&out, NULL, &outputThread, otArg)) { DISPLAY("Error: could not create output thread\n"); ctx->threadError = 1; return 1; @@ -606,14 +603,25 @@ static fcResources createFileCompressionResources(const char* const srcFilename, outFilename = fileAndSuffix; } + { + unsigned const stdoutUsed = !strcmp(outFilename, stdoutmark); + FILE* const dstFile = stdoutUsed ? stdout : fopen(outFilename, "wb"); + fcr.otArg = malloc(sizeof(outputThreadArg)); + if (!fcr.otArg) { + DISPLAY("Error: could not allocate space for output thread argument\n"); + return fcr; + } + fcr.otArg->dstFile = dstFile; + } /* checking for errors */ - if (!outFilename || !srcFile) { - DISPLAY("Error: initial variables could not be allocated\n"); + if (!fcr.otArg->dstFile || !srcFile) { + DISPLAY("Error: some file(s) could not be opened\n"); return fcr; } /* creating context */ - fcr.ctx = createCCtx(numJobs, outFilename); + fcr.ctx = createCCtx(numJobs); + fcr.otArg->ctx = fcr.ctx; fcr.srcFile = srcFile; return fcr; } @@ -625,6 +633,11 @@ static int freeFileCompressionResources(fcResources* fcr) if (g_displayStats) printStats(fcr->ctx->stats); ret |= (fcr->srcFile != NULL) ? fclose(fcr->srcFile) : 0; ret |= (fcr->ctx != NULL) ? freeCCtx(fcr->ctx) : 0; + if (fcr->otArg) { + ret |= (fcr->otArg->dstFile != stdout) ? fclose(fcr->otArg->dstFile) : 0; + free(fcr->otArg); + /* no need to freeCCtx() on otArg->ctx because it should be the same context */ + } return ret; } @@ -634,7 +647,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst UTIL_getTime(&g_startTime); g_streamedSize = 0; fcResources fcr = createFileCompressionResources(srcFilename, dstFilenameOrNull); - ret |= performCompression(fcr.ctx, fcr.srcFile); + ret |= performCompression(fcr.ctx, fcr.srcFile, fcr.otArg); ret |= freeFileCompressionResources(&fcr); return ret; } From 6be22f1f842edc65ec1ddcf9c961e9bb854e1dc1 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 14:39:10 -0700 Subject: [PATCH 159/318] swap buffers instead of copying memory over --- contrib/adaptive-compression/adapt.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 67d7fcc92..3aec50c07 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -501,7 +501,12 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) job->src.size = srcSize; job->jobID = nextJob; job->lastJob = last; - memcpy(job->src.start, ctx->input.buffer.start, ctx->lastDictSize + srcSize); + { + /* swap buffer */ + void* const copy = job->src.start; + job->src.start = ctx->input.buffer.start; + ctx->input.buffer.start = copy; + } job->dictSize = ctx->lastDictSize; pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); ctx->jobReadyID++; @@ -514,7 +519,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) if (!last) { size_t const oldDictSize = ctx->lastDictSize; DEBUG(3, "oldDictSize %zu\n", oldDictSize); - memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize, srcSize); + memcpy(ctx->input.buffer.start, job->src.start + oldDictSize, srcSize); ctx->lastDictSize = srcSize; ctx->input.filled = srcSize; } From 15a041adbf8b59bc88838fe07297d8399319cec0 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 17 Jul 2017 15:16:58 -0700 Subject: [PATCH 160/318] Add function to get valid entries only from table --- contrib/long_distance_matching/Makefile | 6 +-- contrib/long_distance_matching/basic_table.c | 17 ++++++ ...aining_table.c => circular_buffer_table.c} | 21 +++++++- contrib/long_distance_matching/ldm.c | 54 ++++++------------- contrib/long_distance_matching/ldm.h | 11 ++-- .../long_distance_matching/ldm_hashtable.h | 9 +++- 6 files changed, 70 insertions(+), 48 deletions(-) rename contrib/long_distance_matching/{chaining_table.c => circular_buffer_table.c} (79%) diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 3159df756..47085022d 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -25,17 +25,17 @@ LDFLAGS += -lzstd default: all -all: main-basic main-chaining +all: main-basic main-circular-buffer main-basic : basic_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ -main-chaining : chaining_table.c ldm.c main-ldm.c +main-circular-buffer: circular_buffer_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main-basic main-chaining + main-basic main-circular-buffer @echo Cleaning completed diff --git a/contrib/long_distance_matching/basic_table.c b/contrib/long_distance_matching/basic_table.c index c6a5040ee..859bf0618 100644 --- a/contrib/long_distance_matching/basic_table.c +++ b/contrib/long_distance_matching/basic_table.c @@ -27,12 +27,29 @@ LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { return table->entries + hash; } + LDM_hashEntry *HASH_getEntryFromHash( const LDM_hashTable *table, const hash_t hash, const U32 checksum) { (void)checksum; return getBucket(table, hash); } +LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, + const hash_t hash, + const U32 checksum, + const BYTE *pIn, + int (*isValid)(const BYTE *pIn, const BYTE *pMatch)) { + LDM_hashEntry *entry = getBucket(table, hash); + (void)checksum; + if ((*isValid)(pIn, entry->offset + table->offsetBase)) { + return entry; + } else { + return NULL; + } +} + + + void HASH_insert(LDM_hashTable *table, const hash_t hash, const LDM_hashEntry entry) { *getBucket(table, hash) = entry; diff --git a/contrib/long_distance_matching/chaining_table.c b/contrib/long_distance_matching/circular_buffer_table.c similarity index 79% rename from contrib/long_distance_matching/chaining_table.c rename to contrib/long_distance_matching/circular_buffer_table.c index 226f78225..f45f945ce 100644 --- a/contrib/long_distance_matching/chaining_table.c +++ b/contrib/long_distance_matching/circular_buffer_table.c @@ -9,7 +9,7 @@ // refactor code to scale the number of elements appropriately. // Number of elements per hash bucket. -#define HASH_BUCKET_SIZE_LOG 2 // MAX is 4 for now +#define HASH_BUCKET_SIZE_LOG 1 // MAX is 4 for now #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) struct LDM_hashTable { @@ -44,6 +44,25 @@ static LDM_hashEntry *getLastInsertFromHash(const LDM_hashTable *table, } */ +LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, + const hash_t hash, + const U32 checksum, + const BYTE *pIn, + int (*isValid)(const BYTE *pIn, const BYTE *pMatch)) { + LDM_hashEntry *bucket = getBucket(table, hash); + LDM_hashEntry *cur = bucket; + // TODO: in order of recency? + for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { + // CHeck checksum for faster check. + if (cur->checksum == checksum && + (*isValid)(pIn, cur->offset + table->offsetBase)) { + return cur; + } + } + return NULL; +} + + LDM_hashEntry *HASH_getEntryFromHash(const LDM_hashTable *table, const hash_t hash, const U32 checksum) { diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 3cb82ea68..bf54842f6 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -5,7 +5,7 @@ #include // Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#define HASH_ONLY_EVERY 0 +#define HASH_ONLY_EVERY 31 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) @@ -38,8 +38,6 @@ struct LDM_compressStats { U32 numCollisions; U32 numHashInserts; -// U64 numInvalidHashes, numValidHashes; // tmp - U32 offsetHistogram[32]; }; @@ -153,45 +151,25 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { (double) stats->numMatches); } printf("\n"); - - /* - printf("Num invalid hashes, num valid hashes, %llu %llu\n", - stats->numInvalidHashes, stats->numValidHashes); - */ - /* - printf("num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", - stats->numCollisions, stats->numHashInserts, - stats->numHashInserts == 0 ? - 1.0 : (100.0 * (double)stats->numCollisions) / - (double)stats->numHashInserts); - */ printf("=====================\n"); } int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { - /* - if (memcmp(pIn, pMatch, LDM_MIN_MATCH_LENGTH) == 0) { - return 1; - } - return 0; - */ - - //TODO: This seems to be faster for some reason? - U32 lengthLeft = LDM_MIN_MATCH_LENGTH; const BYTE *curIn = pIn; const BYTE *curMatch = pMatch; - for (; lengthLeft >= 8; lengthLeft -= 8) { - if (MEM_read64(curIn) != MEM_read64(curMatch)) { + if (pIn - pMatch > LDM_WINDOW_SIZE) { + return 0; + } + + for (; lengthLeft >= 4; lengthLeft -= 4) { + if (MEM_read32(curIn) != MEM_read32(curMatch)) { return 0; } - curIn += 8; - curMatch += 8; - } - if (lengthLeft > 0) { - return (MEM_read32(curIn) == MEM_read32(curMatch)); + curIn += 4; + curMatch += 4; } return 1; } @@ -307,8 +285,11 @@ static void putHashOfCurrentPositionFromHash( // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. // Note: this works only when cctx->step is 1. if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { + /** const LDM_hashEntry entry = { cctx->ip - cctx->ibase , MEM_read32(cctx->ip) }; + */ + const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; HASH_insert(cctx->hashTable, hash, entry); } @@ -438,7 +419,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { LDM_hashEntry *entry = NULL; cctx->nextIp = cctx->ip + cctx->step; - do { + while (entry == NULL) { hash_t h; U32 sum; setNextHash(cctx); @@ -451,17 +432,14 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { return 1; } - entry = HASH_getEntryFromHash(cctx->hashTable, h, MEM_read32(cctx->ip)); + entry = HASH_getValidEntry(cctx->hashTable, h, sum, cctx->ip, + &LDM_isValidMatch); if (entry != NULL) { *match = entry->offset + cctx->ibase; } - putHashOfCurrentPositionFromHash(cctx, h, sum); - - } while (entry == NULL || - (cctx->ip - *match > LDM_WINDOW_SIZE || - !LDM_isValidMatch(cctx->ip, *match))); + } setNextHash(cctx); return 0; } diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 6325d1b19..6d97bd560 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -11,14 +11,14 @@ #define LDM_OFFSET_SIZE 4 // Defines the size of the hash table. -#define LDM_MEMORY_USAGE 16 +#define LDM_MEMORY_USAGE 20 -#define LDM_WINDOW_SIZE_LOG 25 +#define LDM_WINDOW_SIZE_LOG 30 #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) //These should be multiples of four. -#define LDM_MIN_MATCH_LENGTH 1024 -#define LDM_HASH_LENGTH 1024 +#define LDM_MIN_MATCH_LENGTH 64 +#define LDM_HASH_LENGTH 64 typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; @@ -82,7 +82,8 @@ void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx); void LDM_printCompressStats(const LDM_compressStats *stats); /** * Checks whether the LDM_MIN_MATCH_LENGTH bytes from p are the same as the - * LDM_MIN_MATCH_LENGTH bytes from match. + * LDM_MIN_MATCH_LENGTH bytes from match and also if + * pIn - pMatch <= LDM_WINDOW_SIZE. * * This assumes LDM_MIN_MATCH_LENGTH is a multiple of four. * diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h index 92add96f9..88d19ae20 100644 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -7,7 +7,7 @@ typedef U32 hash_t; typedef struct LDM_hashEntry { U32 offset; - U32 checksum; // Not needed? + U32 checksum; } LDM_hashEntry; typedef struct LDM_hashTable LDM_hashTable; @@ -17,10 +17,17 @@ typedef struct LDM_hashTable LDM_hashTable; LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase); +//TODO: unneeded? LDM_hashEntry *HASH_getEntryFromHash(const LDM_hashTable *table, const hash_t hash, const U32 checksum); +LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, + const hash_t hash, + const U32 checksum, + const BYTE *pIn, + int (*isValid)(const BYTE *pIn, const BYTE *pMatch)); + void HASH_insert(LDM_hashTable *table, const hash_t hash, const LDM_hashEntry entry); From a00e406231d2cb7fbeffd2a65d1a1044b0d7bcde Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 17 Jul 2017 15:17:32 -0700 Subject: [PATCH 161/318] Remove version archive --- .../versions/v0.3/Makefile | 30 - .../versions/v0.3/README | 3 - .../versions/v0.3/ldm.c | 464 ------------ .../versions/v0.3/ldm.h | 19 - .../versions/v0.3/main-ldm.c | 479 ------------ .../versions/v0.3/util.c | 64 -- .../versions/v0.3/util.h | 23 - .../versions/v0.5/Makefile | 37 - .../versions/v0.5/README | 5 - .../versions/v0.5/ldm.c | 710 ------------------ .../versions/v0.5/ldm.h | 159 ---- .../versions/v0.5/main-ldm.c | 270 ------- 12 files changed, 2263 deletions(-) delete mode 100644 contrib/long_distance_matching/versions/v0.3/Makefile delete mode 100644 contrib/long_distance_matching/versions/v0.3/README delete mode 100644 contrib/long_distance_matching/versions/v0.3/ldm.c delete mode 100644 contrib/long_distance_matching/versions/v0.3/ldm.h delete mode 100644 contrib/long_distance_matching/versions/v0.3/main-ldm.c delete mode 100644 contrib/long_distance_matching/versions/v0.3/util.c delete mode 100644 contrib/long_distance_matching/versions/v0.3/util.h delete mode 100644 contrib/long_distance_matching/versions/v0.5/Makefile delete mode 100644 contrib/long_distance_matching/versions/v0.5/README delete mode 100644 contrib/long_distance_matching/versions/v0.5/ldm.c delete mode 100644 contrib/long_distance_matching/versions/v0.5/ldm.h delete mode 100644 contrib/long_distance_matching/versions/v0.5/main-ldm.c diff --git a/contrib/long_distance_matching/versions/v0.3/Makefile b/contrib/long_distance_matching/versions/v0.3/Makefile deleted file mode 100644 index e5153970b..000000000 --- a/contrib/long_distance_matching/versions/v0.3/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -# This Makefile presumes libzstd is installed, using `sudo make install` - -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: main-ldm - -#main : ldm.c main.c -# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -main-ldm : util.c ldm.c main-ldm.c - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -clean: - @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main main-ldm - @echo Cleaning completed - diff --git a/contrib/long_distance_matching/versions/v0.3/README b/contrib/long_distance_matching/versions/v0.3/README deleted file mode 100644 index 8699562e5..000000000 --- a/contrib/long_distance_matching/versions/v0.3/README +++ /dev/null @@ -1,3 +0,0 @@ -This version uses simple lz4-style compression: -- A 4-byte hash is inserted into the hash table for every position. -- Hash table replacement policy: direct overwrite. diff --git a/contrib/long_distance_matching/versions/v0.3/ldm.c b/contrib/long_distance_matching/versions/v0.3/ldm.c deleted file mode 100644 index 1dedf5c37..000000000 --- a/contrib/long_distance_matching/versions/v0.3/ldm.c +++ /dev/null @@ -1,464 +0,0 @@ -#include -#include -#include -#include - - -#include "ldm.h" -#include "util.h" - -#define HASH_EVERY 1 - -#define LDM_MEMORY_USAGE 16 -#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) -#define LDM_HASH_SIZE_U32 (1 << (LDM_HASHLOG)) - -#define LDM_OFFSET_SIZE 4 - -#define WINDOW_SIZE (1 << 20) -#define MAX_WINDOW_SIZE 31 -#define HASH_SIZE 4 -#define LDM_HASH_LENGTH 4 -#define MINMATCH 4 - -#define ML_BITS 4 -#define ML_MASK ((1U<numMatches); - printf("Average match length: %.1f\n", ((double)stats->totalMatchLength) / - (double)stats->numMatches); - printf("Average literal length: %.1f\n", - ((double)stats->totalLiteralLength) / (double)stats->numMatches); - printf("Average offset length: %.1f\n", - ((double)stats->totalOffset) / (double)stats->numMatches); - printf("=====================\n"); -} - -typedef 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_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; - - const BYTE *lastPosHashed; /* Last position hashed */ - hash_t lastHash; /* Hash corresponding to lastPosHashed */ - const BYTE *nextIp; - hash_t nextHash; /* Hash corresponding to nextIp */ - - unsigned step; -} LDM_CCtx; - -#ifdef LDM_ROLLING_HASH -/** - * Convert a sum computed from LDM_getRollingHash to a hash value in the range - * of the hash table. - */ -static hash_t LDM_sumToHash(U32 sum) { - return sum % (LDM_HASHTABLESIZE >> 2); -// return sum & (LDM_HASHTABLESIZE - 1); -} - -static U32 LDM_getRollingHash(const char *data, U32 len) { - U32 i; - U32 s1, s2; - const schar *buf = (const schar *)data; - - s1 = s2 = 0; - for (i = 0; i < (len - 4); i += 4) { - s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + - (2 * buf[i + 2]) + (buf[i + 3]); - s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]; - } - for(; i < len; i++) { - s1 += buf[i]; - s2 += s1; - } - return (s1 & 0xffff) + (s2 << 16); -} - -static hash_t LDM_hashPosition(const void * const p) { - return LDM_sumToHash(LDM_getRollingHash((const char *)p, LDM_HASH_LENGTH)); -} - -typedef struct LDM_sumStruct { - U16 s1, s2; -} LDM_sumStruct; - -static void LDM_getRollingHashParts(U32 sum, LDM_sumStruct *sumStruct) { - sumStruct->s1 = sum & 0xffff; - sumStruct->s2 = sum >> 16; -} - -#else -static hash_t LDM_hash(U32 sequence) { - return ((sequence * 2654435761U) >> ((32)-LDM_HASHLOG)); -} - -static hash_t LDM_hashPosition(const void * const p) { - return LDM_hash(LDM_read32(p)); -} -#endif - -/* -static hash_t LDM_hash5(U64 sequence) { - static const U64 prime5bytes = 889523592379ULL; - static const U64 prime8bytes = 11400714785074694791ULL; - const U32 hashLog = LDM_HASHLOG; - if (LDM_isLittleEndian()) - return (((sequence << 24) * prime5bytes) >> (64 - hashLog)); - else - return (((sequence >> 24) * prime8bytes) >> (64 - hashLog)); -} -*/ - -static void LDM_putHashOfCurrentPositionFromHash( - LDM_CCtx *cctx, hash_t hash) { - if (((cctx->ip - cctx->ibase) & HASH_EVERY) != HASH_EVERY) { - return; - } - (cctx->hashTable)[hash] = (LDM_hashEntry){ (hash_t)(cctx->ip - cctx->ibase) }; - cctx->lastPosHashed = cctx->ip; - cctx->lastHash = hash; -} - -static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - hash_t hash = LDM_hashPosition(cctx->ip); - LDM_putHashOfCurrentPositionFromHash(cctx, hash); -} - -static const BYTE *LDM_get_position_on_hash( - hash_t h, void *tableBase, const BYTE *srcBase) { - const LDM_hashEntry * const hashTable = (LDM_hashEntry *)tableBase; - return hashTable[h].offset + srcBase; -} - -static BYTE LDM_read_byte(const void *memPtr) { - BYTE val; - memcpy(&val, memPtr, 1); - return val; -} - -static unsigned LDM_count(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { - const BYTE * const pStart = pIn; - while (pIn < pInLimit - 1) { - BYTE const diff = LDM_read_byte(pMatch) ^ LDM_read_byte(pIn); - if (!diff) { - pIn++; - pMatch++; - continue; - } - return (unsigned)(pIn - pStart); - } - return (unsigned)(pIn - pStart); -} - -void LDM_readHeader(const void *src, size_t *compressSize, - size_t *decompressSize) { - const U32 *ip = (const U32 *)src; - *compressSize = *ip++; - *decompressSize = *ip; -} - -static void 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 - HASH_SIZE; - cctx->imatchLimit = cctx->iend - MINMATCH; - - cctx->obase = (BYTE *)dst; - cctx->op = (BYTE *)dst; - - cctx->anchor = cctx->ibase; - - memset(&(cctx->stats), 0, sizeof(cctx->stats)); - memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); - - cctx->lastPosHashed = NULL; - cctx->nextIp = NULL; - - cctx->step = 1; -} - -static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { - cctx->nextIp = cctx->ip; - - do { - hash_t const h = cctx->nextHash; - cctx->ip = cctx->nextIp; - cctx->nextIp += cctx->step; - - if (cctx->nextIp > cctx->imatchLimit) { - return 1; - } - - *match = LDM_get_position_on_hash(h, cctx->hashTable, cctx->ibase); - - cctx->nextHash = LDM_hashPosition(cctx->nextIp); - LDM_putHashOfCurrentPositionFromHash(cctx, h); - } while (cctx->ip - *match > WINDOW_SIZE || - LDM_read64(*match) != LDM_read64(cctx->ip)); - return 0; -} - -// TODO: srcSize and maxDstSize is unused -size_t LDM_compress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { - LDM_CCtx cctx; - LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); - - /* Hash the first position and put it into the hash table. */ - LDM_putHashOfCurrentPosition(&cctx); - cctx.ip++; - cctx.nextHash = LDM_hashPosition(cctx.ip); - - // TODO: loop condition is not accurate. - while (1) { - const BYTE *match; - - /** - * 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. - */ - if (LDM_findBestMatch(&cctx, &match) != 0) { - goto _last_literals; - } - - cctx.stats.numMatches++; - - /** - * Catch up: look back to extend the match backwards from the found match. - */ - while (cctx.ip > cctx.anchor && match > cctx.ibase && - cctx.ip[-1] == match[-1]) { - cctx.ip--; - match--; - } - - /** - * Write current block (literals, literal length, match offset, match - * length) and update pointers and hashes. - */ - { - unsigned const literalLength = (unsigned)(cctx.ip - cctx.anchor); - unsigned const offset = cctx.ip - match; - unsigned const matchLength = LDM_count( - cctx.ip + MINMATCH, match + MINMATCH, cctx.ihashLimit); - BYTE *token = cctx.op++; - - cctx.stats.totalLiteralLength += literalLength; - cctx.stats.totalOffset += offset; - cctx.stats.totalMatchLength += matchLength + MINMATCH; - - /* Encode the literal length. */ - if (literalLength >= RUN_MASK) { - int len = (int)literalLength - RUN_MASK; - *token = (RUN_MASK << ML_BITS); - for (; len >= 255; len -= 255) { - *(cctx.op)++ = 255; - } - *(cctx.op)++ = (BYTE)len; - } else { - *token = (BYTE)(literalLength << ML_BITS); - } - - /* Encode the literals. */ - memcpy(cctx.op, cctx.anchor, literalLength); - cctx.op += literalLength; - - /* Encode the offset. */ - LDM_write32(cctx.op, offset); - cctx.op += LDM_OFFSET_SIZE; - - /* Encode match length */ - if (matchLength >= ML_MASK) { - unsigned matchLengthRemaining = matchLength; - *token += ML_MASK; - matchLengthRemaining -= ML_MASK; - LDM_write32(cctx.op, 0xFFFFFFFF); - while (matchLengthRemaining >= 4*0xFF) { - cctx.op += 4; - LDM_write32(cctx.op, 0xffffffff); - matchLengthRemaining -= 4*0xFF; - } - cctx.op += matchLengthRemaining / 255; - *(cctx.op)++ = (BYTE)(matchLengthRemaining % 255); - } else { - *token += (BYTE)(matchLength); - } - - /* Update input pointer, inserting hashes into hash table along the - * way. - */ - while (cctx.ip < cctx.anchor + MINMATCH + matchLength + literalLength) { - LDM_putHashOfCurrentPosition(&cctx); - cctx.ip++; - } - } - - // Set start of next block to current input pointer. - cctx.anchor = cctx.ip; - LDM_putHashOfCurrentPosition(&cctx); - cctx.nextHash = LDM_hashPosition(++cctx.ip); - } -_last_literals: - /* Encode the last literals (no more matches). */ - { - size_t const lastRun = (size_t)(cctx.iend - cctx.anchor); - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *(cctx.op)++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255; accumulator -= 255) { - *(cctx.op)++ = 255; - } - *(cctx.op)++ = (BYTE)accumulator; - } else { - *(cctx.op)++ = (BYTE)(lastRun << ML_BITS); - } - memcpy(cctx.op, cctx.anchor, lastRun); - cctx.op += lastRun; - } - LDM_printCompressStats(&cctx.stats); - return (cctx.op - (const BYTE *)cctx.obase); -} - -typedef struct LDM_DCtx { - size_t compressSize; - size_t maxDecompressSize; - - 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 */ -} LDM_DCtx; - -static void LDM_initializeDCtx(LDM_DCtx *dctx, - const void *src, size_t compressSize, - void *dst, size_t maxDecompressSize) { - dctx->compressSize = compressSize; - dctx->maxDecompressSize = maxDecompressSize; - - dctx->ibase = src; - dctx->ip = (const BYTE *)src; - dctx->iend = dctx->ip + dctx->compressSize; - dctx->op = dst; - dctx->oend = dctx->op + dctx->maxDecompressSize; - -} - -size_t LDM_decompress(const void *src, size_t compressSize, - void *dst, size_t maxDecompressSize) { - LDM_DCtx dctx; - LDM_initializeDCtx(&dctx, src, compressSize, dst, maxDecompressSize); - - while (dctx.ip < dctx.iend) { - BYTE *cpy; - const BYTE *match; - size_t length, offset; - - /* Get the literal length. */ - unsigned const token = *(dctx.ip)++; - if ((length = (token >> ML_BITS)) == RUN_MASK) { - unsigned s; - do { - s = *(dctx.ip)++; - length += s; - } while (s == 255); - } - - /* Copy literals. */ - cpy = dctx.op + length; - memcpy(dctx.op, dctx.ip, length); - dctx.ip += length; - dctx.op = cpy; - - //TODO : dynamic offset size - offset = LDM_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 += MINMATCH; - - /* Copy match. */ - cpy = dctx.op + length; - - // Inefficient for now - while (match < cpy - offset && dctx.op < dctx.oend) { - *(dctx.op)++ = *match++; - } - } - return dctx.op - (BYTE *)dst; -} - - diff --git a/contrib/long_distance_matching/versions/v0.3/ldm.h b/contrib/long_distance_matching/versions/v0.3/ldm.h deleted file mode 100644 index 287d444dd..000000000 --- a/contrib/long_distance_matching/versions/v0.3/ldm.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LDM_H -#define LDM_H - -#include /* size_t */ - -#define LDM_COMPRESS_SIZE 4 -#define LDM_DECOMPRESS_SIZE 4 -#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) - -size_t LDM_compress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -size_t LDM_decompress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -void LDM_readHeader(const void *src, size_t *compressSize, - size_t *decompressSize); - -#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v0.3/main-ldm.c b/contrib/long_distance_matching/versions/v0.3/main-ldm.c deleted file mode 100644 index 724d735dd..000000000 --- a/contrib/long_distance_matching/versions/v0.3/main-ldm.c +++ /dev/null @@ -1,479 +0,0 @@ -// TODO: file size must fit into a U32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "ldm.h" - -// #define BUF_SIZE 16*1024 // Block size -#define DEBUG - -//#define ZSTD - -/* Compress file given by fname and output to oname. - * Returns 0 if successful, error code otherwise. - */ -static int compress(const char *fname, const char *oname) { - int fdin, fdout; - struct stat statbuf; - char *src, *dst; - size_t maxCompressSize, compressSize; - - /* 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; - } - - maxCompressSize = statbuf.st_size + LDM_HEADER_SIZE; - - /* Go to the location corresponding to the last byte. */ - /* TODO: fallocate? */ - if (lseek(fdout, maxCompressSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* Write a dummy byte at the last location. */ - if (write(fdout, "", 1) != 1) { - perror("write 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; - } - - /* mmap the output file */ - if ((dst = mmap(0, maxCompressSize, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - -#ifdef ZSTD - compressSize = ZSTD_compress(dst, statbuf.st_size, - src, statbuf.st_size, 1); -#else - compressSize = LDM_HEADER_SIZE + - LDM_compress(src, statbuf.st_size, - dst + LDM_HEADER_SIZE, statbuf.st_size); - - // Write compress and decompress size to header - // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &compressSize, 4); - memcpy(dst + 4, &(statbuf.st_size), 4); - -#ifdef DEBUG - printf("Compressed size: %zu\n", compressSize); - printf("Decompressed size: %zu\n", (size_t)statbuf.st_size); -#endif -#endif - - // Truncate file to compressSize. - ftruncate(fdout, compressSize); - - printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, - (unsigned)statbuf.st_size, (unsigned)compressSize, oname, - (double)compressSize / (statbuf.st_size) * 100); - - // Close files. - close(fdin); - close(fdout); - return 0; -} - -/* 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; - size_t compressSize, decompressSize, 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, &compressSize, &decompressSize); - -#ifdef DEBUG - printf("Size, compressSize, decompressSize: %zu %zu %zu\n", - (size_t)statbuf.st_size, compressSize, decompressSize); -#endif - - /* Go to the location corresponding to the last byte. */ - if (lseek(fdout, decompressSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write error"); - return 1; - } - - /* mmap the output file */ - if ((dst = mmap(0, decompressSize, PROT_READ | PROT_WRITE, - MAP_SHARED, fdout, 0)) == (caddr_t) - 1) { - perror("mmap error for output"); - return 1; - } - -#ifdef ZSTD - outSize = ZSTD_decompress(dst, decomrpessed_size, - src + LDM_HEADER_SIZE, - statbuf.st_size - LDM_HEADER_SIZE); -#else - outSize = LDM_decompress( - src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, - dst, decompressSize); - - printf("Ret size out: %zu\n", outSize); - #endif - ftruncate(fdout, 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 void verify(const char *inpFilename, const char *decFilename) { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - } - - fclose(decFp); - fclose(inpFp); -} - -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 */ - { - struct timeval tv1, tv2; - gettimeofday(&tv1, NULL); - if (compress(inpFilename, ldmFilename)) { - printf("Compress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - } - - /* Decompress */ - { - struct timeval tv1, tv2; - gettimeofday(&tv1, NULL); - if (decompress(ldmFilename, decFilename)) { - printf("Decompress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - } - /* verify */ - verify(inpFilename, decFilename); - return 0; -} - - -#if 0 -static size_t compress_file(FILE *in, FILE *out, size_t *size_in, - size_t *size_out) { - char *src, *buf = NULL; - size_t r = 1; - size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; - - src = malloc(BUF_SIZE); - if (!src) { - printf("Not enough memory\n"); - goto cleanup; - } - - size = BUF_SIZE + LDM_HEADER_SIZE; - buf = malloc(size); - if (!buf) { - printf("Not enough memory\n"); - goto cleanup; - } - - - for (;;) { - k = fread(src, 1, BUF_SIZE, in); - if (k == 0) - break; - count_in += k; - - n = LDM_compress(src, buf, k, BUF_SIZE); - - // n = k; - // offset += n; - offset = k; - count_out += k; - -// k = fwrite(src, 1, offset, out); - - k = fwrite(buf, 1, offset, out); - if (k < offset) { - if (ferror(out)) - printf("Write failed\n"); - else - printf("Short write\n"); - goto cleanup; - } - - } - *size_in = count_in; - *size_out = count_out; - r = 0; - cleanup: - free(src); - free(buf); - return r; -} - -static size_t decompress_file(FILE *in, FILE *out) { - void *src = malloc(BUF_SIZE); - void *dst = NULL; - size_t dst_capacity = BUF_SIZE; - size_t ret = 1; - size_t bytes_written = 0; - - if (!src) { - perror("decompress_file(src)"); - goto cleanup; - } - - while (ret != 0) { - /* Load more input */ - size_t src_size = fread(src, 1, BUF_SIZE, in); - void *src_ptr = src; - void *src_end = src_ptr + src_size; - if (src_size == 0 || ferror(in)) { - printf("(TODO): Decompress: not enough input or error reading file\n"); - //TODO - ret = 0; - goto cleanup; - } - - /* Allocate destination buffer if it hasn't been allocated already */ - if (!dst) { - dst = malloc(dst_capacity); - if (!dst) { - perror("decompress_file(dst)"); - goto cleanup; - } - } - - // TODO - - /* Decompress: - * Continue while there is more input to read. - */ - while (src_ptr != src_end && ret != 0) { - // size_t dst_size = src_size; - size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); - size_t written = fwrite(dst, 1, dst_size, out); -// printf("Writing %zu bytes\n", dst_size); - bytes_written += dst_size; - if (written != dst_size) { - printf("Decompress: Failed to write to file\n"); - goto cleanup; - } - src_ptr += src_size; - src_size = src_end - src_ptr; - } - - /* Update input */ - - } - - printf("Wrote %zu bytes\n", bytes_written); - - cleanup: - free(src); - free(dst); - - return ret; -} - -int main2(int argc, char *argv[]) { - char inpFilename[256] = { 0 }; - char ldmFilename[256] = { 0 }; - char decFilename[256] = { 0 }; - - if (argc < 2) { - printf("Please specify input filename\n"); - return 0; - } - 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 */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *outFp = fopen(ldmFilename, "wb"); - size_t sizeIn = 0; - size_t sizeOut = 0; - size_t ret; - printf("compress : %s -> %s\n", inpFilename, ldmFilename); - ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); - if (ret) { - printf("compress : failed with code %zu\n", ret); - return ret; - } - printf("%s: %zu → %zu bytes, %.1f%%\n", - inpFilename, sizeIn, sizeOut, - (double)sizeOut / sizeIn * 100); - printf("compress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* decompress */ - { - FILE *inpFp = fopen(ldmFilename, "rb"); - FILE *outFp = fopen(decFilename, "wb"); - size_t ret; - - printf("decompress : %s -> %s\n", ldmFilename, decFilename); - ret = decompress_file(inpFp, outFp); - if (ret) { - printf("decompress : failed with code %zu\n", ret); - return ret; - } - printf("decompress : done\n"); - - fclose(outFp); - fclose(inpFp); - } - - /* verify */ - { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - - fclose(decFp); - fclose(inpFp); - } - return 0; -} -#endif - diff --git a/contrib/long_distance_matching/versions/v0.3/util.c b/contrib/long_distance_matching/versions/v0.3/util.c deleted file mode 100644 index 9ea4ca1e5..000000000 --- a/contrib/long_distance_matching/versions/v0.3/util.c +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include -#include -#include - -#include "util.h" - -typedef uint8_t BYTE; -typedef uint16_t U16; -typedef uint32_t U32; -typedef int32_t S32; -typedef uint64_t U64; - -unsigned LDM_isLittleEndian(void) { - const union { U32 u; BYTE c[4]; } one = { 1 }; - return one.c[0]; -} - -U16 LDM_read16(const void *memPtr) { - U16 val; - memcpy(&val, memPtr, sizeof(val)); - return val; -} - -U16 LDM_readLE16(const void *memPtr) { - if (LDM_isLittleEndian()) { - return LDM_read16(memPtr); - } else { - const BYTE *p = (const BYTE *)memPtr; - return (U16)((U16)p[0] + (p[1] << 8)); - } -} - -void LDM_write16(void *memPtr, U16 value){ - memcpy(memPtr, &value, sizeof(value)); -} - -void LDM_write32(void *memPtr, U32 value) { - memcpy(memPtr, &value, sizeof(value)); -} - -void LDM_writeLE16(void *memPtr, U16 value) { - if (LDM_isLittleEndian()) { - LDM_write16(memPtr, value); - } else { - BYTE* p = (BYTE *)memPtr; - p[0] = (BYTE) value; - p[1] = (BYTE)(value>>8); - } -} - -U32 LDM_read32(const void *ptr) { - return *(const U32 *)ptr; -} - -U64 LDM_read64(const void *ptr) { - return *(const U64 *)ptr; -} - -void LDM_copy8(void *dst, const void *src) { - memcpy(dst, src, 8); -} - - diff --git a/contrib/long_distance_matching/versions/v0.3/util.h b/contrib/long_distance_matching/versions/v0.3/util.h deleted file mode 100644 index 90726412e..000000000 --- a/contrib/long_distance_matching/versions/v0.3/util.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef LDM_UTIL_H -#define LDM_UTIL_H - -unsigned LDM_isLittleEndian(void); - -uint16_t LDM_read16(const void *memPtr); - -uint16_t LDM_readLE16(const void *memPtr); - -void LDM_write16(void *memPtr, uint16_t value); - -void LDM_write32(void *memPtr, uint32_t value); - -void LDM_writeLE16(void *memPtr, uint16_t value); - -uint32_t LDM_read32(const void *ptr); - -uint64_t LDM_read64(const void *ptr); - -void LDM_copy8(void *dst, const void *src); - - -#endif /* LDM_UTIL_H */ diff --git a/contrib/long_distance_matching/versions/v0.5/Makefile b/contrib/long_distance_matching/versions/v0.5/Makefile deleted file mode 100644 index cff786442..000000000 --- a/contrib/long_distance_matching/versions/v0.5/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# ################################################################ -# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. -# All rights reserved. -# -# This source code is licensed under the BSD-style license found in the -# LICENSE file in the root directory of this source tree. An additional grant -# of patent rights can be found in the PATENTS file in the same directory. -# ################################################################ - -# 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: main-ldm - -main-ldm : ldm.h ldm.c main-ldm.c - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -clean: - @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main main-ldm - @echo Cleaning completed - diff --git a/contrib/long_distance_matching/versions/v0.5/README b/contrib/long_distance_matching/versions/v0.5/README deleted file mode 100644 index 7901ae769..000000000 --- a/contrib/long_distance_matching/versions/v0.5/README +++ /dev/null @@ -1,5 +0,0 @@ -This version uses simple lz4-style compression with a rolling hash. -- A rolling checksum based on rsync's Adler-32 style checksum is used. -- The checksum is hashed using lz4's hash function. -- Hash table replacement policy: direct overwrite. -- The length of input to the hash function can be set with LDM_HASH_LENGTH. diff --git a/contrib/long_distance_matching/versions/v0.5/ldm.c b/contrib/long_distance_matching/versions/v0.5/ldm.c deleted file mode 100644 index 06c97bc48..000000000 --- a/contrib/long_distance_matching/versions/v0.5/ldm.c +++ /dev/null @@ -1,710 +0,0 @@ -#include -#include -#include -#include -#include - -#include "ldm.h" - -// Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#define HASH_ONLY_EVERY 0 - -#define ML_BITS 4 -#define ML_MASK ((1U<>= 1) { - ret++; - } - return ret; -} - -// TODO: Maybe we would eventually prefer to have linear rather than -// exponential buckets. -void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx) { - int i = 0; - int buckets[32] = { 0 }; - - printf("\n"); - printf("Hash table histogram\n"); - for (; i < LDM_HASHTABLESIZE_U32; i++) { - int offset = (cctx->ip - cctx->ibase) - cctx->hashTable[i].offset; - buckets[intLog2(offset)]++; - } - - i = 0; - for (; i < 32; i++) { - printf("2^%*d: %10u %6.3f%%\n", 2, i, - buckets[i], - 100.0 * (double) buckets[i] / - (double) LDM_HASHTABLESIZE_U32); - } - printf("\n"); -} - -void LDM_printCompressStats(const LDM_compressStats *stats) { - int i = 0; - printf("=====================\n"); - printf("Compression statistics\n"); - //TODO: compute percentage matched? - printf("Window size, hash table size (bytes): 2^%u, 2^%u\n", - stats->windowSizeLog, stats->hashTableSizeLog); - printf("num matches, total match length: %u, %llu\n", - stats->numMatches, - stats->totalMatchLength); - 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: offset, num matches, %% of matches\n"); - - for (; i <= intLog2(stats->maxOffset); i++) { - printf("2^%*d: %10u %6.3f%%\n", 2, i, - stats->offsetHistogram[i], - 100.0 * (double) stats->offsetHistogram[i] / - (double) stats->numMatches); - } - printf("\n"); - - - printf("num collisions, num hash inserts, %% collisions: %u, %u, %.3f\n", - stats->numCollisions, stats->numHashInserts, - stats->numHashInserts == 0 ? - 1.0 : (100.0 * (double)stats->numCollisions) / - (double)stats->numHashInserts); - printf("=====================\n"); - -} - -int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { - /* - if (memcmp(pIn, pMatch, LDM_MIN_MATCH_LENGTH) == 0) { - return 1; - } - return 0; - */ - - //TODO: This seems to be faster for some reason? - U32 lengthLeft = LDM_MIN_MATCH_LENGTH; - const BYTE *curIn = pIn; - const BYTE *curMatch = pMatch; - - for (; lengthLeft >= 8; lengthLeft -= 8) { - if (MEM_read64(curIn) != MEM_read64(curMatch)) { - return 0; - } - curIn += 8; - curMatch += 8; - } - if (lengthLeft > 0) { - return (MEM_read32(curIn) == MEM_read32(curMatch)); - } - return 1; -} - -/** - * Convert a sum computed from getChecksum to a hash value in the range - * of the hash table. - */ -static hash_t checksumToHash(U32 sum) { - return ((sum * 2654435761U) >> (32 - LDM_HASHLOG)); -} - -/** - * Computes a checksum based on rsync's checksum. - * - * a(k,l) = \sum_{i = k}^l x_i (mod M) - * b(k,l) = \sum_{i = k}^l ((l - i + 1) * x_i) (mod M) - * checksum(k,l) = a(k,l) + 2^{16} * b(k,l) - */ -static U32 getChecksum(const BYTE *buf, U32 len) { - U32 i; - U32 s1, s2; - - s1 = s2 = 0; - for (i = 0; i < (len - 4); i += 4) { - s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + - (2 * buf[i + 2]) + (buf[i + 3]) + - (10 * CHECKSUM_CHAR_OFFSET); - s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3] + - + (4 * CHECKSUM_CHAR_OFFSET); - - } - for(; i < len; i++) { - s1 += buf[i] + CHECKSUM_CHAR_OFFSET; - s2 += s1; - } - return (s1 & 0xffff) + (s2 << 16); -} - -/** - * Update a checksum computed from getChecksum(data, len). - * - * The checksum can be updated along its ends as follows: - * a(k+1, l+1) = (a(k,l) - x_k + x_{l+1}) (mod M) - * b(k+1, l+1) = (b(k,l) - (l-k+1)*x_k + (a(k+1,l+1)) (mod M) - * - * Thus toRemove should correspond to data[0]. - */ -static U32 updateChecksum(U32 sum, U32 len, - BYTE toRemove, BYTE toAdd) { - U32 s1 = (sum & 0xffff) - toRemove + toAdd; - U32 s2 = (sum >> 16) - ((toRemove + CHECKSUM_CHAR_OFFSET) * len) + s1; - - return (s1 & 0xffff) + (s2 << 16); -} - -/** - * Update cctx->nextSum, cctx->nextHash, and cctx->nextPosHashed - * based on cctx->lastSum 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) { -#ifdef RUN_CHECKS - U32 check; - if ((cctx->nextIp - cctx->ibase != 1) && - (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { - printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, - cctx->DEBUG_setNextHash - cctx->ibase); - } - - cctx->DEBUG_setNextHash = cctx->nextIp; -#endif - -// cctx->nextSum = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); - cctx->nextSum = updateChecksum( - cctx->lastSum, LDM_HASH_LENGTH, - cctx->lastPosHashed[0], - cctx->lastPosHashed[LDM_HASH_LENGTH]); - cctx->nextPosHashed = cctx->nextIp; - cctx->nextHash = checksumToHash(cctx->nextSum); - -#ifdef RUN_CHECKS - check = getChecksum(cctx->nextIp, LDM_HASH_LENGTH); - - if (check != cctx->nextSum) { - printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); - } - - if ((cctx->nextIp - cctx->lastPosHashed) != 1) { - printf("setNextHash: nextIp != lastPosHashed + 1. %zu %zu %zu\n", - cctx->nextIp - cctx->ibase, cctx->lastPosHashed - cctx->ibase, - cctx->ip - cctx->ibase); - } -#endif -} - -static void putHashOfCurrentPositionFromHash( - LDM_CCtx *cctx, hash_t hash, U32 sum) { -#ifdef COMPUTE_STATS - if (cctx->stats.numHashInserts < LDM_HASHTABLESIZE_U32) { - offset_t offset = cctx->hashTable[hash].offset; - cctx->stats.numHashInserts++; - if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { - cctx->stats.numCollisions++; - } - } -#endif - - // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. - // Note: this works only when cctx->step is 1. - if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { - const LDM_hashEntry entry = { cctx->ip - cctx->ibase }; - cctx->hashTable[hash] = entry; - } - - cctx->lastPosHashed = cctx->ip; - cctx->lastHash = hash; - cctx->lastSum = sum; -} - -/** - * Copy over the cctx->lastHash, cctx->lastSum, and cctx->lastPosHashed - * fields from the "next" fields. - * - * This requires that cctx->ip == cctx->nextPosHashed. - */ -static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { -#ifdef RUN_CHECKS - if (cctx->ip != cctx->nextPosHashed) { - printf("CHECK failed: updateLastHashFromNextHash %zu\n", - cctx->ip - cctx->ibase); - } -#endif - putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); -} - -/** - * Insert hash of the current position into the hash table. - */ -static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - U32 sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); - hash_t hash = checksumToHash(sum); - -#ifdef RUN_CHECKS - if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { - printf("CHECK failed: putHashOfCurrentPosition %zu\n", - cctx->ip - cctx->ibase); - } -#endif - - putHashOfCurrentPositionFromHash(cctx, hash, sum); -} - -/** - * Returns the position of the entry at hashTable[hash]. - */ -static const BYTE *getPositionOnHash(LDM_CCtx *cctx, hash_t hash) { - return cctx->hashTable[hash].offset + cctx->ibase; -} - -U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { - const BYTE * const pStart = pIn; - while (pIn < pInLimit - 1) { - BYTE const diff = (*pMatch) ^ *(pIn); - if (!diff) { - pIn++; - pMatch++; - continue; - } - return (U32)(pIn - pStart); - } - return (U32)(pIn - pStart); -} - -void LDM_readHeader(const void *src, U64 *compressedSize, - U64 *decompressedSize) { - const BYTE *ip = (const BYTE *)src; - *compressedSize = MEM_readLE64(ip); - ip += sizeof(U64); - *decompressedSize = MEM_readLE64(ip); - // ip += sizeof(U64); -} - -void 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)); - cctx->hashTable = calloc(LDM_HASHTABLESIZE_U32, sizeof(LDM_hashEntry)); -// memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); - 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; - - cctx->DEBUG_setNextHash = 0; -} - -void LDM_destroyCCtx(LDM_CCtx *cctx) { - free(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). - * - */ -static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { - cctx->nextIp = cctx->ip + cctx->step; - - do { - hash_t h; - U32 sum; - setNextHash(cctx); - h = cctx->nextHash; - sum = cctx->nextSum; - cctx->ip = cctx->nextIp; - cctx->nextIp += cctx->step; - - if (cctx->ip > cctx->imatchLimit) { - return 1; - } - - *match = getPositionOnHash(cctx, h); - putHashOfCurrentPositionFromHash(cctx, h, sum); - - } while (cctx->ip - *match > LDM_WINDOW_SIZE || - !LDM_isValidMatch(cctx->ip, *match)); - setNextHash(cctx); - return 0; -} - -void LDM_encodeLiteralLengthAndLiterals( - LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength) { - /* Encode the literal length. */ - if (literalLength >= RUN_MASK) { - int len = (int)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 U32 literalLength, - const U32 offset, - const U32 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) { - unsigned 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; - LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); - - /* Hash the first position and put it into the hash table. */ - LDM_putHashOfCurrentPosition(&cctx); - - /** - * 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) == 0) { -#ifdef COMPUTE_STATS - cctx.stats.numMatches++; -#endif - - /** - * Catch up: look back to extend the match backwards from the found match. - */ - while (cctx.ip > cctx.anchor && match > cctx.ibase && - cctx.ip[-1] == match[-1]) { - cctx.ip--; - match--; - } - - /** - * Write current block (literals, literal length, match offset, match - * length) and update pointers and hashes. - */ - { - const U32 literalLength = cctx.ip - cctx.anchor; - const U32 offset = cctx.ip - match; - const U32 matchLength = LDM_countMatchLength( - cctx.ip + LDM_MIN_MATCH_LENGTH, match + LDM_MIN_MATCH_LENGTH, - cctx.ihashLimit); - - 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)]++; -#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); - } - - // LDM_outputHashTableOffsetHistogram(&cctx); - - /* Encode the last literals (no more matches). */ - { - const size_t lastRun = cctx.iend - cctx.anchor; - BYTE *pToken = cctx.op++; - LDM_encodeLiteralLengthAndLiterals(&cctx, pToken, lastRun); - } - -#ifdef COMPUTE_STATS - LDM_printCompressStats(&cctx.stats); - LDM_outputHashTableOccupancy(cctx.hashTable, LDM_HASHTABLESIZE_U32); -#endif - - { - const size_t ret = cctx.op - cctx.obase; - LDM_destroyCCtx(&cctx); - return ret; - } -} - -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 - 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; - - // Inefficient for now. - while (match < cpy - offset && dctx.op < dctx.oend) { - *(dctx.op)++ = *match++; - } - } - return dctx.op - (BYTE *)dst; -} - -// TODO: implement and test hash function -void LDM_test(void) { -} - -/* -void LDM_test(const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { - const BYTE *ip = (const BYTE *)src + 1125; - U32 sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); - U32 sum2; - ++ip; - for (; ip < (const BYTE *)src + 1125 + 100; ip++) { - sum2 = updateChecksum(sum, LDM_HASH_LENGTH, - ip[-1], ip[LDM_HASH_LENGTH - 1]); - sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); - printf("TEST HASH: %zu %u %u\n", ip - (const BYTE *)src, sum, sum2); - } -} -*/ - - diff --git a/contrib/long_distance_matching/versions/v0.5/ldm.h b/contrib/long_distance_matching/versions/v0.5/ldm.h deleted file mode 100644 index 70cda8b84..000000000 --- a/contrib/long_distance_matching/versions/v0.5/ldm.h +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef LDM_H -#define LDM_H - -#include /* size_t */ - -#include "mem.h" // from /lib/common/mem.h - -#define LDM_COMPRESS_SIZE 8 -#define LDM_DECOMPRESS_SIZE 8 -#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) -#define LDM_OFFSET_SIZE 4 - -// Defines the size of the hash table. -#define LDM_MEMORY_USAGE 16 -#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) - -#define LDM_WINDOW_SIZE_LOG 25 -#define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) - -//These should be multiples of four. -#define LDM_MIN_MATCH_LENGTH 4 -#define LDM_HASH_LENGTH 4 - -typedef U32 offset_t; -typedef U32 hash_t; -typedef struct LDM_hashEntry LDM_hashEntry; -typedef struct LDM_compressStats LDM_compressStats; -typedef struct LDM_CCtx LDM_CCtx; -typedef struct LDM_DCtx LDM_DCtx; - -/** - * Compresses src into dst. - * - * NB: This currently ignores maxDstSize and assumes enough space is available. - * - * Block format (see lz4 documentation for more information): - * github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md - * - * A block is composed of sequences. Each sequence begins with a token, which - * is a one-byte value separated into two 4-bit fields. - * - * The first field uses the four high bits of the token and encodes the literal - * length. If the field value is 0, there is no literal. If it is 15, - * additional bytes are added (each ranging from 0 to 255) to the previous - * value to produce a total length. - * - * Following the token and optional length bytes are the literals. - * - * Next are the 4 bytes representing the offset of the match (2 in lz4), - * representing the position to copy the literals. - * - * The lower four bits of the token encode the match length. With additional - * bytes added similarly to the additional literal length bytes after the offset. - * - * The last sequence is incomplete and stops right after the lieterals. - * - */ -size_t LDM_compress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -/** - * Initialize the compression context. - * - * Allocates memory for the hash table. - */ -void LDM_initializeCCtx(LDM_CCtx *cctx, - const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -/** - * Frees up memory allocating in initializeCCtx - */ -void LDM_destroyCCtx(LDM_CCtx *cctx); - -/** - * Prints the percentage of the hash table occupied (where occupied is defined - * as the entry being non-zero). - */ -void LDM_outputHashTableOccupancy(const LDM_hashEntry *hashTable, - U32 hashTableSize); - -/** - * Prints the distribution of offsets in the hash table. - * - * The offsets are defined as the distance of the hash table entry from the - * current input position of the cctx. - */ -void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx); - -/** - * Outputs compression statistics to stdout. - */ -void LDM_printCompressStats(const LDM_compressStats *stats); -/** - * Checks whether the LDM_MIN_MATCH_LENGTH bytes from p are the same as the - * LDM_MIN_MATCH_LENGTH bytes from match. - * - * This assumes LDM_MIN_MATCH_LENGTH is a multiple of four. - * - * Return 1 if valid, 0 otherwise. - */ -int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch); - -/** - * Counts the number of bytes that match from pIn and pMatch, - * up to pInLimit. - */ -U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit); - -/** - * Encode the literal length followed by the literals. - * - * The literal length is written to the upper four bits of pToken, with - * additional bytes written to the output as needed (see lz4). - * - * This is followed by literalLength bytes corresponding to the literals. - */ -void LDM_encodeLiteralLengthAndLiterals( - LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength); - -/** - * Write current block (literals, literal length, match offset, - * match length). - */ -void LDM_outputBlock(LDM_CCtx *cctx, - const U32 literalLength, - const U32 offset, - const U32 matchLength); - -/** - * Decompresses src into dst. - * - * Note: assumes src does not have a header. - */ -size_t LDM_decompress(const void *src, size_t srcSize, - void *dst, size_t maxDstSize); - -/** - * Initialize the decompression context. - */ -void LDM_initializeDCtx(LDM_DCtx *dctx, - const void *src, size_t compressedSize, - void *dst, size_t maxDecompressedSize); - -/** - * Reads the header from src and writes the compressed size and - * decompressed size into compressedSize and decompressedSize respectively. - * - * NB: LDM_compress and LDM_decompress currently do not add/read headers. - */ -void LDM_readHeader(const void *src, U64 *compressedSize, - U64 *decompressedSize); - -void LDM_test(void); - -#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/versions/v0.5/main-ldm.c b/contrib/long_distance_matching/versions/v0.5/main-ldm.c deleted file mode 100644 index ea6375ba7..000000000 --- a/contrib/long_distance_matching/versions/v0.5/main-ldm.c +++ /dev/null @@ -1,270 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "ldm.h" -#include "zstd.h" - -#define DEBUG -#define TEST - -/* Compress file given by fname and output to oname. - * Returns 0 if successful, error code otherwise. - * - * TODO: This currently seg faults 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; - - /* 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; - - /* Go to the location corresponding to the last byte. */ - /* TODO: fallocate? */ - if (lseek(fdout, maxCompressedSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* Write a dummy byte at the last location. */ - if (write(fdout, "", 1) != 1) { - perror("write 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; - } - - /* 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; - } - - compressedSize = LDM_HEADER_SIZE + - LDM_compress(src, statbuf.st_size, - dst + LDM_HEADER_SIZE, maxCompressedSize); - - // Write compress and decompress size to header - // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &compressedSize, 8); - memcpy(dst + 8, &(statbuf.st_size), 8); - -#ifdef DEBUG - printf("Compressed size: %zu\n", compressedSize); - printf("Decompressed size: %zu\n", (size_t)statbuf.st_size); -#endif - - // Truncate file to compressedSize. - ftruncate(fdout, compressedSize); - - printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, - (unsigned)statbuf.st_size, (unsigned)compressedSize, oname, - (double)compressedSize / (statbuf.st_size) * 100); - - // Close files. - close(fdin); - close(fdout); - return 0; -} - -/* 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); - - /* Go to the location corresponding to the last byte. */ - if (lseek(fdout, decompressedSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write error"); - return 1; - } - - /* 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); - ftruncate(fdout, 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 void verify(const char *inpFilename, const char *decFilename) { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); - - 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"); - } - } - - fclose(decFp); - fclose(inpFp); -} - -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 */ - { - struct timeval tv1, tv2; - gettimeofday(&tv1, NULL); - if (compress(inpFilename, ldmFilename)) { - printf("Compress error"); - return 1; - } - gettimeofday(&tv2, NULL); - printf("Total compress time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); - } - - /* Decompress */ - { - struct timeval tv1, tv2; - gettimeofday(&tv1, NULL); - if (decompress(ldmFilename, decFilename)) { - printf("Decompress error"); - 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 */ - verify(inpFilename, decFilename); - -#ifdef TEST - LDM_test(); -#endif - return 0; -} From 7a28b9e4a3a6e2c7ab1a9a66beceea95e710560e Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 17 Jul 2017 15:29:11 -0700 Subject: [PATCH 162/318] [libzstd] Pull optimal parser state out of seqStore_t --- lib/common/zstd_internal.h | 42 +++---- lib/compress/zstd_compress.c | 23 ++-- lib/compress/zstd_opt.h | 208 ++++++++++++++++++----------------- 3 files changed, 139 insertions(+), 134 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 7f183c8c8..1621bca61 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -215,20 +215,6 @@ MEM_STATIC void ZSTD_wildcopy_e(void* dst, const void* src, void* dstEnd) /* s *********************************************/ typedef struct ZSTD_stats_s ZSTD_stats_t; -typedef struct { - U32 off; - U32 len; -} ZSTD_match_t; - -typedef struct { - U32 price; - U32 off; - U32 mlen; - U32 litlen; - U32 rep[ZSTD_REP_NUM]; -} ZSTD_optimal_t; - - typedef struct seqDef_s { U32 offset; U16 litLength; @@ -248,13 +234,29 @@ typedef struct { U32 longLengthPos; U32 rep[ZSTD_REP_NUM]; U32 repToConfirm[ZSTD_REP_NUM]; - /* opt */ - ZSTD_optimal_t* priceTable; - ZSTD_match_t* matchTable; - U32* matchLengthFreq; - U32* litLengthFreq; +} seqStore_t; + +typedef struct { + U32 off; + U32 len; +} ZSTD_match_t; + +typedef struct { + U32 price; + U32 off; + U32 mlen; + U32 litlen; + U32 rep[ZSTD_REP_NUM]; +} ZSTD_optimal_t; + +typedef struct { U32* litFreq; + U32* litLengthFreq; + U32* matchLengthFreq; U32* offCodeFreq; + ZSTD_match_t* matchTable; + ZSTD_optimal_t* priceTable; + U32 matchLengthSum; U32 matchSum; U32 litLengthSum; @@ -270,7 +272,7 @@ typedef struct { U32 cachedPrice; U32 cachedLitLength; const BYTE* cachedLiterals; -} seqStore_t; +} optState_t; typedef struct { U32 hufCTable[HUF_CTABLE_SIZE_U32(255)]; diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 64f783c71..5d8e26327 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -96,6 +96,7 @@ struct ZSTD_CCtx_s { size_t staticSize; seqStore_t seqStore; /* sequences storage ptrs */ + optState_t optState; U32* hashTable; U32* hashTable3; U32* chainTable; @@ -594,7 +595,7 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_parameters params, U64 ple cctx->dictID = 0; cctx->loadedDictEnd = 0; { int i; for (i=0; iseqStore.rep[i] = repStartValue[i]; } - cctx->seqStore.litLengthSum = 0; /* force reset of btopt stats */ + cctx->optState.litLengthSum = 0; /* force reset of btopt stats */ XXH64_reset(&cctx->xxhState, 0); return 0; } @@ -693,7 +694,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->lowLimit = 0; { int i; for (i=0; iseqStore.rep[i] = repStartValue[i]; } zc->hashLog3 = hashLog3; - zc->seqStore.litLengthSum = 0; + zc->optState.litLengthSum = 0; ptr = zc->entropy + 1; @@ -701,15 +702,15 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, if ((params.cParams.strategy == ZSTD_btopt) || (params.cParams.strategy == ZSTD_btultra)) { DEBUGLOG(5, "reserving optimal parser space"); assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */ - zc->seqStore.litFreq = (U32*)ptr; - zc->seqStore.litLengthFreq = zc->seqStore.litFreq + (1<seqStore.matchLengthFreq = zc->seqStore.litLengthFreq + (MaxLL+1); - zc->seqStore.offCodeFreq = zc->seqStore.matchLengthFreq + (MaxML+1); - ptr = zc->seqStore.offCodeFreq + (MaxOff+1); - zc->seqStore.matchTable = (ZSTD_match_t*)ptr; - ptr = zc->seqStore.matchTable + ZSTD_OPT_NUM+1; - zc->seqStore.priceTable = (ZSTD_optimal_t*)ptr; - ptr = zc->seqStore.priceTable + ZSTD_OPT_NUM+1; + 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; } /* table Space */ diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index d6e3449a7..53e806eb7 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -22,173 +22,173 @@ /*-************************************* * Price functions for optimal parser ***************************************/ -FORCE_INLINE void ZSTD_setLog2Prices(seqStore_t* ssPtr) +FORCE_INLINE void ZSTD_setLog2Prices(optState_t* optPtr) { - ssPtr->log2matchLengthSum = ZSTD_highbit32(ssPtr->matchLengthSum+1); - ssPtr->log2litLengthSum = ZSTD_highbit32(ssPtr->litLengthSum+1); - ssPtr->log2litSum = ZSTD_highbit32(ssPtr->litSum+1); - ssPtr->log2offCodeSum = ZSTD_highbit32(ssPtr->offCodeSum+1); - ssPtr->factor = 1 + ((ssPtr->litSum>>5) / ssPtr->litLengthSum) + ((ssPtr->litSum<<1) / (ssPtr->litSum + ssPtr->matchSum)); + optPtr->log2matchLengthSum = ZSTD_highbit32(optPtr->matchLengthSum+1); + optPtr->log2litLengthSum = ZSTD_highbit32(optPtr->litLengthSum+1); + optPtr->log2litSum = ZSTD_highbit32(optPtr->litSum+1); + optPtr->log2offCodeSum = ZSTD_highbit32(optPtr->offCodeSum+1); + optPtr->factor = 1 + ((optPtr->litSum>>5) / optPtr->litLengthSum) + ((optPtr->litSum<<1) / (optPtr->litSum + optPtr->matchSum)); } -MEM_STATIC void ZSTD_rescaleFreqs(seqStore_t* ssPtr, const BYTE* src, size_t srcSize) +MEM_STATIC void ZSTD_rescaleFreqs(optState_t* optPtr, const BYTE* src, size_t srcSize) { unsigned u; - ssPtr->cachedLiterals = NULL; - ssPtr->cachedPrice = ssPtr->cachedLitLength = 0; - ssPtr->staticPrices = 0; + optPtr->cachedLiterals = NULL; + optPtr->cachedPrice = optPtr->cachedLitLength = 0; + optPtr->staticPrices = 0; - if (ssPtr->litLengthSum == 0) { - if (srcSize <= 1024) ssPtr->staticPrices = 1; + if (optPtr->litLengthSum == 0) { + if (srcSize <= 1024) optPtr->staticPrices = 1; - assert(ssPtr->litFreq!=NULL); + assert(optPtr->litFreq!=NULL); for (u=0; u<=MaxLit; u++) - ssPtr->litFreq[u] = 0; + optPtr->litFreq[u] = 0; for (u=0; ulitFreq[src[u]]++; + optPtr->litFreq[src[u]]++; - ssPtr->litSum = 0; - ssPtr->litLengthSum = MaxLL+1; - ssPtr->matchLengthSum = MaxML+1; - ssPtr->offCodeSum = (MaxOff+1); - ssPtr->matchSum = (ZSTD_LITFREQ_ADD<litSum = 0; + optPtr->litLengthSum = MaxLL+1; + optPtr->matchLengthSum = MaxML+1; + optPtr->offCodeSum = (MaxOff+1); + optPtr->matchSum = (ZSTD_LITFREQ_ADD<litFreq[u] = 1 + (ssPtr->litFreq[u]>>ZSTD_FREQ_DIV); - ssPtr->litSum += ssPtr->litFreq[u]; + optPtr->litFreq[u] = 1 + (optPtr->litFreq[u]>>ZSTD_FREQ_DIV); + optPtr->litSum += optPtr->litFreq[u]; } for (u=0; u<=MaxLL; u++) - ssPtr->litLengthFreq[u] = 1; + optPtr->litLengthFreq[u] = 1; for (u=0; u<=MaxML; u++) - ssPtr->matchLengthFreq[u] = 1; + optPtr->matchLengthFreq[u] = 1; for (u=0; u<=MaxOff; u++) - ssPtr->offCodeFreq[u] = 1; + optPtr->offCodeFreq[u] = 1; } else { - ssPtr->matchLengthSum = 0; - ssPtr->litLengthSum = 0; - ssPtr->offCodeSum = 0; - ssPtr->matchSum = 0; - ssPtr->litSum = 0; + optPtr->matchLengthSum = 0; + optPtr->litLengthSum = 0; + optPtr->offCodeSum = 0; + optPtr->matchSum = 0; + optPtr->litSum = 0; for (u=0; u<=MaxLit; u++) { - ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u]>>(ZSTD_FREQ_DIV+1)); - ssPtr->litSum += ssPtr->litFreq[u]; + optPtr->litFreq[u] = 1 + (optPtr->litFreq[u]>>(ZSTD_FREQ_DIV+1)); + optPtr->litSum += optPtr->litFreq[u]; } for (u=0; u<=MaxLL; u++) { - ssPtr->litLengthFreq[u] = 1 + (ssPtr->litLengthFreq[u]>>(ZSTD_FREQ_DIV+1)); - ssPtr->litLengthSum += ssPtr->litLengthFreq[u]; + optPtr->litLengthFreq[u] = 1 + (optPtr->litLengthFreq[u]>>(ZSTD_FREQ_DIV+1)); + optPtr->litLengthSum += optPtr->litLengthFreq[u]; } for (u=0; u<=MaxML; u++) { - ssPtr->matchLengthFreq[u] = 1 + (ssPtr->matchLengthFreq[u]>>ZSTD_FREQ_DIV); - ssPtr->matchLengthSum += ssPtr->matchLengthFreq[u]; - ssPtr->matchSum += ssPtr->matchLengthFreq[u] * (u + 3); + optPtr->matchLengthFreq[u] = 1 + (optPtr->matchLengthFreq[u]>>ZSTD_FREQ_DIV); + optPtr->matchLengthSum += optPtr->matchLengthFreq[u]; + optPtr->matchSum += optPtr->matchLengthFreq[u] * (u + 3); } - ssPtr->matchSum *= ZSTD_LITFREQ_ADD; + optPtr->matchSum *= ZSTD_LITFREQ_ADD; for (u=0; u<=MaxOff; u++) { - ssPtr->offCodeFreq[u] = 1 + (ssPtr->offCodeFreq[u]>>ZSTD_FREQ_DIV); - ssPtr->offCodeSum += ssPtr->offCodeFreq[u]; + optPtr->offCodeFreq[u] = 1 + (optPtr->offCodeFreq[u]>>ZSTD_FREQ_DIV); + optPtr->offCodeSum += optPtr->offCodeFreq[u]; } } - ZSTD_setLog2Prices(ssPtr); + ZSTD_setLog2Prices(optPtr); } -FORCE_INLINE U32 ZSTD_getLiteralPrice(seqStore_t* ssPtr, U32 litLength, const BYTE* literals) +FORCE_INLINE U32 ZSTD_getLiteralPrice(optState_t* optPtr, U32 litLength, const BYTE* literals) { U32 price, u; - if (ssPtr->staticPrices) + if (optPtr->staticPrices) return ZSTD_highbit32((U32)litLength+1) + (litLength*6); if (litLength == 0) - return ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[0]+1); + return optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[0]+1); /* literals */ - if (ssPtr->cachedLiterals == literals) { - U32 const additional = litLength - ssPtr->cachedLitLength; - const BYTE* literals2 = ssPtr->cachedLiterals + ssPtr->cachedLitLength; - price = ssPtr->cachedPrice + additional * ssPtr->log2litSum; + if (optPtr->cachedLiterals == literals) { + U32 const additional = litLength - optPtr->cachedLitLength; + const BYTE* literals2 = optPtr->cachedLiterals + optPtr->cachedLitLength; + price = optPtr->cachedPrice + additional * optPtr->log2litSum; for (u=0; u < additional; u++) - price -= ZSTD_highbit32(ssPtr->litFreq[literals2[u]]+1); - ssPtr->cachedPrice = price; - ssPtr->cachedLitLength = litLength; + price -= ZSTD_highbit32(optPtr->litFreq[literals2[u]]+1); + optPtr->cachedPrice = price; + optPtr->cachedLitLength = litLength; } else { - price = litLength * ssPtr->log2litSum; + price = litLength * optPtr->log2litSum; for (u=0; u < litLength; u++) - price -= ZSTD_highbit32(ssPtr->litFreq[literals[u]]+1); + price -= ZSTD_highbit32(optPtr->litFreq[literals[u]]+1); if (litLength >= 12) { - ssPtr->cachedLiterals = literals; - ssPtr->cachedPrice = price; - ssPtr->cachedLitLength = litLength; + optPtr->cachedLiterals = literals; + optPtr->cachedPrice = price; + optPtr->cachedLitLength = litLength; } } /* literal Length */ { const BYTE LL_deltaCode = 19; const BYTE llCode = (litLength>63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; - price += LL_bits[llCode] + ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[llCode]+1); + price += LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1); } return price; } -FORCE_INLINE U32 ZSTD_getPrice(seqStore_t* seqStorePtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength, const int ultra) +FORCE_INLINE U32 ZSTD_getPrice(optState_t* optPtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength, const int ultra) { /* offset */ U32 price; BYTE const offCode = (BYTE)ZSTD_highbit32(offset+1); - if (seqStorePtr->staticPrices) - return ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + ZSTD_highbit32((U32)matchLength+1) + 16 + offCode; + if (optPtr->staticPrices) + return ZSTD_getLiteralPrice(optPtr, litLength, literals) + ZSTD_highbit32((U32)matchLength+1) + 16 + offCode; - price = offCode + seqStorePtr->log2offCodeSum - ZSTD_highbit32(seqStorePtr->offCodeFreq[offCode]+1); + price = offCode + optPtr->log2offCodeSum - ZSTD_highbit32(optPtr->offCodeFreq[offCode]+1); if (!ultra && offCode >= 20) price += (offCode-19)*2; /* match Length */ { const BYTE ML_deltaCode = 36; const BYTE mlCode = (matchLength>127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; - price += ML_bits[mlCode] + seqStorePtr->log2matchLengthSum - ZSTD_highbit32(seqStorePtr->matchLengthFreq[mlCode]+1); + price += ML_bits[mlCode] + optPtr->log2matchLengthSum - ZSTD_highbit32(optPtr->matchLengthFreq[mlCode]+1); } - return price + ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + seqStorePtr->factor; + return price + ZSTD_getLiteralPrice(optPtr, litLength, literals) + optPtr->factor; } -MEM_STATIC void ZSTD_updatePrice(seqStore_t* seqStorePtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength) +MEM_STATIC void ZSTD_updatePrice(optState_t* optPtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength) { U32 u; /* literals */ - seqStorePtr->litSum += litLength*ZSTD_LITFREQ_ADD; + optPtr->litSum += litLength*ZSTD_LITFREQ_ADD; for (u=0; u < litLength; u++) - seqStorePtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD; + optPtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD; /* literal Length */ { const BYTE LL_deltaCode = 19; const BYTE llCode = (litLength>63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; - seqStorePtr->litLengthFreq[llCode]++; - seqStorePtr->litLengthSum++; + optPtr->litLengthFreq[llCode]++; + optPtr->litLengthSum++; } /* match offset */ { BYTE const offCode = (BYTE)ZSTD_highbit32(offset+1); - seqStorePtr->offCodeSum++; - seqStorePtr->offCodeFreq[offCode]++; + optPtr->offCodeSum++; + optPtr->offCodeFreq[offCode]++; } /* match Length */ { const BYTE ML_deltaCode = 36; const BYTE mlCode = (matchLength>127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; - seqStorePtr->matchLengthFreq[mlCode]++; - seqStorePtr->matchLengthSum++; + optPtr->matchLengthFreq[mlCode]++; + optPtr->matchLengthSum++; } - ZSTD_setLog2Prices(seqStorePtr); + ZSTD_setLog2Prices(optPtr); } @@ -417,6 +417,7 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, const void* src, size_t srcSize, const int ultra) { seqStore_t* seqStorePtr = &(ctx->seqStore); + optState_t* optStatePtr = &(ctx->optState); const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; @@ -430,14 +431,14 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, const U32 mls = ctx->appliedParams.cParams.searchLength; const U32 minMatch = (ctx->appliedParams.cParams.searchLength == 3) ? 3 : 4; - ZSTD_optimal_t* opt = seqStorePtr->priceTable; - ZSTD_match_t* matches = seqStorePtr->matchTable; + ZSTD_optimal_t* opt = optStatePtr->priceTable; + ZSTD_match_t* matches = optStatePtr->matchTable; const BYTE* inr; U32 offset, rep[ZSTD_REP_NUM]; /* init */ ctx->nextToUpdate3 = ctx->nextToUpdate; - ZSTD_rescaleFreqs(seqStorePtr, (const BYTE*)src, srcSize); + ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize); ip += (ip==prefixStart); { U32 i; for (i=0; irep[i]; } @@ -462,7 +463,7 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, } best_off = i - (ip == anchor); do { - price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); + price = ZSTD_getPrice(optStatePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); if (mlen > last_pos || price < opt[mlen].price) SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */ mlen--; @@ -487,7 +488,7 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, mlen = (u>0) ? matches[u-1].len+1 : best_mlen; best_mlen = matches[u].len; while (mlen <= best_mlen) { - price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra); + price = ZSTD_getPrice(optStatePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra); if (mlen > last_pos || price < opt[mlen].price) SET_PRICE(mlen, mlen, matches[u].off, litlen, price); /* note : macro modifies last_pos */ mlen++; @@ -507,12 +508,12 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, if (opt[cur-1].mlen == 1) { litlen = opt[cur-1].litlen + 1; if (cur > litlen) { - price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr-litlen); + price = opt[cur - litlen].price + ZSTD_getLiteralPrice(optStatePtr, litlen, inr-litlen); } else - price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor); + price = ZSTD_getLiteralPrice(optStatePtr, litlen, anchor); } else { litlen = 1; - price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr-1); + price = opt[cur - 1].price + ZSTD_getLiteralPrice(optStatePtr, litlen, inr-1); } if (cur > last_pos || price <= opt[cur].price) @@ -554,12 +555,12 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, if (opt[cur].mlen == 1) { litlen = opt[cur].litlen; if (cur > litlen) { - price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr-litlen, best_off, mlen - MINMATCH, ultra); + price = opt[cur - litlen].price + ZSTD_getPrice(optStatePtr, litlen, inr-litlen, best_off, mlen - MINMATCH, ultra); } else - price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); + price = ZSTD_getPrice(optStatePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); } else { litlen = 0; - price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); + price = opt[cur].price + ZSTD_getPrice(optStatePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); } if (cur + mlen > last_pos || price <= opt[cur + mlen].price) @@ -586,12 +587,12 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, if (opt[cur].mlen == 1) { litlen = opt[cur].litlen; if (cur > litlen) - price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip+cur-litlen, matches[u].off-1, mlen - MINMATCH, ultra); + price = opt[cur - litlen].price + ZSTD_getPrice(optStatePtr, litlen, ip+cur-litlen, matches[u].off-1, mlen - MINMATCH, ultra); else - price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra); + price = ZSTD_getPrice(optStatePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra); } else { litlen = 0; - price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off-1, mlen - MINMATCH, ultra); + price = opt[cur].price + ZSTD_getPrice(optStatePtr, 0, NULL, matches[u].off-1, mlen - MINMATCH, ultra); } if (cur + mlen > last_pos || (price < opt[cur + mlen].price)) @@ -645,7 +646,7 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ if (litLength==0) offset--; } - ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen-MINMATCH); + ZSTD_updatePrice(optStatePtr, litLength, anchor, offset, mlen-MINMATCH); ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen-MINMATCH); anchor = ip = ip + mlen; } } /* for (cur=0; cur < last_pos; ) */ @@ -666,6 +667,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, const void* src, size_t srcSize, const int ultra) { seqStore_t* seqStorePtr = &(ctx->seqStore); + optState_t* optStatePtr = &(ctx->optState); const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; @@ -683,8 +685,8 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, const U32 mls = ctx->appliedParams.cParams.searchLength; const U32 minMatch = (ctx->appliedParams.cParams.searchLength == 3) ? 3 : 4; - ZSTD_optimal_t* opt = seqStorePtr->priceTable; - ZSTD_match_t* matches = seqStorePtr->matchTable; + ZSTD_optimal_t* opt = optStatePtr->priceTable; + ZSTD_match_t* matches = optStatePtr->matchTable; const BYTE* inr; /* init */ @@ -692,7 +694,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, { U32 i; for (i=0; irep[i]; } ctx->nextToUpdate3 = ctx->nextToUpdate; - ZSTD_rescaleFreqs(seqStorePtr, (const BYTE*)src, srcSize); + ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize); ip += (ip==prefixStart); /* Match Loop */ @@ -726,7 +728,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, best_off = i - (ip==anchor); litlen = opt[0].litlen; do { - price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); + price = ZSTD_getPrice(optStatePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); if (mlen > last_pos || price < opt[mlen].price) SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */ mlen--; @@ -756,7 +758,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, best_mlen = matches[u].len; litlen = opt[0].litlen; while (mlen <= best_mlen) { - price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra); + price = ZSTD_getPrice(optStatePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra); if (mlen > last_pos || price < opt[mlen].price) SET_PRICE(mlen, mlen, matches[u].off, litlen, price); mlen++; @@ -773,12 +775,12 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, if (opt[cur-1].mlen == 1) { litlen = opt[cur-1].litlen + 1; if (cur > litlen) { - price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr-litlen); + price = opt[cur - litlen].price + ZSTD_getLiteralPrice(optStatePtr, litlen, inr-litlen); } else - price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor); + price = ZSTD_getLiteralPrice(optStatePtr, litlen, anchor); } else { litlen = 1; - price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr-1); + price = opt[cur - 1].price + ZSTD_getLiteralPrice(optStatePtr, litlen, inr-1); } if (cur > last_pos || price <= opt[cur].price) @@ -826,12 +828,12 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, if (opt[cur].mlen == 1) { litlen = opt[cur].litlen; if (cur > litlen) { - price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr-litlen, best_off, mlen - MINMATCH, ultra); + price = opt[cur - litlen].price + ZSTD_getPrice(optStatePtr, litlen, inr-litlen, best_off, mlen - MINMATCH, ultra); } else - price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); + price = ZSTD_getPrice(optStatePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); } else { litlen = 0; - price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); + price = opt[cur].price + ZSTD_getPrice(optStatePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); } if (cur + mlen > last_pos || price <= opt[cur + mlen].price) @@ -858,12 +860,12 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, if (opt[cur].mlen == 1) { litlen = opt[cur].litlen; if (cur > litlen) - price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip+cur-litlen, matches[u].off-1, mlen - MINMATCH, ultra); + price = opt[cur - litlen].price + ZSTD_getPrice(optStatePtr, litlen, ip+cur-litlen, matches[u].off-1, mlen - MINMATCH, ultra); else - price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra); + price = ZSTD_getPrice(optStatePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra); } else { litlen = 0; - price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off-1, mlen - MINMATCH, ultra); + price = opt[cur].price + ZSTD_getPrice(optStatePtr, 0, NULL, matches[u].off-1, mlen - MINMATCH, ultra); } if (cur + mlen > last_pos || (price < opt[cur + mlen].price)) @@ -918,7 +920,7 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ if (litLength==0) offset--; } - ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen-MINMATCH); + ZSTD_updatePrice(optStatePtr, litLength, anchor, offset, mlen-MINMATCH); ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen-MINMATCH); anchor = ip = ip + mlen; } } /* for (cur=0; cur < last_pos; ) */ From b3c9e02bb6ab8807990ddc85a99f9953b9ee3578 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 15:34:58 -0700 Subject: [PATCH 163/318] added signal to other threads whenever error occurs --- contrib/adaptive-compression/adapt.c | 44 ++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 3aec50c07..01a73a669 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -247,13 +247,31 @@ static adaptCCtx* createCCtx(unsigned numJobs) return ctx; } +static void signalErrorToThreads(adaptCCtx* ctx) +{ + ctx->threadError = 1; + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); + pthread_cond_signal(&ctx->jobReady_cond.pCond); + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); + pthread_cond_signal(&ctx->jobCompressed_cond.pCond); + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + + pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); + pthread_cond_signal(&ctx->jobWrite_cond.pCond); + pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); + + pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); + pthread_cond_signal(&ctx->allJobsCompleted_cond.pCond); + pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); +} static void waitUntilAllJobsCompleted(adaptCCtx* ctx) { if (!ctx) return; pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); - while (ctx->allJobsCompleted == 0) { + while (ctx->allJobsCompleted == 0 && !ctx->threadError) { pthread_cond_wait(&ctx->allJobsCompleted_cond.pCond, &ctx->allJobsCompleted_mutex.pMutex); } pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); @@ -327,7 +345,7 @@ static void* compressionThread(void* arg) jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); - while(currJob + 1 > ctx->jobReadyID) { + while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { ctx->stats.waitReady++; ctx->stats.readyCounter++; DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); @@ -351,7 +369,7 @@ static void* compressionThread(void* arg) size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return arg; } } @@ -362,7 +380,7 @@ static void* compressionThread(void* arg) if (ZSTD_isError(hSize)) { DISPLAY("Error: something went wrong while continuing compression\n"); job->compressedSize = hSize; - ctx->threadError = 1; + signalErrorToThreads(ctx); return arg; } ZSTD_invalidateRepCodes(ctx->cctx); @@ -372,7 +390,7 @@ static void* compressionThread(void* arg) ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, job->src.size); if (ZSTD_isError(job->compressedSize)) { DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(job->compressedSize)); - ctx->threadError = 1; + signalErrorToThreads(ctx); return arg; } job->dst.size = job->compressedSize; @@ -422,7 +440,7 @@ static void* outputThread(void* arg) jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); - while (currJob + 1 > ctx->jobCompressedID) { + while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; if (!ctx->completionMeasured) { @@ -439,14 +457,14 @@ static void* outputThread(void* arg) size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { DISPLAY("Error: an error occurred during compression\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return arg; } { size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); if (writeSize != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return arg; } } @@ -482,7 +500,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) DEBUG(3, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); - while (nextJob - ctx->jobWriteID >= ctx->numJobs) { + while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; if (!ctx->completionMeasured) { @@ -545,7 +563,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA pthread_t out; if (pthread_create(&out, NULL, &outputThread, otArg)) { DISPLAY("Error: could not create output thread\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return 1; } } @@ -555,7 +573,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA pthread_t compression; if (pthread_create(&compression, NULL, &compressionThread, ctx)) { DISPLAY("Error: could not create compression thread\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return 1; } } @@ -565,7 +583,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA size_t const readSize = fread(ctx->input.buffer.start + ctx->input.filled, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return 1; } g_streamedSize += readSize; @@ -574,7 +592,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA int const last = feof(srcFile); int const error = createCompressionJob(ctx, readSize, last); if (error != 0) { - ctx->threadError = 1; + signalErrorToThreads(ctx); return error; } } From 5af04c57b058fcf7be1e0b8545d918c901c4bbb6 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 17:59:50 -0700 Subject: [PATCH 164/318] change parameters for compression level adapt --- contrib/adaptive-compression/adapt.c | 43 +++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 01a73a669..62f5ec912 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -25,7 +25,7 @@ #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 1 -#define MAX_COMPRESSION_LEVEL_CHANGE 10 +#define MAX_COMPRESSION_LEVEL_CHANGE 3 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -277,6 +277,15 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); } +/* this function normalizes counters when compression level is changing */ +static void reduceCounters(adaptCCtx* ctx) +{ + unsigned const min = MIN(ctx->stats.compressedCounter, MIN(ctx->stats.writeCounter, ctx->stats.readyCounter)); + ctx->stats.writeCounter -= min; + ctx->stats.compressedCounter -= min; + ctx->stats.readyCounter -= min; +} + /* * Compression level is changed depending on which part of the compression process is lagging * Currently, three theads exist for job creation, compression, and file writing respectively. @@ -285,10 +294,10 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) * compression thread lag => decreased compression level * detecting which thread is lagging is done by keeping track of how many calls each thread makes to pthread_cond_wait */ -static unsigned adaptCompressionLevel(adaptCCtx* ctx) +static void adaptCompressionLevel(adaptCCtx* ctx) { if (g_forceCompressionLevel) { - return g_compressionLevel; + ctx->compressionLevel = g_compressionLevel; } else { unsigned reset = 0; @@ -296,10 +305,11 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter; - unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)); - unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)); - unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)); - DEBUG(3, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); + unsigned const writeSlow = (compressWaiting && createWaiting); + unsigned const compressSlow = (writeWaiting && createWaiting); + unsigned const createSlow = (compressWaiting && writeWaiting); + DEBUG(2, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); + DEBUG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; } @@ -310,10 +320,10 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) } else if (compressSlow && ctx->compressionLevel > 1) { double const completion = ctx->completion; - unsigned const maxChange = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(2, "completion: %f\n", completion); + DEBUG(3, "completion: %f\n", completion); ctx->compressionLevel -= change; reset = 1; } @@ -324,7 +334,6 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) ctx->completion = 1; ctx->completionMeasured = 0; } - return ctx->compressionLevel; } } @@ -348,6 +357,8 @@ static void* compressionThread(void* arg) while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { ctx->stats.waitReady++; ctx->stats.readyCounter++; + reduceCounters(ctx); + adaptCompressionLevel(ctx); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } @@ -357,13 +368,13 @@ static void* compressionThread(void* arg) DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); /* compress the data */ { - unsigned const cLevel = adaptCompressionLevel(ctx); + unsigned const cLevel = ctx->compressionLevel; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); /* begin compression */ { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); - DEBUG(2, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); + DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); @@ -443,11 +454,13 @@ static void* outputThread(void* arg) while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; + reduceCounters(ctx); if (!ctx->completionMeasured) { ctx->completion = ZSTD_getCompletion(ctx->cctx); ctx->completionMeasured = 1; } - DEBUG(2, "output detected completion: %f\n", ctx->completion); + adaptCompressionLevel(ctx); + DEBUG(3, "output detected completion: %f\n", ctx->completion); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } @@ -503,11 +516,13 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; + reduceCounters(ctx); if (!ctx->completionMeasured) { ctx->completion = ZSTD_getCompletion(ctx->cctx); ctx->completionMeasured = 1; } - DEBUG(2, "job creation detected completion %f\n", ctx->completion); + adaptCompressionLevel(ctx); + DEBUG(3, "job creation detected completion %f\n", ctx->completion); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } From fc41a8796493063c26103ca7ed32561dd2e649d0 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 17 Jul 2017 18:13:09 -0700 Subject: [PATCH 165/318] Experiment with using a lag when hashing --- contrib/long_distance_matching/Makefile | 6 +- contrib/long_distance_matching/basic_table.c | 6 +- .../circular_buffer_table.c | 20 +++--- contrib/long_distance_matching/ldm.c | 71 ++++++++++++------- contrib/long_distance_matching/ldm.h | 4 +- .../long_distance_matching/ldm_hashtable.h | 32 +++++++-- contrib/long_distance_matching/main-ldm.c | 1 - 7 files changed, 88 insertions(+), 52 deletions(-) diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 47085022d..df4390157 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -25,7 +25,7 @@ LDFLAGS += -lzstd default: all -all: main-basic main-circular-buffer +all: main-basic main-circular-buffer main-lag main-basic : basic_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ @@ -33,9 +33,11 @@ main-basic : basic_table.c ldm.c main-ldm.c main-circular-buffer: circular_buffer_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ +main-lag: lag_table.c ldm.c main-ldm.c + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main-basic main-circular-buffer + main-basic main-circular-buffer main-lag @echo Cleaning completed diff --git a/contrib/long_distance_matching/basic_table.c b/contrib/long_distance_matching/basic_table.c index 859bf0618..893a4caf9 100644 --- a/contrib/long_distance_matching/basic_table.c +++ b/contrib/long_distance_matching/basic_table.c @@ -27,7 +27,6 @@ LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { return table->entries + hash; } - LDM_hashEntry *HASH_getEntryFromHash( const LDM_hashTable *table, const hash_t hash, const U32 checksum) { (void)checksum; @@ -43,13 +42,10 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, (void)checksum; if ((*isValid)(pIn, entry->offset + table->offsetBase)) { return entry; - } else { - return NULL; } + return NULL; } - - void HASH_insert(LDM_hashTable *table, const hash_t hash, const LDM_hashEntry entry) { *getBucket(table, hash) = entry; diff --git a/contrib/long_distance_matching/circular_buffer_table.c b/contrib/long_distance_matching/circular_buffer_table.c index f45f945ce..b578d2bf1 100644 --- a/contrib/long_distance_matching/circular_buffer_table.c +++ b/contrib/long_distance_matching/circular_buffer_table.c @@ -9,7 +9,7 @@ // refactor code to scale the number of elements appropriately. // Number of elements per hash bucket. -#define HASH_BUCKET_SIZE_LOG 1 // MAX is 4 for now +#define HASH_BUCKET_SIZE_LOG 0 // MAX is 4 for now #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) struct LDM_hashTable { @@ -19,6 +19,7 @@ struct LDM_hashTable { // Position corresponding to offset=0 in LDM_hashEntry. const BYTE *offsetBase; BYTE *bucketOffsets; // Pointer to current insert position. + // Last insert was at bucketOffsets - 1? }; @@ -35,15 +36,6 @@ static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { return table->entries + (hash << HASH_BUCKET_SIZE_LOG); } -/* -static LDM_hashEntry *getLastInsertFromHash(const LDM_hashTable *table, - const hash_t hash) { - LDM_hashEntry *bucket = getBucket(table, hash); - BYTE offset = (table->bucketOffsets[hash] - 1) & (HASH_BUCKET_SIZE - 1); - return bucket + offset; -} -*/ - LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const hash_t hash, const U32 checksum, @@ -53,7 +45,12 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, LDM_hashEntry *cur = bucket; // TODO: in order of recency? for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { - // CHeck checksum for faster check. + /* + if (cur->checksum == 0 && cur->offset == 0) { + return NULL; + } + */ + // Check checksum for faster check. if (cur->checksum == checksum && (*isValid)(pIn, cur->offset + table->offsetBase)) { return cur; @@ -62,7 +59,6 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, return NULL; } - LDM_hashEntry *HASH_getEntryFromHash(const LDM_hashTable *table, const hash_t hash, const U32 checksum) { diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index bf54842f6..dedbf79a9 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -5,7 +5,7 @@ #include // Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#define HASH_ONLY_EVERY 31 +#define HASH_ONLY_EVERY 15 #define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) @@ -18,6 +18,10 @@ #define COMPUTE_STATS #define CHECKSUM_CHAR_OFFSET 10 + +#define LAG 0 + +//#define HASH_CHECK //#define RUN_CHECKS //#define LDM_DEBUG @@ -79,6 +83,10 @@ struct LDM_CCtx { unsigned step; // ip step, should be 1. + const BYTE *lagIp; + hash_t lagHash; + U32 lagSum; + // DEBUG const BYTE *DEBUG_setNextHash; }; @@ -253,6 +261,17 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->nextPosHashed = cctx->nextIp; cctx->nextHash = checksumToHash(cctx->nextSum); +#if LAG + if (cctx->ip - cctx->ibase > LAG) { +// printf("LAG %zu\n", cctx->ip - cctx->lagIp); + cctx->lagSum = updateChecksum( + cctx->lagSum, LDM_HASH_LENGTH, + cctx->lagIp[0], cctx->lagIp[LDM_HASH_LENGTH]); + cctx->lagIp++; + cctx->lagHash = checksumToHash(cctx->lagSum); + } +#endif + #ifdef RUN_CHECKS check = getChecksum(cctx->nextIp, LDM_HASH_LENGTH); @@ -270,18 +289,6 @@ static void setNextHash(LDM_CCtx *cctx) { static void putHashOfCurrentPositionFromHash( LDM_CCtx *cctx, hash_t hash, U32 sum) { - /* -#ifdef COMPUTE_STATS - if (cctx->stats.numHashInserts < HASH_getSize(cctx->hashTable)) { - U32 offset = HASH_getEntryFromHash(cctx->hashTable, hash)->offset; - cctx->stats.numHashInserts++; - if (offset != 0 && !LDM_isValidMatch(cctx->ip, offset + cctx->ibase)) { - cctx->stats.numCollisions++; - } - } -#endif -*/ - // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. // Note: this works only when cctx->step is 1. if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { @@ -289,8 +296,19 @@ static void putHashOfCurrentPositionFromHash( const LDM_hashEntry entry = { cctx->ip - cctx->ibase , MEM_read32(cctx->ip) }; */ +#if LAG + // TODO: off by 1, but whatever + if (cctx->lagIp - cctx->ibase > 0) { + const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, cctx->lagSum }; + HASH_insert(cctx->hashTable, cctx->lagHash, entry); + } else { + const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; + HASH_insert(cctx->hashTable, hash, entry); + } +#else const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; HASH_insert(cctx->hashTable, hash, entry); +#endif } cctx->lastPosHashed = cctx->ip; @@ -331,15 +349,6 @@ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { putHashOfCurrentPositionFromHash(cctx, hash, sum); } -/** - * Returns the position of the entry at hashTable[hash]. - */ -/* -static const BYTE *getPositionOnHash(const LDM_CCtx *cctx, const hash_t hash) { - return HASH_getEntryFromHash(cctx->hashTable, hash)->offset + cctx->ibase; -} -*/ - U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, const BYTE *pInLimit) { const BYTE * const pStart = pIn; @@ -431,12 +440,20 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { if (cctx->ip > cctx->imatchLimit) { return 1; } - +#ifdef HASH_CHECK + entry = HASH_getEntryFromHash(cctx->hashTable, h, sum); +#else entry = HASH_getValidEntry(cctx->hashTable, h, sum, cctx->ip, &LDM_isValidMatch); +#endif if (entry != NULL) { *match = entry->offset + cctx->ibase; +#ifdef HASH_CHECK + if (!LDM_isValidMatch(cctx->ip, *match)) { + entry = NULL; + } +#endif } putHashOfCurrentPositionFromHash(cctx, h, sum); } @@ -508,6 +525,12 @@ size_t LDM_compress(const void *src, size_t srcSize, /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); +#if LAG + cctx.lagIp = cctx.ip; + cctx.lagHash = cctx.lastHash; + cctx.lagSum = cctx.lastSum; +#endif + /** * Find a match. * If no more matches can be found (i.e. the length of the remaining input @@ -575,7 +598,7 @@ size_t LDM_compress(const void *src, size_t srcSize, /* Encode the last literals (no more matches). */ { - const size_t lastRun = cctx.iend - cctx.anchor; + const U32 lastRun = cctx.iend - cctx.anchor; BYTE *pToken = cctx.op++; LDM_encodeLiteralLengthAndLiterals(&cctx, pToken, lastRun); } diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 6d97bd560..6d7c4af27 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -10,8 +10,8 @@ #define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) #define LDM_OFFSET_SIZE 4 -// Defines the size of the hash table. -#define LDM_MEMORY_USAGE 20 +// Defines the size of the hash table (currently the number of elements). +#define LDM_MEMORY_USAGE 12 #define LDM_WINDOW_SIZE_LOG 30 #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h index 88d19ae20..83a9ed27a 100644 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -3,34 +3,54 @@ #include "mem.h" +// TODO: clean up comments + typedef U32 hash_t; typedef struct LDM_hashEntry { - U32 offset; + U32 offset; // TODO: Replace with pointer? U32 checksum; } LDM_hashEntry; typedef struct LDM_hashTable LDM_hashTable; -// TODO: rename functions -// TODO: comments - +/** + * Create a hash table with size hash buckets. + * LDM_hashEntry.offset is added to offsetBase to calculate pMatch in + * HASH_getValidEntry. + */ LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase); -//TODO: unneeded? +/** + * Returns an LDM_hashEntry from the table that matches the checksum. + * Returns NULL if one does not exist. + */ LDM_hashEntry *HASH_getEntryFromHash(const LDM_hashTable *table, const hash_t hash, const U32 checksum); +/** + * Gets a valid entry that matches the checksum. A valid entry is defined by + * *isValid. + * + * The function finds an entry matching the checksum, computes pMatch as + * offset + table.offsetBase, and calls isValid. + */ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const hash_t hash, const U32 checksum, const BYTE *pIn, int (*isValid)(const BYTE *pIn, const BYTE *pMatch)); +/** + * Insert an LDM_hashEntry into the bucket corresponding to hash. + */ void HASH_insert(LDM_hashTable *table, const hash_t hash, - const LDM_hashEntry entry); + const LDM_hashEntry entry); +/** + * Return the number of distinct hash buckets. + */ U32 HASH_getSize(const LDM_hashTable *table); void HASH_destroyTable(LDM_hashTable *table); diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index ea6375ba7..a379d3a6d 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -163,7 +163,6 @@ static int decompress(const char *fname, const char *oname) { outSize = LDM_decompress( src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, dst, decompressedSize); - printf("Ret size out: %zu\n", outSize); ftruncate(fdout, outSize); From ae47eab2fdb48cd55f20756f282dfdff3a7f4ee7 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 18 Jul 2017 12:58:50 -0700 Subject: [PATCH 166/318] changed test cases to use -s setting on the diffs --- .../adaptive-compression/test-correctness.sh | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/contrib/adaptive-compression/test-correctness.sh b/contrib/adaptive-compression/test-correctness.sh index b057cbd64..86d39ee4f 100755 --- a/contrib/adaptive-compression/test-correctness.sh +++ b/contrib/adaptive-compression/test-correctness.sh @@ -2,239 +2,239 @@ echo "correctness tests -- general" ./datagen -s1 -g1GB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s2 -g500MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s3 -g250MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s4 -g125MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s5 -g50MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s6 -g25MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s7 -g10MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s8 -g5MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s9 -g500KB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- streaming" ./datagen -s10 -g1GB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s11 -g100MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s12 -g10MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s13 -g1MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s14 -g100KB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s15 -g10KB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- read limit" ./datagen -s16 -g1GB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s17 -g100MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s18 -g10MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s19 -g1MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s20 -g100KB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s21 -g10KB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- write limit" ./datagen -s22 -g1GB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s23 -g100MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s24 -g10MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s25 -g1MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s26 -g100KB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s27 -g10KB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- read and write limits" ./datagen -s28 -g1GB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s29 -g100MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s30 -g10MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s31 -g1MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s32 -g100KB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s33 -g10KB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- forced compression level" ./datagen -s34 -g1GB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s35 -g100MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s36 -g10MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s37 -g1MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s38 -g100KB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s39 -g10KB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* make clean From cc1522351f37e4a5b7a69ed05388003cd5c28ff9 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 18 Jul 2017 11:21:19 -0700 Subject: [PATCH 167/318] [libzstd] Fix bug in Huffman encoding Summary: Huffman encoding with a bad dictionary can encode worse than the HUF_BLOCKBOUND(srcSize), since we don't filter out incompressible input, and even if we did, the dictionaries Huffman table could be ill suited to compressing actual data. The fast optimization doesn't seem to improve compression speed, even when I hard coded fast = 1, the speed didn't improve over hard coding it to 0. Benchmarks: $ ./zstd.dev -b1e5 Benchmarking levels from 1 to 5 1#Synthetic 50% : 10000000 -> 3139163 (3.186), 524.8 MB/s ,1890.0 MB/s 2#Synthetic 50% : 10000000 -> 3115138 (3.210), 372.6 MB/s ,1830.2 MB/s 3#Synthetic 50% : 10000000 -> 3222672 (3.103), 223.3 MB/s ,1400.2 MB/s 4#Synthetic 50% : 10000000 -> 3276678 (3.052), 198.0 MB/s ,1280.1 MB/s 5#Synthetic 50% : 10000000 -> 3271570 (3.057), 107.8 MB/s ,1200.0 MB/s $ ./zstd -b1e5 Benchmarking levels from 1 to 5 1#Synthetic 50% : 10000000 -> 3139163 (3.186), 524.8 MB/s ,1870.2 MB/s 2#Synthetic 50% : 10000000 -> 3115138 (3.210), 370.0 MB/s ,1810.3 MB/s 3#Synthetic 50% : 10000000 -> 3222672 (3.103), 223.3 MB/s ,1380.1 MB/s 4#Synthetic 50% : 10000000 -> 3276678 (3.052), 196.1 MB/s ,1270.0 MB/s 5#Synthetic 50% : 10000000 -> 3271570 (3.057), 106.8 MB/s ,1180.1 MB/s $ ./zstd.dev -b1e5 ../silesia.tar Benchmarking levels from 1 to 5 1#silesia.tar : 211988480 -> 73651685 (2.878), 429.7 MB/s ,1096.5 MB/s 2#silesia.tar : 211988480 -> 70158785 (3.022), 321.2 MB/s ,1029.1 MB/s 3#silesia.tar : 211988480 -> 66993813 (3.164), 243.7 MB/s , 981.4 MB/s 4#silesia.tar : 211988480 -> 66306481 (3.197), 226.7 MB/s , 972.4 MB/s 5#silesia.tar : 211988480 -> 64757852 (3.274), 150.3 MB/s , 963.6 MB/s $ ./zstd -b1e5 ../silesia.tar Benchmarking levels from 1 to 5 1#silesia.tar : 211988480 -> 73651685 (2.878), 429.7 MB/s ,1087.1 MB/s 2#silesia.tar : 211988480 -> 70158785 (3.022), 318.8 MB/s ,1029.1 MB/s 3#silesia.tar : 211988480 -> 66993813 (3.164), 246.5 MB/s , 981.4 MB/s 4#silesia.tar : 211988480 -> 66306481 (3.197), 229.2 MB/s , 972.4 MB/s 5#silesia.tar : 211988480 -> 64757852 (3.274), 149.3 MB/s , 963.6 MB/s Test Plan: I added a test case to the fuzzer which crashed with ASAN before the patch and succeeded after. --- lib/compress/huf_compress.c | 3 +-- tests/fuzzer.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 7af0789a9..beb4fdb60 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -436,7 +436,7 @@ static void HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); } -#define HUF_FLUSHBITS(s) (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s)) +#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) @@ -451,7 +451,6 @@ size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, si BYTE* const oend = ostart + dstSize; BYTE* op = ostart; size_t n; - const unsigned fast = (dstSize >= HUF_BLOCKBOUND(srcSize)); BIT_CStream_t bitC; /* init */ diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 904ce6fd8..3099f346e 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -1003,6 +1003,42 @@ static int basicUnitTests(U32 seed, double compressibility) if (r != _3BYTESTESTLENGTH) goto _output_error; } DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(4, "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; + void* const dictBuffer = malloc(dictSize); + 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)); + if (!dictBuffer || !samplesSizes) goto _output_error; + { U32 u; for (u=0; u Date: Tue, 18 Jul 2017 13:30:29 -0700 Subject: [PATCH 168/318] rename completion variable, split up fwrite operations in order to track progress --- contrib/adaptive-compression/adapt.c | 41 ++++++++++++++++++---------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 62f5ec912..404db6d94 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -87,8 +87,8 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - unsigned completionMeasured; - double completion; + unsigned compressionCompletionMeasured; + double compressionCompletion; mutex_t jobCompressed_mutex; cond_t jobCompressed_cond; mutex_t jobReady_mutex; @@ -319,7 +319,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) reset = 1; } else if (compressSlow && ctx->compressionLevel > 1) { - double const completion = ctx->completion; + double const completion = ctx->compressionCompletion; unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); @@ -331,8 +331,8 @@ static void adaptCompressionLevel(adaptCCtx* ctx) ctx->stats.readyCounter = 0; ctx->stats.writeCounter = 0; ctx->stats.compressedCounter = 0; - ctx->completion = 1; - ctx->completionMeasured = 0; + ctx->compressionCompletion = 1; + ctx->compressionCompletionMeasured = 0; } } } @@ -455,12 +455,12 @@ static void* outputThread(void* arg) ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; reduceCounters(ctx); - if (!ctx->completionMeasured) { - ctx->completion = ZSTD_getCompletion(ctx->cctx); - ctx->completionMeasured = 1; + if (!ctx->compressionCompletionMeasured) { + ctx->compressionCompletion = ZSTD_getCompletion(ctx->cctx); + ctx->compressionCompletionMeasured = 1; } adaptCompressionLevel(ctx); - DEBUG(3, "output detected completion: %f\n", ctx->completion); + DEBUG(3, "output detected completion: %f\n", ctx->compressionCompletion); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } @@ -468,14 +468,25 @@ static void* outputThread(void* arg) DEBUG(3, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; + size_t remaining = compressedSize; if (ZSTD_isError(compressedSize)) { DISPLAY("Error: an error occurred during compression\n"); signalErrorToThreads(ctx); return arg; } { - size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); - if (writeSize != compressedSize) { + // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); + size_t const blockSize = 4 << 20; + size_t pos = 0; + for ( ; ; ) { + size_t const writeSize = MIN(remaining, blockSize); + size_t const ret = fwrite(job->dst.start + pos, 1, writeSize, dstFile); + if (ret != writeSize) break; + pos += ret; + remaining -= ret; + if (remaining == 0) break; + } + if (pos != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); signalErrorToThreads(ctx); return arg; @@ -517,12 +528,12 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->stats.waitWrite++; ctx->stats.writeCounter++; reduceCounters(ctx); - if (!ctx->completionMeasured) { - ctx->completion = ZSTD_getCompletion(ctx->cctx); - ctx->completionMeasured = 1; + if (!ctx->compressionCompletion) { + ctx->compressionCompletion = ZSTD_getCompletion(ctx->cctx); + ctx->compressionCompletionMeasured = 1; } adaptCompressionLevel(ctx); - DEBUG(3, "job creation detected completion %f\n", ctx->completion); + DEBUG(3, "job creation detected completion %f\n", ctx->compressionCompletion); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } From a34bc30237b2e311753198912f3768ff1e7d0edc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 18 Jul 2017 13:31:02 -0700 Subject: [PATCH 169/318] setting up basic readme --- contrib/adaptive-compression/README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 contrib/adaptive-compression/README.md diff --git a/contrib/adaptive-compression/README.md b/contrib/adaptive-compression/README.md new file mode 100644 index 000000000..1d2613377 --- /dev/null +++ b/contrib/adaptive-compression/README.md @@ -0,0 +1,2 @@ +ZSTD_adapt is a compression tool targeted at optimizing performance across network connections. The tool aims at sensing network speeds and adapting compression level based on network or pipe speeds. +In many scenarios, using ZSTD without a properly adjusted compression level results in a pipe bottleneck, or a compressed file that could have been reduced in size. From 19258f51c1d4e9b0e10ae0488e457887b4c383cb Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Tue, 18 Jul 2017 14:25:39 -0700 Subject: [PATCH 170/318] Make the meaning of LDM_MEMORY_USAGE consistent across tables --- contrib/long_distance_matching/Makefile | 7 +-- contrib/long_distance_matching/basic_table.c | 7 +++ .../circular_buffer_table.c | 34 +++++++------ contrib/long_distance_matching/ldm.c | 50 ++++++++++++------- contrib/long_distance_matching/ldm.h | 21 +++++--- .../long_distance_matching/ldm_hashtable.h | 3 +- contrib/long_distance_matching/main-ldm.c | 25 +++++++--- 7 files changed, 94 insertions(+), 53 deletions(-) diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index df4390157..131638fdb 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -25,7 +25,7 @@ LDFLAGS += -lzstd default: all -all: main-basic main-circular-buffer main-lag +all: main-basic main-circular-buffer main-basic : basic_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ @@ -33,11 +33,8 @@ main-basic : basic_table.c ldm.c main-ldm.c main-circular-buffer: circular_buffer_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ -main-lag: lag_table.c ldm.c main-ldm.c - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main-basic main-circular-buffer main-lag + main-basic main-circular-buffer @echo Cleaning completed diff --git a/contrib/long_distance_matching/basic_table.c b/contrib/long_distance_matching/basic_table.c index 893a4caf9..8b3588e81 100644 --- a/contrib/long_distance_matching/basic_table.c +++ b/contrib/long_distance_matching/basic_table.c @@ -1,9 +1,12 @@ #include #include +#include "ldm.h" #include "ldm_hashtable.h" #include "mem.h" +#define LDM_HASHLOG ((LDM_MEMORY_USAGE) - 4) + struct LDM_hashTable { U32 size; LDM_hashEntry *entries; @@ -46,6 +49,10 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, return NULL; } +hash_t HASH_hashU32(U32 value) { + return ((value * 2654435761U) >> (32 - LDM_HASHLOG)); +} + void HASH_insert(LDM_hashTable *table, const hash_t hash, const LDM_hashEntry entry) { *getBucket(table, hash) = entry; diff --git a/contrib/long_distance_matching/circular_buffer_table.c b/contrib/long_distance_matching/circular_buffer_table.c index b578d2bf1..bc7503f17 100644 --- a/contrib/long_distance_matching/circular_buffer_table.c +++ b/contrib/long_distance_matching/circular_buffer_table.c @@ -1,33 +1,36 @@ #include #include +#include "ldm.h" #include "ldm_hashtable.h" #include "mem.h" //TODO: move def somewhere else. -//TODO: memory usage is currently no longer LDM_MEMORY_USAGE. -// refactor code to scale the number of elements appropriately. // Number of elements per hash bucket. +// HASH_BUCKET_SIZE_LOG defined in ldm.h #define HASH_BUCKET_SIZE_LOG 0 // MAX is 4 for now #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) +#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-4-HASH_BUCKET_SIZE_LOG) + struct LDM_hashTable { - U32 size; + U32 size; // Number of buckets + U32 maxEntries; // Rename... LDM_hashEntry *entries; // 1-D array for now. // Position corresponding to offset=0 in LDM_hashEntry. const BYTE *offsetBase; BYTE *bucketOffsets; // Pointer to current insert position. - // Last insert was at bucketOffsets - 1? }; LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase) { LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); - table->size = size; - table->entries = calloc(size * HASH_BUCKET_SIZE, sizeof(LDM_hashEntry)); - table->bucketOffsets = calloc(size, sizeof(BYTE)); + table->size = size >> HASH_BUCKET_SIZE_LOG; + table->maxEntries = size; + table->entries = calloc(size, sizeof(LDM_hashEntry)); + table->bucketOffsets = calloc(size >> HASH_BUCKET_SIZE_LOG, sizeof(BYTE)); table->offsetBase = offsetBase; return table; } @@ -45,11 +48,6 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, LDM_hashEntry *cur = bucket; // TODO: in order of recency? for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { - /* - if (cur->checksum == 0 && cur->offset == 0) { - return NULL; - } - */ // Check checksum for faster check. if (cur->checksum == checksum && (*isValid)(pIn, cur->offset + table->offsetBase)) { @@ -59,6 +57,11 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, return NULL; } +hash_t HASH_hashU32(U32 value) { + return ((value * 2654435761U) >> (32 - LDM_HASHLOG)); +} + + LDM_hashEntry *HASH_getEntryFromHash(const LDM_hashTable *table, const hash_t hash, const U32 checksum) { @@ -82,7 +85,7 @@ void HASH_insert(LDM_hashTable *table, } U32 HASH_getSize(const LDM_hashTable *table) { - return table->size * HASH_BUCKET_SIZE; + return table->size; } void HASH_destroyTable(LDM_hashTable *table) { @@ -101,7 +104,8 @@ void HASH_outputTableOccupancy(const LDM_hashTable *table) { } } + printf("Num buckets, bucket size: %d, %d\n", table->size, HASH_BUCKET_SIZE); printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", - HASH_getSize(table), ctr, - 100.0 * (double)(ctr) / (double)HASH_getSize(table)); + table->maxEntries, ctr, + 100.0 * (double)(ctr) / table->maxEntries); } diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index dedbf79a9..4d8ca40bc 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -4,12 +4,16 @@ #include #include -// Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#define HASH_ONLY_EVERY 15 -#define LDM_HASHLOG (LDM_MEMORY_USAGE-2) #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +//#define LDM_HASH_ENTRY_SIZE 4 #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) +#define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 4) + +// Insert every (HASH_ONLY_EVERY + 1) into the hash table. +#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE) - 4)) +#define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) + #define ML_BITS 4 #define ML_MASK ((1U<> (32 - LDM_HASHLOG)); + return HASH_hashU32(sum); +// return ((sum * 2654435761U) >> (32 - LDM_HASHLOG)); } /** @@ -261,9 +266,9 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->nextPosHashed = cctx->nextIp; cctx->nextHash = checksumToHash(cctx->nextSum); -#if LAG - if (cctx->ip - cctx->ibase > LAG) { -// printf("LAG %zu\n", cctx->ip - cctx->lagIp); +#if LDM_LAG +// printf("LDM_LAG %zu\n", cctx->ip - cctx->lagIp); + if (cctx->ip - cctx->ibase > LDM_LAG) { cctx->lagSum = updateChecksum( cctx->lagSum, LDM_HASH_LENGTH, cctx->lagIp[0], cctx->lagIp[LDM_HASH_LENGTH]); @@ -296,7 +301,7 @@ static void putHashOfCurrentPositionFromHash( const LDM_hashEntry entry = { cctx->ip - cctx->ibase , MEM_read32(cctx->ip) }; */ -#if LAG +#if LDM_LAG // TODO: off by 1, but whatever if (cctx->lagIp - cctx->ibase > 0) { const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, cctx->lagSum }; @@ -364,6 +369,18 @@ U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, return (U32)(pIn - pStart); } +void LDM_outputConfiguration(void) { + printf("=====================\n"); + printf("Configuration\n"); + printf("Window size log: %d\n", LDM_WINDOW_SIZE_LOG); + printf("Min match, 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: %d\n", HASH_ONLY_EVERY); + printf("LDM_LAG %d\n", LDM_LAG); + printf("=====================\n"); +} + void LDM_readHeader(const void *src, U64 *compressedSize, U64 *decompressedSize) { const BYTE *ip = (const BYTE *)src; @@ -392,12 +409,8 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->anchor = cctx->ibase; memset(&(cctx->stats), 0, sizeof(cctx->stats)); - cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U32, cctx->ibase); + cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U64, cctx->ibase); - //HASH_initializeTable(cctx->hashTable, LDM_HASHTABLESIZE_U32); - -// calloc(LDM_HASHTABLESIZE_U32, sizeof(LDM_hashEntry)); -// memset(cctx->hashTable, 0, sizeof(cctx->hashTable)); cctx->stats.minOffset = UINT_MAX; cctx->stats.windowSizeLog = LDM_WINDOW_SIZE_LOG; cctx->stats.hashTableSizeLog = LDM_MEMORY_USAGE; @@ -520,17 +533,19 @@ size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; const BYTE *match = NULL; +// printf("TST: %d\n", LDM_WINDOW_SIZE / LDM_HASHTABLESIZE_U64); + printf("HASH LOG: %d\n", HASH_ONLY_EVERY_LOG); + LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); -#if LAG +#if LDM_LAG cctx.lagIp = cctx.ip; cctx.lagHash = cctx.lastHash; cctx.lagSum = cctx.lastSum; #endif - /** * Find a match. * If no more matches can be found (i.e. the length of the remaining input @@ -542,6 +557,7 @@ size_t LDM_compress(const void *src, size_t srcSize, cctx.stats.numMatches++; #endif +// printf("HERE %zu\n", cctx.ip - cctx.ibase); /** * Catch up: look back to extend the match backwards from the found match. */ diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 6d7c4af27..2d4ff9cf2 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -10,15 +10,20 @@ #define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) #define LDM_OFFSET_SIZE 4 -// Defines the size of the hash table (currently the number of elements). -#define LDM_MEMORY_USAGE 12 +// Defines the size of the hash table. +// Currently this should be less than WINDOW_SIZE_LOG + 4? +#define LDM_MEMORY_USAGE 24 -#define LDM_WINDOW_SIZE_LOG 30 +//#define LDM_LAG (1 << 23) +//#define LDM_LAG (1 << 20) +#define LDM_LAG 0 + +#define LDM_WINDOW_SIZE_LOG 28 #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) -//These should be multiples of four. -#define LDM_MIN_MATCH_LENGTH 64 -#define LDM_HASH_LENGTH 64 +//These should be multiples of four (and perhaps set to the same values?). +#define LDM_MIN_MATCH_LENGTH 512 +#define LDM_HASH_LENGTH 512 typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; @@ -48,7 +53,7 @@ typedef struct LDM_DCtx LDM_DCtx; * The lower four bits of the token encode the match length. With additional * bytes added similarly to the additional literal length bytes after the offset. * - * The last sequence is incomplete and stops right after the lieterals. + * The last sequence is incomplete and stops right after the literals. * */ size_t LDM_compress(const void *src, size_t srcSize, @@ -142,6 +147,8 @@ void LDM_initializeDCtx(LDM_DCtx *dctx, void LDM_readHeader(const void *src, U64 *compressedSize, U64 *decompressedSize); +void LDM_outputConfiguration(void); + void LDM_test(void); #endif /* LDM_H */ diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h index 83a9ed27a..4fef66214 100644 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -42,6 +42,8 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const BYTE *pIn, int (*isValid)(const BYTE *pIn, const BYTE *pMatch)); +hash_t HASH_hashU32(U32 value); + /** * Insert an LDM_hashEntry into the bucket corresponding to hash. */ @@ -61,5 +63,4 @@ void HASH_destroyTable(LDM_hashTable *table); */ void HASH_outputTableOccupancy(const LDM_hashTable *hashTable); - #endif /* LDM_HASHTABLE_H */ diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index a379d3a6d..a43ec0002 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -18,7 +18,7 @@ /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. * - * TODO: This currently seg faults if the compressed size is > the decompress + * TODO: 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. */ @@ -28,6 +28,8 @@ static int compress(const char *fname, const char *oname) { char *src, *dst; size_t maxCompressedSize, compressedSize; + struct timeval tv1, tv2; + /* Open the input file. */ if ((fdin = open(fname, O_RDONLY)) < 0) { perror("Error in file opening"); @@ -46,7 +48,10 @@ static int compress(const char *fname, const char *oname) { return 1; } - maxCompressedSize = statbuf.st_size + LDM_HEADER_SIZE; + maxCompressedSize = (statbuf.st_size + LDM_HEADER_SIZE); + // Handle case where compressed size is > decompressed size. + // The compress function should check before writing or buffer writes. + maxCompressedSize += statbuf.st_size / 255; /* Go to the location corresponding to the last byte. */ /* TODO: fallocate? */ @@ -74,10 +79,12 @@ static int compress(const char *fname, const char *oname) { 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 compress and decompress size to header // TODO: should depend on LDM_DECOMPRESS_SIZE write32 @@ -96,6 +103,14 @@ static int compress(const char *fname, const char *oname) { (unsigned)statbuf.st_size, (unsigned)compressedSize, oname, (double)compressedSize / (statbuf.st_size) * 100); + printf("Total compress time = %.3f seconds, Average compression speed: %.3f MB/s\n", + (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec), + ((double)statbuf.st_size / (double) (1 << 20)) / + ((double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec))); + + // Close files. close(fdin); close(fdout); @@ -234,16 +249,10 @@ int main(int argc, const char *argv[]) { /* Compress */ { - struct timeval tv1, tv2; - gettimeofday(&tv1, NULL); if (compress(inpFilename, ldmFilename)) { printf("Compress error"); return 1; } - gettimeofday(&tv2, NULL); - printf("Total compress time = %f seconds\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec)); } /* Decompress */ From d0b27483ae0eb3d8ab11df998e6e0901f297778b Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 18 Jul 2017 14:45:49 -0700 Subject: [PATCH 171/318] [zstdcli] Fix -t in streaming mode --- programs/zstdcli.c | 5 ++++- tests/playTests.sh | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 35772e0a7..b1268c1f3 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -719,6 +719,10 @@ int main(int argCount, const char* argv[]) goto _end; } +#ifndef ZSTD_NODECOMPRESS + 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 */ @@ -763,7 +767,6 @@ int main(int argCount, const char* argv[]) #endif } else { /* decompression or test */ #ifndef ZSTD_NODECOMPRESS - if (operation==zom_test) { outFileName=nulmark; FIO_setRemoveSrcFile(0); } /* test mode */ FIO_setMemLimit(memLimit); if (filenameIdx==1 && outFileName) operationResult = FIO_decompressFilename(outFileName, filenameTable[0], dictFileName); diff --git a/tests/playTests.sh b/tests/playTests.sh index dd0f2dbfe..77853b1a4 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -383,6 +383,7 @@ $ZSTD -t --rm tmp1.zst test -f tmp1.zst # check file is still present split -b16384 tmp1.zst tmpSplit. $ZSTD -t tmpSplit.* && die "bad file not detected !" +./datagen | $ZSTD -c | $ZSTD -t $ECHO "\n**** benchmark mode tests **** " From ad66faf16a7093c0cc02b86da2d4c583a5bb7dbf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 18 Jul 2017 15:23:11 -0700 Subject: [PATCH 172/318] added progress check for filewriting, put important shared data behind mutex when being read from/written to --- contrib/adaptive-compression/adapt.c | 78 +++++++++++++++++++++------- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 404db6d94..b7f4dccf2 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -88,7 +88,9 @@ typedef struct { unsigned allJobsCompleted; unsigned adaptParam; unsigned compressionCompletionMeasured; + unsigned writeCompletionMeasured; double compressionCompletion; + double writeCompletion; mutex_t jobCompressed_mutex; cond_t jobCompressed_cond; mutex_t jobReady_mutex; @@ -97,6 +99,8 @@ typedef struct { cond_t allJobsCompleted_cond; mutex_t jobWrite_mutex; cond_t jobWrite_cond; + mutex_t completion_mutex; + mutex_t stats_mutex; size_t lastDictSize; inBuff_t input; cStat_t stats; @@ -156,6 +160,8 @@ static int freeCCtx(adaptCCtx* ctx) error |= destroyCond(&ctx->allJobsCompleted_cond); error |= destroyMutex(&ctx->jobWrite_mutex); error |= destroyCond(&ctx->jobWrite_cond); + error |= destroyMutex(&ctx->completion_mutex); + error |= destroyMutex(&ctx->stats_mutex); error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); free(ctx->input.buffer.start); if (ctx->jobs){ @@ -200,6 +206,8 @@ static adaptCCtx* createCCtx(unsigned numJobs) pthreadError |= initCond(&ctx->allJobsCompleted_cond); pthreadError |= initMutex(&ctx->jobWrite_mutex); pthreadError |= initCond(&ctx->jobWrite_cond); + pthreadError |= initMutex(&ctx->completion_mutex); + pthreadError |= initMutex(&ctx->stats_mutex); if (pthreadError) return NULL; } ctx->numJobs = numJobs; @@ -315,24 +323,44 @@ static void adaptCompressionLevel(adaptCCtx* ctx) } else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); - ctx->compressionLevel++; - reset = 1; + double completion; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + completion = ctx->writeCompletion; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + { + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; + unsigned const change = writeSlow ? MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel) : 1; + DEBUG(2, "writeSlow: %u, change: %u\n", writeSlow, change); + DEBUG(2, "write completion: %f\n", completion); + ctx->compressionLevel += change; + reset = 1; + } } else if (compressSlow && ctx->compressionLevel > 1) { - double const completion = ctx->compressionCompletion; - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; - unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); - DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(3, "completion: %f\n", completion); - ctx->compressionLevel -= change; - reset = 1; + double completion; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + completion = ctx->compressionCompletion; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + { + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; + unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); + DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); + DEBUG(3, "completion: %f\n", completion); + ctx->compressionLevel -= change; + reset = 1; + } } if (reset) { ctx->stats.readyCounter = 0; ctx->stats.writeCounter = 0; ctx->stats.compressedCounter = 0; + + pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 1; ctx->compressionCompletionMeasured = 0; + ctx->writeCompletion = 1; + ctx->writeCompletionMeasured = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } } @@ -455,12 +483,14 @@ static void* outputThread(void* arg) ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; reduceCounters(ctx); + pthread_mutex_lock(&ctx->completion_mutex.pMutex); if (!ctx->compressionCompletionMeasured) { ctx->compressionCompletion = ZSTD_getCompletion(ctx->cctx); ctx->compressionCompletionMeasured = 1; + DEBUG(3, "output detected completion: %f\n", ctx->compressionCompletion); } + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); adaptCompressionLevel(ctx); - DEBUG(3, "output detected completion: %f\n", ctx->compressionCompletion); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } @@ -484,6 +514,14 @@ static void* outputThread(void* arg) if (ret != writeSize) break; pos += ret; remaining -= ret; + + /* update completion variable for writing */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + if (!ctx->writeCompletionMeasured) { + ctx->writeCompletion = 1 - (double)remaining/compressedSize; + } + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + if (remaining == 0) break; } if (pos != compressedSize) { @@ -528,12 +566,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->stats.waitWrite++; ctx->stats.writeCounter++; reduceCounters(ctx); - if (!ctx->compressionCompletion) { - ctx->compressionCompletion = ZSTD_getCompletion(ctx->cctx); - ctx->compressionCompletionMeasured = 1; - } + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->writeCompletionMeasured = 1; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); adaptCompressionLevel(ctx); - DEBUG(3, "job creation detected completion %f\n", ctx->compressionCompletion); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } @@ -552,10 +588,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->input.buffer.start = copy; } job->dictSize = ctx->lastDictSize; - pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); - ctx->jobReadyID++; - pthread_cond_signal(&ctx->jobReady_cond.pCond); - pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + DEBUG(3, "finished job creation %u\n", nextJob); ctx->nextJobID++; DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); @@ -567,6 +600,13 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->lastDictSize = srcSize; ctx->input.filled = srcSize; } + + /* signal job ready */ + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); + ctx->jobReadyID++; + pthread_cond_signal(&ctx->jobReady_cond.pCond); + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + return 0; } From 2c4e4ddc50deb1798eb89de6ee0f0a539838e54c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 18 Jul 2017 15:55:58 -0700 Subject: [PATCH 173/318] added mutex for stats struct --- contrib/adaptive-compression/adapt.c | 122 ++++++++++++++++----------- 1 file changed, 71 insertions(+), 51 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index b7f4dccf2..35e26abb7 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -288,10 +288,12 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) /* this function normalizes counters when compression level is changing */ static void reduceCounters(adaptCCtx* ctx) { + pthread_mutex_lock(&ctx->stats_mutex.pMutex); unsigned const min = MIN(ctx->stats.compressedCounter, MIN(ctx->stats.writeCounter, ctx->stats.readyCounter)); ctx->stats.writeCounter -= min; ctx->stats.compressedCounter -= min; ctx->stats.readyCounter -= min; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); } /* @@ -309,58 +311,68 @@ static void adaptCompressionLevel(adaptCCtx* ctx) } else { unsigned reset = 0; - unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; - unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; - unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; - unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter; - unsigned const writeSlow = (compressWaiting && createWaiting); - unsigned const compressSlow = (writeWaiting && createWaiting); - unsigned const createSlow = (compressWaiting && writeWaiting); - DEBUG(2, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); - DEBUG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); - if (allSlow) { - reset = 1; - } - else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); - double completion; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = ctx->writeCompletion; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; - unsigned const change = writeSlow ? MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel) : 1; - DEBUG(2, "writeSlow: %u, change: %u\n", writeSlow, change); - DEBUG(2, "write completion: %f\n", completion); - ctx->compressionLevel += change; - reset = 1; - } - } - else if (compressSlow && ctx->compressionLevel > 1) { - double completion; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = ctx->compressionCompletion; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; - unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); - DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(3, "completion: %f\n", completion); - ctx->compressionLevel -= change; - reset = 1; - } - } - if (reset) { - ctx->stats.readyCounter = 0; - ctx->stats.writeCounter = 0; - ctx->stats.compressedCounter = 0; + unsigned allSlow; + unsigned compressWaiting; + unsigned writeWaiting; + unsigned createWaiting; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletion = 1; - ctx->compressionCompletionMeasured = 0; - ctx->writeCompletion = 1; - ctx->writeCompletionMeasured = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->stats_mutex.pMutex); + allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; + compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; + writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; + createWaiting = ctx->adaptParam < ctx->stats.writeCounter; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); + { + unsigned const writeSlow = (compressWaiting && createWaiting); + unsigned const compressSlow = (writeWaiting && createWaiting); + unsigned const createSlow = (compressWaiting && writeWaiting); + DEBUG(2, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); + if (allSlow) { + reset = 1; + } + else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { + DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); + double completion; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + completion = ctx->writeCompletion; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + { + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; + unsigned const change = writeSlow ? MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel) : 1; + DEBUG(2, "writeSlow: %u, change: %u\n", writeSlow, change); + DEBUG(2, "write completion: %f\n", completion); + ctx->compressionLevel += change; + reset = 1; + } + } + else if (compressSlow && ctx->compressionLevel > 1) { + double completion; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + completion = ctx->compressionCompletion; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + { + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; + unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); + DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); + DEBUG(3, "completion: %f\n", completion); + ctx->compressionLevel -= change; + reset = 1; + } + } + if (reset) { + pthread_mutex_lock(&ctx->stats_mutex.pMutex); + ctx->stats.readyCounter = 0; + ctx->stats.writeCounter = 0; + ctx->stats.compressedCounter = 0; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); + + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->compressionCompletion = 1; + ctx->compressionCompletionMeasured = 0; + ctx->writeCompletion = 1; + ctx->writeCompletionMeasured = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + } } } } @@ -383,8 +395,10 @@ static void* compressionThread(void* arg) DEBUG(3, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { + pthread_mutex_lock(&ctx->stats_mutex.pMutex); ctx->stats.waitReady++; ctx->stats.readyCounter++; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); adaptCompressionLevel(ctx); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); @@ -480,8 +494,10 @@ static void* outputThread(void* arg) DEBUG(3, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { + pthread_mutex_lock(&ctx->stats_mutex.pMutex); ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); if (!ctx->compressionCompletionMeasured) { @@ -563,8 +579,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { + pthread_mutex_lock(&ctx->stats_mutex.pMutex); ctx->stats.waitWrite++; ctx->stats.writeCounter++; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletionMeasured = 1; @@ -719,7 +737,9 @@ static int freeFileCompressionResources(fcResources* fcr) { int ret = 0; waitUntilAllJobsCompleted(fcr->ctx); + pthread_mutex_lock(&fcr->ctx->stats_mutex.pMutex); if (g_displayStats) printStats(fcr->ctx->stats); + pthread_mutex_unlock(&fcr->ctx->stats_mutex.pMutex); ret |= (fcr->srcFile != NULL) ? fclose(fcr->srcFile) : 0; ret |= (fcr->ctx != NULL) ? freeCCtx(fcr->ctx) : 0; if (fcr->otArg) { From 3d7f1afadd508de925783df775414b6e8e24e7bf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 18 Jul 2017 17:32:36 -0700 Subject: [PATCH 174/318] changed createCCtx() to split into initialization and creation --- contrib/adaptive-compression/adapt.c | 52 +++++++++++++++++++--------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 35e26abb7..7eb4333a1 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -187,14 +187,8 @@ static int initCond(cond_t* cond) return ret; } -static adaptCCtx* createCCtx(unsigned numJobs) +static int initCCtx(adaptCCtx* ctx, unsigned numJobs) { - - adaptCCtx* const ctx = calloc(1, sizeof(adaptCCtx)); - if (ctx == NULL) { - DISPLAY("Error: could not allocate space for context\n"); - return NULL; - } ctx->compressionLevel = g_compressionLevel; { int pthreadError = 0; @@ -208,7 +202,7 @@ static adaptCCtx* createCCtx(unsigned numJobs) pthreadError |= initCond(&ctx->jobWrite_cond); pthreadError |= initMutex(&ctx->completion_mutex); pthreadError |= initMutex(&ctx->stats_mutex); - if (pthreadError) return NULL; + if (pthreadError) return pthreadError; } ctx->numJobs = numJobs; ctx->jobReadyID = 0; @@ -216,6 +210,12 @@ static adaptCCtx* createCCtx(unsigned numJobs) ctx->jobWriteID = 0; ctx->lastDictSize = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); + + if (!ctx->jobs) { + DISPLAY("Error: could not allocate space for jobs during context creation\n"); + return 1; + } + /* initializing jobs */ { unsigned jobNum; @@ -226,33 +226,51 @@ static adaptCCtx* createCCtx(unsigned numJobs) job->lastJob = 0; if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); - return NULL; + return 1; } job->src.capacity = FILE_CHUNK_SIZE; job->dst.capacity = ZSTD_compressBound(FILE_CHUNK_SIZE); } } + ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; ctx->adaptParam = DEFAULT_ADAPT_PARAM; + ctx->cctx = ZSTD_createCCtx(); + if (!ctx->cctx) { + DISPLAY("Error: could not allocate ZSTD_CCtx\n"); + return 1; + } + ctx->input.filled = 0; ctx->input.buffer.capacity = 2 * FILE_CHUNK_SIZE; + ctx->input.buffer.start = malloc(ctx->input.buffer.capacity); if (!ctx->input.buffer.start) { DISPLAY("Error: could not allocate input buffer\n"); + return 1; + } + return 0; +} + +static adaptCCtx* createCCtx(unsigned numJobs) +{ + + adaptCCtx* const ctx = calloc(1, sizeof(adaptCCtx)); + if (ctx == NULL) { + DISPLAY("Error: could not allocate space for context\n"); return NULL; } - if (!ctx->cctx) { - DISPLAY("Error: could not allocate ZSTD_CCtx\n"); - return NULL; + { + int const error = initCCtx(ctx, numJobs); + if (error) { + freeCCtx(ctx); + return NULL; + } + return ctx; } - if (!ctx->jobs) { - DISPLAY("Error: could not allocate space for jobs during context creation\n"); - return NULL; - } - return ctx; } static void signalErrorToThreads(adaptCCtx* ctx) From 1fa223859fb7f53d5a03c8b30dfd74d40898b05f Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Tue, 18 Jul 2017 18:05:10 -0700 Subject: [PATCH 175/318] Switch to using ZSTD_count instead of function pointer --- contrib/long_distance_matching/basic_table.c | 28 +++- .../circular_buffer_table.c | 151 +++++++++++++++++- contrib/long_distance_matching/ldm.c | 22 +-- contrib/long_distance_matching/ldm.h | 9 +- .../long_distance_matching/ldm_hashtable.h | 4 +- 5 files changed, 194 insertions(+), 20 deletions(-) diff --git a/contrib/long_distance_matching/basic_table.c b/contrib/long_distance_matching/basic_table.c index 8b3588e81..6c12b5087 100644 --- a/contrib/long_distance_matching/basic_table.c +++ b/contrib/long_distance_matching/basic_table.c @@ -36,14 +36,38 @@ LDM_hashEntry *HASH_getEntryFromHash( return getBucket(table, hash); } +static int isValidMatch(const BYTE *pIn, const BYTE *pMatch, + U32 minMatchLength, U32 maxWindowSize) { + U32 lengthLeft = minMatchLength; + const BYTE *curIn = pIn; + const BYTE *curMatch = pMatch; + + if (pIn - pMatch > maxWindowSize) { + return 0; + } + + for (; lengthLeft >= 4; lengthLeft -= 4) { + if (MEM_read32(curIn) != MEM_read32(curMatch)) { + return 0; + } + curIn += 4; + curMatch += 4; + } + return 1; +} + LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const hash_t hash, const U32 checksum, const BYTE *pIn, - int (*isValid)(const BYTE *pIn, const BYTE *pMatch)) { + const BYTE *pEnd, + U32 minMatchLength, + U32 maxWindowSize) { LDM_hashEntry *entry = getBucket(table, hash); (void)checksum; - if ((*isValid)(pIn, entry->offset + table->offsetBase)) { + (void)pEnd; + if (isValidMatch(pIn, entry->offset + table->offsetBase, + minMatchLength, maxWindowSize)) { return entry; } return NULL; diff --git a/contrib/long_distance_matching/circular_buffer_table.c b/contrib/long_distance_matching/circular_buffer_table.c index bc7503f17..653d9e51b 100644 --- a/contrib/long_distance_matching/circular_buffer_table.c +++ b/contrib/long_distance_matching/circular_buffer_table.c @@ -9,11 +9,14 @@ // Number of elements per hash bucket. // HASH_BUCKET_SIZE_LOG defined in ldm.h -#define HASH_BUCKET_SIZE_LOG 0 // MAX is 4 for now +#define HASH_BUCKET_SIZE_LOG 2 // MAX is 4 for now #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) +// TODO: rename. Number of hash buckets. #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-4-HASH_BUCKET_SIZE_LOG) +//#define TMP_ZSTDTOGGLE + struct LDM_hashTable { U32 size; // Number of buckets U32 maxEntries; // Rename... @@ -39,20 +42,162 @@ static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { return table->entries + (hash << HASH_BUCKET_SIZE_LOG); } +#ifdef TMP_ZSTDTOGGLE +static unsigned ZSTD_NbCommonBytes (register 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; + const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ + 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); +} + +#else + +static int isValidMatch(const BYTE *pIn, const BYTE *pMatch, + U32 minMatchLength, U32 maxWindowSize) { + U32 lengthLeft = minMatchLength; + const BYTE *curIn = pIn; + const BYTE *curMatch = pMatch; + + if (pIn - pMatch > maxWindowSize) { + return 0; + } + + for (; lengthLeft >= 4; lengthLeft -= 4) { + if (MEM_read32(curIn) != MEM_read32(curMatch)) { + return 0; + } + curIn += 4; + curMatch += 4; + } + return 1; +} + +#endif // TMP_ZSTDTOGGLE + LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const hash_t hash, const U32 checksum, const BYTE *pIn, - int (*isValid)(const BYTE *pIn, const BYTE *pMatch)) { + const BYTE *pEnd, + U32 minMatchLength, + U32 maxWindowSize) { LDM_hashEntry *bucket = getBucket(table, hash); LDM_hashEntry *cur = bucket; // TODO: in order of recency? for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { // Check checksum for faster check. + const BYTE *pMatch = cur->offset + table->offsetBase; +#ifdef TMP_ZSTDTOGGLE + if (cur->checksum == checksum && pIn - pMatch <= maxWindowSize) { + U32 matchLength = ZSTD_count(pIn, pMatch, pEnd); + if (matchLength >= minMatchLength) { + return cur; + } + } +#else + (void)pEnd; + (void)minMatchLength; + (void)maxWindowSize; + if (cur->checksum == checksum && - (*isValid)(pIn, cur->offset + table->offsetBase)) { + isValidMatch(pIn, pMatch, minMatchLength, maxWindowSize)) { return cur; } +#endif } return NULL; } diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 4d8ca40bc..56b22d288 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -91,6 +91,7 @@ struct LDM_CCtx { hash_t lagHash; U32 lagSum; + U64 numHashInserts; // DEBUG const BYTE *DEBUG_setNextHash; }; @@ -164,7 +165,6 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { } printf("\n"); printf("=====================\n"); - } int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { @@ -376,7 +376,7 @@ void LDM_outputConfiguration(void) { printf("Min match, 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: %d\n", HASH_ONLY_EVERY); + printf("HASH_ONLY_EVERY_LOG: %d\n", HASH_ONLY_EVERY_LOG); printf("LDM_LAG %d\n", LDM_LAG); printf("=====================\n"); } @@ -456,8 +456,10 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { #ifdef HASH_CHECK entry = HASH_getEntryFromHash(cctx->hashTable, h, sum); #else - entry = HASH_getValidEntry(cctx->hashTable, h, sum, cctx->ip, - &LDM_isValidMatch); + entry = HASH_getValidEntry(cctx->hashTable, h, sum, + cctx->ip, cctx->iend, + LDM_MIN_MATCH_LENGTH, + LDM_WINDOW_SIZE); #endif if (entry != NULL) { @@ -534,9 +536,10 @@ size_t LDM_compress(const void *src, size_t srcSize, LDM_CCtx cctx; const BYTE *match = NULL; // printf("TST: %d\n", LDM_WINDOW_SIZE / LDM_HASHTABLESIZE_U64); - printf("HASH LOG: %d\n", HASH_ONLY_EVERY_LOG); +// printf("HASH LOG: %d\n", HASH_ONLY_EVERY_LOG); LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); + LDM_outputConfiguration(); /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); @@ -553,11 +556,10 @@ size_t LDM_compress(const void *src, size_t srcSize, * and encode the final literals. */ while (LDM_findBestMatch(&cctx, &match) == 0) { + U32 backwardsMatchLen = 0; #ifdef COMPUTE_STATS cctx.stats.numMatches++; #endif - -// printf("HERE %zu\n", cctx.ip - cctx.ibase); /** * Catch up: look back to extend the match backwards from the found match. */ @@ -565,6 +567,7 @@ size_t LDM_compress(const void *src, size_t srcSize, cctx.ip[-1] == match[-1]) { cctx.ip--; match--; + backwardsMatchLen++; } /** @@ -575,8 +578,9 @@ size_t LDM_compress(const void *src, size_t srcSize, const U32 literalLength = cctx.ip - cctx.anchor; const U32 offset = cctx.ip - match; const U32 matchLength = LDM_countMatchLength( - cctx.ip + LDM_MIN_MATCH_LENGTH, match + LDM_MIN_MATCH_LENGTH, - cctx.ihashLimit); + cctx.ip + LDM_MIN_MATCH_LENGTH + backwardsMatchLen, + match + LDM_MIN_MATCH_LENGTH + backwardsMatchLen, + cctx.ihashLimit) + backwardsMatchLen; LDM_outputBlock(&cctx, literalLength, offset, matchLength); diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 2d4ff9cf2..735435e8d 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -12,18 +12,17 @@ // Defines the size of the hash table. // Currently this should be less than WINDOW_SIZE_LOG + 4? -#define LDM_MEMORY_USAGE 24 +#define LDM_MEMORY_USAGE 23 -//#define LDM_LAG (1 << 23) //#define LDM_LAG (1 << 20) -#define LDM_LAG 0 +#define LDM_LAG (0) #define LDM_WINDOW_SIZE_LOG 28 #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) //These should be multiples of four (and perhaps set to the same values?). -#define LDM_MIN_MATCH_LENGTH 512 -#define LDM_HASH_LENGTH 512 +#define LDM_MIN_MATCH_LENGTH 64 +#define LDM_HASH_LENGTH 64 typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h index 4fef66214..7566751dc 100644 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -40,7 +40,9 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const hash_t hash, const U32 checksum, const BYTE *pIn, - int (*isValid)(const BYTE *pIn, const BYTE *pMatch)); + const BYTE *pEnd, + U32 minMatchLength, + U32 maxWindowSize); hash_t HASH_hashU32(U32 value); From 4352e09cb002873f3c2eec5d79eddeefca28160f Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Tue, 18 Jul 2017 18:35:25 -0700 Subject: [PATCH 176/318] Avoid recounting match lengths with ZSTD_count --- contrib/long_distance_matching/basic_table.c | 6 +++++- .../circular_buffer_table.c | 13 +++++++------ contrib/long_distance_matching/ldm.c | 18 +++++++++++++----- contrib/long_distance_matching/ldm_hashtable.h | 5 +++-- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/contrib/long_distance_matching/basic_table.c b/contrib/long_distance_matching/basic_table.c index 6c12b5087..30c548d2a 100644 --- a/contrib/long_distance_matching/basic_table.c +++ b/contrib/long_distance_matching/basic_table.c @@ -62,12 +62,16 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const BYTE *pIn, const BYTE *pEnd, U32 minMatchLength, - U32 maxWindowSize) { + U32 maxWindowSize, + U32 *matchLength) { LDM_hashEntry *entry = getBucket(table, hash); (void)checksum; (void)pEnd; + (void)matchLength; + // TODO: Count the entire forward match length rather than check if valid. if (isValidMatch(pIn, entry->offset + table->offsetBase, minMatchLength, maxWindowSize)) { + return entry; } return NULL; diff --git a/contrib/long_distance_matching/circular_buffer_table.c b/contrib/long_distance_matching/circular_buffer_table.c index 653d9e51b..104d1b339 100644 --- a/contrib/long_distance_matching/circular_buffer_table.c +++ b/contrib/long_distance_matching/circular_buffer_table.c @@ -15,17 +15,16 @@ // TODO: rename. Number of hash buckets. #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-4-HASH_BUCKET_SIZE_LOG) -//#define TMP_ZSTDTOGGLE +#define TMP_ZSTDTOGGLE struct LDM_hashTable { U32 size; // Number of buckets U32 maxEntries; // Rename... LDM_hashEntry *entries; // 1-D array for now. + BYTE *bucketOffsets; // Pointer to current insert position. // Position corresponding to offset=0 in LDM_hashEntry. const BYTE *offsetBase; - BYTE *bucketOffsets; // Pointer to current insert position. - // Last insert was at bucketOffsets - 1? }; LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase) { @@ -174,7 +173,8 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const BYTE *pIn, const BYTE *pEnd, U32 minMatchLength, - U32 maxWindowSize) { + U32 maxWindowSize, + U32 *matchLength) { LDM_hashEntry *bucket = getBucket(table, hash); LDM_hashEntry *cur = bucket; // TODO: in order of recency? @@ -183,8 +183,9 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const BYTE *pMatch = cur->offset + table->offsetBase; #ifdef TMP_ZSTDTOGGLE if (cur->checksum == checksum && pIn - pMatch <= maxWindowSize) { - U32 matchLength = ZSTD_count(pIn, pMatch, pEnd); - if (matchLength >= minMatchLength) { + U32 forwardMatchLength = ZSTD_count(pIn, pMatch, pEnd); + if (forwardMatchLength >= minMatchLength) { + *matchLength = forwardMatchLength; return cur; } } diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 56b22d288..1512ab8c5 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -28,6 +28,7 @@ //#define HASH_CHECK //#define RUN_CHECKS +//#define TMP_RECOMPUTE_LENGTHS #include "ldm.h" #include "ldm_hashtable.h" @@ -435,8 +436,10 @@ void LDM_destroyCCtx(LDM_CCtx *cctx) { * Returns 0 if successful and 1 otherwise (i.e. no match can be found * in the remaining input that is long enough). * + * matchLength contains the forward length of the match. */ -static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { +static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, + U32 *matchLength) { LDM_hashEntry *entry = NULL; cctx->nextIp = cctx->ip + cctx->step; @@ -459,7 +462,8 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match) { entry = HASH_getValidEntry(cctx->hashTable, h, sum, cctx->ip, cctx->iend, LDM_MIN_MATCH_LENGTH, - LDM_WINDOW_SIZE); + LDM_WINDOW_SIZE, + matchLength); #endif if (entry != NULL) { @@ -535,8 +539,7 @@ size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; const BYTE *match = NULL; -// printf("TST: %d\n", LDM_WINDOW_SIZE / LDM_HASHTABLESIZE_U64); -// printf("HASH LOG: %d\n", HASH_ONLY_EVERY_LOG); + U32 forwardMatchLength = 0; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); LDM_outputConfiguration(); @@ -555,7 +558,7 @@ size_t LDM_compress(const void *src, size_t srcSize, * is less than the minimum match length), then stop searching for matches * and encode the final literals. */ - while (LDM_findBestMatch(&cctx, &match) == 0) { + while (LDM_findBestMatch(&cctx, &match, &forwardMatchLength) == 0) { U32 backwardsMatchLen = 0; #ifdef COMPUTE_STATS cctx.stats.numMatches++; @@ -577,10 +580,15 @@ size_t LDM_compress(const void *src, size_t srcSize, { const U32 literalLength = cctx.ip - cctx.anchor; const U32 offset = cctx.ip - match; +#ifdef TMP_RECOMPUTE_LENGTHS const U32 matchLength = LDM_countMatchLength( cctx.ip + LDM_MIN_MATCH_LENGTH + backwardsMatchLen, match + LDM_MIN_MATCH_LENGTH + backwardsMatchLen, cctx.ihashLimit) + backwardsMatchLen; +#else + const U32 matchLength = forwardMatchLength + backwardsMatchLen - + LDM_MIN_MATCH_LENGTH; +#endif LDM_outputBlock(&cctx, literalLength, offset, matchLength); diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h index 7566751dc..2ea159f71 100644 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -41,8 +41,9 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const U32 checksum, const BYTE *pIn, const BYTE *pEnd, - U32 minMatchLength, - U32 maxWindowSize); + const U32 minMatchLength, + const U32 maxWindowSize, + U32 *matchLength); hash_t HASH_hashU32(U32 value); From b71363b967376f676bcfff842bf2c75bc9e2daec Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 19 Jul 2017 01:05:40 -0700 Subject: [PATCH 177/318] check pthread_*_init() success condition --- lib/common/bitstream.h | 81 +++++++++++++++++----------------- lib/common/threading.h | 6 +-- lib/compress/zstdmt_compress.c | 20 +++++++-- lib/dictBuilder/cover.c | 8 ++-- 4 files changed, 62 insertions(+), 53 deletions(-) diff --git a/lib/common/bitstream.h b/lib/common/bitstream.h index 07b85026c..06121f21c 100644 --- a/lib/common/bitstream.h +++ b/lib/common/bitstream.h @@ -80,9 +80,9 @@ extern "C" { * bitStream encoding API (write forward) ********************************************/ /* bitStream can mix input from multiple sources. -* A critical property of these streams is that they encode and decode in **reverse** direction. -* So the first bit sequence you add will be the last to be read, like a LIFO stack. -*/ + * A critical property of these streams is that they encode and decode in **reverse** direction. + * So the first bit sequence you add will be the last to be read, like a LIFO stack. + */ typedef struct { size_t bitContainer; @@ -203,7 +203,7 @@ static const unsigned BIT_mask[] = { 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, /*! BIT_initCStream() : * `dstCapacity` must be > sizeof(size_t) * @return : 0 if success, - otherwise an error code (can be tested using ERR_isError() ) */ + * otherwise an error code (can be tested using ERR_isError()) */ MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC, void* startPtr, size_t dstCapacity) { @@ -217,8 +217,8 @@ MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC, } /*! BIT_addBits() : - can add up to 26 bits into `bitC`. - Does not check for register overflow ! */ + * can add up to 26 bits into `bitC`. + * Note : does not check for register overflow ! */ MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC, size_t value, unsigned nbBits) { @@ -268,7 +268,7 @@ MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC) /*! BIT_closeCStream() : * @return : size of CStream, in bytes, - or 0 if it could not fit into dstBuffer */ + * or 0 if it could not fit into dstBuffer */ MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC) { BIT_addBitsFast(bitC, 1, 1); /* endMark */ @@ -279,14 +279,14 @@ MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC) /*-******************************************************** -* bitStream decoding +* bitStream decoding **********************************************************/ /*! BIT_initDStream() : -* Initialize a BIT_DStream_t. -* `bitD` : a pointer to an already allocated BIT_DStream_t structure. -* `srcSize` must be the *exact* size of the bitStream, in bytes. -* @return : size of stream (== srcSize) or an errorCode if a problem is detected -*/ + * Initialize a BIT_DStream_t. + * `bitD` : a pointer to an already allocated BIT_DStream_t structure. + * `srcSize` must be the *exact* size of the bitStream, in bytes. + * @return : size of stream (== srcSize), or an errorCode if a problem is detected + */ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize) { if (srcSize < 1) { memset(bitD, 0, sizeof(*bitD)); return ERROR(srcSize_wrong); } @@ -305,29 +305,30 @@ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, si bitD->bitContainer = *(const BYTE*)(bitD->start); switch(srcSize) { - case 7: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[6]) << (sizeof(bitD->bitContainer)*8 - 16); - /* fall-through */ + case 7: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[6]) << (sizeof(bitD->bitContainer)*8 - 16); + /* fall-through */ - case 6: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[5]) << (sizeof(bitD->bitContainer)*8 - 24); - /* fall-through */ + case 6: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[5]) << (sizeof(bitD->bitContainer)*8 - 24); + /* fall-through */ - case 5: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[4]) << (sizeof(bitD->bitContainer)*8 - 32); - /* fall-through */ + case 5: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[4]) << (sizeof(bitD->bitContainer)*8 - 32); + /* fall-through */ - case 4: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[3]) << 24; - /* fall-through */ + case 4: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[3]) << 24; + /* fall-through */ - case 3: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[2]) << 16; - /* fall-through */ + case 3: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[2]) << 16; + /* fall-through */ - case 2: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[1]) << 8; - /* fall-through */ + case 2: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[1]) << 8; + /* fall-through */ - default: break; + default: break; + } + { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1]; + bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; + if (lastByte == 0) return ERROR(corruption_detected); /* endMark not present */ } - { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1]; - bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; - if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ } bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8; } @@ -363,9 +364,8 @@ MEM_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits) * local register is not modified. * On 32-bits, maxNbBits==24. * On 64-bits, maxNbBits==56. - * @return : value extracted - */ - MEM_STATIC size_t BIT_lookBits(const BIT_DStream_t* bitD, U32 nbBits) + * @return : value extracted */ +MEM_STATIC size_t BIT_lookBits(const BIT_DStream_t* bitD, U32 nbBits) { #if defined(__BMI__) && defined(__GNUC__) /* experimental; fails if bitD->bitsConsumed + nbBits > sizeof(bitD->bitContainer)*8 */ return BIT_getMiddleBits(bitD->bitContainer, (sizeof(bitD->bitContainer)*8) - bitD->bitsConsumed - nbBits, nbBits); @@ -392,8 +392,7 @@ MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits) /*! BIT_readBits() : * Read (consume) next n bits from local register and update. * Pay attention to not read more than nbBits contained into local register. - * @return : extracted value. - */ + * @return : extracted value. */ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits) { size_t const value = BIT_lookBits(bitD, nbBits); @@ -402,7 +401,7 @@ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits) } /*! BIT_readBitsFast() : -* unsafe version; only works only if nbBits >= 1 */ + * unsafe version; only works only if nbBits >= 1 */ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits) { size_t const value = BIT_lookBitsFast(bitD, nbBits); @@ -412,10 +411,10 @@ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits) } /*! BIT_reloadDStream() : -* 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. - if status == BIT_DStream_unfinished, internal register is filled with >= (sizeof(bitD->bitContainer)*8 - 7) bits */ + * 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 */ MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) { if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* overflow detected, like end of stream */ @@ -446,8 +445,8 @@ MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) } /*! BIT_endOfDStream() : -* @return Tells if DStream has exactly reached its end (all bits consumed). -*/ + * @return : 1 if DStream has _exactly_ reached its end (all bits consumed). + */ MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream) { return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer)*8)); diff --git a/lib/common/threading.h b/lib/common/threading.h index c0086139e..ec9e07a90 100644 --- a/lib/common/threading.h +++ b/lib/common/threading.h @@ -80,14 +80,14 @@ int _pthread_join(pthread_t* thread, void** value_ptr); #else /* ZSTD_MULTITHREAD not defined */ /* No multithreading support */ -#define pthread_mutex_t int /* #define rather than typedef, as sometimes pthread support is implicit, resulting in duplicated symbols */ -#define pthread_mutex_init(a,b) +#define pthread_mutex_t int /* #define rather than typedef, because sometimes pthread support is implicit, resulting in duplicated symbols */ +#define pthread_mutex_init(a,b) ((void)a, 0) #define pthread_mutex_destroy(a) #define pthread_mutex_lock(a) #define pthread_mutex_unlock(a) #define pthread_cond_t int -#define pthread_cond_init(a,b) +#define pthread_cond_init(a,b) ((void)a, 0) #define pthread_cond_destroy(a) #define pthread_cond_wait(a,b) #define pthread_cond_signal(a) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index ccc20c78a..234ced9de 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -98,7 +98,10 @@ static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbThreads, ZSTD_custo ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)ZSTD_calloc( sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t), cMem); if (bufPool==NULL) return NULL; - pthread_mutex_init(&bufPool->poolMutex, NULL); + if (pthread_mutex_init(&bufPool->poolMutex, NULL)) { + ZSTD_free(bufPool, cMem); + return NULL; + } bufPool->bufferSize = 64 KB; bufPool->totalBuffers = maxNbBuffers; bufPool->nbBuffers = 0; @@ -213,7 +216,10 @@ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads, ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) ZSTD_calloc( sizeof(ZSTDMT_CCtxPool) + (nbThreads-1)*sizeof(ZSTD_CCtx*), cMem); if (!cctxPool) return NULL; - pthread_mutex_init(&cctxPool->poolMutex, NULL); + if (pthread_mutex_init(&cctxPool->poolMutex, NULL)) { + ZSTD_free(cctxPool, cMem); + return NULL; + } cctxPool->cMem = cMem; cctxPool->totalCCtx = nbThreads; cctxPool->availCCtx = 1; /* at least one cctx for single-thread mode */ @@ -429,8 +435,14 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem) ZSTDMT_freeCCtx(mtctx); return NULL; } - pthread_mutex_init(&mtctx->jobCompleted_mutex, NULL); /* Todo : check init function return */ - pthread_cond_init(&mtctx->jobCompleted_cond, NULL); + if (pthread_mutex_init(&mtctx->jobCompleted_mutex, NULL)) { + ZSTDMT_freeCCtx(mtctx); + return NULL; + } + if (pthread_cond_init(&mtctx->jobCompleted_cond, NULL)) { + ZSTDMT_freeCCtx(mtctx); + return NULL; + } DEBUGLOG(3, "mt_cctx created, for %u threads", nbThreads); return mtctx; } diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c index 06c1b9fad..38376d08b 100644 --- a/lib/dictBuilder/cover.c +++ b/lib/dictBuilder/cover.c @@ -714,11 +714,9 @@ typedef struct COVER_best_s { * Initialize the `COVER_best_t`. */ static void COVER_best_init(COVER_best_t *best) { - if (!best) { - return; - } - pthread_mutex_init(&best->mutex, NULL); - pthread_cond_init(&best->cond, NULL); + if (best==NULL) return; /* compatible with init on NULL */ + (void)pthread_mutex_init(&best->mutex, NULL); + (void)pthread_cond_init(&best->cond, NULL); best->liveJobs = 0; best->dict = NULL; best->dictSize = 0; From 6119cd216458d46909d2d12d858ac9bfa1c7837f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 09:43:17 -0700 Subject: [PATCH 178/318] added additional print for help menu --- contrib/adaptive-compression/adapt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 7eb4333a1..2182ad1e8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -827,6 +827,7 @@ static void help() PRINT(" -i# : provide initial compression level\n"); PRINT(" -s : display information stats\n"); PRINT(" -h : display help/information\n"); + PRINT(" -f : force the compression level to stay constant\n"); } /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) From 559ea4ff25b3728024a55fdc9966925de2e116ba Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 09:59:17 -0700 Subject: [PATCH 179/318] split up read process into smaller chunks --- contrib/adaptive-compression/adapt.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 2182ad1e8..d3ffe6a81 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -682,17 +682,30 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA /* creating jobs */ for ( ; ; ) { - size_t const readSize = fread(ctx->input.buffer.start + ctx->input.filled, 1, FILE_CHUNK_SIZE, srcFile); - if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { + size_t pos = 0; + size_t const readBlockSize = 1 << 15; + size_t remaining = FILE_CHUNK_SIZE; + while (remaining != 0 && !feof(srcFile)) { + size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); + if (ret != readBlockSize && !feof(srcFile)) { + /* error could not read correct number of bytes */ + DISPLAY("Error: problem occurred during read from src file\n"); + signalErrorToThreads(ctx); + return 1; + } + pos += ret; + remaining -= ret; + } + if (remaining != 0 && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); signalErrorToThreads(ctx); return 1; } - g_streamedSize += readSize; + g_streamedSize += pos; /* reading was fine, now create the compression job */ { int const last = feof(srcFile); - int const error = createCompressionJob(ctx, readSize, last); + int const error = createCompressionJob(ctx, pos, last); if (error != 0) { signalErrorToThreads(ctx); return error; From e11bf55d0bf0a83327031b921480625781133033 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 10:10:47 -0700 Subject: [PATCH 180/318] added mechanism for measuring how much of a job has been created --- contrib/adaptive-compression/adapt.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index d3ffe6a81..de2e5e139 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -89,8 +89,10 @@ typedef struct { unsigned adaptParam; unsigned compressionCompletionMeasured; unsigned writeCompletionMeasured; + unsigned createCompletionMeasured; double compressionCompletion; double writeCompletion; + double createCompletion; mutex_t jobCompressed_mutex; cond_t jobCompressed_cond; mutex_t jobReady_mutex; @@ -344,7 +346,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const writeSlow = (compressWaiting && createWaiting); unsigned const compressSlow = (writeWaiting && createWaiting); unsigned const createSlow = (compressWaiting && writeWaiting); - DEBUG(2, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); + DEBUG(3, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); if (allSlow) { reset = 1; } @@ -352,13 +354,14 @@ static void adaptCompressionLevel(adaptCCtx* ctx) DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); double completion; pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = ctx->writeCompletion; + completion = writeSlow ? ctx->writeCompletion : ctx->createCompletion; + DEBUG(2, "write completion: %f, create completion: %f\n", ctx->writeCompletion, ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; unsigned const change = writeSlow ? MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel) : 1; - DEBUG(2, "writeSlow: %u, change: %u\n", writeSlow, change); - DEBUG(2, "write completion: %f\n", completion); + DEBUG(3, "writeSlow: %u, change: %u\n", writeSlow, change); + DEBUG(3, "write completion: %f\n", completion); ctx->compressionLevel += change; reset = 1; } @@ -418,6 +421,9 @@ static void* compressionThread(void* arg) ctx->stats.readyCounter++; pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletionMeasured = 1; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); adaptCompressionLevel(ctx); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); @@ -695,6 +701,12 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA } pos += ret; remaining -= ret; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + if (!ctx->createCompletionMeasured) { + ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); + } + DEBUG(3, "create completion: %f\n", ctx->createCompletion); + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); From 4497ecf297d3d9d71dca2f85aec3ee5467f1a37b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 10:14:00 -0700 Subject: [PATCH 181/318] change compression level only right before actually performing compression. When waiting, only update waiting statistics. --- contrib/adaptive-compression/adapt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index de2e5e139..b928d0a2c 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -424,7 +424,6 @@ static void* compressionThread(void* arg) pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletionMeasured = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - adaptCompressionLevel(ctx); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } @@ -434,6 +433,7 @@ static void* compressionThread(void* arg) DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); /* compress the data */ { + adaptCompressionLevel(ctx); unsigned const cLevel = ctx->compressionLevel; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); @@ -530,7 +530,6 @@ static void* outputThread(void* arg) DEBUG(3, "output detected completion: %f\n", ctx->compressionCompletion); } pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - adaptCompressionLevel(ctx); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } @@ -611,7 +610,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletionMeasured = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - adaptCompressionLevel(ctx); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } From 338951cd482f630cd12b7ca4d15d84c594a77308 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 10:23:46 -0700 Subject: [PATCH 182/318] moved compression adapt to avoid warning --- contrib/adaptive-compression/adapt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index b928d0a2c..cf908ca94 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -431,9 +431,12 @@ static void* compressionThread(void* arg) DEBUG(3, "compressionThread(): continuing after job ready\n"); DEBUG(3, "DICTIONARY ENDED\n"); DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); + + /* adapt compression level */ + adaptCompressionLevel(ctx); + /* compress the data */ { - adaptCompressionLevel(ctx); unsigned const cLevel = ctx->compressionLevel; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); From f1ac518b59f471434fd4e9c7ce7c4ae609189914 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 11:23:40 -0700 Subject: [PATCH 183/318] split compression into smaller blocks --- contrib/adaptive-compression/adapt.c | 84 ++++++++++++++++++---------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index cf908ca94..17721f87e 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -434,44 +434,66 @@ static void* compressionThread(void* arg) /* adapt compression level */ adaptCompressionLevel(ctx); - + /* compress the data */ { + size_t const compressionBlockSize = 4 << 17; /* 128 KB */ unsigned const cLevel = ctx->compressionLevel; + unsigned blockNum = 0; + size_t remaining = job->src.size; + size_t srcPos = 0; + size_t dstPos = 0; + size_t dictPos = 0; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); - /* begin compression */ - { - size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); - DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); - size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); - size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); - if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { - DISPLAY("Error: something went wrong while starting compression\n"); - signalErrorToThreads(ctx); - return arg; - } - } - /* continue compression */ - if (currJob != 0) { /* not first job flush/overwrite the frame header */ - size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, 0); - if (ZSTD_isError(hSize)) { - DISPLAY("Error: something went wrong while continuing compression\n"); - job->compressedSize = hSize; - signalErrorToThreads(ctx); - return arg; + /* reset compressed size */ + job->compressedSize = 0; + + while (remaining != 0) { + size_t const actualBlockSize = MIN(remaining, compressionBlockSize); + /* begin compression */ + { + size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); + DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); + size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize + dictPos, useDictSize, cLevel); + size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); + if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { + DISPLAY("Error: something went wrong while starting compression\n"); + signalErrorToThreads(ctx); + return arg; + } + } + + /* continue compression */ + if (currJob != 0 || blockNum != 0) { /* not first block of first job flush/overwrite the frame header */ + size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, 0); + if (ZSTD_isError(hSize)) { + DISPLAY("Error: something went wrong while continuing compression\n"); + job->compressedSize = hSize; + signalErrorToThreads(ctx); + return arg; + } + ZSTD_invalidateRepCodes(ctx->cctx); + } + { + + size_t const ret = (job->lastJob && remaining <= compressionBlockSize) ? + ZSTD_compressEnd (ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize) : + ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize); + if (ZSTD_isError(ret)) { + DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(ret)); + signalErrorToThreads(ctx); + return arg; + } + job->compressedSize += ret; + remaining -= actualBlockSize; + srcPos += actualBlockSize; + dstPos += ret; + dictPos += actualBlockSize; + blockNum++; } - ZSTD_invalidateRepCodes(ctx->cctx); - } - job->compressedSize = (job->lastJob) ? - ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, job->src.size) : - ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, job->src.size); - if (ZSTD_isError(job->compressedSize)) { - DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(job->compressedSize)); - signalErrorToThreads(ctx); - return arg; } job->dst.size = job->compressedSize; } From 5a85c57e3055b9a1bace0623bdfe8600d7efb128 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 11:47:17 -0700 Subject: [PATCH 184/318] set up new calculations compression completion progress --- contrib/adaptive-compression/adapt.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 17721f87e..ab332f495 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -355,7 +355,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double completion; pthread_mutex_lock(&ctx->completion_mutex.pMutex); completion = writeSlow ? ctx->writeCompletion : ctx->createCompletion; - DEBUG(2, "write completion: %f, create completion: %f\n", ctx->writeCompletion, ctx->createCompletion); + DEBUG(3, "write completion: %f, create completion: %f\n", ctx->writeCompletion, ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; @@ -375,7 +375,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(3, "completion: %f\n", completion); + DEBUG(2, "completion: %f\n", completion); ctx->compressionLevel -= change; reset = 1; } @@ -493,6 +493,13 @@ static void* compressionThread(void* arg) dstPos += ret; dictPos += actualBlockSize; blockNum++; + + /* update completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + if (!ctx->compressionCompletionMeasured) { + ctx->compressionCompletion = 1 - (double)remaining/job->src.size; + } + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } job->dst.size = job->compressedSize; @@ -549,11 +556,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - if (!ctx->compressionCompletionMeasured) { - ctx->compressionCompletion = ZSTD_getCompletion(ctx->cctx); - ctx->compressionCompletionMeasured = 1; - DEBUG(3, "output detected completion: %f\n", ctx->compressionCompletion); - } + ctx->compressionCompletionMeasured = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); From 6945b3c43d5be449d0bd1ff06c40bca2e5230fa3 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 11:51:50 -0700 Subject: [PATCH 185/318] removed previous version of completion for compression --- lib/compress/zstd_compress.c | 10 ---------- lib/zstd.h | 5 ----- 2 files changed, 15 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 0c8edecec..90002c2c4 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -140,9 +140,6 @@ struct ZSTD_CCtx_s { /* Multi-threading */ U32 nbThreads; ZSTDMT_CCtx* mtctx; - - /* adaptive compression */ - double completion; }; @@ -2848,7 +2845,6 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, BYTE* op = ostart; U32 const maxDist = 1 << cctx->appliedParams.cParams.windowLog; - cctx->completion = 0; if (cctx->appliedParams.fParams.checksumFlag && srcSize) XXH64_update(&cctx->xxhState, src, srcSize); @@ -2899,7 +2895,6 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, } remaining -= blockSize; - cctx->completion = 1 - (double)remaining/srcSize; dstCapacity -= cSize; ip += blockSize; op += cSize; @@ -3002,11 +2997,6 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, return fhSize; } -ZSTDLIB_API double ZSTD_getCompletion(ZSTD_CCtx* cctx) -{ - return cctx->completion; -} - size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) diff --git a/lib/zstd.h b/lib/zstd.h index e835ad3a7..6d69d94e6 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -808,11 +808,6 @@ ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -/*! ZSTD_getCompletion: get a double representing how much of a file/buffer has been compressed - * using ZSTD_compressContinue() - * return: a double value in the range of 0 to 1 representing how much a compression job has finished - */ -ZSTDLIB_API double ZSTD_getCompletion(ZSTD_CCtx* cctx); /*- Buffer-less streaming decompression (synchronous mode) From 42382c121639c8f620c2c67acfc2d2983781dffa Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 13:30:07 -0700 Subject: [PATCH 186/318] added some debug statements, adjusted end condition --- contrib/adaptive-compression/adapt.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ab332f495..d572ee191 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -375,7 +375,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(2, "completion: %f\n", completion); + DEBUG(3, "completion: %f\n", completion); ctx->compressionLevel -= change; reset = 1; } @@ -452,6 +452,8 @@ static void* compressionThread(void* arg) while (remaining != 0) { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); + DEBUG(2, "remaining: %zu\n", remaining); + DEBUG(2, "actualBlockSize: %zu\n", actualBlockSize); /* begin compression */ { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); @@ -478,8 +480,10 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } { - - size_t const ret = (job->lastJob && remaining <= compressionBlockSize) ? + DEBUG(2, "write out ending: %d\n", job->lastJob && (remaining == actualBlockSize)); + DEBUG(2, "lastJob %u\n", job->lastJob); + DEBUG(2, "compressionBlockSize %zu\n", compressionBlockSize); + size_t const ret = (job->lastJob && remaining == actualBlockSize) ? ZSTD_compressEnd (ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize) : ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize); if (ZSTD_isError(ret)) { From 3974d2b38a8069fdc24d09f5a4adcc84971dfaa1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 19 Jul 2017 13:33:21 -0700 Subject: [PATCH 187/318] blind fix for Windows Multithreading module adds a fake 0 return value for mutex/cond init --- lib/common/threading.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/common/threading.h b/lib/common/threading.h index ec9e07a90..ee7864555 100644 --- a/lib/common/threading.h +++ b/lib/common/threading.h @@ -42,14 +42,14 @@ extern "C" { /* mutex */ #define pthread_mutex_t CRITICAL_SECTION -#define pthread_mutex_init(a,b) InitializeCriticalSection((a)) +#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), 0) #define pthread_mutex_destroy(a) DeleteCriticalSection((a)) #define pthread_mutex_lock(a) EnterCriticalSection((a)) #define pthread_mutex_unlock(a) LeaveCriticalSection((a)) /* condition variable */ #define pthread_cond_t CONDITION_VARIABLE -#define pthread_cond_init(a, b) InitializeConditionVariable((a)) +#define pthread_cond_init(a, b) (InitializeConditionVariable((a)), 0) #define pthread_cond_destroy(a) /* No delete */ #define pthread_cond_wait(a, b) SleepConditionVariableCS((a), (b), INFINITE) #define pthread_cond_signal(a) WakeConditionVariable((a)) From 030264ca51814d3bef8debcedace65f56779f691 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 19 Jul 2017 14:14:26 -0700 Subject: [PATCH 188/318] Experiment with integrating ZSTD_count with findBestMatch --- contrib/long_distance_matching/Makefile | 12 +- .../circular_buffer_table.c | 117 ++- contrib/long_distance_matching/ldm.c | 37 +- contrib/long_distance_matching/ldm.h | 9 +- .../long_distance_matching/ldm_hashtable.h | 9 +- .../long_distance_matching/ldm_with_table.c | 959 ++++++++++++++++++ 6 files changed, 1093 insertions(+), 50 deletions(-) create mode 100644 contrib/long_distance_matching/ldm_with_table.c diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 131638fdb..3aa3f8bd9 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -25,16 +25,20 @@ LDFLAGS += -lzstd default: all -all: main-basic main-circular-buffer +all: main-circular-buffer main-integrated -main-basic : basic_table.c ldm.c main-ldm.c - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ +#main-basic : basic_table.c ldm.c main-ldm.c +# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ main-circular-buffer: circular_buffer_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ +main-integrated: ldm_with_table.c main-ldm.c + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + + clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main-basic main-circular-buffer + main-basic main-circular-buffer main-integrated @echo Cleaning completed diff --git a/contrib/long_distance_matching/circular_buffer_table.c b/contrib/long_distance_matching/circular_buffer_table.c index 104d1b339..9b7ad088c 100644 --- a/contrib/long_distance_matching/circular_buffer_table.c +++ b/contrib/long_distance_matching/circular_buffer_table.c @@ -14,8 +14,8 @@ // TODO: rename. Number of hash buckets. #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-4-HASH_BUCKET_SIZE_LOG) - -#define TMP_ZSTDTOGGLE +#define ZSTD_SKIP +//#define TMP_TST struct LDM_hashTable { U32 size; // Number of buckets @@ -25,15 +25,20 @@ struct LDM_hashTable { // Position corresponding to offset=0 in LDM_hashEntry. const BYTE *offsetBase; + U32 minMatchLength; + U32 maxWindowSize; }; -LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase) { +LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase, + U32 minMatchLength, U32 maxWindowSize) { LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); table->size = size >> HASH_BUCKET_SIZE_LOG; table->maxEntries = size; table->entries = calloc(size, sizeof(LDM_hashEntry)); table->bucketOffsets = calloc(size >> HASH_BUCKET_SIZE_LOG, sizeof(BYTE)); table->offsetBase = offsetBase; + table->minMatchLength = minMatchLength; + table->maxWindowSize = maxWindowSize; return table; } @@ -41,7 +46,7 @@ static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { return table->entries + (hash << HASH_BUCKET_SIZE_LOG); } -#ifdef TMP_ZSTDTOGGLE +#if TMP_ZSTDTOGGLE static unsigned ZSTD_NbCommonBytes (register size_t val) { if (MEM_isLittleEndian()) { @@ -143,10 +148,85 @@ static size_t ZSTD_count(const BYTE *pIn, const BYTE *pMatch, return (size_t)(pIn - pStart); } +U32 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, + const BYTE *pMatch, const BYTE *pBase) { + U32 matchLength = 0; + while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) { + pIn--; + pMatch--; + matchLength++; + } + return matchLength; +} + +LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, + const hash_t hash, + const U32 checksum, + const BYTE *pIn, + const BYTE *pEnd, + U32 *matchLength, + U32 *backwardsMatchLength, + const BYTE *pAnchor) { + LDM_hashEntry *bucket = getBucket(table, hash); + LDM_hashEntry *cur = bucket; + LDM_hashEntry *bestEntry = NULL; + U32 bestMatchLength = 0; + U32 forwardMatch = 0; + U32 backwardMatch = 0; +#ifdef TMP_TST + U32 numBetter = 0; +#endif + for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { + // Check checksum for faster check. + const BYTE *pMatch = cur->offset + table->offsetBase; + if (cur->checksum == checksum && pIn - pMatch <= table->maxWindowSize) { + U32 forwardMatchLength = ZSTD_count(pIn, pMatch, pEnd); + U32 backwardMatchLength, totalMatchLength; + if (forwardMatchLength < table->minMatchLength) { + continue; + } + backwardMatchLength = + countBackwardsMatch(pIn, pAnchor, cur->offset + table->offsetBase, + table->offsetBase); + + totalMatchLength = forwardMatchLength + backwardMatchLength; + + if (totalMatchLength >= bestMatchLength) { + bestMatchLength = totalMatchLength; + forwardMatch = forwardMatchLength; + backwardMatch = backwardMatchLength; + bestEntry = cur; +#ifdef TMP_TST + numBetter++; +#endif + +#ifdef ZSTD_SKIP + *matchLength = forwardMatchLength; + *backwardsMatchLength = backwardMatchLength; + + return cur; +#endif +// *matchLength = forwardMatchLength; +// return cur; + } + } + } + if (bestEntry != NULL && bestMatchLength > table->minMatchLength) { +#ifdef TMP_TST + printf("Num better %u\n", numBetter - 1); +#endif + *matchLength = forwardMatch; + *backwardsMatchLength = backwardMatch; + return bestEntry; + } + return NULL; +} + #else static int isValidMatch(const BYTE *pIn, const BYTE *pMatch, U32 minMatchLength, U32 maxWindowSize) { + printf("HERE\n"); U32 lengthLeft = minMatchLength; const BYTE *curIn = pIn; const BYTE *curMatch = pMatch; @@ -165,44 +245,33 @@ static int isValidMatch(const BYTE *pIn, const BYTE *pMatch, return 1; } -#endif // TMP_ZSTDTOGGLE - +//TODO: clean up function call. This is not at all decoupled from LDM. LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const hash_t hash, const U32 checksum, const BYTE *pIn, const BYTE *pEnd, - U32 minMatchLength, - U32 maxWindowSize, - U32 *matchLength) { + U32 *matchLength, + U32 *backwardsMatchLength, + const BYTE *pAnchor) { LDM_hashEntry *bucket = getBucket(table, hash); LDM_hashEntry *cur = bucket; - // TODO: in order of recency? - for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { + (void)matchLength; + (void)backwardsMatchLength; + (void)pAnchor; for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { // Check checksum for faster check. const BYTE *pMatch = cur->offset + table->offsetBase; -#ifdef TMP_ZSTDTOGGLE - if (cur->checksum == checksum && pIn - pMatch <= maxWindowSize) { - U32 forwardMatchLength = ZSTD_count(pIn, pMatch, pEnd); - if (forwardMatchLength >= minMatchLength) { - *matchLength = forwardMatchLength; - return cur; - } - } -#else (void)pEnd; - (void)minMatchLength; - (void)maxWindowSize; if (cur->checksum == checksum && - isValidMatch(pIn, pMatch, minMatchLength, maxWindowSize)) { + isValidMatch(pIn, pMatch, table->minMatchLength, table->maxWindowSize)) { return cur; } -#endif } return NULL; } +#endif hash_t HASH_hashU32(U32 value) { return ((value * 2654435761U) >> (32 - LDM_HASHLOG)); } diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 1512ab8c5..a116af70a 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -24,8 +24,6 @@ #define OUTPUT_CONFIGURATION #define CHECKSUM_CHAR_OFFSET 10 -//#define LDM_LAG 0 - //#define HASH_CHECK //#define RUN_CHECKS //#define TMP_RECOMPUTE_LENGTHS @@ -410,7 +408,8 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->anchor = cctx->ibase; memset(&(cctx->stats), 0, sizeof(cctx->stats)); - cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U64, cctx->ibase); + cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U64, cctx->ibase, + LDM_MIN_MATCH_LENGTH, LDM_WINDOW_SIZE); cctx->stats.minOffset = UINT_MAX; cctx->stats.windowSizeLog = LDM_WINDOW_SIZE_LOG; @@ -439,7 +438,7 @@ void LDM_destroyCCtx(LDM_CCtx *cctx) { * matchLength contains the forward length of the match. */ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, - U32 *matchLength) { + U32 *matchLength, U32 *backwardMatchLength) { LDM_hashEntry *entry = NULL; cctx->nextIp = cctx->ip + cctx->step; @@ -461,9 +460,8 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, #else entry = HASH_getValidEntry(cctx->hashTable, h, sum, cctx->ip, cctx->iend, - LDM_MIN_MATCH_LENGTH, - LDM_WINDOW_SIZE, - matchLength); + matchLength, backwardMatchLength, + cctx->anchor); #endif if (entry != NULL) { @@ -540,6 +538,7 @@ size_t LDM_compress(const void *src, size_t srcSize, LDM_CCtx cctx; const BYTE *match = NULL; U32 forwardMatchLength = 0; + U32 backwardsMatchLength = 0; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); LDM_outputConfiguration(); @@ -558,11 +557,14 @@ size_t LDM_compress(const void *src, size_t srcSize, * is less than the minimum match length), then stop searching for matches * and encode the final literals. */ - while (LDM_findBestMatch(&cctx, &match, &forwardMatchLength) == 0) { - U32 backwardsMatchLen = 0; + while (LDM_findBestMatch(&cctx, &match, &forwardMatchLength, + &backwardsMatchLength) == 0) { #ifdef COMPUTE_STATS cctx.stats.numMatches++; #endif + +#if TMP_RECOMPUTE_LENGTHS + backwardsMatchLength = 0; /** * Catch up: look back to extend the match backwards from the found match. */ @@ -570,8 +572,12 @@ size_t LDM_compress(const void *src, size_t srcSize, cctx.ip[-1] == match[-1]) { cctx.ip--; match--; - backwardsMatchLen++; + backwardsMatchLength++; } +#else + cctx.ip -= backwardsMatchLength; + match -= backwardsMatchLength; +#endif /** * Write current block (literals, literal length, match offset, match @@ -580,13 +586,14 @@ size_t LDM_compress(const void *src, size_t srcSize, { const U32 literalLength = cctx.ip - cctx.anchor; const U32 offset = cctx.ip - match; -#ifdef TMP_RECOMPUTE_LENGTHS +#if TMP_RECOMPUTE_LENGTHS const U32 matchLength = LDM_countMatchLength( - cctx.ip + LDM_MIN_MATCH_LENGTH + backwardsMatchLen, - match + LDM_MIN_MATCH_LENGTH + backwardsMatchLen, - cctx.ihashLimit) + backwardsMatchLen; + cctx.ip + LDM_MIN_MATCH_LENGTH + backwardsMatchLength, + match + LDM_MIN_MATCH_LENGTH + backwardsMatchLength, + cctx.ihashLimit) + backwardsMatchLength; #else - const U32 matchLength = forwardMatchLength + backwardsMatchLen - + const U32 matchLength = forwardMatchLength + + backwardsMatchLength - LDM_MIN_MATCH_LENGTH; #endif diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 735435e8d..2396227d2 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -20,9 +20,12 @@ #define LDM_WINDOW_SIZE_LOG 28 #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) -//These should be multiples of four (and perhaps set to the same values?). -#define LDM_MIN_MATCH_LENGTH 64 -#define LDM_HASH_LENGTH 64 +//These should be multiples of four (and perhaps set to the same value?). +#define LDM_MIN_MATCH_LENGTH 1024 +#define LDM_HASH_LENGTH 1024 + +#define TMP_ZSTDTOGGLE 1 +#define TMP_RECOMPUTE_LENGTHS (!(TMP_ZSTDTOGGLE)) typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h index 2ea159f71..51d825258 100644 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -19,7 +19,8 @@ typedef struct LDM_hashTable LDM_hashTable; * LDM_hashEntry.offset is added to offsetBase to calculate pMatch in * HASH_getValidEntry. */ -LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase); +LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase, + U32 minMatchLength, U32 maxWindowSize); /** * Returns an LDM_hashEntry from the table that matches the checksum. @@ -41,9 +42,9 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, const U32 checksum, const BYTE *pIn, const BYTE *pEnd, - const U32 minMatchLength, - const U32 maxWindowSize, - U32 *matchLength); + U32 *matchLength, + U32 *backwardsMatchLength, + const BYTE *pAnchor); hash_t HASH_hashU32(U32 value); diff --git a/contrib/long_distance_matching/ldm_with_table.c b/contrib/long_distance_matching/ldm_with_table.c new file mode 100644 index 000000000..68a33d0ff --- /dev/null +++ b/contrib/long_distance_matching/ldm_with_table.c @@ -0,0 +1,959 @@ +#include +#include +#include +#include +#include + +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +//#define LDM_HASH_ENTRY_SIZE 4 +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) +#define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 4) + +// Insert every (HASH_ONLY_EVERY + 1) into the hash table. +#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE) - 4)) +#define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) + +/* Hash table stuff. */ +#define HASH_BUCKET_SIZE_LOG 3 // MAX is 4 for now +#define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) +#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-4-HASH_BUCKET_SIZE_LOG) + +#define ML_BITS 4 +#define ML_MASK ((1U<size = size >> HASH_BUCKET_SIZE_LOG; + table->maxEntries = size; + table->entries = calloc(size, sizeof(LDM_hashEntry)); + table->bucketOffsets = calloc(size >> HASH_BUCKET_SIZE_LOG, sizeof(BYTE)); + 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 (register 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; + const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ + 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); +} + +U32 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, + const BYTE *pMatch, const BYTE *pBase) { + U32 matchLength = 0; + while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) { + pIn--; + pMatch--; + matchLength++; + } + return matchLength; +} + +LDM_hashEntry *HASH_getValidEntry(const LDM_CCtx *cctx, + const hash_t hash, + const U32 checksum, + U32 *matchLength, + U32 *backwardsMatchLength) { + LDM_hashTable *table = cctx->hashTable; + LDM_hashEntry *bucket = getBucket(table, hash); + LDM_hashEntry *cur = bucket; + LDM_hashEntry *bestEntry = NULL; + U32 bestMatchLength = 0; + for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { + // Check checksum for faster check. + const BYTE *pMatch = cur->offset + cctx->ibase; + + if (cur->checksum == checksum && + cctx->ip - pMatch <= LDM_WINDOW_SIZE) { + U32 forwardMatchLength = ZSTD_count(cctx->ip, pMatch, cctx->iend); + U32 backwardMatchLength, totalMatchLength; + + // 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 && + totalMatchLength >= LDM_MIN_MATCH_LENGTH) { + bestMatchLength = totalMatchLength; + *matchLength = forwardMatchLength; + *backwardsMatchLength = backwardMatchLength; + + bestEntry = cur; +#ifdef ZSTD_SKIP + return cur; +#endif + } + } + } + if (bestEntry != NULL && bestMatchLength > LDM_MIN_MATCH_LENGTH) { + return bestEntry; + } + return NULL; +} + +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; +} + +U32 HASH_getSize(const LDM_hashTable *table) { + return table->size; +} + +void HASH_destroyTable(LDM_hashTable *table) { + free(table->entries); + free(table->bucketOffsets); + free(table); +} + +void HASH_outputTableOccupancy(const LDM_hashTable *table) { + U32 ctr = 0; + LDM_hashEntry *cur = table->entries; + LDM_hashEntry *end = table->entries + (table->size * HASH_BUCKET_SIZE); + for (; cur < end; ++cur) { + if (cur->offset == 0) { + ctr++; + } + } + + printf("Num buckets, bucket size: %d, %d\n", table->size, HASH_BUCKET_SIZE); + printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", + table->maxEntries, ctr, + 100.0 * (double)(ctr) / table->maxEntries); +} + + +// TODO: This can be done more efficiently (but it is not that important as it +// is only used for computing stats). +static int intLog2(U32 x) { + int ret = 0; + while (x >>= 1) { + ret++; + } + return ret; +} + +// TODO: Maybe we would eventually prefer to have linear rather than +// exponential buckets. +/** +void HASH_outputTableOffsetHistogram(const LDM_CCtx *cctx) { + U32 i = 0; + int buckets[32] = { 0 }; + + printf("\n"); + printf("Hash table histogram\n"); + for (; i < HASH_getSize(cctx->hashTable); i++) { + int offset = (cctx->ip - cctx->ibase) - + HASH_getEntryFromHash(cctx->hashTable, i)->offset; + buckets[intLog2(offset)]++; + } + + i = 0; + for (; i < 32; i++) { + printf("2^%*d: %10u %6.3f%%\n", 2, i, + buckets[i], + 100.0 * (double) buckets[i] / + (double) HASH_getSize(cctx->hashTable)); + } + printf("\n"); +} +*/ + +void LDM_printCompressStats(const LDM_compressStats *stats) { + int i = 0; + printf("=====================\n"); + printf("Compression statistics\n"); + //TODO: compute percentage matched? + 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: offset, num matches, %% of matches\n"); + + for (; i <= intLog2(stats->maxOffset); i++) { + printf("2^%*d: %10u %6.3f%%\n", 2, i, + stats->offsetHistogram[i], + 100.0 * (double) stats->offsetHistogram[i] / + (double) stats->numMatches); + } + printf("\n"); + printf("=====================\n"); +} + +int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { + U32 lengthLeft = LDM_MIN_MATCH_LENGTH; + const BYTE *curIn = pIn; + const BYTE *curMatch = pMatch; + + if (pIn - pMatch > LDM_WINDOW_SIZE) { + return 0; + } + + for (; lengthLeft >= 4; lengthLeft -= 4) { + if (MEM_read32(curIn) != MEM_read32(curMatch)) { + return 0; + } + curIn += 4; + curMatch += 4; + } + return 1; +} + +hash_t HASH_hashU32(U32 value) { + return ((value * 2654435761U) >> (32 - LDM_HASHLOG)); +} + +/** + * Convert a sum computed from getChecksum to a hash value in the range + * of the hash table. + */ +static hash_t checksumToHash(U32 sum) { + return HASH_hashU32(sum); +// return ((sum * 2654435761U) >> (32 - LDM_HASHLOG)); +} + +/** + * Computes a checksum based on rsync's checksum. + * + * a(k,l) = \sum_{i = k}^l x_i (mod M) + * b(k,l) = \sum_{i = k}^l ((l - i + 1) * x_i) (mod M) + * checksum(k,l) = a(k,l) + 2^{16} * b(k,l) + */ +static U32 getChecksum(const BYTE *buf, U32 len) { + U32 i; + U32 s1, s2; + + s1 = s2 = 0; + for (i = 0; i < (len - 4); i += 4) { + s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + + (2 * buf[i + 2]) + (buf[i + 3]) + + (10 * CHECKSUM_CHAR_OFFSET); + s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3] + + + (4 * CHECKSUM_CHAR_OFFSET); + + } + for(; i < len; i++) { + s1 += buf[i] + CHECKSUM_CHAR_OFFSET; + s2 += s1; + } + return (s1 & 0xffff) + (s2 << 16); +} + +/** + * Update a checksum computed from getChecksum(data, len). + * + * The checksum can be updated along its ends as follows: + * a(k+1, l+1) = (a(k,l) - x_k + x_{l+1}) (mod M) + * b(k+1, l+1) = (b(k,l) - (l-k+1)*x_k + (a(k+1,l+1)) (mod M) + * + * Thus toRemove should correspond to data[0]. + */ +static U32 updateChecksum(U32 sum, U32 len, + BYTE toRemove, BYTE toAdd) { + U32 s1 = (sum & 0xffff) - toRemove + toAdd; + U32 s2 = (sum >> 16) - ((toRemove + CHECKSUM_CHAR_OFFSET) * len) + s1; + + return (s1 & 0xffff) + (s2 << 16); +} + +/** + * Update cctx->nextSum, cctx->nextHash, and cctx->nextPosHashed + * based on cctx->lastSum 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) { +#ifdef RUN_CHECKS + U32 check; + if ((cctx->nextIp - cctx->ibase != 1) && + (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { + printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, + cctx->DEBUG_setNextHash - cctx->ibase); + } + + cctx->DEBUG_setNextHash = cctx->nextIp; +#endif + +// cctx->nextSum = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); + cctx->nextSum = updateChecksum( + cctx->lastSum, LDM_HASH_LENGTH, + cctx->lastPosHashed[0], + cctx->lastPosHashed[LDM_HASH_LENGTH]); + cctx->nextPosHashed = cctx->nextIp; + cctx->nextHash = checksumToHash(cctx->nextSum); + +#if LDM_LAG +// printf("LDM_LAG %zu\n", cctx->ip - cctx->lagIp); + if (cctx->ip - cctx->ibase > LDM_LAG) { + cctx->lagSum = updateChecksum( + cctx->lagSum, LDM_HASH_LENGTH, + cctx->lagIp[0], cctx->lagIp[LDM_HASH_LENGTH]); + cctx->lagIp++; + cctx->lagHash = checksumToHash(cctx->lagSum); + } +#endif + +#ifdef RUN_CHECKS + check = getChecksum(cctx->nextIp, LDM_HASH_LENGTH); + + if (check != cctx->nextSum) { + printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); + } + + if ((cctx->nextIp - cctx->lastPosHashed) != 1) { + printf("setNextHash: nextIp != lastPosHashed + 1. %zu %zu %zu\n", + cctx->nextIp - cctx->ibase, cctx->lastPosHashed - cctx->ibase, + cctx->ip - cctx->ibase); + } +#endif +} + +static void putHashOfCurrentPositionFromHash( + LDM_CCtx *cctx, hash_t hash, U32 sum) { + // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. + // Note: this works only when cctx->step is 1. + if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { + /** + const LDM_hashEntry entry = { cctx->ip - cctx->ibase , + MEM_read32(cctx->ip) }; + */ +#if LDM_LAG + // TODO: off by 1, but whatever + if (cctx->lagIp - cctx->ibase > 0) { + const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, cctx->lagSum }; + HASH_insert(cctx->hashTable, cctx->lagHash, entry); + } else { + const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; + HASH_insert(cctx->hashTable, hash, entry); + } +#else + const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; + HASH_insert(cctx->hashTable, hash, entry); +#endif + } + + cctx->lastPosHashed = cctx->ip; + cctx->lastHash = hash; + cctx->lastSum = sum; +} + +/** + * Copy over the cctx->lastHash, cctx->lastSum, and cctx->lastPosHashed + * fields from the "next" fields. + * + * This requires that cctx->ip == cctx->nextPosHashed. + */ +static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { +#ifdef RUN_CHECKS + if (cctx->ip != cctx->nextPosHashed) { + printf("CHECK failed: updateLastHashFromNextHash %zu\n", + cctx->ip - cctx->ibase); + } +#endif + putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); +} + +/** + * Insert hash of the current position into the hash table. + */ +static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { + U32 sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); + hash_t hash = checksumToHash(sum); + +#ifdef RUN_CHECKS + if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { + printf("CHECK failed: putHashOfCurrentPosition %zu\n", + cctx->ip - cctx->ibase); + } +#endif + + putHashOfCurrentPositionFromHash(cctx, hash, sum); +} + +U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { + const BYTE * const pStart = pIn; + while (pIn < pInLimit - 1) { + BYTE const diff = (*pMatch) ^ *(pIn); + if (!diff) { + pIn++; + pMatch++; + continue; + } + return (U32)(pIn - pStart); + } + return (U32)(pIn - pStart); +} + +void LDM_outputConfiguration(void) { + printf("=====================\n"); + printf("Configuration\n"); + printf("Window size log: %d\n", LDM_WINDOW_SIZE_LOG); + printf("Min match, 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("LDM_LAG %d\n", LDM_LAG); + printf("=====================\n"); +} + +void LDM_readHeader(const void *src, U64 *compressedSize, + U64 *decompressedSize) { + const BYTE *ip = (const BYTE *)src; + *compressedSize = MEM_readLE64(ip); + ip += sizeof(U64); + *decompressedSize = MEM_readLE64(ip); + // ip += sizeof(U64); +} + +void 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)); + cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U64); + + 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; + + cctx->DEBUG_setNextHash = 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). + * + * matchLength contains the forward length of the match. + */ +static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, + U32 *matchLength, U32 *backwardMatchLength) { + + LDM_hashEntry *entry = NULL; + cctx->nextIp = cctx->ip + cctx->step; + + while (entry == NULL) { + hash_t h; + U32 sum; + setNextHash(cctx); + h = cctx->nextHash; + sum = cctx->nextSum; + cctx->ip = cctx->nextIp; + cctx->nextIp += cctx->step; + + if (cctx->ip > cctx->imatchLimit) { + return 1; + } + + entry = HASH_getValidEntry(cctx, h, sum, + matchLength, backwardMatchLength); + + if (entry != NULL) { + *match = entry->offset + cctx->ibase; + } + putHashOfCurrentPositionFromHash(cctx, h, sum); + } + setNextHash(cctx); + return 0; +} + +void LDM_encodeLiteralLengthAndLiterals( + LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength) { + /* Encode the literal length. */ + if (literalLength >= RUN_MASK) { + int len = (int)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 U32 literalLength, + const U32 offset, + const U32 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) { + unsigned 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; + U32 forwardMatchLength = 0; + U32 backwardsMatchLength = 0; + + LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); + LDM_outputConfiguration(); + + /* Hash the first position and put it into the hash table. */ + LDM_putHashOfCurrentPosition(&cctx); + +#if LDM_LAG + cctx.lagIp = cctx.ip; + cctx.lagHash = cctx.lastHash; + cctx.lagSum = cctx.lastSum; +#endif + /** + * 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) == 0) { +#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 U32 literalLength = cctx.ip - cctx.anchor; + const U32 offset = cctx.ip - match; + const U32 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)]++; +#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); + } + + // HASH_outputTableOffsetHistogram(&cctx); + + /* Encode the last literals (no more matches). */ + { + const U32 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; + } +} + +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 + 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; + + // Inefficient for now. + while (match < cpy - offset && dctx.op < dctx.oend) { + *(dctx.op)++ = *match++; + } + } + return dctx.op - (BYTE *)dst; +} + +// TODO: implement and test hash function +void LDM_test(void) { +} + +/* +void LDM_test(const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { + const BYTE *ip = (const BYTE *)src + 1125; + U32 sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); + U32 sum2; + ++ip; + for (; ip < (const BYTE *)src + 1125 + 100; ip++) { + sum2 = updateChecksum(sum, LDM_HASH_LENGTH, + ip[-1], ip[LDM_HASH_LENGTH - 1]); + sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); + printf("TEST HASH: %zu %u %u\n", ip - (const BYTE *)src, sum, sum2); + } +} +*/ + + From 6767abe65273056289e431c5454b937b13bc34bc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 14:54:15 -0700 Subject: [PATCH 189/318] fixing error when file size is multiple of job size (in which case, the srcSize of the last job is 0) --- contrib/adaptive-compression/adapt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index d572ee191..dcaf3aeff 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -450,7 +450,7 @@ static void* compressionThread(void* arg) /* reset compressed size */ job->compressedSize = 0; - while (remaining != 0) { + do { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); DEBUG(2, "remaining: %zu\n", remaining); DEBUG(2, "actualBlockSize: %zu\n", actualBlockSize); @@ -505,7 +505,7 @@ static void* compressionThread(void* arg) } pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } - } + } while (remaining != 0); job->dst.size = job->compressedSize; } pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); From 2a22c7915e749879e1abc8283b86212d8947e5fb Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 16:00:54 -0700 Subject: [PATCH 190/318] call ZSTD_compressBegin() once --- contrib/adaptive-compression/adapt.c | 31 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index dcaf3aeff..cc72d1558 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -437,36 +437,36 @@ static void* compressionThread(void* arg) /* compress the data */ { - size_t const compressionBlockSize = 4 << 17; /* 128 KB */ + size_t const compressionBlockSize = 1 << 17; /* 128 KB */ unsigned const cLevel = ctx->compressionLevel; unsigned blockNum = 0; size_t remaining = job->src.size; size_t srcPos = 0; size_t dstPos = 0; - size_t dictPos = 0; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); /* reset compressed size */ job->compressedSize = 0; + /* begin compression */ + { + size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); + DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); + size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); + size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); + if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { + DISPLAY("Error: something went wrong while starting compression\n"); + signalErrorToThreads(ctx); + return arg; + } + } + do { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); DEBUG(2, "remaining: %zu\n", remaining); DEBUG(2, "actualBlockSize: %zu\n", actualBlockSize); - /* begin compression */ - { - size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); - DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); - size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize + dictPos, useDictSize, cLevel); - size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); - if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { - DISPLAY("Error: something went wrong while starting compression\n"); - signalErrorToThreads(ctx); - return arg; - } - } /* continue compression */ if (currJob != 0 || blockNum != 0) { /* not first block of first job flush/overwrite the frame header */ @@ -495,7 +495,6 @@ static void* compressionThread(void* arg) remaining -= actualBlockSize; srcPos += actualBlockSize; dstPos += ret; - dictPos += actualBlockSize; blockNum++; /* update completion */ From 1ca128868912a978d815663bce9583520e47ace5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 19 Jul 2017 16:01:16 -0700 Subject: [PATCH 191/318] added --memtest=# command to fuzzer to jump directly to relevant test section --- tests/fuzzer.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 3099f346e..06f984087 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -139,7 +139,7 @@ static void FUZ_freeDebug(void* counter, void* address) static void FUZ_displayMallocStats(mallocCounter_t count) { - DISPLAYLEVEL(3, "peak:%u KB, nbMallocs:%u, total:%u KB \n", + DISPLAYLEVEL(3, "peak:%6u KB, nbMallocs:%2u, total:%6u KB \n", (U32)(count.peakMalloc >> 10), count.nbMalloc, (U32)(count.totalMalloc >> 10)); @@ -153,7 +153,7 @@ static void FUZ_displayMallocStats(mallocCounter_t count) exit(1); \ } } -static int FUZ_mallocTests(unsigned seed, double compressibility) +static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) { size_t const inSize = 64 MB + 16 MB + 4 MB + 1 MB + 256 KB + 64 KB; /* 85.3 MB */ size_t const outSize = ZSTD_compressBound(inSize); @@ -171,6 +171,7 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) RDG_genBuffer(inBuffer, inSize, compressibility, 0. /*auto*/, seed); /* simple compression tests */ + if (part <= 1) { int compressionLevel; for (compressionLevel=1; compressionLevel<=6; compressionLevel++) { mallocCounter_t malcount = INIT_MALLOC_COUNTER; @@ -183,6 +184,7 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) } } /* streaming compression tests */ + if (part <= 2) { int compressionLevel; for (compressionLevel=1; compressionLevel<=6; compressionLevel++) { mallocCounter_t malcount = INIT_MALLOC_COUNTER; @@ -199,6 +201,7 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) } } /* advanced MT API test */ + if (part <= 3) { U32 nbThreads; for (nbThreads=1; nbThreads<=4; nbThreads++) { int compressionLevel; @@ -218,6 +221,7 @@ static int FUZ_mallocTests(unsigned seed, double compressibility) } } } /* advanced MT streaming API test */ + if (part <= 4) { U32 nbThreads; for (nbThreads=1; nbThreads<=4; nbThreads++) { int compressionLevel; @@ -1442,6 +1446,19 @@ static unsigned readU32FromChar(const char** stringPtr) return result; } +/** 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. + */ +static unsigned longCommandWArg(const char** stringPtr, const char* longCommand) +{ + size_t const comSize = strlen(longCommand); + int const result = !strncmp(*stringPtr, longCommand, comSize); + if (result) *stringPtr += comSize; + return result; +} + int main(int argc, const char** argv) { U32 seed = 0; @@ -1465,6 +1482,8 @@ int main(int argc, const char** argv) /* Handle commands. Aggregated commands are allowed */ if (argument[0]=='-') { + if (longCommandWArg(&argument, "--memtest=")) { memTestsOnly = readU32FromChar(&argument); continue; } + if (!strcmp(argument, "--memtest")) { memTestsOnly=1; continue; } if (!strcmp(argument, "--no-big-tests")) { bigTests=0; continue; } @@ -1539,7 +1558,7 @@ int main(int argc, const char** argv) if (memTestsOnly) { g_displayLevel = MAX(3, g_displayLevel); - return FUZ_mallocTests(seed, ((double)proba) / 100); + return FUZ_mallocTests(seed, ((double)proba) / 100, memTestsOnly); } if (nbTests < testNb) nbTests = testNb; From dcf609f835b0c0fb2499d13c8fa755d99808341a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 16:36:33 -0700 Subject: [PATCH 192/318] make adaptCompressionLevel oscillate less --- contrib/adaptive-compression/adapt.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index cc72d1558..ce92d9140 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -358,8 +358,8 @@ static void adaptCompressionLevel(adaptCCtx* ctx) DEBUG(3, "write completion: %f, create completion: %f\n", ctx->writeCompletion, ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; - unsigned const change = writeSlow ? MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel) : 1; + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE)); + unsigned const change = MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel); DEBUG(3, "writeSlow: %u, change: %u\n", writeSlow, change); DEBUG(3, "write completion: %f\n", completion); ctx->compressionLevel += change; @@ -372,7 +372,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) completion = ctx->compressionCompletion; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE)); unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); DEBUG(3, "completion: %f\n", completion); @@ -388,10 +388,12 @@ static void adaptCompressionLevel(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->stats_mutex.pMutex); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletion = 1; + ctx->compressionCompletion = 0; ctx->compressionCompletionMeasured = 0; - ctx->writeCompletion = 1; + ctx->writeCompletion = 0; ctx->writeCompletionMeasured = 0; + ctx->createCompletion = 0; + ctx->createCompletionMeasured = 0; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } @@ -423,6 +425,7 @@ static void* compressionThread(void* arg) reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletionMeasured = 1; + DEBUG(2, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); @@ -465,8 +468,8 @@ static void* compressionThread(void* arg) do { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); - DEBUG(2, "remaining: %zu\n", remaining); - DEBUG(2, "actualBlockSize: %zu\n", actualBlockSize); + DEBUG(3, "remaining: %zu\n", remaining); + DEBUG(3, "actualBlockSize: %zu\n", actualBlockSize); /* continue compression */ if (currJob != 0 || blockNum != 0) { /* not first block of first job flush/overwrite the frame header */ @@ -480,9 +483,9 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } { - DEBUG(2, "write out ending: %d\n", job->lastJob && (remaining == actualBlockSize)); - DEBUG(2, "lastJob %u\n", job->lastJob); - DEBUG(2, "compressionBlockSize %zu\n", compressionBlockSize); + DEBUG(3, "write out ending: %d\n", job->lastJob && (remaining == actualBlockSize)); + DEBUG(3, "lastJob %u\n", job->lastJob); + DEBUG(3, "compressionBlockSize %zu\n", compressionBlockSize); size_t const ret = (job->lastJob && remaining == actualBlockSize) ? ZSTD_compressEnd (ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize) : ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize); @@ -560,6 +563,7 @@ static void* outputThread(void* arg) reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletionMeasured = 1; + DEBUG(2, "compressionCompletion %f\n", ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -576,7 +580,7 @@ static void* outputThread(void* arg) } { // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); - size_t const blockSize = 4 << 20; + size_t const blockSize = 64 << 10; /* 64 KB */ size_t pos = 0; for ( ; ; ) { size_t const writeSize = MIN(remaining, blockSize); @@ -640,6 +644,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletionMeasured = 1; + DEBUG(2, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); From 2427a154cb6a5af622bdbe679f4b4c5b906b4821 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 19 Jul 2017 16:56:28 -0700 Subject: [PATCH 193/318] Minor refactoring --- contrib/long_distance_matching/basic_table.c | 109 -------------- .../circular_buffer_table.c | 137 ++++-------------- contrib/long_distance_matching/ldm.c | 42 +----- contrib/long_distance_matching/ldm.h | 12 +- .../long_distance_matching/ldm_hashtable.h | 36 +---- .../long_distance_matching/ldm_with_table.c | 84 ++++++----- contrib/long_distance_matching/main-ldm.c | 36 +---- 7 files changed, 102 insertions(+), 354 deletions(-) delete mode 100644 contrib/long_distance_matching/basic_table.c diff --git a/contrib/long_distance_matching/basic_table.c b/contrib/long_distance_matching/basic_table.c deleted file mode 100644 index 30c548d2a..000000000 --- a/contrib/long_distance_matching/basic_table.c +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include - -#include "ldm.h" -#include "ldm_hashtable.h" -#include "mem.h" - -#define LDM_HASHLOG ((LDM_MEMORY_USAGE) - 4) - -struct LDM_hashTable { - U32 size; - LDM_hashEntry *entries; - const BYTE *offsetBase; -}; - -LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase) { - LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); - table->size = size; - table->entries = calloc(size, sizeof(LDM_hashEntry)); - table->offsetBase = offsetBase; - return table; -} - -void HASH_initializeTable(LDM_hashTable *table, U32 size) { - table->size = size; - table->entries = calloc(size, sizeof(LDM_hashEntry)); -} - -LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { - return table->entries + hash; -} - -LDM_hashEntry *HASH_getEntryFromHash( - const LDM_hashTable *table, const hash_t hash, const U32 checksum) { - (void)checksum; - return getBucket(table, hash); -} - -static int isValidMatch(const BYTE *pIn, const BYTE *pMatch, - U32 minMatchLength, U32 maxWindowSize) { - U32 lengthLeft = minMatchLength; - const BYTE *curIn = pIn; - const BYTE *curMatch = pMatch; - - if (pIn - pMatch > maxWindowSize) { - return 0; - } - - for (; lengthLeft >= 4; lengthLeft -= 4) { - if (MEM_read32(curIn) != MEM_read32(curMatch)) { - return 0; - } - curIn += 4; - curMatch += 4; - } - return 1; -} - -LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, - const hash_t hash, - const U32 checksum, - const BYTE *pIn, - const BYTE *pEnd, - U32 minMatchLength, - U32 maxWindowSize, - U32 *matchLength) { - LDM_hashEntry *entry = getBucket(table, hash); - (void)checksum; - (void)pEnd; - (void)matchLength; - // TODO: Count the entire forward match length rather than check if valid. - if (isValidMatch(pIn, entry->offset + table->offsetBase, - minMatchLength, maxWindowSize)) { - - return entry; - } - return NULL; -} - -hash_t HASH_hashU32(U32 value) { - return ((value * 2654435761U) >> (32 - LDM_HASHLOG)); -} - -void HASH_insert(LDM_hashTable *table, - const hash_t hash, const LDM_hashEntry entry) { - *getBucket(table, hash) = entry; -} - -U32 HASH_getSize(const LDM_hashTable *table) { - return table->size; -} - -void HASH_destroyTable(LDM_hashTable *table) { - free(table->entries); - free(table); -} - -void HASH_outputTableOccupancy(const LDM_hashTable *hashTable) { - U32 i = 0; - U32 ctr = 0; - for (; i < HASH_getSize(hashTable); i++) { - if (getBucket(hashTable, i)->offset == 0) { - ctr++; - } - } - printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", - HASH_getSize(hashTable), ctr, - 100.0 * (double)(ctr) / (double)HASH_getSize(hashTable)); -} diff --git a/contrib/long_distance_matching/circular_buffer_table.c b/contrib/long_distance_matching/circular_buffer_table.c index 9b7ad088c..9429fbcde 100644 --- a/contrib/long_distance_matching/circular_buffer_table.c +++ b/contrib/long_distance_matching/circular_buffer_table.c @@ -5,22 +5,19 @@ #include "ldm_hashtable.h" #include "mem.h" -//TODO: move def somewhere else. // Number of elements per hash bucket. // HASH_BUCKET_SIZE_LOG defined in ldm.h -#define HASH_BUCKET_SIZE_LOG 2 // MAX is 4 for now #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) // TODO: rename. Number of hash buckets. #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-4-HASH_BUCKET_SIZE_LOG) -#define ZSTD_SKIP -//#define TMP_TST +//#define ZSTD_SKIP struct LDM_hashTable { - U32 size; // Number of buckets - U32 maxEntries; // Rename... - LDM_hashEntry *entries; // 1-D array for now. + U32 numBuckets; + U32 numEntries; + LDM_hashEntry *entries; BYTE *bucketOffsets; // Pointer to current insert position. // Position corresponding to offset=0 in LDM_hashEntry. @@ -32,8 +29,8 @@ struct LDM_hashTable { LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase, U32 minMatchLength, U32 maxWindowSize) { LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); - table->size = size >> HASH_BUCKET_SIZE_LOG; - table->maxEntries = size; + 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)); table->offsetBase = offsetBase; @@ -46,7 +43,6 @@ static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { return table->entries + (hash << HASH_BUCKET_SIZE_LOG); } -#if TMP_ZSTDTOGGLE static unsigned ZSTD_NbCommonBytes (register size_t val) { if (MEM_isLittleEndian()) { @@ -159,26 +155,22 @@ U32 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, return matchLength; } -LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, - const hash_t hash, - const U32 checksum, - const BYTE *pIn, - const BYTE *pEnd, - U32 *matchLength, - U32 *backwardsMatchLength, - const BYTE *pAnchor) { +LDM_hashEntry *HASH_getBestEntry(const LDM_hashTable *table, + const hash_t hash, + const U32 checksum, + const BYTE *pIn, + const BYTE *pEnd, + const BYTE *pAnchor, + U32 *pForwardMatchLength, + U32 *pBackwardMatchLength) { LDM_hashEntry *bucket = getBucket(table, hash); LDM_hashEntry *cur = bucket; LDM_hashEntry *bestEntry = NULL; U32 bestMatchLength = 0; - U32 forwardMatch = 0; - U32 backwardMatch = 0; -#ifdef TMP_TST - U32 numBetter = 0; -#endif for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { - // Check checksum for faster check. const BYTE *pMatch = cur->offset + table->offsetBase; + + // Check checksum for faster check. if (cur->checksum == checksum && pIn - pMatch <= table->maxWindowSize) { U32 forwardMatchLength = ZSTD_count(pIn, pMatch, pEnd); U32 backwardMatchLength, totalMatchLength; @@ -193,105 +185,27 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, if (totalMatchLength >= bestMatchLength) { bestMatchLength = totalMatchLength; - forwardMatch = forwardMatchLength; - backwardMatch = backwardMatchLength; + *pForwardMatchLength = forwardMatchLength; + *pBackwardMatchLength = backwardMatchLength; + bestEntry = cur; -#ifdef TMP_TST - numBetter++; -#endif #ifdef ZSTD_SKIP - *matchLength = forwardMatchLength; - *backwardsMatchLength = backwardMatchLength; - return cur; #endif -// *matchLength = forwardMatchLength; -// return cur; } } } - if (bestEntry != NULL && bestMatchLength > table->minMatchLength) { -#ifdef TMP_TST - printf("Num better %u\n", numBetter - 1); -#endif - *matchLength = forwardMatch; - *backwardsMatchLength = backwardMatch; + if (bestEntry != NULL) { return bestEntry; } return NULL; } -#else - -static int isValidMatch(const BYTE *pIn, const BYTE *pMatch, - U32 minMatchLength, U32 maxWindowSize) { - printf("HERE\n"); - U32 lengthLeft = minMatchLength; - const BYTE *curIn = pIn; - const BYTE *curMatch = pMatch; - - if (pIn - pMatch > maxWindowSize) { - return 0; - } - - for (; lengthLeft >= 4; lengthLeft -= 4) { - if (MEM_read32(curIn) != MEM_read32(curMatch)) { - return 0; - } - curIn += 4; - curMatch += 4; - } - return 1; -} - -//TODO: clean up function call. This is not at all decoupled from LDM. -LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, - const hash_t hash, - const U32 checksum, - const BYTE *pIn, - const BYTE *pEnd, - U32 *matchLength, - U32 *backwardsMatchLength, - const BYTE *pAnchor) { - LDM_hashEntry *bucket = getBucket(table, hash); - LDM_hashEntry *cur = bucket; - (void)matchLength; - (void)backwardsMatchLength; - (void)pAnchor; for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { - // Check checksum for faster check. - const BYTE *pMatch = cur->offset + table->offsetBase; - (void)pEnd; - - if (cur->checksum == checksum && - isValidMatch(pIn, pMatch, table->minMatchLength, table->maxWindowSize)) { - return cur; - } - } - return NULL; -} - -#endif hash_t HASH_hashU32(U32 value) { return ((value * 2654435761U) >> (32 - LDM_HASHLOG)); } - -LDM_hashEntry *HASH_getEntryFromHash(const LDM_hashTable *table, - const hash_t hash, - const U32 checksum) { - // Loop through bucket. - // TODO: in order of recency??? - LDM_hashEntry *bucket = getBucket(table, hash); - LDM_hashEntry *cur = bucket; - for(; cur < bucket + HASH_BUCKET_SIZE; ++cur) { - if (cur->checksum == checksum) { - return cur; - } - } - return NULL; -} - void HASH_insert(LDM_hashTable *table, const hash_t hash, const LDM_hashEntry entry) { *(getBucket(table, hash) + table->bucketOffsets[hash]) = entry; @@ -300,7 +214,7 @@ void HASH_insert(LDM_hashTable *table, } U32 HASH_getSize(const LDM_hashTable *table) { - return table->size; + return table->numBuckets; } void HASH_destroyTable(LDM_hashTable *table) { @@ -312,15 +226,16 @@ void HASH_destroyTable(LDM_hashTable *table) { void HASH_outputTableOccupancy(const LDM_hashTable *table) { U32 ctr = 0; LDM_hashEntry *cur = table->entries; - LDM_hashEntry *end = table->entries + (table->size * HASH_BUCKET_SIZE); + LDM_hashEntry *end = table->entries + (table->numBuckets * HASH_BUCKET_SIZE); for (; cur < end; ++cur) { if (cur->offset == 0) { ctr++; } } - printf("Num buckets, bucket size: %d, %d\n", table->size, HASH_BUCKET_SIZE); + printf("Num buckets, bucket size: %d, %d\n", + table->numBuckets, HASH_BUCKET_SIZE); printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", - table->maxEntries, ctr, - 100.0 * (double)(ctr) / table->maxEntries); + table->numEntries, ctr, + 100.0 * (double)(ctr) / table->numEntries); } diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index a116af70a..6e9addf73 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -14,7 +14,6 @@ #define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE) - 4)) #define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) - #define ML_BITS 4 #define ML_MASK ((1U<windowSizeLog, stats->hashTableSizeLog); printf("num matches, total match length, %% matched: %u, %llu, %.3f\n", @@ -191,7 +188,6 @@ int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { */ static hash_t checksumToHash(U32 sum) { return HASH_hashU32(sum); -// return ((sum * 2654435761U) >> (32 - LDM_HASHLOG)); } /** @@ -455,22 +451,14 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, if (cctx->ip > cctx->imatchLimit) { return 1; } -#ifdef HASH_CHECK - entry = HASH_getEntryFromHash(cctx->hashTable, h, sum); -#else - entry = HASH_getValidEntry(cctx->hashTable, h, sum, - cctx->ip, cctx->iend, - matchLength, backwardMatchLength, - cctx->anchor); -#endif + + entry = HASH_getBestEntry(cctx->hashTable, h, sum, + cctx->ip, cctx->iend, + cctx->anchor, + matchLength, backwardMatchLength); if (entry != NULL) { *match = entry->offset + cctx->ibase; -#ifdef HASH_CHECK - if (!LDM_isValidMatch(cctx->ip, *match)) { - entry = NULL; - } -#endif } putHashOfCurrentPositionFromHash(cctx, h, sum); } @@ -563,21 +551,8 @@ size_t LDM_compress(const void *src, size_t srcSize, cctx.stats.numMatches++; #endif -#if TMP_RECOMPUTE_LENGTHS - backwardsMatchLength = 0; - /** - * Catch up: look back to extend the match backwards from the found match. - */ - while (cctx.ip > cctx.anchor && match > cctx.ibase && - cctx.ip[-1] == match[-1]) { - cctx.ip--; - match--; - backwardsMatchLength++; - } -#else cctx.ip -= backwardsMatchLength; match -= backwardsMatchLength; -#endif /** * Write current block (literals, literal length, match offset, match @@ -586,16 +561,9 @@ size_t LDM_compress(const void *src, size_t srcSize, { const U32 literalLength = cctx.ip - cctx.anchor; const U32 offset = cctx.ip - match; -#if TMP_RECOMPUTE_LENGTHS - const U32 matchLength = LDM_countMatchLength( - cctx.ip + LDM_MIN_MATCH_LENGTH + backwardsMatchLength, - match + LDM_MIN_MATCH_LENGTH + backwardsMatchLength, - cctx.ihashLimit) + backwardsMatchLength; -#else const U32 matchLength = forwardMatchLength + backwardsMatchLength - LDM_MIN_MATCH_LENGTH; -#endif LDM_outputBlock(&cctx, literalLength, offset, matchLength); diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 2396227d2..1d5b2f13b 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -11,21 +11,21 @@ #define LDM_OFFSET_SIZE 4 // 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 +#define HASH_BUCKET_SIZE_LOG 3 // MAX is 4 for now -//#define LDM_LAG (1 << 20) -#define LDM_LAG (0) +// Defines the lag in inserting elements into the hash table. +#define LDM_LAG 0 #define LDM_WINDOW_SIZE_LOG 28 #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) //These should be multiples of four (and perhaps set to the same value?). -#define LDM_MIN_MATCH_LENGTH 1024 -#define LDM_HASH_LENGTH 1024 +#define LDM_MIN_MATCH_LENGTH 64 +#define LDM_HASH_LENGTH 64 -#define TMP_ZSTDTOGGLE 1 -#define TMP_RECOMPUTE_LENGTHS (!(TMP_ZSTDTOGGLE)) typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h index 51d825258..df9dcd789 100644 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -14,37 +14,17 @@ typedef struct LDM_hashEntry { typedef struct LDM_hashTable LDM_hashTable; -/** - * Create a hash table with size hash buckets. - * LDM_hashEntry.offset is added to offsetBase to calculate pMatch in - * HASH_getValidEntry. - */ LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase, U32 minMatchLength, U32 maxWindowSize); -/** - * Returns an LDM_hashEntry from the table that matches the checksum. - * Returns NULL if one does not exist. - */ -LDM_hashEntry *HASH_getEntryFromHash(const LDM_hashTable *table, - const hash_t hash, - const U32 checksum); - -/** - * Gets a valid entry that matches the checksum. A valid entry is defined by - * *isValid. - * - * The function finds an entry matching the checksum, computes pMatch as - * offset + table.offsetBase, and calls isValid. - */ -LDM_hashEntry *HASH_getValidEntry(const LDM_hashTable *table, - const hash_t hash, - const U32 checksum, - const BYTE *pIn, - const BYTE *pEnd, - U32 *matchLength, - U32 *backwardsMatchLength, - const BYTE *pAnchor); +LDM_hashEntry *HASH_getBestEntry(const LDM_hashTable *table, + const hash_t hash, + const U32 checksum, + const BYTE *pIn, + const BYTE *pEnd, + const BYTE *pAnchor, + U32 *matchLength, + U32 *backwardsMatchLength); hash_t HASH_hashU32(U32 value); diff --git a/contrib/long_distance_matching/ldm_with_table.c b/contrib/long_distance_matching/ldm_with_table.c index 68a33d0ff..5919d588c 100644 --- a/contrib/long_distance_matching/ldm_with_table.c +++ b/contrib/long_distance_matching/ldm_with_table.c @@ -4,6 +4,8 @@ #include #include +#include "ldm.h" + #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) //#define LDM_HASH_ENTRY_SIZE 4 #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) @@ -14,7 +16,6 @@ #define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) /* Hash table stuff. */ -#define HASH_BUCKET_SIZE_LOG 3 // MAX is 4 for now #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-4-HASH_BUCKET_SIZE_LOG) @@ -32,18 +33,15 @@ //#define RUN_CHECKS -#include "ldm.h" - /* Hash table stuff */ typedef U32 hash_t; typedef struct LDM_hashEntry { - U32 offset; // TODO: Replace with pointer? + U32 offset; U32 checksum; } LDM_hashEntry; -// TODO: Scanning speed // TODO: Memory usage struct LDM_compressStats { U32 windowSizeLog, hashTableSizeLog; @@ -110,18 +108,22 @@ struct LDM_CCtx { }; struct LDM_hashTable { - U32 size; // Number of buckets - U32 maxEntries; // Rename... - LDM_hashEntry *entries; // 1-D array for now. + U32 numBuckets; // Number of buckets + U32 numEntries; // Rename... + LDM_hashEntry *entries; BYTE *bucketOffsets; // Position corresponding to offset=0 in LDM_hashEntry. }; +/** + * Create a hash table that can contain size elements. + * The number of buckets is determined by size >> HASH_BUCKET_SIZE_LOG. + */ LDM_hashTable *HASH_createTable(U32 size) { LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); - table->size = size >> HASH_BUCKET_SIZE_LOG; - table->maxEntries = size; + 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)); return table; @@ -131,10 +133,7 @@ static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { return table->entries + (hash << HASH_BUCKET_SIZE_LOG); } - - -static unsigned ZSTD_NbCommonBytes (register size_t val) -{ +static unsigned ZSTD_NbCommonBytes (register size_t val) { if (MEM_isLittleEndian()) { if (MEM_64bits()) { # if defined(_MSC_VER) && defined(_WIN64) @@ -234,6 +233,11 @@ static size_t ZSTD_count(const BYTE *pIn, const BYTE *pMatch, return (size_t)(pIn - pStart); } +/** + * Count number of bytes that match backwards before pIn and pMatch. + * + * We count only bytes where pMatch > pBaes and pIn > pAnchor. + */ U32 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, const BYTE *pMatch, const BYTE *pBase) { U32 matchLength = 0; @@ -245,20 +249,32 @@ U32 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, return matchLength; } -LDM_hashEntry *HASH_getValidEntry(const LDM_CCtx *cctx, - const hash_t hash, - const U32 checksum, - U32 *matchLength, - U32 *backwardsMatchLength) { +/** + * 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. + * + */ +LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, + const hash_t hash, + const U32 checksum, + U32 *pForwardMatchLength, + U32 *pBackwardMatchLength) { LDM_hashTable *table = cctx->hashTable; LDM_hashEntry *bucket = getBucket(table, hash); LDM_hashEntry *cur = bucket; LDM_hashEntry *bestEntry = NULL; U32 bestMatchLength = 0; for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { - // Check checksum for faster check. const BYTE *pMatch = cur->offset + cctx->ibase; + // Check checksum for faster check. if (cur->checksum == checksum && cctx->ip - pMatch <= LDM_WINDOW_SIZE) { U32 forwardMatchLength = ZSTD_count(cctx->ip, pMatch, cctx->iend); @@ -279,8 +295,8 @@ LDM_hashEntry *HASH_getValidEntry(const LDM_CCtx *cctx, if (totalMatchLength >= bestMatchLength && totalMatchLength >= LDM_MIN_MATCH_LENGTH) { bestMatchLength = totalMatchLength; - *matchLength = forwardMatchLength; - *backwardsMatchLength = backwardMatchLength; + *pForwardMatchLength = forwardMatchLength; + *pBackwardMatchLength = backwardMatchLength; bestEntry = cur; #ifdef ZSTD_SKIP @@ -303,7 +319,7 @@ void HASH_insert(LDM_hashTable *table, } U32 HASH_getSize(const LDM_hashTable *table) { - return table->size; + return table->numBuckets; } void HASH_destroyTable(LDM_hashTable *table) { @@ -315,20 +331,20 @@ void HASH_destroyTable(LDM_hashTable *table) { void HASH_outputTableOccupancy(const LDM_hashTable *table) { U32 ctr = 0; LDM_hashEntry *cur = table->entries; - LDM_hashEntry *end = table->entries + (table->size * HASH_BUCKET_SIZE); + LDM_hashEntry *end = table->entries + (table->numBuckets * HASH_BUCKET_SIZE); for (; cur < end; ++cur) { if (cur->offset == 0) { ctr++; } } - printf("Num buckets, bucket size: %d, %d\n", table->size, HASH_BUCKET_SIZE); + printf("Num buckets, bucket size: %d, %d\n", + table->numBuckets, HASH_BUCKET_SIZE); printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", - table->maxEntries, ctr, - 100.0 * (double)(ctr) / table->maxEntries); + table->numEntries, ctr, + 100.0 * (double)(ctr) / table->numEntries); } - // TODO: This can be done more efficiently (but it is not that important as it // is only used for computing stats). static int intLog2(U32 x) { @@ -339,7 +355,7 @@ static int intLog2(U32 x) { return ret; } -// TODO: Maybe we would eventually prefer to have linear rather than +// Maybe we would eventually prefer to have linear rather than // exponential buckets. /** void HASH_outputTableOffsetHistogram(const LDM_CCtx *cctx) { @@ -369,7 +385,6 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { int i = 0; printf("=====================\n"); printf("Compression statistics\n"); - //TODO: compute percentage matched? 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", @@ -429,7 +444,6 @@ hash_t HASH_hashU32(U32 value) { */ static hash_t checksumToHash(U32 sum) { return HASH_hashU32(sum); -// return ((sum * 2654435761U) >> (32 - LDM_HASHLOG)); } /** @@ -672,10 +686,10 @@ void LDM_destroyCCtx(LDM_CCtx *cctx) { * Returns 0 if successful and 1 otherwise (i.e. no match can be found * in the remaining input that is long enough). * - * matchLength contains the forward length of the match. + * forwardMatchLength contains the forward length of the match. */ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, - U32 *matchLength, U32 *backwardMatchLength) { + U32 *forwardMatchLength, U32 *backwardMatchLength) { LDM_hashEntry *entry = NULL; cctx->nextIp = cctx->ip + cctx->step; @@ -693,8 +707,8 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, return 1; } - entry = HASH_getValidEntry(cctx, h, sum, - matchLength, backwardMatchLength); + entry = HASH_getBestEntry(cctx, h, sum, + forwardMatchLength, backwardMatchLength); if (entry != NULL) { *match = entry->offset + cctx->ibase; diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index a43ec0002..96db0c220 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -29,6 +29,7 @@ static int compress(const char *fname, const char *oname) { size_t maxCompressedSize, compressedSize; struct timeval tv1, tv2; + double timeTaken; /* Open the input file. */ if ((fdin = open(fname, O_RDONLY)) < 0) { @@ -53,18 +54,7 @@ static int compress(const char *fname, const char *oname) { // The compress function should check before writing or buffer writes. maxCompressedSize += statbuf.st_size / 255; - /* Go to the location corresponding to the last byte. */ - /* TODO: fallocate? */ - if (lseek(fdout, maxCompressedSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* Write a dummy byte at the last location. */ - if (write(fdout, "", 1) != 1) { - perror("write error"); - return 1; - } + ftruncate(fdout, maxCompressedSize); /* mmap the input file. */ if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdin, 0)) @@ -103,12 +93,12 @@ static int compress(const char *fname, const char *oname) { (unsigned)statbuf.st_size, (unsigned)compressedSize, oname, (double)compressedSize / (statbuf.st_size) * 100); + timeTaken = (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + + (double) (tv2.tv_sec - tv1.tv_sec), + printf("Total compress time = %.3f seconds, Average compression speed: %.3f MB/s\n", - (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec), - ((double)statbuf.st_size / (double) (1 << 20)) / - ((double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + - (double) (tv2.tv_sec - tv1.tv_sec))); + timeTaken, + ((double)statbuf.st_size / (double) (1 << 20)) / timeTaken); // Close files. @@ -156,17 +146,7 @@ static int decompress(const char *fname, const char *oname) { /* Read the header. */ LDM_readHeader(src, &compressedSize, &decompressedSize); - /* Go to the location corresponding to the last byte. */ - if (lseek(fdout, decompressedSize - 1, SEEK_SET) == -1) { - perror("lseek error"); - return 1; - } - - /* write a dummy byte at the last location */ - if (write(fdout, "", 1) != 1) { - perror("write error"); - return 1; - } + ftruncate(fdout, decompressedSize); /* mmap the output file */ if ((dst = mmap(0, decompressedSize, PROT_READ | PROT_WRITE, From 13a01ffb27f58d24dae8dc56c1ef75f1743cca27 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 19 Jul 2017 17:24:09 -0700 Subject: [PATCH 194/318] Fix off-by-one in size calculations --- contrib/long_distance_matching/circular_buffer_table.c | 3 ++- contrib/long_distance_matching/ldm.c | 6 +++--- contrib/long_distance_matching/ldm.h | 2 +- contrib/long_distance_matching/ldm_with_table.c | 8 ++++---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/contrib/long_distance_matching/circular_buffer_table.c b/contrib/long_distance_matching/circular_buffer_table.c index 9429fbcde..ad7ae9e10 100644 --- a/contrib/long_distance_matching/circular_buffer_table.c +++ b/contrib/long_distance_matching/circular_buffer_table.c @@ -11,7 +11,8 @@ #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) // TODO: rename. Number of hash buckets. -#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-4-HASH_BUCKET_SIZE_LOG) +// TODO: Link to HASH_ENTRY_SIZE_LOG +#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-3-(HASH_BUCKET_SIZE_LOG)) //#define ZSTD_SKIP struct LDM_hashTable { diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 6e9addf73..9ffbab48d 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -6,12 +6,12 @@ #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -//#define LDM_HASH_ENTRY_SIZE 4 +#define LDM_HASH_ENTRY_SIZE_LOG 3 #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) -#define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 4) +#define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) // Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE) - 4)) +#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE) - (LDM_HASH_ENTRY_SIZE_LOG))) #define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) #define ML_BITS 4 diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 1d5b2f13b..04b6410c2 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -13,7 +13,7 @@ // 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 +#define LDM_MEMORY_USAGE 22 #define HASH_BUCKET_SIZE_LOG 3 // MAX is 4 for now // Defines the lag in inserting elements into the hash table. diff --git a/contrib/long_distance_matching/ldm_with_table.c b/contrib/long_distance_matching/ldm_with_table.c index 5919d588c..813ead6ae 100644 --- a/contrib/long_distance_matching/ldm_with_table.c +++ b/contrib/long_distance_matching/ldm_with_table.c @@ -7,17 +7,17 @@ #include "ldm.h" #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -//#define LDM_HASH_ENTRY_SIZE 4 +#define LDM_HASH_ENTRY_SIZE_LOG 3 #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) -#define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 4) +#define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) // Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE) - 4)) +#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE) - (LDM_HASH_ENTRY_SIZE_LOG))) #define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) /* Hash table stuff. */ #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) -#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-4-HASH_BUCKET_SIZE_LOG) +#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) #define ML_BITS 4 #define ML_MASK ((1U< Date: Thu, 20 Jul 2017 01:13:14 -0700 Subject: [PATCH 195/318] pool.c : blindfix for Visual warnings --- lib/common/threading.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/common/threading.h b/lib/common/threading.h index ee7864555..99e39b551 100644 --- a/lib/common/threading.h +++ b/lib/common/threading.h @@ -42,14 +42,14 @@ extern "C" { /* mutex */ #define pthread_mutex_t CRITICAL_SECTION -#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), 0) +#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), (void)0) #define pthread_mutex_destroy(a) DeleteCriticalSection((a)) #define pthread_mutex_lock(a) EnterCriticalSection((a)) #define pthread_mutex_unlock(a) LeaveCriticalSection((a)) /* condition variable */ #define pthread_cond_t CONDITION_VARIABLE -#define pthread_cond_init(a, b) (InitializeConditionVariable((a)), 0) +#define pthread_cond_init(a, b) (InitializeConditionVariable((a)), (void)0) #define pthread_cond_destroy(a) /* No delete */ #define pthread_cond_wait(a, b) SleepConditionVariableCS((a), (b), INFINITE) #define pthread_cond_signal(a) WakeConditionVariable((a)) From 7ab758a640629665ba43f4f8ce5fa38c9260b1ba Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 20 Jul 2017 10:53:51 -0700 Subject: [PATCH 196/318] changed how completion is actually sampled --- contrib/adaptive-compression/adapt.c | 65 ++++++++++++---------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ce92d9140..cf232ca7b 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -24,8 +24,8 @@ #define MAX_PATH 256 #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 -#define DEFAULT_ADAPT_PARAM 1 -#define MAX_COMPRESSION_LEVEL_CHANGE 3 +#define DEFAULT_ADAPT_PARAM 0 +#define MAX_COMPRESSION_LEVEL_CHANGE 4 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -87,9 +87,9 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - unsigned compressionCompletionMeasured; - unsigned writeCompletionMeasured; - unsigned createCompletionMeasured; + double compressionCompletionMeasured; + double writeCompletionMeasured; + double createCompletionMeasured; double compressionCompletion; double writeCompletion; double createCompletion; @@ -342,6 +342,9 @@ static void adaptCompressionLevel(adaptCCtx* ctx) writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; createWaiting = ctx->adaptParam < ctx->stats.writeCounter; pthread_mutex_unlock(&ctx->stats_mutex.pMutex); + DEBUG(2, "createWaiting %u\n", createWaiting); + DEBUG(2, "compressWaiting %u\n", compressWaiting); + DEBUG(2, "writeWaiting %u\n\n", writeWaiting); { unsigned const writeSlow = (compressWaiting && createWaiting); unsigned const compressSlow = (writeWaiting && createWaiting); @@ -351,14 +354,14 @@ static void adaptCompressionLevel(adaptCCtx* ctx) reset = 1; } else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); + DEBUG(2, "increasing compression level %u\n", ctx->compressionLevel); double completion; pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = writeSlow ? ctx->writeCompletion : ctx->createCompletion; - DEBUG(3, "write completion: %f, create completion: %f\n", ctx->writeCompletion, ctx->createCompletion); + completion = writeSlow ? ctx->writeCompletionMeasured : ctx->createCompletionMeasured; + DEBUG(2, "write completion: %f, create completion: %f\n", ctx->writeCompletionMeasured, ctx->createCompletionMeasured); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE)); + unsigned const maxChange = MAX_COMPRESSION_LEVEL_CHANGE - (unsigned)(completion*MAX_COMPRESSION_LEVEL_CHANGE); unsigned const change = MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel); DEBUG(3, "writeSlow: %u, change: %u\n", writeSlow, change); DEBUG(3, "write completion: %f\n", completion); @@ -369,13 +372,13 @@ static void adaptCompressionLevel(adaptCCtx* ctx) else if (compressSlow && ctx->compressionLevel > 1) { double completion; pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = ctx->compressionCompletion; + completion = ctx->compressionCompletionMeasured; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE)); + unsigned const maxChange = MAX_COMPRESSION_LEVEL_CHANGE - (unsigned)(completion*MAX_COMPRESSION_LEVEL_CHANGE); unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); - DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(3, "completion: %f\n", completion); + DEBUG(2, "decreasing compression level %u\n", ctx->compressionLevel); + DEBUG(2, "completion: %f\n", completion); ctx->compressionLevel -= change; reset = 1; } @@ -386,15 +389,6 @@ static void adaptCompressionLevel(adaptCCtx* ctx) ctx->stats.writeCounter = 0; ctx->stats.compressedCounter = 0; pthread_mutex_unlock(&ctx->stats_mutex.pMutex); - - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletion = 0; - ctx->compressionCompletionMeasured = 0; - ctx->writeCompletion = 0; - ctx->writeCompletionMeasured = 0; - ctx->createCompletion = 0; - ctx->createCompletionMeasured = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } } @@ -424,8 +418,8 @@ static void* compressionThread(void* arg) pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletionMeasured = 1; - DEBUG(2, "create completion: %f\n", ctx->createCompletion); + ctx->createCompletionMeasured = ctx->createCompletion; + DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); @@ -502,9 +496,8 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - if (!ctx->compressionCompletionMeasured) { - ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - } + ctx->compressionCompletion = 1 - (double)remaining/job->src.size; + DEBUG(2, "update on job %u: compression completion %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } while (remaining != 0); @@ -562,8 +555,8 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletionMeasured = 1; - DEBUG(2, "compressionCompletion %f\n", ctx->compressionCompletion); + ctx->compressionCompletionMeasured = ctx->compressionCompletion; + DEBUG(2, "waited on job %u: compressionCompletion %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -580,7 +573,7 @@ static void* outputThread(void* arg) } { // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); - size_t const blockSize = 64 << 10; /* 64 KB */ + size_t const blockSize = compressedSize >> 7; size_t pos = 0; for ( ; ; ) { size_t const writeSize = MIN(remaining, blockSize); @@ -591,9 +584,7 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - if (!ctx->writeCompletionMeasured) { - ctx->writeCompletion = 1 - (double)remaining/compressedSize; - } + ctx->writeCompletion = 1 - (double)remaining/compressedSize; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); if (remaining == 0) break; @@ -643,8 +634,8 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->writeCompletionMeasured = 1; - DEBUG(2, "writeCompletion: %f\n", ctx->writeCompletion); + ctx->writeCompletionMeasured = ctx->writeCompletion; + DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); @@ -736,9 +727,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA pos += ret; remaining -= ret; pthread_mutex_lock(&ctx->completion_mutex.pMutex); - if (!ctx->createCompletionMeasured) { - ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - } + ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } From 7d3ac0710d4371e2d05874b655a0a11dbdb25421 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 20 Jul 2017 13:33:55 -0700 Subject: [PATCH 197/318] [linux] Update patches for v3 --- contrib/linux-kernel/0000-cover-letter.patch | 21 +++-- .../0001-lib-Add-xxhash-module.patch | 14 ++-- .../0002-lib-Add-zstd-modules.patch | 62 ++++++--------- .../0003-btrfs-Add-zstd-support.patch | 76 ++++++++++--------- .../0004-squashfs-Add-zstd-support.patch | 6 +- contrib/linux-kernel/fs/btrfs/zstd.c | 20 ++--- contrib/linux-kernel/lib/zstd/huf_compress.c | 3 +- contrib/linux-kernel/lib/zstd/zstd_internal.h | 29 ++----- 8 files changed, 105 insertions(+), 126 deletions(-) diff --git a/contrib/linux-kernel/0000-cover-letter.patch b/contrib/linux-kernel/0000-cover-letter.patch index 763b5a9cb..33a5189b8 100644 --- a/contrib/linux-kernel/0000-cover-letter.patch +++ b/contrib/linux-kernel/0000-cover-letter.patch @@ -1,7 +1,7 @@ -From 8bc9a0ae5c86a6d02d9a5274b9965ddac0e8d330 Mon Sep 17 00:00:00 2001 +From 0cd63464d182bb9708f8b25f7da3dc8e5ec6b4fa Mon Sep 17 00:00:00 2001 From: Nick Terrell -Date: Wed, 28 Jun 2017 22:00:00 -0700 -Subject: [PATCH v2 0/4] Add xxhash and zstd modules +Date: Thu, 20 Jul 2017 13:18:30 -0700 +Subject: [PATCH v3 0/4] Add xxhash and zstd modules Hi all, @@ -24,6 +24,13 @@ v1 -> v2: HUF_compressWeights(), HUF_readDTableX2(), and HUF_readDTableX4() (2/4) - No zstd function uses more than 400 B of stack space (2/4) +v2 -> v3: +- Work around gcc-7 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388 + (2/4) +- Fix bug in dictionary compression from upstream commit cc1522351f (2/4) +- Port upstream BtrFS commits e1ddce71d6, 389a6cfc2a, and 6acafd1eff (3/4) +- Change default compression level for BtrFS to 3 (3/4) + Nick Terrell (4): lib: Add xxhash module lib: Add zstd modules @@ -40,7 +47,7 @@ Nick Terrell (4): fs/btrfs/props.c | 6 + fs/btrfs/super.c | 12 +- fs/btrfs/sysfs.c | 2 + - fs/btrfs/zstd.c | 433 ++++++ + fs/btrfs/zstd.c | 435 ++++++ fs/squashfs/Kconfig | 14 + fs/squashfs/Makefile | 1 + fs/squashfs/decompressor.c | 7 + @@ -63,13 +70,13 @@ Nick Terrell (4): lib/zstd/fse_compress.c | 795 ++++++++++ lib/zstd/fse_decompress.c | 332 +++++ lib/zstd/huf.h | 212 +++ - lib/zstd/huf_compress.c | 771 ++++++++++ + lib/zstd/huf_compress.c | 770 ++++++++++ lib/zstd/huf_decompress.c | 960 ++++++++++++ lib/zstd/mem.h | 151 ++ lib/zstd/zstd_common.c | 75 + - lib/zstd/zstd_internal.h | 269 ++++ + lib/zstd/zstd_internal.h | 250 ++++ lib/zstd/zstd_opt.h | 1014 +++++++++++++ - 39 files changed, 14400 insertions(+), 12 deletions(-) + 39 files changed, 14382 insertions(+), 12 deletions(-) create mode 100644 fs/btrfs/zstd.c create mode 100644 fs/squashfs/zstd_wrapper.c create mode 100644 include/linux/xxhash.h diff --git a/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch b/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch index 84a2c53c6..f86731cec 100644 --- a/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch +++ b/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch @@ -1,7 +1,7 @@ -From 5ac909c415ab4a18fd90794793c96e450795e8c6 Mon Sep 17 00:00:00 2001 +From fc7f26acbabda35f1c61dfc357dbb207dc8ed23d Mon Sep 17 00:00:00 2001 From: Nick Terrell -Date: Wed, 21 Jun 2017 17:37:36 -0700 -Subject: [PATCH v2 1/4] lib: Add xxhash module +Date: Mon, 17 Jul 2017 17:07:18 -0700 +Subject: [PATCH v3 1/4] lib: Add xxhash module Adds xxhash kernel module with xxh32 and xxh64 hashes. xxhash is an extremely fast non-cryptographic hash algorithm for checksumming. @@ -327,10 +327,10 @@ index 0000000..9e1f42c + +#endif /* XXHASH_H */ diff --git a/lib/Kconfig b/lib/Kconfig -index 0c8b78a..b6009d7 100644 +index 6762529..5e7541f 100644 --- a/lib/Kconfig +++ b/lib/Kconfig -@@ -184,6 +184,9 @@ config CRC8 +@@ -192,6 +192,9 @@ config CRC8 when they need to do cyclic redundancy check according CRC8 algorithm. Module will be called crc8. @@ -341,10 +341,10 @@ index 0c8b78a..b6009d7 100644 bool depends on AUDIT && !AUDIT_ARCH diff --git a/lib/Makefile b/lib/Makefile -index 0166fbc..1338226 100644 +index 40c1837..d06b68a 100644 --- a/lib/Makefile +++ b/lib/Makefile -@@ -102,6 +102,7 @@ obj-$(CONFIG_CRC32_SELFTEST) += crc32test.o +@@ -102,6 +102,7 @@ obj-$(CONFIG_CRC4) += crc4.o obj-$(CONFIG_CRC7) += crc7.o obj-$(CONFIG_LIBCRC32C) += libcrc32c.o obj-$(CONFIG_CRC8) += crc8.o diff --git a/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch b/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch index 971093996..268307cf1 100644 --- a/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch +++ b/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch @@ -1,7 +1,7 @@ -From d2626127c6d6e60e940dd9a3ed58323bdcdc4930 Mon Sep 17 00:00:00 2001 +From 686a6149b98250d66b5951e3ae05e79063e9de98 Mon Sep 17 00:00:00 2001 From: Nick Terrell -Date: Tue, 16 May 2017 14:55:36 -0700 -Subject: [PATCH v2 2/4] lib: Add zstd modules +Date: Mon, 17 Jul 2017 17:08:19 -0700 +Subject: [PATCH v3 2/4] lib: Add zstd modules Add zstd compression and decompression kernel modules. zstd offers a wide varity of compression speed and quality trade-offs. @@ -110,6 +110,10 @@ v1 -> v2: HUF_compressWeights(), HUF_readDTableX2(), and HUF_readDTableX4() - No function uses more than 400 B of stack space +v2 -> v3: +- Work around gcc-7 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388 +- Fix bug in dictionary compression from upstream commit cc1522351f + include/linux/zstd.h | 1157 +++++++++++++++ lib/Kconfig | 8 + lib/Makefile | 2 + @@ -123,13 +127,13 @@ v1 -> v2: lib/zstd/fse_compress.c | 795 +++++++++++ lib/zstd/fse_decompress.c | 332 +++++ lib/zstd/huf.h | 212 +++ - lib/zstd/huf_compress.c | 771 ++++++++++ + lib/zstd/huf_compress.c | 770 ++++++++++ lib/zstd/huf_decompress.c | 960 +++++++++++++ lib/zstd/mem.h | 151 ++ lib/zstd/zstd_common.c | 75 + - lib/zstd/zstd_internal.h | 269 ++++ + lib/zstd/zstd_internal.h | 250 ++++ lib/zstd/zstd_opt.h | 1014 +++++++++++++ - 19 files changed, 13014 insertions(+) + 19 files changed, 12994 insertions(+) create mode 100644 include/linux/zstd.h create mode 100644 lib/zstd/Makefile create mode 100644 lib/zstd/bitstream.h @@ -1312,10 +1316,10 @@ index 0000000..249575e + +#endif /* ZSTD_H */ diff --git a/lib/Kconfig b/lib/Kconfig -index b6009d7..f00ddab 100644 +index 5e7541f..0d49ed0 100644 --- a/lib/Kconfig +++ b/lib/Kconfig -@@ -241,6 +241,14 @@ config LZ4HC_COMPRESS +@@ -249,6 +249,14 @@ config LZ4HC_COMPRESS config LZ4_DECOMPRESS tristate @@ -1331,7 +1335,7 @@ index b6009d7..f00ddab 100644 # diff --git a/lib/Makefile b/lib/Makefile -index 1338226..4fcef16 100644 +index d06b68a..d5c8a4f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -116,6 +116,8 @@ obj-$(CONFIG_LZO_DECOMPRESS) += lzo/ @@ -10012,10 +10016,10 @@ index 0000000..2143da2 +#endif /* HUF_H_298734234 */ diff --git a/lib/zstd/huf_compress.c b/lib/zstd/huf_compress.c new file mode 100644 -index 0000000..0361f38 +index 0000000..40055a7 --- /dev/null +++ b/lib/zstd/huf_compress.c -@@ -0,0 +1,771 @@ +@@ -0,0 +1,770 @@ +/* + * Huffman encoder, part of New Generation Entropy library + * Copyright (C) 2013-2016, Yann Collet. @@ -10543,7 +10547,7 @@ index 0000000..0361f38 + +size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); } + -+#define HUF_FLUSHBITS(s) (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s)) ++#define HUF_FLUSHBITS(s) BIT_flushBits(s) + +#define HUF_FLUSHBITS_1(stream) \ + if (sizeof((stream)->bitContainer) * 8 < HUF_TABLELOG_MAX * 2 + 7) \ @@ -10560,7 +10564,6 @@ index 0000000..0361f38 + BYTE *const oend = ostart + dstSize; + BYTE *op = ostart; + size_t n; -+ const unsigned fast = (dstSize >= HUF_BLOCKBOUND(srcSize)); + BIT_CStream_t bitC; + + /* init */ @@ -11993,10 +11996,10 @@ index 0000000..a282624 +} diff --git a/lib/zstd/zstd_internal.h b/lib/zstd/zstd_internal.h new file mode 100644 -index 0000000..6748719 +index 0000000..f0ba474 --- /dev/null +++ b/lib/zstd/zstd_internal.h -@@ -0,0 +1,269 @@ +@@ -0,0 +1,250 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. @@ -12125,35 +12128,16 @@ index 0000000..6748719 +/*-******************************************* +* Shared functions to include for inlining +*********************************************/ -+static void ZSTD_copy8(void *dst, const void *src) { memcpy(dst, src, 8); } -+#define COPY8(d, s) \ -+ { \ -+ ZSTD_copy8(d, s); \ -+ d += 8; \ -+ s += 8; \ -+ } -+ ++static void ZSTD_copy8(void *dst, const void *src) { ++ memcpy(dst, src, 8); ++} +/*! ZSTD_wildcopy() : +* custom version of memcpy(), can copy up to 7 bytes too many (8 bytes if length==0) */ +#define WILDCOPY_OVERLENGTH 8 +ZSTD_STATIC void ZSTD_wildcopy(void *dst, const void *src, ptrdiff_t length) +{ -+ const BYTE *ip = (const BYTE *)src; -+ BYTE *op = (BYTE *)dst; -+ BYTE *const oend = op + length; -+ do -+ COPY8(op, ip) -+ while (op < oend); -+} -+ -+ZSTD_STATIC void ZSTD_wildcopy_e(void *dst, const void *src, void *dstEnd) /* should be faster for decoding, but strangely, not verified on all platform */ -+{ -+ const BYTE *ip = (const BYTE *)src; -+ BYTE *op = (BYTE *)dst; -+ BYTE *const oend = (BYTE *)dstEnd; -+ do -+ COPY8(op, ip) -+ while (op < oend); ++ if (length > 0) ++ memcpy(dst, src, length); +} + +/*-******************************************* diff --git a/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch b/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch index abc8326cc..5578fa383 100644 --- a/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch +++ b/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch @@ -1,7 +1,7 @@ -From 599f8f2aaace3df939cb145368574a52268d82d0 Mon Sep 17 00:00:00 2001 +From b0ef8fc63c9ca251ceca632f53aa1de8f1f17772 Mon Sep 17 00:00:00 2001 From: Nick Terrell -Date: Wed, 21 Jun 2017 17:31:39 -0700 -Subject: [PATCH v2 3/4] btrfs: Add zstd support +Date: Mon, 17 Jul 2017 17:08:39 -0700 +Subject: [PATCH v3 3/4] btrfs: Add zstd support Add zstd compression and decompression support to BtrFS. zstd at its fastest level compresses almost as well as zlib, while offering much @@ -63,6 +63,10 @@ zstd source repository: https://github.com/facebook/zstd Signed-off-by: Nick Terrell --- +v2 -> v3: +- Port upstream BtrFS commits e1ddce71d6, 389a6cfc2a, and 6acafd1eff +- Change default compression level for BtrFS to 3 + fs/btrfs/Kconfig | 2 + fs/btrfs/Makefile | 2 +- fs/btrfs/compression.c | 1 + @@ -73,9 +77,9 @@ Signed-off-by: Nick Terrell fs/btrfs/props.c | 6 + fs/btrfs/super.c | 12 +- fs/btrfs/sysfs.c | 2 + - fs/btrfs/zstd.c | 433 +++++++++++++++++++++++++++++++++++++++++++++ + fs/btrfs/zstd.c | 435 +++++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/btrfs.h | 8 +- - 12 files changed, 469 insertions(+), 12 deletions(-) + 12 files changed, 471 insertions(+), 12 deletions(-) create mode 100644 fs/btrfs/zstd.c diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig @@ -105,10 +109,10 @@ index 128ce17..962a95a 100644 reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \ uuid-tree.o props.o hash.o free-space-tree.o diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c -index 10e6b28..3beb0d0 100644 +index d2ef9ac..4ff42d1 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c -@@ -761,6 +761,7 @@ static struct { +@@ -704,6 +704,7 @@ static struct { static const struct btrfs_compress_op * const btrfs_compress_op[] = { &btrfs_zlib_compress, &btrfs_lzo_compress, @@ -117,10 +121,10 @@ index 10e6b28..3beb0d0 100644 void __init btrfs_init_compress(void) diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h -index 39ec43a..d99fc21 100644 +index 87f6d33..2269e00 100644 --- a/fs/btrfs/compression.h +++ b/fs/btrfs/compression.h -@@ -60,8 +60,9 @@ enum btrfs_compression_type { +@@ -99,8 +99,9 @@ enum btrfs_compression_type { BTRFS_COMPRESS_NONE = 0, BTRFS_COMPRESS_ZLIB = 1, BTRFS_COMPRESS_LZO = 2, @@ -132,7 +136,7 @@ index 39ec43a..d99fc21 100644 }; struct btrfs_compress_op { -@@ -92,5 +93,6 @@ struct btrfs_compress_op { +@@ -128,5 +129,6 @@ struct btrfs_compress_op { extern const struct btrfs_compress_op btrfs_zlib_compress; extern const struct btrfs_compress_op btrfs_lzo_compress; @@ -140,10 +144,10 @@ index 39ec43a..d99fc21 100644 #endif diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h -index 4f8f75d..61dd3dd 100644 +index 3f3eb7b..845d77c 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h -@@ -271,6 +271,7 @@ struct btrfs_super_block { +@@ -270,6 +270,7 @@ struct btrfs_super_block { BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS | \ BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \ BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO | \ @@ -152,10 +156,10 @@ index 4f8f75d..61dd3dd 100644 BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF | \ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c -index 5f678dc..49c0e91 100644 +index 080e2eb..04632f4 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c -@@ -2831,6 +2831,8 @@ int open_ctree(struct super_block *sb, +@@ -2828,6 +2828,8 @@ int open_ctree(struct super_block *sb, features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF; if (fs_info->compress_type == BTRFS_COMPRESS_LZO) features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO; @@ -165,7 +169,7 @@ index 5f678dc..49c0e91 100644 if (features & BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA) btrfs_info(fs_info, "has skinny extents"); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c -index e176375..f732cfd 100644 +index fa1b78c..b9963d9 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -327,8 +327,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) @@ -180,7 +184,7 @@ index e176375..f732cfd 100644 ret = btrfs_set_prop(inode, "btrfs.compression", comp, strlen(comp), 0); if (ret) -@@ -1463,6 +1465,8 @@ int btrfs_defrag_file(struct inode *inode, struct file *file, +@@ -1466,6 +1468,8 @@ int btrfs_defrag_file(struct inode *inode, struct file *file, if (range->compress_type == BTRFS_COMPRESS_LZO) { btrfs_set_fs_incompat(fs_info, COMPRESS_LZO); @@ -190,10 +194,10 @@ index e176375..f732cfd 100644 ret = defrag_count; diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c -index d6cb155..162105f 100644 +index 4b23ae5..20631e9 100644 --- a/fs/btrfs/props.c +++ b/fs/btrfs/props.c -@@ -383,6 +383,8 @@ static int prop_compression_validate(const char *value, size_t len) +@@ -390,6 +390,8 @@ static int prop_compression_validate(const char *value, size_t len) return 0; else if (!strncmp("zlib", value, len)) return 0; @@ -202,7 +206,7 @@ index d6cb155..162105f 100644 return -EINVAL; } -@@ -405,6 +407,8 @@ static int prop_compression_apply(struct inode *inode, +@@ -412,6 +414,8 @@ static int prop_compression_apply(struct inode *inode, type = BTRFS_COMPRESS_LZO; else if (!strncmp("zlib", value, len)) type = BTRFS_COMPRESS_ZLIB; @@ -211,7 +215,7 @@ index d6cb155..162105f 100644 else return -EINVAL; -@@ -422,6 +426,8 @@ static const char *prop_compression_extract(struct inode *inode) +@@ -429,6 +433,8 @@ static const char *prop_compression_extract(struct inode *inode) return "zlib"; case BTRFS_COMPRESS_LZO: return "lzo"; @@ -221,7 +225,7 @@ index d6cb155..162105f 100644 return NULL; diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c -index 4f1cdd5..4f792d5 100644 +index 12540b6..c370dea 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -513,6 +513,14 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options, @@ -239,7 +243,7 @@ index 4f1cdd5..4f792d5 100644 } else if (strncmp(args[0].from, "no", 2) == 0) { compress_type = "no"; btrfs_clear_opt(info->mount_opt, COMPRESS); -@@ -1240,8 +1248,10 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry) +@@ -1227,8 +1235,10 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry) if (btrfs_test_opt(info, COMPRESS)) { if (info->compress_type == BTRFS_COMPRESS_ZLIB) compress_type = "zlib"; @@ -252,7 +256,7 @@ index 4f1cdd5..4f792d5 100644 seq_printf(seq, ",compress-force=%s", compress_type); else diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c -index 1f157fb..b0dec90 100644 +index c2d5f35..2b6d37c 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -200,6 +200,7 @@ BTRFS_FEAT_ATTR_INCOMPAT(mixed_backref, MIXED_BACKREF); @@ -273,10 +277,10 @@ index 1f157fb..b0dec90 100644 BTRFS_FEAT_ATTR_PTR(raid56), diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c new file mode 100644 -index 0000000..838741b +index 0000000..1822068 --- /dev/null +++ b/fs/btrfs/zstd.c -@@ -0,0 +1,433 @@ +@@ -0,0 +1,435 @@ +/* + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. @@ -308,10 +312,11 @@ index 0000000..838741b + +#define ZSTD_BTRFS_MAX_WINDOWLOG 17 +#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG) ++#define ZSTD_BTRFS_DEFAULT_LEVEL 3 + +static ZSTD_parameters zstd_get_btrfs_parameters(size_t src_len) +{ -+ ZSTD_parameters params = ZSTD_getParams(1, src_len, 0); ++ ZSTD_parameters params = ZSTD_getParams(ZSTD_BTRFS_DEFAULT_LEVEL, src_len, 0); + + if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG) + params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG; @@ -330,7 +335,7 @@ index 0000000..838741b +{ + struct workspace *workspace = list_entry(ws, struct workspace, list); + -+ vfree(workspace->mem); ++ kvfree(workspace->mem); + kfree(workspace->buf); + kfree(workspace); +} @@ -341,15 +346,15 @@ index 0000000..838741b + zstd_get_btrfs_parameters(ZSTD_BTRFS_MAX_INPUT); + struct workspace *workspace; + -+ workspace = kzalloc(sizeof(*workspace), GFP_NOFS); ++ workspace = kzalloc(sizeof(*workspace), GFP_KERNEL); + if (!workspace) + return ERR_PTR(-ENOMEM); + + workspace->size = max_t(size_t, + ZSTD_CStreamWorkspaceBound(params.cParams), + ZSTD_DStreamWorkspaceBound(ZSTD_BTRFS_MAX_INPUT)); -+ workspace->mem = vmalloc(workspace->size); -+ workspace->buf = kmalloc(PAGE_SIZE, GFP_NOFS); ++ workspace->mem = kvmalloc(workspace->size, GFP_KERNEL); ++ workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!workspace->mem || !workspace->buf) + goto fail; + @@ -541,12 +546,13 @@ index 0000000..838741b + return ret; +} + -+static int zstd_decompress_bio(struct list_head *ws, struct page **pages_in, -+ u64 disk_start, -+ struct bio *orig_bio, -+ size_t srclen) ++static int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) +{ + struct workspace *workspace = list_entry(ws, struct workspace, list); ++ struct page **pages_in = cb->compressed_pages; ++ u64 disk_start = cb->start; ++ struct bio *orig_bio = cb->orig_bio; ++ size_t srclen = cb->compressed_len; + ZSTD_DStream *stream; + int ret = 0; + unsigned long page_in_index = 0; @@ -711,7 +717,7 @@ index 0000000..838741b + .decompress = zstd_decompress, +}; diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h -index a456e53..992c150 100644 +index 9aa74f3..378230c 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -255,13 +255,7 @@ struct btrfs_ioctl_fs_info_args { diff --git a/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch b/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch index b638194f6..02bd10733 100644 --- a/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch +++ b/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch @@ -1,7 +1,7 @@ -From 5ff6a64abaea7b7f11d37cb0fdf08642316a3a90 Mon Sep 17 00:00:00 2001 +From 0cd63464d182bb9708f8b25f7da3dc8e5ec6b4fa Mon Sep 17 00:00:00 2001 From: Nick Terrell -Date: Mon, 12 Jun 2017 12:18:23 -0700 -Subject: [PATCH v2 4/4] squashfs: Add zstd support +Date: Mon, 17 Jul 2017 17:08:59 -0700 +Subject: [PATCH v3 4/4] squashfs: Add zstd support Add zstd compression and decompression support to SquashFS. zstd is a great fit for SquashFS because it can compress at ratios approaching xz, diff --git a/contrib/linux-kernel/fs/btrfs/zstd.c b/contrib/linux-kernel/fs/btrfs/zstd.c index 838741b37..182206872 100644 --- a/contrib/linux-kernel/fs/btrfs/zstd.c +++ b/contrib/linux-kernel/fs/btrfs/zstd.c @@ -29,10 +29,11 @@ #define ZSTD_BTRFS_MAX_WINDOWLOG 17 #define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG) +#define ZSTD_BTRFS_DEFAULT_LEVEL 3 static ZSTD_parameters zstd_get_btrfs_parameters(size_t src_len) { - ZSTD_parameters params = ZSTD_getParams(1, src_len, 0); + ZSTD_parameters params = ZSTD_getParams(ZSTD_BTRFS_DEFAULT_LEVEL, src_len, 0); if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG) params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG; @@ -51,7 +52,7 @@ static void zstd_free_workspace(struct list_head *ws) { struct workspace *workspace = list_entry(ws, struct workspace, list); - vfree(workspace->mem); + kvfree(workspace->mem); kfree(workspace->buf); kfree(workspace); } @@ -62,15 +63,15 @@ static struct list_head *zstd_alloc_workspace(void) zstd_get_btrfs_parameters(ZSTD_BTRFS_MAX_INPUT); struct workspace *workspace; - workspace = kzalloc(sizeof(*workspace), GFP_NOFS); + workspace = kzalloc(sizeof(*workspace), GFP_KERNEL); if (!workspace) return ERR_PTR(-ENOMEM); workspace->size = max_t(size_t, ZSTD_CStreamWorkspaceBound(params.cParams), ZSTD_DStreamWorkspaceBound(ZSTD_BTRFS_MAX_INPUT)); - workspace->mem = vmalloc(workspace->size); - workspace->buf = kmalloc(PAGE_SIZE, GFP_NOFS); + workspace->mem = kvmalloc(workspace->size, GFP_KERNEL); + workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!workspace->mem || !workspace->buf) goto fail; @@ -262,12 +263,13 @@ out: return ret; } -static int zstd_decompress_bio(struct list_head *ws, struct page **pages_in, - u64 disk_start, - struct bio *orig_bio, - size_t srclen) +static int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) { struct workspace *workspace = list_entry(ws, struct workspace, list); + struct page **pages_in = cb->compressed_pages; + u64 disk_start = cb->start; + struct bio *orig_bio = cb->orig_bio; + size_t srclen = cb->compressed_len; ZSTD_DStream *stream; int ret = 0; unsigned long page_in_index = 0; diff --git a/contrib/linux-kernel/lib/zstd/huf_compress.c b/contrib/linux-kernel/lib/zstd/huf_compress.c index 0361f387f..40055a701 100644 --- a/contrib/linux-kernel/lib/zstd/huf_compress.c +++ b/contrib/linux-kernel/lib/zstd/huf_compress.c @@ -525,7 +525,7 @@ static void HUF_encodeSymbol(BIT_CStream_t *bitCPtr, U32 symbol, const HUF_CElt size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); } -#define HUF_FLUSHBITS(s) (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s)) +#define HUF_FLUSHBITS(s) BIT_flushBits(s) #define HUF_FLUSHBITS_1(stream) \ if (sizeof((stream)->bitContainer) * 8 < HUF_TABLELOG_MAX * 2 + 7) \ @@ -542,7 +542,6 @@ size_t HUF_compress1X_usingCTable(void *dst, size_t dstSize, const void *src, si BYTE *const oend = ostart + dstSize; BYTE *op = ostart; size_t n; - const unsigned fast = (dstSize >= HUF_BLOCKBOUND(srcSize)); BIT_CStream_t bitC; /* init */ diff --git a/contrib/linux-kernel/lib/zstd/zstd_internal.h b/contrib/linux-kernel/lib/zstd/zstd_internal.h index 67487199e..f0ba47442 100644 --- a/contrib/linux-kernel/lib/zstd/zstd_internal.h +++ b/contrib/linux-kernel/lib/zstd/zstd_internal.h @@ -126,35 +126,16 @@ static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG; /*-******************************************* * Shared functions to include for inlining *********************************************/ -static void ZSTD_copy8(void *dst, const void *src) { memcpy(dst, src, 8); } -#define COPY8(d, s) \ - { \ - ZSTD_copy8(d, s); \ - d += 8; \ - s += 8; \ - } - +static void ZSTD_copy8(void *dst, const void *src) { + memcpy(dst, src, 8); +} /*! ZSTD_wildcopy() : * custom version of memcpy(), can copy up to 7 bytes too many (8 bytes if length==0) */ #define WILDCOPY_OVERLENGTH 8 ZSTD_STATIC void ZSTD_wildcopy(void *dst, const void *src, ptrdiff_t length) { - const BYTE *ip = (const BYTE *)src; - BYTE *op = (BYTE *)dst; - BYTE *const oend = op + length; - do - COPY8(op, ip) - while (op < oend); -} - -ZSTD_STATIC void ZSTD_wildcopy_e(void *dst, const void *src, void *dstEnd) /* should be faster for decoding, but strangely, not verified on all platform */ -{ - const BYTE *ip = (const BYTE *)src; - BYTE *op = (BYTE *)dst; - BYTE *const oend = (BYTE *)dstEnd; - do - COPY8(op, ip) - while (op < oend); + if (length > 0) + memcpy(dst, src, length); } /*-******************************************* From 5e6c5203f3370445da2903d302bde1a0a4fa7230 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 20 Jul 2017 15:11:56 -0700 Subject: [PATCH 198/318] fixed fuzzer test for non OS-X platforms --- tests/fuzzer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 06f984087..d63f1352c 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -246,7 +246,7 @@ static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) #else -static int FUZ_mallocTests(unsigned seed, double compressibility) +static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) { (void)seed; (void)compressibility; return 0; From a90b16e150d393b76210dc8c2c0b8f32fbd39826 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 20 Jul 2017 15:57:55 -0700 Subject: [PATCH 199/318] Visual blind fix 2 --- lib/common/pool.c | 6 +++--- lib/common/threading.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index 06d8a5f57..aeaca7e79 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -95,9 +95,9 @@ POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { ctx->queue = (POOL_job*) malloc(ctx->queueSize * sizeof(POOL_job)); ctx->queueHead = 0; ctx->queueTail = 0; - pthread_mutex_init(&ctx->queueMutex, NULL); - pthread_cond_init(&ctx->queuePushCond, NULL); - pthread_cond_init(&ctx->queuePopCond, NULL); + (void)pthread_mutex_init(&ctx->queueMutex, NULL); + (void)pthread_cond_init(&ctx->queuePushCond, NULL); + (void)pthread_cond_init(&ctx->queuePopCond, NULL); ctx->shutdown = 0; /* Allocate space for the thread handles */ ctx->threads = (pthread_t*)malloc(numThreads * sizeof(pthread_t)); diff --git a/lib/common/threading.h b/lib/common/threading.h index 99e39b551..ee7864555 100644 --- a/lib/common/threading.h +++ b/lib/common/threading.h @@ -42,14 +42,14 @@ extern "C" { /* mutex */ #define pthread_mutex_t CRITICAL_SECTION -#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), (void)0) +#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), 0) #define pthread_mutex_destroy(a) DeleteCriticalSection((a)) #define pthread_mutex_lock(a) EnterCriticalSection((a)) #define pthread_mutex_unlock(a) LeaveCriticalSection((a)) /* condition variable */ #define pthread_cond_t CONDITION_VARIABLE -#define pthread_cond_init(a, b) (InitializeConditionVariable((a)), (void)0) +#define pthread_cond_init(a, b) (InitializeConditionVariable((a)), 0) #define pthread_cond_destroy(a) /* No delete */ #define pthread_cond_wait(a, b) SleepConditionVariableCS((a), (b), INFINITE) #define pthread_cond_signal(a) WakeConditionVariable((a)) From a19916425d5d932e0e34e22dd4496114b4af6e73 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 20 Jul 2017 16:19:16 -0700 Subject: [PATCH 200/318] reworked adaptCompressionLevel to only account for completion information --- contrib/adaptive-compression/adapt.c | 267 ++++++++++++--------------- 1 file changed, 119 insertions(+), 148 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index cf232ca7b..84e689a20 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -29,7 +29,6 @@ static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; -static unsigned g_displayStats = 0; static UTIL_time_t g_startTime; static size_t g_streamedSize = 0; static unsigned g_useProgressBar = 0; @@ -47,15 +46,6 @@ typedef struct { buffer_t buffer; } inBuff_t; -typedef struct { - unsigned waitCompressed; - unsigned waitReady; - unsigned waitWrite; - unsigned readyCounter; - unsigned compressedCounter; - unsigned writeCounter; -} cStat_t; - typedef struct { buffer_t src; buffer_t dst; @@ -102,10 +92,9 @@ typedef struct { mutex_t jobWrite_mutex; cond_t jobWrite_cond; mutex_t completion_mutex; - mutex_t stats_mutex; + mutex_t wait_mutex; size_t lastDictSize; inBuff_t input; - cStat_t stats; jobDescription* jobs; ZSTD_CCtx* cctx; } adaptCCtx; @@ -163,7 +152,7 @@ static int freeCCtx(adaptCCtx* ctx) error |= destroyMutex(&ctx->jobWrite_mutex); error |= destroyCond(&ctx->jobWrite_cond); error |= destroyMutex(&ctx->completion_mutex); - error |= destroyMutex(&ctx->stats_mutex); + error |= destroyMutex(&ctx->wait_mutex); error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); free(ctx->input.buffer.start); if (ctx->jobs){ @@ -203,7 +192,7 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) pthreadError |= initMutex(&ctx->jobWrite_mutex); pthreadError |= initCond(&ctx->jobWrite_cond); pthreadError |= initMutex(&ctx->completion_mutex); - pthreadError |= initMutex(&ctx->stats_mutex); + pthreadError |= initMutex(&ctx->wait_mutex); if (pthreadError) return pthreadError; } ctx->numJobs = numJobs; @@ -211,6 +200,10 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->jobCompressedID = 0; ctx->jobWriteID = 0; ctx->lastDictSize = 0; + ctx->createCompletionMeasured = 1; + ctx->compressionCompletionMeasured = 1; + ctx->writeCompletionMeasured = 1; + ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); if (!ctx->jobs) { @@ -305,17 +298,6 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); } -/* this function normalizes counters when compression level is changing */ -static void reduceCounters(adaptCCtx* ctx) -{ - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - unsigned const min = MIN(ctx->stats.compressedCounter, MIN(ctx->stats.writeCounter, ctx->stats.readyCounter)); - ctx->stats.writeCounter -= min; - ctx->stats.compressedCounter -= min; - ctx->stats.readyCounter -= min; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); -} - /* * Compression level is changed depending on which part of the compression process is lagging * Currently, three theads exist for job creation, compression, and file writing respectively. @@ -330,67 +312,42 @@ static void adaptCompressionLevel(adaptCCtx* ctx) ctx->compressionLevel = g_compressionLevel; } else { - unsigned reset = 0; - unsigned allSlow; - unsigned compressWaiting; - unsigned writeWaiting; - unsigned createWaiting; + DEBUG(2, "compression level %u\n", ctx->compressionLevel); + /* check if compression is too slow */ + unsigned createChange; + unsigned writeChange; + unsigned compressionChange; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + createChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->createCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + writeChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->writeCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + compressionChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->compressionCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + DEBUG(2, "createCompletionMeasured %f\n", ctx->createCompletionMeasured); + DEBUG(2, "compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); + DEBUG(2, "writeCompletionMeasured %f\n", ctx->writeCompletionMeasured); + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; - compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; - writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; - createWaiting = ctx->adaptParam < ctx->stats.writeCounter; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); - DEBUG(2, "createWaiting %u\n", createWaiting); - DEBUG(2, "compressWaiting %u\n", compressWaiting); - DEBUG(2, "writeWaiting %u\n\n", writeWaiting); { - unsigned const writeSlow = (compressWaiting && createWaiting); - unsigned const compressSlow = (writeWaiting && createWaiting); - unsigned const createSlow = (compressWaiting && writeWaiting); - DEBUG(3, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); - if (allSlow) { - reset = 1; + unsigned const compressionFastChange = MIN(MIN(createChange, writeChange), ZSTD_maxCLevel() - ctx->compressionLevel); + DEBUG(2, "compressionFastChange %u\n", compressionFastChange); + + if (compressionFastChange) { + DEBUG(2, "compression level too low\n"); + ctx->compressionLevel += compressionFastChange; } - else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUG(2, "increasing compression level %u\n", ctx->compressionLevel); - double completion; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = writeSlow ? ctx->writeCompletionMeasured : ctx->createCompletionMeasured; - DEBUG(2, "write completion: %f, create completion: %f\n", ctx->writeCompletionMeasured, ctx->createCompletionMeasured); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - { - unsigned const maxChange = MAX_COMPRESSION_LEVEL_CHANGE - (unsigned)(completion*MAX_COMPRESSION_LEVEL_CHANGE); - unsigned const change = MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel); - DEBUG(3, "writeSlow: %u, change: %u\n", writeSlow, change); - DEBUG(3, "write completion: %f\n", completion); - ctx->compressionLevel += change; - reset = 1; - } - } - else if (compressSlow && ctx->compressionLevel > 1) { - double completion; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = ctx->compressionCompletionMeasured; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - { - unsigned const maxChange = MAX_COMPRESSION_LEVEL_CHANGE - (unsigned)(completion*MAX_COMPRESSION_LEVEL_CHANGE); - unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); - DEBUG(2, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(2, "completion: %f\n", completion); - ctx->compressionLevel -= change; - reset = 1; - } - } - if (reset) { - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - ctx->stats.readyCounter = 0; - ctx->stats.writeCounter = 0; - ctx->stats.compressedCounter = 0; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); + else { + unsigned const compressionSlowChange = MIN(compressionChange, ctx->compressionLevel-1); + DEBUG(2, "compression level too high\n"); + ctx->compressionLevel -= compressionSlowChange; } } + + /* reset */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletionMeasured = 1; + ctx->compressionCompletionMeasured = 1; + ctx->writeCompletionMeasured = 1; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + DEBUG(2, "\n"); } } @@ -410,21 +367,31 @@ static void* compressionThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); + + /* new job, reset completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->compressionCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - ctx->stats.waitReady++; - ctx->stats.readyCounter++; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); - reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); + /* compression thread is waiting, take measurements of write completion and read completion */ ctx->createCompletionMeasured = ctx->createCompletion; + ctx->writeCompletionMeasured = ctx->writeCompletion; + DEBUG(2, "compression thread waiting : createCompletionMeasured %f : writeCompletionMeasured %f\n", ctx->createCompletionMeasured, ctx->writeCompletionMeasured); DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + + /* reset create completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + DEBUG(3, "compressionThread(): continuing after job ready\n"); DEBUG(3, "DICTIONARY ENDED\n"); DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); @@ -497,7 +464,7 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(2, "update on job %u: compression completion %f\n", currJob, ctx->compressionCompletion); + DEBUG(3, "update on job %u: compression completion %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } while (remaining != 0); @@ -547,21 +514,29 @@ static void* outputThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "outputThread(): waiting on job compressed\n"); + + /* new job, reset completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->writeCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - ctx->stats.waitCompressed++; - ctx->stats.compressedCounter++; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); - reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); + /* write thread is waiting, take measurement of compression completion */ ctx->compressionCompletionMeasured = ctx->compressionCompletion; - DEBUG(2, "waited on job %u: compressionCompletion %f\n", currJob, ctx->compressionCompletion); + DEBUG(2, "write thread waiting : compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); + + /* reset compression completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->compressionCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + DEBUG(3, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; @@ -615,6 +590,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); break; } + } return arg; } @@ -628,19 +604,21 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - ctx->stats.waitWrite++; - ctx->stats.writeCounter++; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); - reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->writeCompletionMeasured = ctx->writeCompletion; + /* creation thread is waiting, take measurement of compression completion */ + ctx->compressionCompletionMeasured = ctx->compressionCompletion; + DEBUG(2, "creation thread waiting : compression completion measured : %f\n", ctx->compressionCompletionMeasured); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); + + /* reset write completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->writeCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "createCompressionJob(): continuing after job write\n"); DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); @@ -677,14 +655,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) return 0; } -static void printStats(cStat_t stats) -{ - DISPLAY("========STATISTICS========\n"); - DISPLAY("# times waited on job ready: %u\n", stats.waitReady); - DISPLAY("# times waited on job compressed: %u\n", stats.waitCompressed); - DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite); -} - static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadArg* otArg) { if (!ctx || !srcFile || !otArg) { @@ -710,48 +680,56 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA return 1; } } + { + unsigned currJob = 0; + /* creating jobs */ + for ( ; ; ) { + size_t pos = 0; + size_t const readBlockSize = 1 << 15; + size_t remaining = FILE_CHUNK_SIZE; - /* creating jobs */ - for ( ; ; ) { - size_t pos = 0; - size_t const readBlockSize = 1 << 15; - size_t remaining = FILE_CHUNK_SIZE; - while (remaining != 0 && !feof(srcFile)) { - size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); - if (ret != readBlockSize && !feof(srcFile)) { - /* error could not read correct number of bytes */ + /* new job reset completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + + while (remaining != 0 && !feof(srcFile)) { + size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); + if (ret != readBlockSize && !feof(srcFile)) { + /* error could not read correct number of bytes */ + DISPLAY("Error: problem occurred during read from src file\n"); + signalErrorToThreads(ctx); + return 1; + } + pos += ret; + remaining -= ret; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); + DEBUG(3, "create completion: %f\n", ctx->createCompletion); + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + } + if (remaining != 0 && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); signalErrorToThreads(ctx); return 1; } - pos += ret; - remaining -= ret; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - DEBUG(3, "create completion: %f\n", ctx->createCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - } - if (remaining != 0 && !feof(srcFile)) { - DISPLAY("Error: problem occurred during read from src file\n"); - signalErrorToThreads(ctx); - return 1; - } - g_streamedSize += pos; - /* reading was fine, now create the compression job */ - { - int const last = feof(srcFile); - int const error = createCompressionJob(ctx, pos, last); - if (error != 0) { - signalErrorToThreads(ctx); - return error; + g_streamedSize += pos; + /* reading was fine, now create the compression job */ + { + int const last = feof(srcFile); + int const error = createCompressionJob(ctx, pos, last); + if (error != 0) { + signalErrorToThreads(ctx); + return error; + } + } + currJob++; + if (feof(srcFile)) { + DEBUG(3, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); + break; } } - if (feof(srcFile)) { - DEBUG(3, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); - break; - } } - /* success -- created all jobs */ return 0; } @@ -803,9 +781,6 @@ static int freeFileCompressionResources(fcResources* fcr) { int ret = 0; waitUntilAllJobsCompleted(fcr->ctx); - pthread_mutex_lock(&fcr->ctx->stats_mutex.pMutex); - if (g_displayStats) printStats(fcr->ctx->stats); - pthread_mutex_unlock(&fcr->ctx->stats_mutex.pMutex); ret |= (fcr->srcFile != NULL) ? fclose(fcr->srcFile) : 0; ret |= (fcr->ctx != NULL) ? freeCCtx(fcr->ctx) : 0; if (fcr->otArg) { @@ -873,7 +848,6 @@ static void help() PRINT(" -oFILE : specify the output file name\n"); PRINT(" -v : display debug information\n"); PRINT(" -i# : provide initial compression level\n"); - PRINT(" -s : display information stats\n"); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); } @@ -913,9 +887,6 @@ int main(int argCount, const char* argv[]) g_compressionLevel = readU32FromChar(&argument); DEBUG(3, "g_compressionLevel: %u\n", g_compressionLevel); break; - case 's': - g_displayStats = 1; - break; case 'h': help(); goto _main_exit; From 82e488770c979d6bef9655bc0cd409c0df6b126d Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 20 Jul 2017 16:38:02 -0700 Subject: [PATCH 201/318] fixed bug where writeSize could be zero --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 84e689a20..3c6b7e904 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -548,7 +548,7 @@ static void* outputThread(void* arg) } { // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); - size_t const blockSize = compressedSize >> 7; + size_t const blockSize = MAX(compressedSize >> 7, 64 << 10); size_t pos = 0; for ( ; ; ) { size_t const writeSize = MIN(remaining, blockSize); From 38ba7002f2c9156842c5042990c322fc9546dd86 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 20 Jul 2017 18:39:04 -0700 Subject: [PATCH 202/318] fixed minor warning on unused variable in shell function --- tests/fuzzer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index d63f1352c..046c67ea8 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -248,7 +248,7 @@ static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) { - (void)seed; (void)compressibility; + (void)seed; (void)compressibility; (void)part; return 0; } From 9259c7afa412af57e7f578bc8e1f2c5d3906949c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 20 Jul 2017 18:45:33 -0700 Subject: [PATCH 203/318] semi working version that stabilizes --- contrib/adaptive-compression/adapt.c | 104 ++++++++++++--------------- 1 file changed, 45 insertions(+), 59 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 3c6b7e904..f25d22a4c 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -308,47 +308,47 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) */ static void adaptCompressionLevel(adaptCCtx* ctx) { + /* check if compression is too slow */ + unsigned createChange; + unsigned writeChange; + unsigned compressionChange; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + createChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->createCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + writeChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->writeCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + compressionChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->compressionCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + DEBUG(2, "compression level %u\n", ctx->compressionLevel); + DEBUG(2, "createCompletionMeasured %f\n", ctx->createCompletionMeasured); + DEBUG(2, "compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); + DEBUG(2, "writeCompletionMeasured %f\n", ctx->writeCompletionMeasured); + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + + { + unsigned const compressionFastChange = MIN(MIN(createChange, writeChange), ZSTD_maxCLevel() - ctx->compressionLevel); + + DEBUG(2, "compressionFastChange %u\n", compressionFastChange); + + if (compressionFastChange) { + DEBUG(2, "compression level too low\n"); + ctx->compressionLevel += compressionFastChange; + } + else { + unsigned const compressionSlowChange = MIN(compressionChange, ctx->compressionLevel-1); + DEBUG(2, "compression level too high\n"); + ctx->compressionLevel -= compressionSlowChange; + } + } + + /* reset */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletionMeasured = 1; + ctx->compressionCompletionMeasured = 1; + ctx->writeCompletionMeasured = 1; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + DEBUG(2, "\n"); + if (g_forceCompressionLevel) { ctx->compressionLevel = g_compressionLevel; } - else { - DEBUG(2, "compression level %u\n", ctx->compressionLevel); - /* check if compression is too slow */ - unsigned createChange; - unsigned writeChange; - unsigned compressionChange; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - createChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->createCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - writeChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->writeCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - compressionChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->compressionCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - DEBUG(2, "createCompletionMeasured %f\n", ctx->createCompletionMeasured); - DEBUG(2, "compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); - DEBUG(2, "writeCompletionMeasured %f\n", ctx->writeCompletionMeasured); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - - { - unsigned const compressionFastChange = MIN(MIN(createChange, writeChange), ZSTD_maxCLevel() - ctx->compressionLevel); - DEBUG(2, "compressionFastChange %u\n", compressionFastChange); - - if (compressionFastChange) { - DEBUG(2, "compression level too low\n"); - ctx->compressionLevel += compressionFastChange; - } - else { - unsigned const compressionSlowChange = MIN(compressionChange, ctx->compressionLevel-1); - DEBUG(2, "compression level too high\n"); - ctx->compressionLevel -= compressionSlowChange; - } - } - - /* reset */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletionMeasured = 1; - ctx->compressionCompletionMeasured = 1; - ctx->writeCompletionMeasured = 1; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "\n"); - } } static size_t getUseableDictSize(unsigned compressionLevel) @@ -368,10 +368,6 @@ static void* compressionThread(void* arg) jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); - /* new job, reset completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { @@ -387,9 +383,9 @@ static void* compressionThread(void* arg) } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); - /* reset create completion */ + /* reset compression completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletion = 0; + ctx->compressionCompletion = 0; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "compressionThread(): continuing after job ready\n"); @@ -397,7 +393,7 @@ static void* compressionThread(void* arg) DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); /* adapt compression level */ - adaptCompressionLevel(ctx); + if (currJob) adaptCompressionLevel(ctx); /* compress the data */ { @@ -515,11 +511,6 @@ static void* outputThread(void* arg) jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "outputThread(): waiting on job compressed\n"); - /* new job, reset completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->writeCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); @@ -532,9 +523,9 @@ static void* outputThread(void* arg) } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); - /* reset compression completion */ + /* reset write completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletion = 0; + ctx->writeCompletion = 0; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "outputThread(): continuing after job compressed\n"); @@ -615,9 +606,9 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); - /* reset write completion */ + /* reset create completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->writeCompletion = 0; + ctx->createCompletion = 0; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "createCompressionJob(): continuing after job write\n"); @@ -688,11 +679,6 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA size_t const readBlockSize = 1 << 15; size_t remaining = FILE_CHUNK_SIZE; - /* new job reset completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - while (remaining != 0 && !feof(srcFile)) { size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); if (ret != readBlockSize && !feof(srcFile)) { From e929d3b787b4a75b14546aa5f3e2c2e601eb2299 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 09:26:35 -0700 Subject: [PATCH 204/318] added priority decision making for adapt compression level --- contrib/adaptive-compression/adapt.c | 59 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index f25d22a4c..758bf5589 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -308,43 +308,42 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) */ static void adaptCompressionLevel(adaptCCtx* ctx) { - /* check if compression is too slow */ - unsigned createChange; - unsigned writeChange; - unsigned compressionChange; + double createCompletion, compressionCompletion, writeCompletion; + double const threshold = 0.00001; pthread_mutex_lock(&ctx->completion_mutex.pMutex); - createChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->createCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - writeChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->writeCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - compressionChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->compressionCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - DEBUG(2, "compression level %u\n", ctx->compressionLevel); - DEBUG(2, "createCompletionMeasured %f\n", ctx->createCompletionMeasured); - DEBUG(2, "compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); - DEBUG(2, "writeCompletionMeasured %f\n", ctx->writeCompletionMeasured); + createCompletion = ctx->createCompletionMeasured; + compressionCompletion = ctx->compressionCompletionMeasured; + writeCompletion = ctx->writeCompletionMeasured; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - { - unsigned const compressionFastChange = MIN(MIN(createChange, writeChange), ZSTD_maxCLevel() - ctx->compressionLevel); - - DEBUG(2, "compressionFastChange %u\n", compressionFastChange); - - if (compressionFastChange) { - DEBUG(2, "compression level too low\n"); - ctx->compressionLevel += compressionFastChange; - } - else { - unsigned const compressionSlowChange = MIN(compressionChange, ctx->compressionLevel-1); - DEBUG(2, "compression level too high\n"); - ctx->compressionLevel -= compressionSlowChange; - } + DEBUG(2, "create completion: %f\n", createCompletion); + DEBUG(2, "compression completion: %f\n", compressionCompletion); + DEBUG(2, "write completion: %f\n", writeCompletion); + /* adapt compression based on bottleneck */ + if (1 - createCompletion > threshold) { + /* job creation was not finished, compression thread waited */ + unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - createCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); + ctx->compressionLevel += change; + } + else if (1 - writeCompletion > threshold) { + /* write thread was not finished, compression thread waited */ + unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - writeCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); + ctx->compressionLevel += change; + } + else if (1 - compressionCompletion > threshold) { + /* compression thread was not finished, one of the other two threads waited */ + unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - compressionCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + DEBUG(2, "decreasing compression level %u by %u\n", ctx->compressionLevel, change); + ctx->compressionLevel -= change; } - /* reset */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletionMeasured = 1; ctx->compressionCompletionMeasured = 1; ctx->writeCompletionMeasured = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "\n"); if (g_forceCompressionLevel) { ctx->compressionLevel = g_compressionLevel; @@ -375,7 +374,7 @@ static void* compressionThread(void* arg) /* compression thread is waiting, take measurements of write completion and read completion */ ctx->createCompletionMeasured = ctx->createCompletion; ctx->writeCompletionMeasured = ctx->writeCompletion; - DEBUG(2, "compression thread waiting : createCompletionMeasured %f : writeCompletionMeasured %f\n", ctx->createCompletionMeasured, ctx->writeCompletionMeasured); + DEBUG(3, "compression thread waiting : createCompletionMeasured %f : writeCompletionMeasured %f\n", ctx->createCompletionMeasured, ctx->writeCompletionMeasured); DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); @@ -516,7 +515,7 @@ static void* outputThread(void* arg) pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* write thread is waiting, take measurement of compression completion */ ctx->compressionCompletionMeasured = ctx->compressionCompletion; - DEBUG(2, "write thread waiting : compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); + DEBUG(3, "write thread waiting : compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -598,7 +597,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* creation thread is waiting, take measurement of compression completion */ ctx->compressionCompletionMeasured = ctx->compressionCompletion; - DEBUG(2, "creation thread waiting : compression completion measured : %f\n", ctx->compressionCompletionMeasured); + DEBUG(3, "creation thread waiting : compression completion measured : %f\n", ctx->compressionCompletionMeasured); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); From 721c6a8b973c8a2e23a4f4eec56bf77e253098ea Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 09:30:24 -0700 Subject: [PATCH 205/318] added bounding to compression level change --- contrib/adaptive-compression/adapt.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 758bf5589..c542c9a3d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -323,20 +323,23 @@ static void adaptCompressionLevel(adaptCCtx* ctx) if (1 - createCompletion > threshold) { /* job creation was not finished, compression thread waited */ unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - createCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); - ctx->compressionLevel += change; + ctx->compressionLevel += boundChange; } else if (1 - writeCompletion > threshold) { /* write thread was not finished, compression thread waited */ unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - writeCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); - ctx->compressionLevel += change; + ctx->compressionLevel += boundChange; } else if (1 - compressionCompletion > threshold) { /* compression thread was not finished, one of the other two threads waited */ unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - compressionCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); DEBUG(2, "decreasing compression level %u by %u\n", ctx->compressionLevel, change); - ctx->compressionLevel -= change; + ctx->compressionLevel -= boundChange; } /* reset */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); From ceda7a9a589169d49c9d846d159cfb93f4eaa36e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 21 Jul 2017 11:44:39 -0700 Subject: [PATCH 206/318] minor Makefile refactor --- Makefile | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 5e887364a..c58b54c9e 100644 --- a/Makefile +++ b/Makefile @@ -74,12 +74,8 @@ zstdmt: zlibwrapper: $(MAKE) -C $(ZWRAPDIR) test -.PHONY: shortest -shortest: - $(MAKE) -C $(TESTDIR) $@ - -.PHONY: test -test: +.PHONY: test shortest +test shortest: $(MAKE) -C $(TESTDIR) $@ .PHONY: examples From db109f8fef99c51154a7ff56371ff6470cbb43eb Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 13:38:24 -0700 Subject: [PATCH 207/318] measure multiple completion levels during each wait --- contrib/adaptive-compression/adapt.c | 123 ++++++++++++++++----------- 1 file changed, 75 insertions(+), 48 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index c542c9a3d..b466dbdd8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -25,7 +25,7 @@ #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 0 -#define MAX_COMPRESSION_LEVEL_CHANGE 4 +#define MAX_COMPRESSION_LEVEL_CHANGE 3 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -77,9 +77,12 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - double compressionCompletionMeasured; - double writeCompletionMeasured; - double createCompletionMeasured; + double createWaitWriteCompletion; + double createWaitCompressionCompletion; + double compressWaitCreateCompletion; + double compressWaitWriteCompletion; + double writeWaitCreateCompletion; + double writeWaitCompressionCompletion; double compressionCompletion; double writeCompletion; double createCompletion; @@ -200,9 +203,14 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->jobCompressedID = 0; ctx->jobWriteID = 0; ctx->lastDictSize = 0; - ctx->createCompletionMeasured = 1; - ctx->compressionCompletionMeasured = 1; - ctx->writeCompletionMeasured = 1; + + + ctx->createWaitWriteCompletion = 1; + ctx->createWaitCompressionCompletion = 1; + ctx->compressWaitCreateCompletion = 1; + ctx->compressWaitWriteCompletion = 1; + ctx->writeWaitCreateCompletion = 1; + ctx->writeWaitCompressionCompletion = 1; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -308,45 +316,61 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) */ static void adaptCompressionLevel(adaptCCtx* ctx) { - double createCompletion, compressionCompletion, writeCompletion; + double createWaitWriteCompletion; + double createWaitCompressionCompletion; + double compressWaitCreateCompletion; + double compressWaitWriteCompletion; + double writeWaitCreateCompletion; + double writeWaitCompressionCompletion; double const threshold = 0.00001; + + + /* read and reset completion measurements */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - createCompletion = ctx->createCompletionMeasured; - compressionCompletion = ctx->compressionCompletionMeasured; - writeCompletion = ctx->writeCompletionMeasured; + DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); + DEBUG(2, "rw %f\n", ctx->createWaitWriteCompletion); + DEBUG(2, "cr %f\n", ctx->compressWaitCreateCompletion); + DEBUG(2, "cw %f\n", ctx->compressWaitWriteCompletion); + DEBUG(2, "wr %f\n", ctx->writeWaitCreateCompletion); + DEBUG(2, "wc %f\n\n", ctx->writeWaitCompressionCompletion); + + createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; + createWaitWriteCompletion = ctx->createWaitWriteCompletion; + compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; + compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; + writeWaitCreateCompletion = ctx->writeWaitCreateCompletion; + writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; + + ctx->createWaitWriteCompletion = 1; + ctx->createWaitCompressionCompletion = 1; + ctx->compressWaitCreateCompletion = 1; + ctx->compressWaitWriteCompletion = 1; + ctx->writeWaitCreateCompletion = 1; + ctx->writeWaitCompressionCompletion = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "create completion: %f\n", createCompletion); - DEBUG(2, "compression completion: %f\n", compressionCompletion); - DEBUG(2, "write completion: %f\n", writeCompletion); - /* adapt compression based on bottleneck */ - if (1 - createCompletion > threshold) { - /* job creation was not finished, compression thread waited */ - unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - createCompletion * MAX_COMPRESSION_LEVEL_CHANGE; - unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); - ctx->compressionLevel += boundChange; - } - else if (1 - writeCompletion > threshold) { - /* write thread was not finished, compression thread waited */ - unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - writeCompletion * MAX_COMPRESSION_LEVEL_CHANGE; - unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); - ctx->compressionLevel += boundChange; - } - else if (1 - compressionCompletion > threshold) { - /* compression thread was not finished, one of the other two threads waited */ - unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - compressionCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + /* adaptation logic */ + if (1-createWaitCompressionCompletion > threshold && 1-writeWaitCompressionCompletion > threshold) { + /* both create and write threads waiting on compression */ + /* use writeWaitCompressionCompletion */ + unsigned const change = (unsigned)((1-writeWaitCompressionCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); - DEBUG(2, "decreasing compression level %u by %u\n", ctx->compressionLevel, change); ctx->compressionLevel -= boundChange; } - /* reset */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletionMeasured = 1; - ctx->compressionCompletionMeasured = 1; - ctx->writeCompletionMeasured = 1; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + else if (1-createWaitWriteCompletion > threshold && 1-compressWaitWriteCompletion > threshold) { + /* both create and compression thread waiting on write */ + /* use createWaitWriteCompletion */ + unsigned const change = (unsigned)((1-createWaitWriteCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); + ctx->compressionLevel += boundChange; + } + else if (1-writeWaitCreateCompletion > threshold && 1-compressWaitCreateCompletion > threshold) { + /* both compression and write waiting on create */ + /* use compressWaitCreateCompletion */ + unsigned const change = (unsigned)((1-compressWaitCreateCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); + ctx->compressionLevel += boundChange; + } if (g_forceCompressionLevel) { ctx->compressionLevel = g_compressionLevel; @@ -375,9 +399,9 @@ static void* compressionThread(void* arg) while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* compression thread is waiting, take measurements of write completion and read completion */ - ctx->createCompletionMeasured = ctx->createCompletion; - ctx->writeCompletionMeasured = ctx->writeCompletion; - DEBUG(3, "compression thread waiting : createCompletionMeasured %f : writeCompletionMeasured %f\n", ctx->createCompletionMeasured, ctx->writeCompletionMeasured); + ctx->compressWaitCreateCompletion = ctx->createCompletion; + ctx->compressWaitWriteCompletion = ctx->writeCompletion; + DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f : compressWaitWriteCompletion %f\n", ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); @@ -462,7 +486,7 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(3, "update on job %u: compression completion %f\n", currJob, ctx->compressionCompletion); + DEBUG(3, "compression completion %f\n", ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } while (remaining != 0); @@ -517,8 +541,9 @@ static void* outputThread(void* arg) while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* write thread is waiting, take measurement of compression completion */ - ctx->compressionCompletionMeasured = ctx->compressionCompletion; - DEBUG(3, "write thread waiting : compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); + ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; + ctx->writeWaitCreateCompletion = ctx->createCompletion; + DEBUG(3, "write thread waiting : writeWaitCreateCompletion %f : writeWaitCompressionCompletion %f\n", ctx->writeWaitCreateCompletion, ctx->writeWaitCompressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -541,7 +566,7 @@ static void* outputThread(void* arg) } { // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); - size_t const blockSize = MAX(compressedSize >> 7, 64 << 10); + size_t const blockSize = MAX(compressedSize >> 7, 1 << 10); size_t pos = 0; for ( ; ; ) { size_t const writeSize = MIN(remaining, blockSize); @@ -553,6 +578,7 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; + DEBUG(3, "write completion %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); if (remaining == 0) break; @@ -599,8 +625,9 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* creation thread is waiting, take measurement of compression completion */ - ctx->compressionCompletionMeasured = ctx->compressionCompletion; - DEBUG(3, "creation thread waiting : compression completion measured : %f\n", ctx->compressionCompletionMeasured); + ctx->createWaitCompressionCompletion = ctx->compressionCompletion; + ctx->createWaitWriteCompletion = ctx->writeCompletion; + DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f : createWaitWriteCompletion %f\n", ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); From 05fe8dd47c77f2a826a4deb58eb8a98a40f8ffdf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 14:06:24 -0700 Subject: [PATCH 208/318] updating debug statements --- contrib/adaptive-compression/adapt.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index b466dbdd8..5a3b723d3 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -324,7 +324,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double writeWaitCompressionCompletion; double const threshold = 0.00001; - + DEBUG(2, "adapting compression level %u\n", ctx->compressionLevel); /* read and reset completion measurements */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); @@ -341,6 +341,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) writeWaitCreateCompletion = ctx->writeWaitCreateCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; + DEBUG(2, "resetting adaptive variables\n"); ctx->createWaitWriteCompletion = 1; ctx->createWaitCompressionCompletion = 1; ctx->compressWaitCreateCompletion = 1; @@ -404,7 +405,7 @@ static void* compressionThread(void* arg) DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f : compressWaitWriteCompletion %f\n", ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); + DEBUG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); @@ -545,7 +546,7 @@ static void* outputThread(void* arg) ctx->writeWaitCreateCompletion = ctx->createCompletion; DEBUG(3, "write thread waiting : writeWaitCreateCompletion %f : writeWaitCompressionCompletion %f\n", ctx->writeWaitCreateCompletion, ctx->writeWaitCompressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); + DEBUG(2, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); @@ -630,7 +631,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f : createWaitWriteCompletion %f\n", ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); + DEBUG(2, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); From 6455ec482cbb4a76619fed60ca9ef28a8c6a2abc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 16:05:01 -0700 Subject: [PATCH 209/318] taking the maximum of the completion level reads in order to determine which one was waiting more --- contrib/adaptive-compression/adapt.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 5a3b723d3..e07333fc3 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -354,23 +354,29 @@ static void adaptCompressionLevel(adaptCCtx* ctx) if (1-createWaitCompressionCompletion > threshold && 1-writeWaitCompressionCompletion > threshold) { /* both create and write threads waiting on compression */ /* use writeWaitCompressionCompletion */ - unsigned const change = (unsigned)((1-writeWaitCompressionCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); + double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); + unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); ctx->compressionLevel -= boundChange; + DEBUG(2, "create and write threads waiting, tried to decrease compression level by %u\n", boundChange); } else if (1-createWaitWriteCompletion > threshold && 1-compressWaitWriteCompletion > threshold) { /* both create and compression thread waiting on write */ /* use createWaitWriteCompletion */ - unsigned const change = (unsigned)((1-createWaitWriteCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); + double const completion = MAX(createWaitWriteCompletion, compressWaitWriteCompletion); + unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; + DEBUG(2, "create and compression threads waiting, tried to increase compression level by %u\n", boundChange); } else if (1-writeWaitCreateCompletion > threshold && 1-compressWaitCreateCompletion > threshold) { /* both compression and write waiting on create */ /* use compressWaitCreateCompletion */ - unsigned const change = (unsigned)((1-compressWaitCreateCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); + double const completion = MAX(writeWaitCreateCompletion, compressWaitCreateCompletion); + unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; + DEBUG(2, "compression and write threads waiting, tried to increase compression level by %u\n", boundChange); } if (g_forceCompressionLevel) { @@ -404,8 +410,8 @@ static void* compressionThread(void* arg) ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f : compressWaitWriteCompletion %f\n", ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); DEBUG(3, "create completion: %f\n", ctx->createCompletion); + DEBUG(2, "compression thread waiting for nextJob: %u, compressWaitCreateCompletion %f, compressWaitWriteCompletion %f\n", currJob, ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); @@ -422,6 +428,7 @@ static void* compressionThread(void* arg) /* adapt compression level */ if (currJob) adaptCompressionLevel(ctx); + DEBUG(2, "job %u compressed with level %u\n", currJob, ctx->compressionLevel); /* compress the data */ { size_t const compressionBlockSize = 1 << 17; /* 128 KB */ @@ -487,7 +494,7 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(3, "compression completion %f\n", ctx->compressionCompletion); + DEBUG(2, "compression completion %f\n", ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } while (remaining != 0); @@ -545,8 +552,8 @@ static void* outputThread(void* arg) ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; ctx->writeWaitCreateCompletion = ctx->createCompletion; DEBUG(3, "write thread waiting : writeWaitCreateCompletion %f : writeWaitCompressionCompletion %f\n", ctx->writeWaitCreateCompletion, ctx->writeWaitCompressionCompletion); + DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f, writeWaitCreateCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion, ctx->writeWaitCreateCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); @@ -579,7 +586,7 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; - DEBUG(3, "write completion %f\n", ctx->writeCompletion); + DEBUG(2, "write completion %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); if (remaining == 0) break; @@ -630,8 +637,8 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->createWaitWriteCompletion = ctx->writeCompletion; DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f : createWaitWriteCompletion %f\n", ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); + DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f, createWaitWriteCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); @@ -721,7 +728,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA remaining -= ret; pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - DEBUG(3, "create completion: %f\n", ctx->createCompletion); + DEBUG(2, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { From 95bef759b3f1fc52bbcf5a82c1dddaf00906df8f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 17:49:39 -0700 Subject: [PATCH 210/318] switched over to model where reading only waits on compression thread --- contrib/adaptive-compression/adapt.c | 65 +++++++++++++++------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index e07333fc3..9057bfcfc 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -51,7 +51,7 @@ typedef struct { buffer_t dst; unsigned compressionLevel; unsigned jobID; - unsigned lastJob; + unsigned lastJobPlusOne; size_t compressedSize; size_t dictSize; } jobDescription; @@ -226,7 +226,7 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) jobDescription* job = &ctx->jobs[jobNum]; job->src.start = malloc(2 * FILE_CHUNK_SIZE); job->dst.start = malloc(ZSTD_compressBound(FILE_CHUNK_SIZE)); - job->lastJob = 0; + job->lastJobPlusOne = 0; if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); return 1; @@ -400,22 +400,30 @@ static void* compressionThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); - - + /* wait until job is ready */ pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); - while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { + while (currJob + 1 > ctx->jobReadyID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); - /* compression thread is waiting, take measurements of write completion and read completion */ + /* compression thread is waiting on creation thread, take measurement */ ctx->compressWaitCreateCompletion = ctx->createCompletion; - ctx->compressWaitWriteCompletion = ctx->writeCompletion; - DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f : compressWaitWriteCompletion %f\n", ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); + DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f\n", ctx->compressWaitCreateCompletion); DEBUG(3, "create completion: %f\n", ctx->createCompletion); - DEBUG(2, "compression thread waiting for nextJob: %u, compressWaitCreateCompletion %f, compressWaitWriteCompletion %f\n", currJob, ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); + DEBUG(2, "compression thread waiting for ready: %u, compressWaitCreateCompletion %f\n", currJob, ctx->compressWaitCreateCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + /* wait until job previously in this space is written */ + pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); + while (currJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->compressWaitWriteCompletion = ctx->writeCompletion; + DEBUG(2, "compression thread waiting for write: %u, compressWaitWriteCompletion %f\n", currJob, ctx->compressWaitWriteCompletion); + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); + } + pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); /* reset compression completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 0; @@ -439,10 +447,8 @@ static void* compressionThread(void* arg) size_t dstPos = 0; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); - /* reset compressed size */ job->compressedSize = 0; - /* begin compression */ { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); @@ -474,10 +480,10 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } { - DEBUG(3, "write out ending: %d\n", job->lastJob && (remaining == actualBlockSize)); - DEBUG(3, "lastJob %u\n", job->lastJob); + DEBUG(3, "write out ending: %d\n", (job->lastJobPlusOne == currJob + 1) && (remaining == actualBlockSize)); + DEBUG(3, "lastJobPlusOne %u\n", job->lastJobPlusOne); DEBUG(3, "compressionBlockSize %zu\n", compressionBlockSize); - size_t const ret = (job->lastJob && remaining == actualBlockSize) ? + size_t const ret = (job->lastJobPlusOne == currJob + 1 && remaining == actualBlockSize) ? ZSTD_compressEnd (ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize) : ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize); if (ZSTD_isError(ret)) { @@ -503,15 +509,16 @@ static void* compressionThread(void* arg) pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); ctx->jobCompressedID++; DEBUG(3, "signaling for job %u\n", currJob); - pthread_cond_signal(&ctx->jobCompressed_cond.pCond); + pthread_cond_broadcast(&ctx->jobCompressed_cond.pCond); pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); DEBUG(3, "finished job compression %u\n", currJob); - currJob++; - if (job->lastJob || ctx->threadError) { + if (job->lastJobPlusOne == currJob + 1 || ctx->threadError) { /* finished compressing all jobs */ DEBUG(3, "all jobs finished compressing\n"); break; } + + currJob++; } return arg; } @@ -544,7 +551,6 @@ static void* outputThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "outputThread(): waiting on job compressed\n"); - pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); @@ -599,8 +605,7 @@ static void* outputThread(void* arg) } } DEBUG(3, "finished job write %u\n", currJob); - currJob++; - displayProgress(currJob, ctx->compressionLevel, job->lastJob); + displayProgress(currJob, ctx->compressionLevel, job->lastJobPlusOne == currJob + 1); DEBUG(3, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); ctx->jobWriteID++; @@ -608,7 +613,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "unlocking job write mutex\n"); - if (job->lastJob || ctx->threadError) { + if (job->lastJobPlusOne == currJob + 1 || ctx->threadError) { /* finished with all jobs */ DEBUG(3, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); @@ -617,6 +622,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); break; } + currJob++; } return arg; @@ -628,21 +634,22 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; DEBUG(3, "createCompressionJob(): wait for job write\n"); - pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); - DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); - while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { + + + /* wait until the job has been compressed */ + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); + while (nextJob - ctx->jobCompressedID >= ctx->numJobs && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); - /* creation thread is waiting, take measurement of compression completion */ + /* creation thread is waiting, take measurement of completion */ ctx->createWaitCompressionCompletion = ctx->compressionCompletion; ctx->createWaitWriteCompletion = ctx->writeCompletion; DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f : createWaitWriteCompletion %f\n", ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f, createWaitWriteCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); + pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } - pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); - + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); /* reset create completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletion = 0; @@ -653,7 +660,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) job->compressionLevel = ctx->compressionLevel; job->src.size = srcSize; job->jobID = nextJob; - job->lastJob = last; + if (last) job->lastJobPlusOne = nextJob + 1; { /* swap buffer */ void* const copy = job->src.start; From 08d9e42ec61a51fe1a0189ac3b61b1f200918e57 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 18:02:55 -0700 Subject: [PATCH 211/318] removed useless measurements --- contrib/adaptive-compression/adapt.c | 34 ++++++++-------------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 9057bfcfc..68727cbd8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -77,11 +77,9 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - double createWaitWriteCompletion; double createWaitCompressionCompletion; double compressWaitCreateCompletion; double compressWaitWriteCompletion; - double writeWaitCreateCompletion; double writeWaitCompressionCompletion; double compressionCompletion; double writeCompletion; @@ -205,11 +203,9 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->lastDictSize = 0; - ctx->createWaitWriteCompletion = 1; ctx->createWaitCompressionCompletion = 1; ctx->compressWaitCreateCompletion = 1; ctx->compressWaitWriteCompletion = 1; - ctx->writeWaitCreateCompletion = 1; ctx->writeWaitCompressionCompletion = 1; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -316,11 +312,9 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) */ static void adaptCompressionLevel(adaptCCtx* ctx) { - double createWaitWriteCompletion; double createWaitCompressionCompletion; double compressWaitCreateCompletion; double compressWaitWriteCompletion; - double writeWaitCreateCompletion; double writeWaitCompressionCompletion; double const threshold = 0.00001; @@ -328,25 +322,19 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* read and reset completion measurements */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); - DEBUG(2, "rw %f\n", ctx->createWaitWriteCompletion); DEBUG(2, "cr %f\n", ctx->compressWaitCreateCompletion); DEBUG(2, "cw %f\n", ctx->compressWaitWriteCompletion); - DEBUG(2, "wr %f\n", ctx->writeWaitCreateCompletion); DEBUG(2, "wc %f\n\n", ctx->writeWaitCompressionCompletion); createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; - createWaitWriteCompletion = ctx->createWaitWriteCompletion; compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; - writeWaitCreateCompletion = ctx->writeWaitCreateCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; DEBUG(2, "resetting adaptive variables\n"); - ctx->createWaitWriteCompletion = 1; ctx->createWaitCompressionCompletion = 1; ctx->compressWaitCreateCompletion = 1; ctx->compressWaitWriteCompletion = 1; - ctx->writeWaitCreateCompletion = 1; ctx->writeWaitCompressionCompletion = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); @@ -360,19 +348,18 @@ static void adaptCompressionLevel(adaptCCtx* ctx) ctx->compressionLevel -= boundChange; DEBUG(2, "create and write threads waiting, tried to decrease compression level by %u\n", boundChange); } - else if (1-createWaitWriteCompletion > threshold && 1-compressWaitWriteCompletion > threshold) { + else if (1-compressWaitWriteCompletion > threshold) { /* both create and compression thread waiting on write */ - /* use createWaitWriteCompletion */ - double const completion = MAX(createWaitWriteCompletion, compressWaitWriteCompletion); + double const completion = compressWaitWriteCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; DEBUG(2, "create and compression threads waiting, tried to increase compression level by %u\n", boundChange); } - else if (1-writeWaitCreateCompletion > threshold && 1-compressWaitCreateCompletion > threshold) { + else if (1-compressWaitCreateCompletion > threshold) { /* both compression and write waiting on create */ /* use compressWaitCreateCompletion */ - double const completion = MAX(writeWaitCreateCompletion, compressWaitCreateCompletion); + double const completion = compressWaitCreateCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; @@ -417,6 +404,7 @@ static void* compressionThread(void* arg) /* wait until job previously in this space is written */ pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); while (currJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { + /* compression thread is waiting on writer thread, take measurement */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(2, "compression thread waiting for write: %u, compressWaitWriteCompletion %f\n", currJob, ctx->compressWaitWriteCompletion); @@ -554,11 +542,10 @@ static void* outputThread(void* arg) pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); - /* write thread is waiting, take measurement of compression completion */ + /* write thread is waiting on compression thread */ ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; - ctx->writeWaitCreateCompletion = ctx->createCompletion; - DEBUG(3, "write thread waiting : writeWaitCreateCompletion %f : writeWaitCompressionCompletion %f\n", ctx->writeWaitCreateCompletion, ctx->writeWaitCompressionCompletion); - DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f, writeWaitCreateCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion, ctx->writeWaitCreateCompletion); + DEBUG(3, "write thread waiting : writeWaitCompressionCompletion %f\n", ctx->writeWaitCompressionCompletion); + DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } @@ -642,10 +629,9 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* creation thread is waiting, take measurement of completion */ ctx->createWaitCompressionCompletion = ctx->compressionCompletion; - ctx->createWaitWriteCompletion = ctx->writeCompletion; - DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f : createWaitWriteCompletion %f\n", ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); + DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f\n", ctx->createWaitCompressionCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); - DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f, createWaitWriteCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); + DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } From 880f08d1049ee320aa54673b99c9ec0e57c5e397 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Sun, 23 Jul 2017 10:18:54 -0700 Subject: [PATCH 212/318] change how completion is measured in compression thread --- contrib/adaptive-compression/adapt.c | 48 ++++++++++++++++++---------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 68727cbd8..248421457 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -25,7 +25,7 @@ #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 0 -#define MAX_COMPRESSION_LEVEL_CHANGE 3 +#define MAX_COMPRESSION_LEVEL_CHANGE 2 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -207,6 +207,9 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->compressWaitCreateCompletion = 1; ctx->compressWaitWriteCompletion = 1; ctx->writeWaitCompressionCompletion = 1; + ctx->createCompletion = 1; + ctx->writeCompletion = 1; + ctx->compressionCompletion = 1; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -387,16 +390,34 @@ static void* compressionThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); + + { + /* check if compression thread will have to wait */ + unsigned willWaitForCreate = 0; + unsigned willWaitForWrite = 0; + + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); + if (currJob + 1 > ctx->jobReadyID) willWaitForCreate = 1; + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + + pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); + if (currJob - ctx->jobWriteID >= ctx->numJobs) willWaitForWrite = 1; + pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); + + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + if (willWaitForCreate || willWaitForWrite) { + ctx->compressWaitCreateCompletion = ctx->createCompletion; + ctx->compressWaitWriteCompletion = ctx->writeCompletion; + DEBUG(2, "compression will wait for create or write\n"); + DEBUG(2, "create completion %f\n", ctx->compressWaitCreateCompletion); + DEBUG(2, "write completion %f\n", ctx->compressWaitWriteCompletion); + } + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + } + /* wait until job is ready */ pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); while (currJob + 1 > ctx->jobReadyID && !ctx->threadError) { - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - /* compression thread is waiting on creation thread, take measurement */ - ctx->compressWaitCreateCompletion = ctx->createCompletion; - DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f\n", ctx->compressWaitCreateCompletion); - DEBUG(3, "create completion: %f\n", ctx->createCompletion); - DEBUG(2, "compression thread waiting for ready: %u, compressWaitCreateCompletion %f\n", currJob, ctx->compressWaitCreateCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); @@ -404,11 +425,6 @@ static void* compressionThread(void* arg) /* wait until job previously in this space is written */ pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); while (currJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { - /* compression thread is waiting on writer thread, take measurement */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressWaitWriteCompletion = ctx->writeCompletion; - DEBUG(2, "compression thread waiting for write: %u, compressWaitWriteCompletion %f\n", currJob, ctx->compressWaitWriteCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); @@ -488,7 +504,7 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(2, "compression completion %f\n", ctx->compressionCompletion); + DEBUG(2, "compression completion %u %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } while (remaining != 0); @@ -579,7 +595,7 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; - DEBUG(2, "write completion %f\n", ctx->writeCompletion); + DEBUG(2, "write completion %u %f\n", currJob, ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); if (remaining == 0) break; @@ -721,7 +737,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA remaining -= ret; pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - DEBUG(2, "create completion: %f\n", ctx->createCompletion); + DEBUG(2, "create completion %u %f\n", currJob, ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { From 483d936b8760819f8f6b71c2eb0c635165293086 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Sun, 23 Jul 2017 14:09:16 -0700 Subject: [PATCH 213/318] reduced competition for completion mutex by separating mutex use based on which values is updated --- contrib/adaptive-compression/adapt.c | 93 ++++++++++++++++------------ 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 248421457..4e5a86c72 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -92,8 +92,9 @@ typedef struct { cond_t allJobsCompleted_cond; mutex_t jobWrite_mutex; cond_t jobWrite_cond; - mutex_t completion_mutex; - mutex_t wait_mutex; + mutex_t compressionCompletion_mutex; + mutex_t createCompletion_mutex; + mutex_t writeCompletion_mutex; size_t lastDictSize; inBuff_t input; jobDescription* jobs; @@ -152,8 +153,9 @@ static int freeCCtx(adaptCCtx* ctx) error |= destroyCond(&ctx->allJobsCompleted_cond); error |= destroyMutex(&ctx->jobWrite_mutex); error |= destroyCond(&ctx->jobWrite_cond); - error |= destroyMutex(&ctx->completion_mutex); - error |= destroyMutex(&ctx->wait_mutex); + error |= destroyMutex(&ctx->compressionCompletion_mutex); + error |= destroyMutex(&ctx->createCompletion_mutex); + error |= destroyMutex(&ctx->writeCompletion_mutex); error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); free(ctx->input.buffer.start); if (ctx->jobs){ @@ -192,8 +194,9 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) pthreadError |= initCond(&ctx->allJobsCompleted_cond); pthreadError |= initMutex(&ctx->jobWrite_mutex); pthreadError |= initCond(&ctx->jobWrite_cond); - pthreadError |= initMutex(&ctx->completion_mutex); - pthreadError |= initMutex(&ctx->wait_mutex); + pthreadError |= initMutex(&ctx->compressionCompletion_mutex); + pthreadError |= initMutex(&ctx->createCompletion_mutex); + pthreadError |= initMutex(&ctx->writeCompletion_mutex); if (pthreadError) return pthreadError; } ctx->numJobs = numJobs; @@ -323,28 +326,32 @@ static void adaptCompressionLevel(adaptCCtx* ctx) DEBUG(2, "adapting compression level %u\n", ctx->compressionLevel); /* read and reset completion measurements */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); - DEBUG(2, "cr %f\n", ctx->compressWaitCreateCompletion); - DEBUG(2, "cw %f\n", ctx->compressWaitWriteCompletion); DEBUG(2, "wc %f\n\n", ctx->writeWaitCompressionCompletion); - createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; - compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; - compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; - - DEBUG(2, "resetting adaptive variables\n"); ctx->createWaitCompressionCompletion = 1; - ctx->compressWaitCreateCompletion = 1; - ctx->compressWaitWriteCompletion = 1; ctx->writeWaitCompressionCompletion = 1; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); + + pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); + DEBUG(2, "cw %f\n", ctx->compressWaitWriteCompletion); + compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; + ctx->compressWaitWriteCompletion = 1; + pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); + + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); + DEBUG(2, "cr %f\n", ctx->compressWaitCreateCompletion); + compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; + ctx->compressWaitCreateCompletion = 1; + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); /* adaptation logic */ - if (1-createWaitCompressionCompletion > threshold && 1-writeWaitCompressionCompletion > threshold) { - /* both create and write threads waiting on compression */ - /* use writeWaitCompressionCompletion */ + if (1-createWaitCompressionCompletion > threshold || 1-writeWaitCompressionCompletion > threshold) { + /* compression waiting on either create or write */ + /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); @@ -404,15 +411,21 @@ static void* compressionThread(void* arg) if (currJob - ctx->jobWriteID >= ctx->numJobs) willWaitForWrite = 1; pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + if (willWaitForCreate || willWaitForWrite) { - ctx->compressWaitCreateCompletion = ctx->createCompletion; - ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(2, "compression will wait for create or write\n"); + + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); + ctx->compressWaitCreateCompletion = ctx->createCompletion; DEBUG(2, "create completion %f\n", ctx->compressWaitCreateCompletion); + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); + + pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); + ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(2, "write completion %f\n", ctx->compressWaitWriteCompletion); + pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); } - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + } /* wait until job is ready */ @@ -429,9 +442,9 @@ static void* compressionThread(void* arg) } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); /* reset compression completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); ctx->compressionCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); DEBUG(3, "compressionThread(): continuing after job ready\n"); DEBUG(3, "DICTIONARY ENDED\n"); @@ -502,10 +515,10 @@ static void* compressionThread(void* arg) blockNum++; /* update completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; DEBUG(2, "compression completion %u %f\n", currJob, ctx->compressionCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); } } while (remaining != 0); job->dst.size = job->compressedSize; @@ -557,20 +570,20 @@ static void* outputThread(void* arg) DEBUG(3, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); /* write thread is waiting on compression thread */ ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; DEBUG(3, "write thread waiting : writeWaitCompressionCompletion %f\n", ctx->writeWaitCompressionCompletion); DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); /* reset write completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->writeCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); DEBUG(3, "outputThread(): continuing after job compressed\n"); { @@ -593,10 +606,10 @@ static void* outputThread(void* arg) remaining -= ret; /* update completion variable for writing */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; DEBUG(2, "write completion %u %f\n", currJob, ctx->writeCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); if (remaining == 0) break; } @@ -642,20 +655,20 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) /* wait until the job has been compressed */ pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (nextJob - ctx->jobCompressedID >= ctx->numJobs && !ctx->threadError) { - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); /* creation thread is waiting, take measurement of completion */ ctx->createWaitCompressionCompletion = ctx->compressionCompletion; DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f\n", ctx->createWaitCompressionCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); /* reset create completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); DEBUG(3, "createCompressionJob(): continuing after job write\n"); DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); @@ -735,10 +748,10 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA } pos += ret; remaining -= ret; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); DEBUG(2, "create completion %u %f\n", currJob, ctx->createCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); From 273c17b350ac329d2db72f83ded00ba5477ec084 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 20 Jul 2017 16:50:06 -0700 Subject: [PATCH 214/318] Experiment with 64-bit hash and checksum --- contrib/long_distance_matching/Makefile | 8 +- .../circular_buffer_table.c | 7 +- contrib/long_distance_matching/ldm.c | 65 +- contrib/long_distance_matching/ldm.h | 3 +- .../long_distance_matching/ldm_hashtable.h | 2 + contrib/long_distance_matching/ldm_hf_test.c | 1048 +++++++++++++++++ .../long_distance_matching/ldm_with_table.c | 26 +- contrib/long_distance_matching/main-ldm.c | 12 +- 8 files changed, 1096 insertions(+), 75 deletions(-) create mode 100644 contrib/long_distance_matching/ldm_hf_test.c diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 3aa3f8bd9..5119f464d 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -25,7 +25,7 @@ LDFLAGS += -lzstd default: all -all: main-circular-buffer main-integrated +all: main-circular-buffer main-integrated main-hf #main-basic : basic_table.c ldm.c main-ldm.c # $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ @@ -33,12 +33,14 @@ all: main-circular-buffer main-integrated main-circular-buffer: circular_buffer_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ +main-hf: ldm_hf_test.c main-ldm.c + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + main-integrated: ldm_with_table.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main-basic main-circular-buffer main-integrated + main-basic main-circular-buffer main-integrated main-hf @echo Cleaning completed diff --git a/contrib/long_distance_matching/circular_buffer_table.c b/contrib/long_distance_matching/circular_buffer_table.c index ad7ae9e10..66107e069 100644 --- a/contrib/long_distance_matching/circular_buffer_table.c +++ b/contrib/long_distance_matching/circular_buffer_table.c @@ -5,14 +5,16 @@ #include "ldm_hashtable.h" #include "mem.h" - // Number of elements per hash bucket. // HASH_BUCKET_SIZE_LOG defined in ldm.h #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) +#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) + + // TODO: rename. Number of hash buckets. // TODO: Link to HASH_ENTRY_SIZE_LOG -#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-3-(HASH_BUCKET_SIZE_LOG)) + //#define ZSTD_SKIP struct LDM_hashTable { @@ -175,6 +177,7 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_hashTable *table, if (cur->checksum == checksum && pIn - pMatch <= table->maxWindowSize) { U32 forwardMatchLength = ZSTD_count(pIn, pMatch, pEnd); U32 backwardMatchLength, totalMatchLength; + if (forwardMatchLength < table->minMatchLength) { continue; } diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 9ffbab48d..ab2de7c1e 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -4,14 +4,15 @@ #include #include +#include "ldm.h" +#include "ldm_hashtable.h" #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASH_ENTRY_SIZE_LOG 3 #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) #define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) // Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE) - (LDM_HASH_ENTRY_SIZE_LOG))) +#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG))) #define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) #define ML_BITS 4 @@ -26,8 +27,7 @@ //#define RUN_CHECKS //#define TMP_RECOMPUTE_LENGTHS -#include "ldm.h" -#include "ldm_hashtable.h" +typedef U32 checksum_t; // TODO: Scanning speed // TODO: Memory usage @@ -71,22 +71,22 @@ struct LDM_CCtx { LDM_hashTable *hashTable; -// LDM_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; - const BYTE *lastPosHashed; /* Last position hashed */ hash_t lastHash; /* Hash corresponding to lastPosHashed */ - U32 lastSum; + checksum_t lastSum; const BYTE *nextIp; // TODO: this is redundant (ip + step) const BYTE *nextPosHashed; hash_t nextHash; /* Hash corresponding to nextPosHashed */ - U32 nextSum; + checksum_t nextSum; + + unsigned step; // ip step, should be 1. const BYTE *lagIp; hash_t lagHash; - U32 lagSum; + checksum_t lagSum; U64 numHashInserts; // DEBUG @@ -191,15 +191,15 @@ static hash_t checksumToHash(U32 sum) { } /** - * Computes a checksum based on rsync's checksum. + * Computes a 32-bit checksum based on rsync's checksum. * * a(k,l) = \sum_{i = k}^l x_i (mod M) * b(k,l) = \sum_{i = k}^l ((l - i + 1) * x_i) (mod M) * checksum(k,l) = a(k,l) + 2^{16} * b(k,l) */ -static U32 getChecksum(const BYTE *buf, U32 len) { +static checksum_t getChecksum(const BYTE *buf, U32 len) { U32 i; - U32 s1, s2; + checksum_t s1, s2; s1 = s2 = 0; for (i = 0; i < (len - 4); i += 4) { @@ -226,8 +226,8 @@ static U32 getChecksum(const BYTE *buf, U32 len) { * * Thus toRemove should correspond to data[0]. */ -static U32 updateChecksum(U32 sum, U32 len, - BYTE toRemove, BYTE toAdd) { +static checksum_t updateChecksum(checksum_t sum, U32 len, + BYTE toRemove, BYTE toAdd) { U32 s1 = (sum & 0xffff) - toRemove + toAdd; U32 s2 = (sum >> 16) - ((toRemove + CHECKSUM_CHAR_OFFSET) * len) + s1; @@ -262,7 +262,6 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->nextHash = checksumToHash(cctx->nextSum); #if LDM_LAG -// printf("LDM_LAG %zu\n", cctx->ip - cctx->lagIp); if (cctx->ip - cctx->ibase > LDM_LAG) { cctx->lagSum = updateChecksum( cctx->lagSum, LDM_HASH_LENGTH, @@ -288,32 +287,28 @@ static void setNextHash(LDM_CCtx *cctx) { } static void putHashOfCurrentPositionFromHash( - LDM_CCtx *cctx, hash_t hash, U32 sum) { + LDM_CCtx *cctx, hash_t hash, U32 checksum) { // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. // Note: this works only when cctx->step is 1. if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { - /** - const LDM_hashEntry entry = { cctx->ip - cctx->ibase , - MEM_read32(cctx->ip) }; - */ #if LDM_LAG // TODO: off by 1, but whatever if (cctx->lagIp - cctx->ibase > 0) { const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, cctx->lagSum }; HASH_insert(cctx->hashTable, cctx->lagHash, entry); } else { - const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; + const LDM_hashEntry entry = { cctx->ip - cctx->ibase, checksum }; HASH_insert(cctx->hashTable, hash, entry); } #else - const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; + const LDM_hashEntry entry = { cctx->ip - cctx->ibase, checksum }; HASH_insert(cctx->hashTable, hash, entry); #endif } cctx->lastPosHashed = cctx->ip; cctx->lastHash = hash; - cctx->lastSum = sum; + cctx->lastSum = checksum; } /** @@ -336,7 +331,7 @@ static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { * Insert hash of the current position into the hash table. */ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - U32 sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); + checksum_t sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); hash_t hash = checksumToHash(sum); #ifdef RUN_CHECKS @@ -441,7 +436,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, while (entry == NULL) { hash_t h; - U32 sum; + checksum_t sum; setNextHash(cctx); h = cctx->nextHash; sum = cctx->nextSum; @@ -698,23 +693,7 @@ size_t LDM_decompress(const void *src, size_t compressedSize, } // TODO: implement and test hash function -void LDM_test(void) { +void LDM_test(const BYTE *src) { + (void)src; } -/* -void LDM_test(const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { - const BYTE *ip = (const BYTE *)src + 1125; - U32 sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); - U32 sum2; - ++ip; - for (; ip < (const BYTE *)src + 1125 + 100; ip++) { - sum2 = updateChecksum(sum, LDM_HASH_LENGTH, - ip[-1], ip[LDM_HASH_LENGTH - 1]); - sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); - printf("TEST HASH: %zu %u %u\n", ip - (const BYTE *)src, sum, sum2); - } -} -*/ - - diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 04b6410c2..c420e60c7 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -31,6 +31,7 @@ typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; typedef struct LDM_DCtx LDM_DCtx; + /** * Compresses src into dst. * @@ -151,6 +152,6 @@ void LDM_readHeader(const void *src, U64 *compressedSize, void LDM_outputConfiguration(void); -void LDM_test(void); +void LDM_test(const BYTE *src); #endif /* LDM_H */ diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h index df9dcd789..9d5ba0e27 100644 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -3,6 +3,8 @@ #include "mem.h" +#define LDM_HASH_ENTRY_SIZE_LOG 3 + // TODO: clean up comments typedef U32 hash_t; diff --git a/contrib/long_distance_matching/ldm_hf_test.c b/contrib/long_distance_matching/ldm_hf_test.c new file mode 100644 index 000000000..63be82d15 --- /dev/null +++ b/contrib/long_distance_matching/ldm_hf_test.c @@ -0,0 +1,1048 @@ +#include +#include +#include +#include +#include + +#include "ldm.h" + +#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASH_ENTRY_SIZE_LOG 3 +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) +#define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) + +// Insert every (HASH_ONLY_EVERY + 1) into the hash table. +#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE) - (LDM_HASH_ENTRY_SIZE_LOG))) +#define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) + +/* Hash table stuff. */ +#define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) +#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) + +#define ML_BITS 4 +#define ML_MASK ((1U<> HASH_BUCKET_SIZE_LOG. + */ +LDM_hashTable *HASH_createTable(U32 size) { + LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); + 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)); + 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 (register 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; + const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ + 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 > pBaes and pIn > pAnchor. + */ +U32 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, + const BYTE *pMatch, const BYTE *pBase) { + U32 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. + * + */ +LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, + const hash_t hash, + const U32 checksum, + U32 *pForwardMatchLength, + U32 *pBackwardMatchLength) { + LDM_hashTable *table = cctx->hashTable; + LDM_hashEntry *bucket = getBucket(table, hash); + LDM_hashEntry *cur = bucket; + LDM_hashEntry *bestEntry = NULL; + U32 bestMatchLength = 0; + for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { + const BYTE *pMatch = cur->offset + cctx->ibase; + + // Check checksum for faster check. + if (cur->checksum == checksum && + cctx->ip - pMatch <= LDM_WINDOW_SIZE) { + U32 forwardMatchLength = ZSTD_count(cctx->ip, pMatch, cctx->iend); + U32 backwardMatchLength, totalMatchLength; + + // 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; +} + +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; +} + +U32 HASH_getSize(const LDM_hashTable *table) { + return table->numBuckets; +} + +void HASH_destroyTable(LDM_hashTable *table) { + free(table->entries); + free(table->bucketOffsets); + free(table); +} + +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++; + } + } + + printf("Num buckets, bucket size: %d, %d\n", + table->numBuckets, 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 (but it is not that important as it +// is only used for computing stats). +static int intLog2(U32 x) { + int ret = 0; + while (x >>= 1) { + ret++; + } + return ret; +} + +// Maybe we would eventually prefer to have linear rather than +// exponential buckets. +/** +void HASH_outputTableOffsetHistogram(const LDM_CCtx *cctx) { + U32 i = 0; + int buckets[32] = { 0 }; + + printf("\n"); + printf("Hash table histogram\n"); + for (; i < HASH_getSize(cctx->hashTable); i++) { + int offset = (cctx->ip - cctx->ibase) - + HASH_getEntryFromHash(cctx->hashTable, i)->offset; + buckets[intLog2(offset)]++; + } + + i = 0; + for (; i < 32; i++) { + printf("2^%*d: %10u %6.3f%%\n", 2, i, + buckets[i], + 100.0 * (double) buckets[i] / + (double) HASH_getSize(cctx->hashTable)); + } + printf("\n"); +} +*/ + +void LDM_printCompressStats(const LDM_compressStats *stats) { + int i = 0; + 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: offset, num matches, %% of matches\n"); + + for (; i <= intLog2(stats->maxOffset); i++) { + printf("2^%*d: %10u %6.3f%%\n", 2, i, + stats->offsetHistogram[i], + 100.0 * (double) stats->offsetHistogram[i] / + (double) stats->numMatches); + } + printf("\n"); + printf("=====================\n"); +} + +int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { + U32 lengthLeft = LDM_MIN_MATCH_LENGTH; + const BYTE *curIn = pIn; + const BYTE *curMatch = pMatch; + + if (pIn - pMatch > LDM_WINDOW_SIZE) { + return 0; + } + + for (; lengthLeft >= 4; lengthLeft -= 4) { + if (MEM_read32(curIn) != MEM_read32(curMatch)) { + return 0; + } + curIn += 4; + curMatch += 4; + } + return 1; +} + +#if 0 +hash_t HASH_hashU32(U32 value) { + return ((value * 2654435761U) >> (32 - LDM_HASHLOG)); +} +#endif + +/** + * Convert a sum computed from getChecksum to a hash value in the range + * of the hash table. + */ +#if 0 +static hash_t checksumToHash(U32 sum) { + return HASH_hashU32(sum); +} +#endif + +// Upper LDM_HASH_LOG bits. +static hash_t checksumToHash(U64 sum) { + return sum >> (64 - LDM_HASHLOG); +} + +// 32 bits after LDM_HASH_LOG bits. +static U32 checksumFromHfHash(U64 hfHash) { + return (hfHash >> (64 - 32 - LDM_HASHLOG)) & 0xFFFFFFFF; +} + +#if 0 +/** + * Computes a checksum based on rsync's checksum. + * + * a(k,l) = \sum_{i = k}^l x_i (mod M) + * b(k,l) = \sum_{i = k}^l ((l - i + 1) * x_i) (mod M) + * checksum(k,l) = a(k,l) + 2^{16} * b(k,l) + */ +static U32 getChecksum(const BYTE *buf, U32 len) { + U32 i; + U32 s1, s2; + + s1 = s2 = 0; + for (i = 0; i < (len - 4); i += 4) { + s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + + (2 * buf[i + 2]) + (buf[i + 3]) + + (10 * CHECKSUM_CHAR_OFFSET); + s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3] + + + (4 * CHECKSUM_CHAR_OFFSET); + + } + for(; i < len; i++) { + s1 += buf[i] + CHECKSUM_CHAR_OFFSET; + s2 += s1; + } + return (s1 & 0xffff) + (s2 << 16); +} +#endif + +static U64 getChecksum(const BYTE *buf, U32 len) { + static const U64 prime8bytes = 11400714785074694791ULL; + +// static const U64 prime8bytes = 5; + U64 ret = 0; + U32 i; + for (i = 0; i < len; i++) { + ret *= prime8bytes; + ret += buf[i] + CHECKSUM_CHAR_OFFSET; +// printf("HERE %llu\n", ret); + } + return ret; + +} + +#if 0 +/** + * Update a checksum computed from getChecksum(data, len). + * + * The checksum can be updated along its ends as follows: + * a(k+1, l+1) = (a(k,l) - x_k + x_{l+1}) (mod M) + * b(k+1, l+1) = (b(k,l) - (l-k+1)*x_k + (a(k+1,l+1)) (mod M) + * + * Thus toRemove should correspond to data[0]. + */ +static U32 updateChecksum(U32 sum, U32 len, + BYTE toRemove, BYTE toAdd) { + U32 s1 = (sum & 0xffff) - toRemove + toAdd; + U32 s2 = (sum >> 16) - ((toRemove + CHECKSUM_CHAR_OFFSET) * len) + s1; + + return (s1 & 0xffff) + (s2 << 16); +} +#endif + +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 updateChecksum(U64 sum, U32 len, + BYTE toRemove, BYTE toAdd) { + // TODO: deduplicate. + static const U64 prime8bytes = 11400714785074694791ULL; +// static const U64 prime8bytes = 5; + sum -= ((toRemove + CHECKSUM_CHAR_OFFSET) * + ipow(prime8bytes, len - 1)); + sum *= prime8bytes; + sum += toAdd + CHECKSUM_CHAR_OFFSET; + return sum; +} + +/** + * Update cctx->nextSum, cctx->nextHash, and cctx->nextPosHashed + * based on cctx->lastSum 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) { +#ifdef RUN_CHECKS + U32 check; + if ((cctx->nextIp - cctx->ibase != 1) && + (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { + printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, + cctx->DEBUG_setNextHash - cctx->ibase); + } + + cctx->DEBUG_setNextHash = cctx->nextIp; +#endif + +// cctx->nextSum = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); + cctx->nextSum = updateChecksum( + cctx->lastSum, LDM_HASH_LENGTH, + cctx->lastPosHashed[0], + cctx->lastPosHashed[LDM_HASH_LENGTH]); + cctx->nextPosHashed = cctx->nextIp; +#if 0 + cctx->nextHash = checksumToHash(cctx->nextSum); +#endif + + +#if LDM_LAG +// printf("LDM_LAG %zu\n", cctx->ip - cctx->lagIp); + if (cctx->ip - cctx->ibase > LDM_LAG) { + cctx->lagSum = updateChecksum( + cctx->lagSum, LDM_HASH_LENGTH, + cctx->lagIp[0], cctx->lagIp[LDM_HASH_LENGTH]); + cctx->lagIp++; +#if 0 + cctx->lagHash = checksumToHash(cctx->lagSum); +#endif + } +#endif + +#ifdef RUN_CHECKS + check = getChecksum(cctx->nextIp, LDM_HASH_LENGTH); + + if (check != cctx->nextSum) { + printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); + } + + if ((cctx->nextIp - cctx->lastPosHashed) != 1) { + printf("setNextHash: nextIp != lastPosHashed + 1. %zu %zu %zu\n", + cctx->nextIp - cctx->ibase, cctx->lastPosHashed - cctx->ibase, + cctx->ip - cctx->ibase); + } +#endif +} + +static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hfHash) { + // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. + // Note: this works only when cctx->step is 1. + U32 hash = checksumToHash(hfHash); + U32 sum = checksumFromHfHash(hfHash); +// printf("TMP %u %u %llu\n", hash, sum, hfHash); + + if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { + +#if LDM_LAG + // TODO: off by 1, but whatever + if (cctx->lagIp - cctx->ibase > 0) { + const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, cctx->lagSum }; + HASH_insert(cctx->hashTable, cctx->lagHash, entry); + } else { + const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; + HASH_insert(cctx->hashTable, hash, entry); + } +#else + const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; + HASH_insert(cctx->hashTable, hash, entry); +#endif + } + + cctx->lastPosHashed = cctx->ip; +#if 0 + cctx->lastHash = hash; +#endif + cctx->lastSum = hfHash; +} + +/** + * Copy over the cctx->lastHash, cctx->lastSum, and cctx->lastPosHashed + * fields from the "next" fields. + * + * This requires that cctx->ip == cctx->nextPosHashed. + */ +static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { +#ifdef RUN_CHECKS + if (cctx->ip != cctx->nextPosHashed) { + printf("CHECK failed: updateLastHashFromNextHash %zu\n", + cctx->ip - cctx->ibase); + } +#endif +#if 0 + putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); +#endif + putHashOfCurrentPositionFromHash(cctx, cctx->nextSum); +} + +/** + * Insert hash of the current position into the hash table. + */ +static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { + U32 sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); +#if 0 + hash_t hash = checksumToHash(sum); +#endif + +#ifdef RUN_CHECKS + if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { + printf("CHECK failed: putHashOfCurrentPosition %zu\n", + cctx->ip - cctx->ibase); + } +#endif +#if 0 + putHashOfCurrentPositionFromHash(cctx, hash, sum); +#endif + putHashOfCurrentPositionFromHash(cctx, sum); +} + +U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, + const BYTE *pInLimit) { + const BYTE * const pStart = pIn; + while (pIn < pInLimit - 1) { + BYTE const diff = (*pMatch) ^ *(pIn); + if (!diff) { + pIn++; + pMatch++; + continue; + } + return (U32)(pIn - pStart); + } + return (U32)(pIn - pStart); +} + +void LDM_outputConfiguration(void) { + printf("=====================\n"); + printf("Configuration\n"); + printf("Window size log: %d\n", LDM_WINDOW_SIZE_LOG); + printf("Min match, 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("LDM_LAG %d\n", LDM_LAG); + printf("=====================\n"); +} + +void LDM_readHeader(const void *src, U64 *compressedSize, + U64 *decompressedSize) { + const BYTE *ip = (const BYTE *)src; + *compressedSize = MEM_readLE64(ip); + ip += sizeof(U64); + *decompressedSize = MEM_readLE64(ip); + // ip += sizeof(U64); +} + +void 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)); + cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U64); + + 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; + + cctx->DEBUG_setNextHash = 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, + U32 *forwardMatchLength, U32 *backwardMatchLength) { + + LDM_hashEntry *entry = NULL; + cctx->nextIp = cctx->ip + cctx->step; + + while (entry == NULL) { + hash_t h; + U64 hash; + U32 sum; + setNextHash(cctx); +#if 0 + h = cctx->nextHash; + sum = cctx->nextSum; +#endif + hash = cctx->nextSum; + h = checksumToHash(hash); + sum = checksumFromHfHash(hash); + + cctx->ip = cctx->nextIp; + cctx->nextIp += cctx->step; + + if (cctx->ip > cctx->imatchLimit) { + return 1; + } + + entry = HASH_getBestEntry(cctx, h, sum, + forwardMatchLength, backwardMatchLength); + + if (entry != NULL) { + *match = entry->offset + cctx->ibase; + } + putHashOfCurrentPositionFromHash(cctx, hash); + } + setNextHash(cctx); + return 0; +} + +void LDM_encodeLiteralLengthAndLiterals( + LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength) { + /* Encode the literal length. */ + if (literalLength >= RUN_MASK) { + int len = (int)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 U32 literalLength, + const U32 offset, + const U32 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) { + unsigned 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; + U32 forwardMatchLength = 0; + U32 backwardsMatchLength = 0; + + LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); + LDM_outputConfiguration(); + + /* Hash the first position and put it into the hash table. */ + LDM_putHashOfCurrentPosition(&cctx); + +#if LDM_LAG + cctx.lagIp = cctx.ip; +// cctx.lagHash = cctx.lastHash; + cctx.lagSum = cctx.lastSum; +#endif + /** + * 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) == 0) { +#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 U32 literalLength = cctx.ip - cctx.anchor; + const U32 offset = cctx.ip - match; + const U32 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)]++; +#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); + } + + // HASH_outputTableOffsetHistogram(&cctx); + + /* Encode the last literals (no more matches). */ + { + const U32 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; + } +} + +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 + 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; + + // Inefficient for now. + while (match < cpy - offset && dctx.op < dctx.oend) { + *(dctx.op)++ = *match++; + } + } + return dctx.op - (BYTE *)dst; +} + +// TODO: implement and test hash function +void LDM_test(const BYTE *src) { + const U32 diff = 100; + const BYTE *pCur = src + diff; + U64 checksum = getChecksum(pCur, LDM_HASH_LENGTH); + + for (; pCur < src + diff + 60; ++pCur) { + U64 nextSum = getChecksum(pCur + 1, LDM_HASH_LENGTH); + U64 updateSum = updateChecksum(checksum, LDM_HASH_LENGTH, + pCur[0], pCur[LDM_HASH_LENGTH]); + checksum = nextSum; + printf("%llu %llu\n", nextSum, updateSum); + } +} + + diff --git a/contrib/long_distance_matching/ldm_with_table.c b/contrib/long_distance_matching/ldm_with_table.c index 813ead6ae..c727616af 100644 --- a/contrib/long_distance_matching/ldm_with_table.c +++ b/contrib/long_distance_matching/ldm_with_table.c @@ -29,7 +29,7 @@ #define CHECKSUM_CHAR_OFFSET 10 // Take first match only. -#define ZSTD_SKIP +//#define ZSTD_SKIP //#define RUN_CHECKS @@ -292,8 +292,7 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, totalMatchLength = forwardMatchLength + backwardMatchLength; - if (totalMatchLength >= bestMatchLength && - totalMatchLength >= LDM_MIN_MATCH_LENGTH) { + if (totalMatchLength >= bestMatchLength) { bestMatchLength = totalMatchLength; *pForwardMatchLength = forwardMatchLength; *pBackwardMatchLength = backwardMatchLength; @@ -305,7 +304,7 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, } } } - if (bestEntry != NULL && bestMatchLength > LDM_MIN_MATCH_LENGTH) { + if (bestEntry != NULL) { return bestEntry; } return NULL; @@ -951,23 +950,8 @@ size_t LDM_decompress(const void *src, size_t compressedSize, } // TODO: implement and test hash function -void LDM_test(void) { +void LDM_test(const BYTE *src) { + (void)src; } -/* -void LDM_test(const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { - const BYTE *ip = (const BYTE *)src + 1125; - U32 sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); - U32 sum2; - ++ip; - for (; ip < (const BYTE *)src + 1125 + 100; ip++) { - sum2 = updateChecksum(sum, LDM_HASH_LENGTH, - ip[-1], ip[LDM_HASH_LENGTH - 1]); - sum = getChecksum((const char *)ip, LDM_HASH_LENGTH); - printf("TEST HASH: %zu %u %u\n", ip - (const BYTE *)src, sum, sum2); - } -} -*/ - diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 96db0c220..3582d5a21 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -13,13 +13,13 @@ #include "zstd.h" #define DEBUG -#define TEST +//#define TEST /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. * * TODO: 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. + * 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) { @@ -69,6 +69,11 @@ static int compress(const char *fname, const char *oname) { perror("mmap error for output"); return 1; } + +#ifdef TEST + LDM_test((const BYTE *)src); +#endif + gettimeofday(&tv1, NULL); compressedSize = LDM_HEADER_SIZE + @@ -251,8 +256,5 @@ int main(int argc, const char *argv[]) { /* verify */ verify(inpFilename, decFilename); -#ifdef TEST - LDM_test(); -#endif return 0; } From 0b8fb1703b39ae1f07aea1eaac46b97372d9238d Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 20 Jul 2017 16:51:01 -0700 Subject: [PATCH 215/318] Experiment with 64-bit hash insertion policy --- contrib/long_distance_matching/Makefile | 6 +-- .../circular_buffer_table.c | 10 ++--- contrib/long_distance_matching/ldm.c | 10 ++--- contrib/long_distance_matching/ldm.h | 8 ++-- .../{ldm_hf_test.c => ldm_64_hash.c} | 38 ++++++++++--------- .../long_distance_matching/ldm_hashtable.h | 4 +- .../long_distance_matching/ldm_with_table.c | 12 +++--- contrib/long_distance_matching/main-ldm.c | 7 ++-- 8 files changed, 48 insertions(+), 47 deletions(-) rename contrib/long_distance_matching/{ldm_hf_test.c => ldm_64_hash.c} (97%) diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 5119f464d..9dc33fae7 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -25,7 +25,7 @@ LDFLAGS += -lzstd default: all -all: main-circular-buffer main-integrated main-hf +all: main-circular-buffer main-integrated main-64 #main-basic : basic_table.c ldm.c main-ldm.c # $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ @@ -33,7 +33,7 @@ all: main-circular-buffer main-integrated main-hf main-circular-buffer: circular_buffer_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ -main-hf: ldm_hf_test.c main-ldm.c +main-64: ldm_64_hash.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ main-integrated: ldm_with_table.c main-ldm.c @@ -41,6 +41,6 @@ main-integrated: ldm_with_table.c main-ldm.c clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main-basic main-circular-buffer main-integrated main-hf + main-basic main-circular-buffer main-integrated main-64 @echo Cleaning completed diff --git a/contrib/long_distance_matching/circular_buffer_table.c b/contrib/long_distance_matching/circular_buffer_table.c index 66107e069..fb6c19d2a 100644 --- a/contrib/long_distance_matching/circular_buffer_table.c +++ b/contrib/long_distance_matching/circular_buffer_table.c @@ -164,19 +164,19 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_hashTable *table, const BYTE *pIn, const BYTE *pEnd, const BYTE *pAnchor, - U32 *pForwardMatchLength, - U32 *pBackwardMatchLength) { + U64 *pForwardMatchLength, + U64 *pBackwardMatchLength) { LDM_hashEntry *bucket = getBucket(table, hash); LDM_hashEntry *cur = bucket; LDM_hashEntry *bestEntry = NULL; - U32 bestMatchLength = 0; + U64 bestMatchLength = 0; for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { const BYTE *pMatch = cur->offset + table->offsetBase; // Check checksum for faster check. if (cur->checksum == checksum && pIn - pMatch <= table->maxWindowSize) { - U32 forwardMatchLength = ZSTD_count(pIn, pMatch, pEnd); - U32 backwardMatchLength, totalMatchLength; + U64 forwardMatchLength = ZSTD_count(pIn, pMatch, pEnd); + U64 backwardMatchLength, totalMatchLength; if (forwardMatchLength < table->minMatchLength) { continue; diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index ab2de7c1e..b018c4755 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -429,7 +429,7 @@ void LDM_destroyCCtx(LDM_CCtx *cctx) { * matchLength contains the forward length of the match. */ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, - U32 *matchLength, U32 *backwardMatchLength) { + U64 *matchLength, U64 *backwardMatchLength) { LDM_hashEntry *entry = NULL; cctx->nextIp = cctx->ip + cctx->step; @@ -462,7 +462,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, } void LDM_encodeLiteralLengthAndLiterals( - LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength) { + LDM_CCtx *cctx, BYTE *pToken, const U64 literalLength) { /* Encode the literal length. */ if (literalLength >= RUN_MASK) { int len = (int)literalLength - RUN_MASK; @@ -481,9 +481,9 @@ void LDM_encodeLiteralLengthAndLiterals( } void LDM_outputBlock(LDM_CCtx *cctx, - const U32 literalLength, + const U64 literalLength, const U32 offset, - const U32 matchLength) { + const U64 matchLength) { BYTE *pToken = cctx->op++; /* Encode the literal length and literals. */ @@ -495,7 +495,7 @@ void LDM_outputBlock(LDM_CCtx *cctx, /* Encode the match length. */ if (matchLength >= ML_MASK) { - unsigned matchLengthRemaining = matchLength; + U64 matchLengthRemaining = matchLength; *pToken += ML_MASK; matchLengthRemaining -= ML_MASK; MEM_write32(cctx->op, 0xFFFFFFFF); diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index c420e60c7..83cd36230 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -14,7 +14,7 @@ // Note that this is not the number of buckets. // Currently this should be less than WINDOW_SIZE_LOG + 4? #define LDM_MEMORY_USAGE 22 -#define HASH_BUCKET_SIZE_LOG 3 // MAX is 4 for now +#define HASH_BUCKET_SIZE_LOG 1 // MAX is 4 for now // Defines the lag in inserting elements into the hash table. #define LDM_LAG 0 @@ -115,16 +115,16 @@ U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, * This is followed by literalLength bytes corresponding to the literals. */ void LDM_encodeLiteralLengthAndLiterals( - LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength); + LDM_CCtx *cctx, BYTE *pToken, const U64 literalLength); /** * Write current block (literals, literal length, match offset, * match length). */ void LDM_outputBlock(LDM_CCtx *cctx, - const U32 literalLength, + const U64 literalLength, const U32 offset, - const U32 matchLength); + const U64 matchLength); /** * Decompresses src into dst. diff --git a/contrib/long_distance_matching/ldm_hf_test.c b/contrib/long_distance_matching/ldm_64_hash.c similarity index 97% rename from contrib/long_distance_matching/ldm_hf_test.c rename to contrib/long_distance_matching/ldm_64_hash.c index 63be82d15..a72c283f2 100644 --- a/contrib/long_distance_matching/ldm_hf_test.c +++ b/contrib/long_distance_matching/ldm_64_hash.c @@ -241,9 +241,9 @@ static size_t ZSTD_count(const BYTE *pIn, const BYTE *pMatch, * * We count only bytes where pMatch > pBaes and pIn > pAnchor. */ -U32 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, +U64 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, const BYTE *pMatch, const BYTE *pBase) { - U32 matchLength = 0; + U64 matchLength = 0; while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) { pIn--; pMatch--; @@ -267,8 +267,8 @@ U32 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, const hash_t hash, const U32 checksum, - U32 *pForwardMatchLength, - U32 *pBackwardMatchLength) { + U64 *pForwardMatchLength, + U64 *pBackwardMatchLength) { LDM_hashTable *table = cctx->hashTable; LDM_hashEntry *bucket = getBucket(table, hash); LDM_hashEntry *cur = bucket; @@ -541,7 +541,7 @@ static U64 updateChecksum(U64 sum, U32 len, BYTE toRemove, BYTE toAdd) { // TODO: deduplicate. static const U64 prime8bytes = 11400714785074694791ULL; -// static const U64 prime8bytes = 5; + sum -= ((toRemove + CHECKSUM_CHAR_OFFSET) * ipow(prime8bytes, len - 1)); sum *= prime8bytes; @@ -696,11 +696,12 @@ U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, void LDM_outputConfiguration(void) { printf("=====================\n"); printf("Configuration\n"); - printf("Window size log: %d\n", LDM_WINDOW_SIZE_LOG); - printf("Min match, hash length: %d, %d\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("=====================\n"); } @@ -762,7 +763,7 @@ void LDM_destroyCCtx(LDM_CCtx *cctx) { * forwardMatchLength contains the forward length of the match. */ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, - U32 *forwardMatchLength, U32 *backwardMatchLength) { + U64 *forwardMatchLength, U64 *backwardMatchLength) { LDM_hashEntry *entry = NULL; cctx->nextIp = cctx->ip + cctx->step; @@ -800,10 +801,10 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, } void LDM_encodeLiteralLengthAndLiterals( - LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength) { + LDM_CCtx *cctx, BYTE *pToken, const U64 literalLength) { /* Encode the literal length. */ if (literalLength >= RUN_MASK) { - int len = (int)literalLength - RUN_MASK; + U64 len = (U64)literalLength - RUN_MASK; *pToken = (RUN_MASK << ML_BITS); for (; len >= 255; len -= 255) { *(cctx->op)++ = 255; @@ -819,9 +820,9 @@ void LDM_encodeLiteralLengthAndLiterals( } void LDM_outputBlock(LDM_CCtx *cctx, - const U32 literalLength, + const U64 literalLength, const U32 offset, - const U32 matchLength) { + const U64 matchLength) { BYTE *pToken = cctx->op++; /* Encode the literal length and literals. */ @@ -833,7 +834,7 @@ void LDM_outputBlock(LDM_CCtx *cctx, /* Encode the match length. */ if (matchLength >= ML_MASK) { - unsigned matchLengthRemaining = matchLength; + U64 matchLengthRemaining = matchLength; *pToken += ML_MASK; matchLengthRemaining -= ML_MASK; MEM_write32(cctx->op, 0xFFFFFFFF); @@ -858,8 +859,8 @@ size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; const BYTE *match = NULL; - U32 forwardMatchLength = 0; - U32 backwardsMatchLength = 0; + U64 forwardMatchLength = 0; + U64 backwardsMatchLength = 0; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); LDM_outputConfiguration(); @@ -892,9 +893,9 @@ size_t LDM_compress(const void *src, size_t srcSize, * length) and update pointers and hashes. */ { - const U32 literalLength = cctx.ip - cctx.anchor; + const U64 literalLength = cctx.ip - cctx.anchor; const U32 offset = cctx.ip - match; - const U32 matchLength = forwardMatchLength + + const U64 matchLength = forwardMatchLength + backwardsMatchLength - LDM_MIN_MATCH_LENGTH; @@ -934,7 +935,7 @@ size_t LDM_compress(const void *src, size_t srcSize, /* Encode the last literals (no more matches). */ { - const U32 lastRun = cctx.iend - cctx.anchor; + const U64 lastRun = cctx.iend - cctx.anchor; BYTE *pToken = cctx.op++; LDM_encodeLiteralLengthAndLiterals(&cctx, pToken, lastRun); } @@ -979,6 +980,7 @@ void LDM_initializeDCtx(LDM_DCtx *dctx, 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); diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h index 9d5ba0e27..d59f401ec 100644 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -25,8 +25,8 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_hashTable *table, const BYTE *pIn, const BYTE *pEnd, const BYTE *pAnchor, - U32 *matchLength, - U32 *backwardsMatchLength); + U64 *matchLength, + U64 *backwardsMatchLength); hash_t HASH_hashU32(U32 value); diff --git a/contrib/long_distance_matching/ldm_with_table.c b/contrib/long_distance_matching/ldm_with_table.c index c727616af..babfdf3f2 100644 --- a/contrib/long_distance_matching/ldm_with_table.c +++ b/contrib/long_distance_matching/ldm_with_table.c @@ -719,10 +719,10 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, } void LDM_encodeLiteralLengthAndLiterals( - LDM_CCtx *cctx, BYTE *pToken, const U32 literalLength) { + LDM_CCtx *cctx, BYTE *pToken, const U64 literalLength) { /* Encode the literal length. */ if (literalLength >= RUN_MASK) { - int len = (int)literalLength - RUN_MASK; + U64 len = (U64)literalLength - RUN_MASK; *pToken = (RUN_MASK << ML_BITS); for (; len >= 255; len -= 255) { *(cctx->op)++ = 255; @@ -738,9 +738,9 @@ void LDM_encodeLiteralLengthAndLiterals( } void LDM_outputBlock(LDM_CCtx *cctx, - const U32 literalLength, + const U64 literalLength, const U32 offset, - const U32 matchLength) { + const U64 matchLength) { BYTE *pToken = cctx->op++; /* Encode the literal length and literals. */ @@ -811,9 +811,9 @@ size_t LDM_compress(const void *src, size_t srcSize, * length) and update pointers and hashes. */ { - const U32 literalLength = cctx.ip - cctx.anchor; + const U64 literalLength = cctx.ip - cctx.anchor; const U32 offset = cctx.ip - match; - const U32 matchLength = forwardMatchLength + + const U64 matchLength = forwardMatchLength + backwardsMatchLength - LDM_MIN_MATCH_LENGTH; diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 3582d5a21..b6788c67e 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -94,8 +94,8 @@ static int compress(const char *fname, const char *oname) { // Truncate file to compressedSize. ftruncate(fdout, compressedSize); - printf("%25s : %6u -> %7u - %s (%.1f%%)\n", fname, - (unsigned)statbuf.st_size, (unsigned)compressedSize, oname, + printf("%25s : %10lu -> %10lu - %s (%.1f%%)\n", fname, + (size_t)statbuf.st_size, (size_t)compressedSize, oname, (double)compressedSize / (statbuf.st_size) * 100); timeTaken = (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + @@ -164,7 +164,7 @@ static int decompress(const char *fname, const char *oname) { src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, dst, decompressedSize); printf("Ret size out: %zu\n", outSize); - ftruncate(fdout, outSize); +// ftruncate(fdout, decompressedSize); close(fdin); close(fdout); @@ -231,7 +231,6 @@ int main(int argc, const char *argv[]) { printf("ldm = [%s]\n", ldmFilename); printf("dec = [%s]\n", decFilename); - /* Compress */ { if (compress(inpFilename, ldmFilename)) { From 1a188fe864c67673621b0b20a9911e3d23ef935a Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Fri, 21 Jul 2017 10:44:39 -0700 Subject: [PATCH 216/318] Fix overflow bug when calculating hash --- contrib/long_distance_matching/Makefile | 4 +- contrib/long_distance_matching/ldm.c | 4 +- contrib/long_distance_matching/ldm.h | 11 +- contrib/long_distance_matching/ldm_64_hash.c | 172 ++++--------------- contrib/long_distance_matching/main-ldm.c | 5 +- 5 files changed, 46 insertions(+), 150 deletions(-) diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 9dc33fae7..168442970 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -25,7 +25,7 @@ LDFLAGS += -lzstd default: all -all: main-circular-buffer main-integrated main-64 +all: main-circular-buffer main-integrated main-64 #main-basic : basic_table.c ldm.c main-ldm.c # $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ @@ -41,6 +41,6 @@ main-integrated: ldm_with_table.c main-ldm.c clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main-basic main-circular-buffer main-integrated main-64 + main-circular-buffer main-integrated main-64 @echo Cleaning completed diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index b018c4755..bfaff1f5a 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -520,8 +520,8 @@ size_t LDM_compress(const void *src, size_t srcSize, void *dst, size_t maxDstSize) { LDM_CCtx cctx; const BYTE *match = NULL; - U32 forwardMatchLength = 0; - U32 backwardsMatchLength = 0; + U64 forwardMatchLength = 0; + U64 backwardsMatchLength = 0; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); LDM_outputConfiguration(); diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 83cd36230..e2f786979 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -14,7 +14,7 @@ // Note that this is not the number of buckets. // Currently this should be less than WINDOW_SIZE_LOG + 4? #define LDM_MEMORY_USAGE 22 -#define HASH_BUCKET_SIZE_LOG 1 // MAX is 4 for now +#define HASH_BUCKET_SIZE_LOG 2 // MAX is 4 for now // Defines the lag in inserting elements into the hash table. #define LDM_LAG 0 @@ -26,7 +26,8 @@ #define LDM_MIN_MATCH_LENGTH 64 #define LDM_HASH_LENGTH 64 - +#define TMP_EVICTION +#define TMP_TAG_INSERT typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; typedef struct LDM_DCtx LDM_DCtx; @@ -99,12 +100,6 @@ void LDM_printCompressStats(const LDM_compressStats *stats); */ int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch); -/** - * Counts the number of bytes that match from pIn and pMatch, - * up to pInLimit. - */ -U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit); /** * Encode the literal length followed by the literals. diff --git a/contrib/long_distance_matching/ldm_64_hash.c b/contrib/long_distance_matching/ldm_64_hash.c index a72c283f2..7a8135342 100644 --- a/contrib/long_distance_matching/ldm_64_hash.c +++ b/contrib/long_distance_matching/ldm_64_hash.c @@ -89,21 +89,16 @@ struct LDM_CCtx { const BYTE *lastPosHashed; /* Last position hashed */ hash_t lastHash; /* Hash corresponding to lastPosHashed */ - U32 lastSum; + U64 lastSum; const BYTE *nextIp; // TODO: this is redundant (ip + step) const BYTE *nextPosHashed; U64 nextSum; -// hash_t nextHash; /* Hash corresponding to nextPosHashed */ -// U32 nextSum; - unsigned step; // ip step, should be 1. const BYTE *lagIp; U64 lagSum; -// hash_t lagHash; -// U32 lagSum; U64 numHashInserts; // DEBUG @@ -273,15 +268,15 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, LDM_hashEntry *bucket = getBucket(table, hash); LDM_hashEntry *cur = bucket; LDM_hashEntry *bestEntry = NULL; - U32 bestMatchLength = 0; + U64 bestMatchLength = 0; for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { const BYTE *pMatch = cur->offset + cctx->ibase; // Check checksum for faster check. if (cur->checksum == checksum && cctx->ip - pMatch <= LDM_WINDOW_SIZE) { - U32 forwardMatchLength = ZSTD_count(cctx->ip, pMatch, cctx->iend); - U32 backwardMatchLength, totalMatchLength; + U64 forwardMatchLength = ZSTD_count(cctx->ip, pMatch, cctx->iend); + U64 backwardMatchLength, totalMatchLength; // For speed. if (forwardMatchLength < LDM_MIN_MATCH_LENGTH) { @@ -313,6 +308,8 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, return NULL; } +#ifdef TMP_EVICTION + void HASH_insert(LDM_hashTable *table, const hash_t hash, const LDM_hashEntry entry) { *(getBucket(table, hash) + table->bucketOffsets[hash]) = entry; @@ -320,6 +317,17 @@ void HASH_insert(LDM_hashTable *table, table->bucketOffsets[hash] &= HASH_BUCKET_SIZE - 1; } +#else + +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; +} +#endif // TMP_EVICTION + + U32 HASH_getSize(const LDM_hashTable *table) { return table->numBuckets; } @@ -349,7 +357,7 @@ void HASH_outputTableOccupancy(const LDM_hashTable *table) { // TODO: This can be done more efficiently (but it is not that important as it // is only used for computing stats). -static int intLog2(U32 x) { +static int intLog2(U64 x) { int ret = 0; while (x >>= 1) { ret++; @@ -357,32 +365,6 @@ static int intLog2(U32 x) { return ret; } -// Maybe we would eventually prefer to have linear rather than -// exponential buckets. -/** -void HASH_outputTableOffsetHistogram(const LDM_CCtx *cctx) { - U32 i = 0; - int buckets[32] = { 0 }; - - printf("\n"); - printf("Hash table histogram\n"); - for (; i < HASH_getSize(cctx->hashTable); i++) { - int offset = (cctx->ip - cctx->ibase) - - HASH_getEntryFromHash(cctx->hashTable, i)->offset; - buckets[intLog2(offset)]++; - } - - i = 0; - for (; i < 32; i++) { - printf("2^%*d: %10u %6.3f%%\n", 2, i, - buckets[i], - 100.0 * (double) buckets[i] / - (double) HASH_getSize(cctx->hashTable)); - } - printf("\n"); -} -*/ - void LDM_printCompressStats(const LDM_compressStats *stats) { int i = 0; printf("=====================\n"); @@ -436,22 +418,6 @@ int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { return 1; } -#if 0 -hash_t HASH_hashU32(U32 value) { - return ((value * 2654435761U) >> (32 - LDM_HASHLOG)); -} -#endif - -/** - * Convert a sum computed from getChecksum to a hash value in the range - * of the hash table. - */ -#if 0 -static hash_t checksumToHash(U32 sum) { - return HASH_hashU32(sum); -} -#endif - // Upper LDM_HASH_LOG bits. static hash_t checksumToHash(U64 sum) { return sum >> (64 - LDM_HASHLOG); @@ -462,69 +428,19 @@ static U32 checksumFromHfHash(U64 hfHash) { return (hfHash >> (64 - 32 - LDM_HASHLOG)) & 0xFFFFFFFF; } -#if 0 -/** - * Computes a checksum based on rsync's checksum. - * - * a(k,l) = \sum_{i = k}^l x_i (mod M) - * b(k,l) = \sum_{i = k}^l ((l - i + 1) * x_i) (mod M) - * checksum(k,l) = a(k,l) + 2^{16} * b(k,l) - */ -static U32 getChecksum(const BYTE *buf, U32 len) { - U32 i; - U32 s1, s2; - - s1 = s2 = 0; - for (i = 0; i < (len - 4); i += 4) { - s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + - (2 * buf[i + 2]) + (buf[i + 3]) + - (10 * CHECKSUM_CHAR_OFFSET); - s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3] + - + (4 * CHECKSUM_CHAR_OFFSET); - - } - for(; i < len; i++) { - s1 += buf[i] + CHECKSUM_CHAR_OFFSET; - s2 += s1; - } - return (s1 & 0xffff) + (s2 << 16); -} -#endif - static U64 getChecksum(const BYTE *buf, U32 len) { static const U64 prime8bytes = 11400714785074694791ULL; -// static const U64 prime8bytes = 5; U64 ret = 0; U32 i; for (i = 0; i < len; i++) { ret *= prime8bytes; ret += buf[i] + CHECKSUM_CHAR_OFFSET; -// printf("HERE %llu\n", ret); } return ret; } -#if 0 -/** - * Update a checksum computed from getChecksum(data, len). - * - * The checksum can be updated along its ends as follows: - * a(k+1, l+1) = (a(k,l) - x_k + x_{l+1}) (mod M) - * b(k+1, l+1) = (b(k,l) - (l-k+1)*x_k + (a(k+1,l+1)) (mod M) - * - * Thus toRemove should correspond to data[0]. - */ -static U32 updateChecksum(U32 sum, U32 len, - BYTE toRemove, BYTE toAdd) { - U32 s1 = (sum & 0xffff) - toRemove + toAdd; - U32 s2 = (sum >> 16) - ((toRemove + CHECKSUM_CHAR_OFFSET) * len) + s1; - - return (s1 & 0xffff) + (s2 << 16); -} -#endif - static U64 ipow(U64 base, U64 exp) { U64 ret = 1; while (exp) { @@ -542,6 +458,8 @@ static U64 updateChecksum(U64 sum, U32 len, // TODO: deduplicate. static const U64 prime8bytes = 11400714785074694791ULL; + // TODO: relying on compiler optimization here. + // The exponential can be calculated explicitly. sum -= ((toRemove + CHECKSUM_CHAR_OFFSET) * ipow(prime8bytes, len - 1)); sum *= prime8bytes; @@ -558,7 +476,7 @@ static U64 updateChecksum(U64 sum, U32 len, */ static void setNextHash(LDM_CCtx *cctx) { #ifdef RUN_CHECKS - U32 check; + U64 check; if ((cctx->nextIp - cctx->ibase != 1) && (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, @@ -568,16 +486,11 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->DEBUG_setNextHash = cctx->nextIp; #endif -// cctx->nextSum = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); cctx->nextSum = updateChecksum( cctx->lastSum, LDM_HASH_LENGTH, cctx->lastPosHashed[0], cctx->lastPosHashed[LDM_HASH_LENGTH]); cctx->nextPosHashed = cctx->nextIp; -#if 0 - cctx->nextHash = checksumToHash(cctx->nextSum); -#endif - #if LDM_LAG // printf("LDM_LAG %zu\n", cctx->ip - cctx->lagIp); @@ -586,9 +499,6 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->lagSum, LDM_HASH_LENGTH, cctx->lagIp[0], cctx->lagIp[LDM_HASH_LENGTH]); cctx->lagIp++; -#if 0 - cctx->lagHash = checksumToHash(cctx->lagSum); -#endif } #endif @@ -596,7 +506,7 @@ static void setNextHash(LDM_CCtx *cctx) { check = getChecksum(cctx->nextIp, LDM_HASH_LENGTH); if (check != cctx->nextSum) { - printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); + printf("CHECK: setNextHash failed %llu %llu\n", check, cctx->nextSum); } if ((cctx->nextIp - cctx->lastPosHashed) != 1) { @@ -610,8 +520,6 @@ static void setNextHash(LDM_CCtx *cctx) { static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hfHash) { // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. // Note: this works only when cctx->step is 1. - U32 hash = checksumToHash(hfHash); - U32 sum = checksumFromHfHash(hfHash); // printf("TMP %u %u %llu\n", hash, sum, hfHash); if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { @@ -619,22 +527,26 @@ static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hfHash) { #if LDM_LAG // TODO: off by 1, but whatever if (cctx->lagIp - cctx->ibase > 0) { - const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, cctx->lagSum }; - HASH_insert(cctx->hashTable, cctx->lagHash, entry); + U32 hash = checksumToHash(cctx->lagSum); + U32 sum = checksumFromHfHash(cctx->lagSum); + const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, sum }; + HASH_insert(cctx->hashTable, hash, entry); } else { + U32 hash = checksumToHash(hfHash); + U32 sum = checksumFromHfHash(hfHash); + const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; HASH_insert(cctx->hashTable, hash, entry); } #else + U32 hash = checksumToHash(hfHash); + U32 sum = checksumFromHfHash(hfHash); const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; HASH_insert(cctx->hashTable, hash, entry); #endif } cctx->lastPosHashed = cctx->ip; -#if 0 - cctx->lastHash = hash; -#endif cctx->lastSum = hfHash; } @@ -650,9 +562,6 @@ static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { printf("CHECK failed: updateLastHashFromNextHash %zu\n", cctx->ip - cctx->ibase); } -#endif -#if 0 - putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); #endif putHashOfCurrentPositionFromHash(cctx, cctx->nextSum); } @@ -661,10 +570,7 @@ static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { * Insert hash of the current position into the hash table. */ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - U32 sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); -#if 0 - hash_t hash = checksumToHash(sum); -#endif + U64 sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); #ifdef RUN_CHECKS if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { @@ -672,13 +578,11 @@ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { cctx->ip - cctx->ibase); } #endif -#if 0 - putHashOfCurrentPositionFromHash(cctx, hash, sum); -#endif + putHashOfCurrentPositionFromHash(cctx, sum); } -U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, +U64 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, const BYTE *pInLimit) { const BYTE * const pStart = pIn; while (pIn < pInLimit - 1) { @@ -688,9 +592,9 @@ U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, pMatch++; continue; } - return (U32)(pIn - pStart); + return (U64)(pIn - pStart); } - return (U32)(pIn - pStart); + return (U64)(pIn - pStart); } void LDM_outputConfiguration(void) { @@ -773,10 +677,6 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, U64 hash; U32 sum; setNextHash(cctx); -#if 0 - h = cctx->nextHash; - sum = cctx->nextSum; -#endif hash = cctx->nextSum; h = checksumToHash(hash); sum = checksumFromHfHash(hash); diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index b6788c67e..9769f10e7 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -94,9 +94,10 @@ static int compress(const char *fname, const char *oname) { // Truncate file to compressedSize. ftruncate(fdout, compressedSize); - printf("%25s : %10lu -> %10lu - %s (%.1f%%)\n", fname, + printf("%25s : %10lu -> %10lu - %s (%.2fx --- %.1f%%)\n", fname, (size_t)statbuf.st_size, (size_t)compressedSize, oname, - (double)compressedSize / (statbuf.st_size) * 100); + (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), From eb16da647d38df0f2180c9c335ddadc559624e85 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 24 Jul 2017 10:18:58 -0700 Subject: [PATCH 217/318] Minor clean up --- contrib/long_distance_matching/ldm.h | 13 +- contrib/long_distance_matching/ldm_64_hash.c | 199 +++++++++++++++++-- 2 files changed, 188 insertions(+), 24 deletions(-) diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index e2f786979..840824c46 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -13,21 +13,26 @@ // 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 22 -#define HASH_BUCKET_SIZE_LOG 2 // MAX is 4 for now +#define LDM_MEMORY_USAGE 24 +#define HASH_BUCKET_SIZE_LOG 0 // MAX is 4 for now // Defines the lag in inserting elements into the hash table. #define LDM_LAG 0 -#define LDM_WINDOW_SIZE_LOG 28 +#define LDM_WINDOW_SIZE_LOG 28 // Max value is 30 #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) //These should be multiples of four (and perhaps set to the same value?). #define LDM_MIN_MATCH_LENGTH 64 #define LDM_HASH_LENGTH 64 -#define TMP_EVICTION +// Experimental. +//:w +//#define TMP_EVICTION #define TMP_TAG_INSERT +//#define TMP_SIMPLE_LOWER +//#define TMP_FORCE_HASH_ONLY + typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; typedef struct LDM_DCtx LDM_DCtx; diff --git a/contrib/long_distance_matching/ldm_64_hash.c b/contrib/long_distance_matching/ldm_64_hash.c index 7a8135342..bdbdd1997 100644 --- a/contrib/long_distance_matching/ldm_64_hash.c +++ b/contrib/long_distance_matching/ldm_64_hash.c @@ -12,7 +12,11 @@ #define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) // Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE) - (LDM_HASH_ENTRY_SIZE_LOG))) +#ifdef TMP_FORCE_HASH_ONLY + #define HASH_ONLY_EVERY_LOG 7 +#else + #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) /* Hash table stuff. */ @@ -26,12 +30,15 @@ #define COMPUTE_STATS #define OUTPUT_CONFIGURATION -#define CHECKSUM_CHAR_OFFSET 10 +#define CHECKSUM_CHAR_OFFSET 1 // Take first match only. //#define ZSTD_SKIP //#define RUN_CHECKS +// +// +static const U64 prime8bytes = 11400714785074694791ULL; /* Hash table stuff */ @@ -56,6 +63,14 @@ struct LDM_compressStats { U32 numHashInserts; U32 offsetHistogram[32]; + + U64 TMP_hashCount[1 << HASH_ONLY_EVERY_LOG]; + U64 TMP_totalHashCount; + + U64 TMP_totalInWindow; + U64 TMP_totalInserts; + + U64 TMP_matchCount; }; typedef struct LDM_hashTable LDM_hashTable; @@ -311,10 +326,80 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, #ifdef TMP_EVICTION void HASH_insert(LDM_hashTable *table, - const hash_t hash, const LDM_hashEntry entry) { + const hash_t hash, const LDM_hashEntry entry, + LDM_CCtx *cctx) { + // Overwrite based on part of checksum. + /* + LDM_hashEntry *toOverwrite = + getBucket(table, hash) + table->bucketOffsets[hash]; + const BYTE *pMatch = toOverwrite->offset + cctx->ibase; + if (toOverwrite->offset != 0 && + cctx->ip - pMatch <= LDM_WINDOW_SIZE) { + cctx->stats.TMP_totalInWindow++; + } + + cctx->stats.TMP_totalInserts++; + *(toOverwrite) = entry; + */ + + /* + int i; + LDM_hashEntry *bucket = getBucket(table, hash); + for (i = 0; i < HASH_BUCKET_SIZE; i++) { + if (bucket[i].checksum == entry.checksum) { + bucket[i] = entry; + cctx->stats.TMP_matchCount++; + return; + } + } + */ + + // Find entry beyond window size, replace. Else, random. + int i; + LDM_hashEntry *bucket = getBucket(table, hash); + for (i = 0; i < HASH_BUCKET_SIZE; i++) { + if (cctx->ip - cctx->ibase - bucket[i].offset > LDM_WINDOW_SIZE) { + bucket[i] = entry; + return; + } + } + + i = rand() & (HASH_BUCKET_SIZE - 1); + *(bucket + i) = entry; + + + /** + * Sliding buffer style pointer + * Keep old entry as temporary. If the old entry is outside the window, + * overwrite and we are done. + * + * Backwards (insert at x): + * x, a, b b, c c c c, d d d d d d d d + * x, d d d d d d d d, c c c c, b b, a + * + * Else, find something to evict. + * If old entry has more ones, it takes + * the next spot. <-- reversed order? + * + * If window size > LDM_WINDOW_SIZE, + * overwrite, + * + * Insert forwards. If > tag, keep. Else evict. + * + * + * + * + */ + + + /* *(getBucket(table, hash) + table->bucketOffsets[hash]) = entry; table->bucketOffsets[hash]++; table->bucketOffsets[hash] &= HASH_BUCKET_SIZE - 1; + */ + +// U16 mask = entry.checksum & (HASH_BUCKET_SIZE - 1); +// *(getBucket(table, hash) + mask) = entry; } #else @@ -348,8 +433,9 @@ void HASH_outputTableOccupancy(const LDM_hashTable *table) { } } - printf("Num buckets, bucket size: %d, %d\n", - table->numBuckets, HASH_BUCKET_SIZE); + // TODO: repeat numBuckets as a check for now. + printf("Num buckets, bucket size: %d (2^%d), %d\n", + table->numBuckets, LDM_HASHLOG, HASH_BUCKET_SIZE); printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", table->numEntries, ctr, 100.0 * (double)(ctr) / table->numEntries); @@ -396,6 +482,24 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { (double) stats->numMatches); } printf("\n"); +#ifdef TMP_TAG_INSERT +/* + printf("Lower bit distribution\n"); + for (i = 0; i < (1 << HASH_ONLY_EVERY_LOG); i++) { + printf("%5d %5llu %6.3f\n", i, stats->TMP_hashCount[i], + 100.0 * (double) stats->TMP_hashCount[i] / + (double) stats->TMP_totalHashCount); + } +*/ +#endif + +#ifdef TMP_EVICTION + printf("Evicted something in window: %llu %6.3f\n", + stats->TMP_totalInWindow, + 100.0 * (double)stats->TMP_totalInWindow / + (double)stats->TMP_totalInserts); + printf("Match count: %llu\n", stats->TMP_matchCount); +#endif printf("=====================\n"); } @@ -418,7 +522,7 @@ int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { return 1; } -// Upper LDM_HASH_LOG bits. +// Upper LDM_HASHLOG bits. static hash_t checksumToHash(U64 sum) { return sum >> (64 - LDM_HASHLOG); } @@ -428,9 +532,30 @@ static U32 checksumFromHfHash(U64 hfHash) { return (hfHash >> (64 - 32 - LDM_HASHLOG)) & 0xFFFFFFFF; } -static U64 getChecksum(const BYTE *buf, U32 len) { - static const U64 prime8bytes = 11400714785074694791ULL; +#ifdef TMP_TAG_INSERT +static U32 lowerBitsFromHfHash(U64 hfHash) { + // The number of bits used so far is LDM_HASHLOG + 32. + // So there are 32 - LDM_HASHLOG 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. +#ifdef TMP_SIMPLE_LOWER + return hfHash & HASH_ONLY_EVERY; +#else + if (32 - LDM_HASHLOG < HASH_ONLY_EVERY_LOG) { + return hfHash & HASH_ONLY_EVERY; + } else { + // Otherwise shift by (32 - LDM_HASHLOG - HASH_ONLY_EVERY_LOG) bits first. + return (hfHash >> (32 - LDM_HASHLOG - HASH_ONLY_EVERY_LOG)) & + HASH_ONLY_EVERY; + } +#endif +} +#endif + + +static U64 getChecksum(const BYTE *buf, U32 len) { U64 ret = 0; U32 i; for (i = 0; i < len; i++) { @@ -455,11 +580,8 @@ static U64 ipow(U64 base, U64 exp) { static U64 updateChecksum(U64 sum, U32 len, BYTE toRemove, BYTE toAdd) { - // TODO: deduplicate. - static const U64 prime8bytes = 11400714785074694791ULL; - // TODO: relying on compiler optimization here. - // The exponential can be calculated explicitly. + // The exponential can (should?) be calculated explicitly. sum -= ((toRemove + CHECKSUM_CHAR_OFFSET) * ipow(prime8bytes, len - 1)); sum *= prime8bytes; @@ -492,6 +614,14 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->lastPosHashed[LDM_HASH_LENGTH]); cctx->nextPosHashed = cctx->nextIp; +#ifdef TMP_TAG_INSERT + { + U32 hashEveryMask = lowerBitsFromHfHash(cctx->nextSum); + cctx->stats.TMP_totalHashCount++; + cctx->stats.TMP_hashCount[hashEveryMask]++; + } +#endif + #if LDM_LAG // printf("LDM_LAG %zu\n", cctx->ip - cctx->lagIp); if (cctx->ip - cctx->ibase > LDM_LAG) { @@ -520,31 +650,48 @@ static void setNextHash(LDM_CCtx *cctx) { static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hfHash) { // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. // Note: this works only when cctx->step is 1. -// printf("TMP %u %u %llu\n", hash, sum, hfHash); - - if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { - #if LDM_LAG - // TODO: off by 1, but whatever + if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { + // TODO: off by 1, but whatever. if (cctx->lagIp - cctx->ibase > 0) { U32 hash = checksumToHash(cctx->lagSum); U32 sum = checksumFromHfHash(cctx->lagSum); const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, sum }; +#ifdef TMP_EVICTION + HASH_insert(cctx->hashTable, hash, entry, cctx); +#else HASH_insert(cctx->hashTable, hash, entry); +#endif } else { U32 hash = checksumToHash(hfHash); U32 sum = checksumFromHfHash(hfHash); const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; - HASH_insert(cctx->hashTable, hash, entry); - } +#ifdef TMP_EVICTION + HASH_insert(cctx->hashTable, hash, entry, cctx); #else + HASH_insert(cctx->hashTable, hash, entry); +#endif + } + } +#else +#ifdef TMP_TAG_INSERT + U32 hashEveryMask = lowerBitsFromHfHash(hfHash); + // TODO: look at stats. + if (hashEveryMask == HASH_ONLY_EVERY) { +#else + if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { +#endif U32 hash = checksumToHash(hfHash); U32 sum = checksumFromHfHash(hfHash); const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; +#ifdef TMP_EVICTION + HASH_insert(cctx->hashTable, hash, entry, cctx); +#else HASH_insert(cctx->hashTable, hash, entry); #endif } +#endif cctx->lastPosHashed = cctx->ip; cctx->lastSum = hfHash; @@ -676,10 +823,16 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, hash_t h; U64 hash; U32 sum; +#ifdef TMP_TAG_INSERT + U32 hashEveryMask; +#endif setNextHash(cctx); hash = cctx->nextSum; h = checksumToHash(hash); sum = checksumFromHfHash(hash); +#ifdef TMP_TAG_INSERT + hashEveryMask = lowerBitsFromHfHash(hash); +#endif cctx->ip = cctx->nextIp; cctx->nextIp += cctx->step; @@ -687,9 +840,15 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, if (cctx->ip > cctx->imatchLimit) { return 1; } - +#ifdef TMP_TAG_INSERT + if (hashEveryMask == HASH_ONLY_EVERY) { + entry = HASH_getBestEntry(cctx, h, sum, + forwardMatchLength, backwardMatchLength); + } +#else entry = HASH_getBestEntry(cctx, h, sum, forwardMatchLength, backwardMatchLength); +#endif if (entry != NULL) { *match = entry->offset + cctx->ibase; From e508f632d6d4f92836b339a7f2d2e70a009bbaf3 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 11:01:36 -0700 Subject: [PATCH 218/318] updated comments and debug statements --- contrib/adaptive-compression/adapt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 4e5a86c72..a90b59902 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -350,30 +350,30 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* adaptation logic */ if (1-createWaitCompressionCompletion > threshold || 1-writeWaitCompressionCompletion > threshold) { - /* compression waiting on either create or write */ + /* create or write waiting on compression */ /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); ctx->compressionLevel -= boundChange; - DEBUG(2, "create and write threads waiting, tried to decrease compression level by %u\n", boundChange); + DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n", boundChange); } else if (1-compressWaitWriteCompletion > threshold) { - /* both create and compression thread waiting on write */ + /* compress waiting on write */ double const completion = compressWaitWriteCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; - DEBUG(2, "create and compression threads waiting, tried to increase compression level by %u\n", boundChange); + DEBUG(2, "compress waiting on write, tried to increase compression level by %u\n", boundChange); } else if (1-compressWaitCreateCompletion > threshold) { - /* both compression and write waiting on create */ + /* compress waiting on create*/ /* use compressWaitCreateCompletion */ double const completion = compressWaitCreateCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; - DEBUG(2, "compression and write threads waiting, tried to increase compression level by %u\n", boundChange); + DEBUG(2, "compression waiting on create, tried to increase compression level by %u\n", boundChange); } if (g_forceCompressionLevel) { From 8ed92201024e889333a9ac89dd10b188d28d8647 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 24 Jul 2017 12:05:43 -0700 Subject: [PATCH 219/318] Experiment with eviction policies and minor code cleanup --- contrib/long_distance_matching/Makefile | 7 +- .../circular_buffer_table.c | 43 +++++++----- contrib/long_distance_matching/ldm.c | 51 ++++----------- contrib/long_distance_matching/ldm.h | 44 ++++++------- contrib/long_distance_matching/ldm_64_hash.c | 32 ++++----- .../long_distance_matching/ldm_hashtable.h | 49 ++++++++++++-- .../{ldm_with_table.c => ldm_integrated.c} | 65 +++---------------- contrib/long_distance_matching/main-ldm.c | 17 ++--- 8 files changed, 139 insertions(+), 169 deletions(-) rename contrib/long_distance_matching/{ldm_with_table.c => ldm_integrated.c} (94%) diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 168442970..c8129f678 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -27,20 +27,17 @@ default: all all: main-circular-buffer main-integrated main-64 -#main-basic : basic_table.c ldm.c main-ldm.c -# $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - main-circular-buffer: circular_buffer_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ main-64: ldm_64_hash.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ -main-integrated: ldm_with_table.c main-ldm.c +main-integrated: ldm_integrated.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main-circular-buffer main-integrated main-64 + main-circular-buffer main-64 main-integrated @echo Cleaning completed diff --git a/contrib/long_distance_matching/circular_buffer_table.c b/contrib/long_distance_matching/circular_buffer_table.c index fb6c19d2a..92ffc55bd 100644 --- a/contrib/long_distance_matching/circular_buffer_table.c +++ b/contrib/long_distance_matching/circular_buffer_table.c @@ -5,26 +5,24 @@ #include "ldm_hashtable.h" #include "mem.h" -// Number of elements per hash bucket. -// HASH_BUCKET_SIZE_LOG defined in ldm.h +// THe number of elements per hash bucket. +// HASH_BUCKET_SIZE_LOG is defined in ldm.h. #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) + +// The number of hash buckets. #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) - - -// TODO: rename. Number of hash buckets. -// TODO: Link to HASH_ENTRY_SIZE_LOG - +// If ZSTD_SKIP is defined, then the first entry is returned in HASH_getBestEntry +// (without looking at other entries in the bucket). //#define ZSTD_SKIP struct LDM_hashTable { - U32 numBuckets; - U32 numEntries; + U32 numBuckets; // The number of buckets. + U32 numEntries; // numBuckets * HASH_BUCKET_SIZE. LDM_hashEntry *entries; - BYTE *bucketOffsets; // Pointer to current insert position. + BYTE *bucketOffsets; // A pointer (per bucket) to the next insert position. - // Position corresponding to offset=0 in LDM_hashEntry. - const BYTE *offsetBase; + const BYTE *offsetBase; // Corresponds to offset=0 in LDM_hashEntry. U32 minMatchLength; U32 maxWindowSize; }; @@ -46,6 +44,7 @@ static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { return table->entries + (hash << HASH_BUCKET_SIZE_LOG); } +// From lib/compress/zstd_compress.c static unsigned ZSTD_NbCommonBytes (register size_t val) { if (MEM_isLittleEndian()) { @@ -114,7 +113,11 @@ static unsigned ZSTD_NbCommonBytes (register size_t val) } } } -// From lib/compress/zstd_compress.c +/** + * From lib/compress/zstd_compress.c + * Returns the number of bytes (consecutively) in common between pIn and pMatch + * up to pInLimit. + */ static size_t ZSTD_count(const BYTE *pIn, const BYTE *pMatch, const BYTE *const pInLimit) { const BYTE * const pStart = pIn; @@ -147,9 +150,14 @@ static size_t ZSTD_count(const BYTE *pIn, const BYTE *pMatch, return (size_t)(pIn - pStart); } -U32 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, - const BYTE *pMatch, const BYTE *pBase) { - U32 matchLength = 0; +/** + * Returns the number of bytes in common between pIn and pMatch, + * counting backwards, with pIn having a lower limit of pAnchor and + * pMatch having a lower limit of pBase. + */ +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--; @@ -178,6 +186,8 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_hashTable *table, U64 forwardMatchLength = ZSTD_count(pIn, pMatch, pEnd); U64 backwardMatchLength, totalMatchLength; + // Only take matches where the forwardMatchLength is large enough + // for speed. if (forwardMatchLength < table->minMatchLength) { continue; } @@ -212,6 +222,7 @@ hash_t HASH_hashU32(U32 value) { void HASH_insert(LDM_hashTable *table, const hash_t hash, const LDM_hashEntry entry) { + // Circular buffer. *(getBucket(table, hash) + table->bucketOffsets[hash]) = entry; table->bucketOffsets[hash]++; table->bucketOffsets[hash] &= HASH_BUCKET_SIZE - 1; diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index bfaff1f5a..a5594ff6d 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -29,7 +29,6 @@ typedef U32 checksum_t; -// TODO: Scanning speed // TODO: Memory usage struct LDM_compressStats { U32 windowSizeLog, hashTableSizeLog; @@ -40,9 +39,6 @@ struct LDM_compressStats { U32 minOffset, maxOffset; - U32 numCollisions; - U32 numHashInserts; - U32 offsetHistogram[32]; }; @@ -80,15 +76,12 @@ struct LDM_CCtx { hash_t nextHash; /* Hash corresponding to nextPosHashed */ checksum_t nextSum; - - unsigned step; // ip step, should be 1. const BYTE *lagIp; hash_t lagHash; checksum_t lagSum; - U64 numHashInserts; // DEBUG const BYTE *DEBUG_setNextHash; }; @@ -103,32 +96,6 @@ static int intLog2(U32 x) { return ret; } -// TODO: Maybe we would eventually prefer to have linear rather than -// exponential buckets. -/** -void HASH_outputTableOffsetHistogram(const LDM_CCtx *cctx) { - U32 i = 0; - int buckets[32] = { 0 }; - - printf("\n"); - printf("Hash table histogram\n"); - for (; i < HASH_getSize(cctx->hashTable); i++) { - int offset = (cctx->ip - cctx->ibase) - - HASH_getEntryFromHash(cctx->hashTable, i)->offset; - buckets[intLog2(offset)]++; - } - - i = 0; - for (; i < 32; i++) { - printf("2^%*d: %10u %6.3f%%\n", 2, i, - buckets[i], - 100.0 * (double) buckets[i] / - (double) HASH_getSize(cctx->hashTable)); - } - printf("\n"); -} -*/ - void LDM_printCompressStats(const LDM_compressStats *stats) { int i = 0; printf("=====================\n"); @@ -163,7 +130,8 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { printf("=====================\n"); } -int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { +/* +static int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { U32 lengthLeft = LDM_MIN_MATCH_LENGTH; const BYTE *curIn = pIn; const BYTE *curMatch = pMatch; @@ -181,6 +149,7 @@ int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { } return 1; } +*/ /** * Convert a sum computed from getChecksum to a hash value in the range @@ -253,7 +222,6 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->DEBUG_setNextHash = cctx->nextIp; #endif -// cctx->nextSum = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); cctx->nextSum = updateChecksum( cctx->lastSum, LDM_HASH_LENGTH, cctx->lastPosHashed[0], @@ -292,7 +260,7 @@ static void putHashOfCurrentPositionFromHash( // Note: this works only when cctx->step is 1. if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { #if LDM_LAG - // TODO: off by 1, but whatever + // Off by 1, but whatever if (cctx->lagIp - cctx->ibase > 0) { const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, cctx->lagSum }; HASH_insert(cctx->hashTable, cctx->lagHash, entry); @@ -344,6 +312,7 @@ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { putHashOfCurrentPositionFromHash(cctx, hash, sum); } +/* U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, const BYTE *pInLimit) { const BYTE * const pStart = pIn; @@ -358,6 +327,7 @@ U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, } return (U32)(pIn - pStart); } +*/ void LDM_outputConfiguration(void) { printf("=====================\n"); @@ -380,6 +350,12 @@ void LDM_readHeader(const void *src, U64 *compressedSize, // ip += sizeof(U64); } +void LDM_writeHeader(void *memPtr, U64 compressedSize, + U64 decompressedSize) { + MEM_write64(memPtr, compressedSize); + MEM_write64((BYTE *)memPtr + 8, decompressedSize); +} + void LDM_initializeCCtx(LDM_CCtx *cctx, const void *src, size_t srcSize, void *dst, size_t maxDstSize) { @@ -592,8 +568,6 @@ size_t LDM_compress(const void *src, size_t srcSize, LDM_updateLastHashFromNextHash(&cctx); } - // HASH_outputTableOffsetHistogram(&cctx); - /* Encode the last literals (no more matches). */ { const U32 lastRun = cctx.iend - cctx.anchor; @@ -692,7 +666,6 @@ size_t LDM_decompress(const void *src, size_t compressedSize, return dctx.op - (BYTE *)dst; } -// TODO: implement and test hash function void LDM_test(const BYTE *src) { (void)src; } diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 840824c46..adbe35bfa 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -1,20 +1,24 @@ #ifndef LDM_H #define LDM_H -#include /* size_t */ - #include "mem.h" // from /lib/common/mem.h -#define LDM_COMPRESS_SIZE 8 -#define LDM_DECOMPRESS_SIZE 8 -#define LDM_HEADER_SIZE ((LDM_COMPRESS_SIZE)+(LDM_DECOMPRESS_SIZE)) +// 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)) + +// THe number of bytes storing the offset. #define LDM_OFFSET_SIZE 4 // 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 24 -#define HASH_BUCKET_SIZE_LOG 0 // MAX is 4 for now +#define LDM_MEMORY_USAGE 23 + +// The number of entries in a hash bucket. +#define HASH_BUCKET_SIZE_LOG 0 // The maximum is 4 for now. // Defines the lag in inserting elements into the hash table. #define LDM_LAG 0 @@ -23,11 +27,10 @@ #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) //These should be multiples of four (and perhaps set to the same value?). -#define LDM_MIN_MATCH_LENGTH 64 -#define LDM_HASH_LENGTH 64 +#define LDM_MIN_MATCH_LENGTH 16 +#define LDM_HASH_LENGTH 16 // Experimental. -//:w //#define TMP_EVICTION #define TMP_TAG_INSERT //#define TMP_SIMPLE_LOWER @@ -37,7 +40,6 @@ typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; typedef struct LDM_DCtx LDM_DCtx; - /** * Compresses src into dst. * @@ -94,17 +96,6 @@ void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx); * Outputs compression statistics to stdout. */ void LDM_printCompressStats(const LDM_compressStats *stats); -/** - * Checks whether the LDM_MIN_MATCH_LENGTH bytes from p are the same as the - * LDM_MIN_MATCH_LENGTH bytes from match and also if - * pIn - pMatch <= LDM_WINDOW_SIZE. - * - * This assumes LDM_MIN_MATCH_LENGTH is a multiple of four. - * - * Return 1 if valid, 0 otherwise. - */ -int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch); - /** * Encode the literal length followed by the literals. @@ -150,6 +141,15 @@ void LDM_initializeDCtx(LDM_DCtx *dctx, void LDM_readHeader(const void *src, U64 *compressedSize, U64 *decompressedSize); +/** + * Write the compressed and decompressed size. + */ +void LDM_writeHeader(void *memPtr, U64 compressedSize, + U64 decompressedSize); + +/** + * Output the configuration used. + */ void LDM_outputConfiguration(void); void LDM_test(const BYTE *src); diff --git a/contrib/long_distance_matching/ldm_64_hash.c b/contrib/long_distance_matching/ldm_64_hash.c index bdbdd1997..d0080efd0 100644 --- a/contrib/long_distance_matching/ldm_64_hash.c +++ b/contrib/long_distance_matching/ldm_64_hash.c @@ -36,8 +36,7 @@ //#define ZSTD_SKIP //#define RUN_CHECKS -// -// + static const U64 prime8bytes = 11400714785074694791ULL; /* Hash table stuff */ @@ -49,7 +48,6 @@ typedef struct LDM_hashEntry { U32 checksum; } LDM_hashEntry; -// TODO: Memory usage struct LDM_compressStats { U32 windowSizeLog, hashTableSizeLog; U32 numMatches; @@ -59,9 +57,6 @@ struct LDM_compressStats { U32 minOffset, maxOffset; - U32 numCollisions; - U32 numHashInserts; - U32 offsetHistogram[32]; U64 TMP_hashCount[1 << HASH_ONLY_EVERY_LOG]; @@ -115,20 +110,19 @@ struct LDM_CCtx { const BYTE *lagIp; U64 lagSum; - U64 numHashInserts; // DEBUG const BYTE *DEBUG_setNextHash; }; struct LDM_hashTable { - U32 numBuckets; // Number of buckets - U32 numEntries; // Rename... - LDM_hashEntry *entries; + U32 numBuckets; // The number of buckets. + U32 numEntries; // numBuckets * HASH_BUCKET_SIZE. - BYTE *bucketOffsets; - // Position corresponding to offset=0 in LDM_hashEntry. + LDM_hashEntry *entries; + BYTE *bucketOffsets; // A pointer (per bucket) to the next insert position. }; + /** * Create a hash table that can contain size elements. * The number of buckets is determined by size >> HASH_BUCKET_SIZE_LOG. @@ -251,9 +245,9 @@ static size_t ZSTD_count(const BYTE *pIn, const BYTE *pMatch, * * We count only bytes where pMatch > pBaes and pIn > pAnchor. */ -U64 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, +size_t countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, const BYTE *pMatch, const BYTE *pBase) { - U64 matchLength = 0; + size_T matchLength = 0; while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) { pIn--; pMatch--; @@ -293,7 +287,8 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, U64 forwardMatchLength = ZSTD_count(cctx->ip, pMatch, cctx->iend); U64 backwardMatchLength, totalMatchLength; - // For speed. + // Only take matches where the forward match length is large enough + // for speed. if (forwardMatchLength < LDM_MIN_MATCH_LENGTH) { continue; } @@ -766,6 +761,13 @@ void LDM_readHeader(const void *src, U64 *compressedSize, // ip += sizeof(U64); } +void LDM_writeHeader(void *memPtr, U64 compressedSize, + U64 decompressedSize) { + MEM_write64(memPtr, compressedSize); + MEM_write64((BYTE *)memPtr + 8, decompressedSize); +} + + void LDM_initializeCCtx(LDM_CCtx *cctx, const void *src, size_t srcSize, void *dst, size_t maxDstSize) { diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h index d59f401ec..6093197dd 100644 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ b/contrib/long_distance_matching/ldm_hashtable.h @@ -1,37 +1,73 @@ +/** + * A "hash" table used in LDM compression. + * + * This is not exactly a hash table in the sense that inserted entries + * are not guaranteed to remain in the hash table. + */ + #ifndef LDM_HASHTABLE_H #define LDM_HASHTABLE_H #include "mem.h" +// The log size of LDM_hashEntry in bytes. #define LDM_HASH_ENTRY_SIZE_LOG 3 -// TODO: clean up comments - typedef U32 hash_t; typedef struct LDM_hashEntry { - U32 offset; // TODO: Replace with pointer? - U32 checksum; + U32 offset; // Represents the offset of the entry from offsetBase. + U32 checksum; // A checksum to select entries with the same hash value. } LDM_hashEntry; typedef struct LDM_hashTable LDM_hashTable; +/** + * Create a table that can contain size elements. This does not necessarily + * correspond to the number of hash buckets. The number of hash buckets + * is size / (1 << HASH_BUCKET_SIZE_LOG) + * + * minMatchLength is the minimum match length required in HASH_getBestEntry. + * + * maxWindowSize is the maximum distance from pIn in HASH_getBestEntry. + * The window is defined to be (pIn - offsetBase - offset). + */ LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase, U32 minMatchLength, U32 maxWindowSize); +/** + * Return the "best" entry from the table with the same hash and checksum. + * + * pIn: a pointer to the current input position. + * pEnd: a pointer to the maximum input position. + * pAnchor: a pointer to the minimum input position. + * + * This function computes the forward and backward match length from pIn + * and writes it to forwardMatchLength and backwardsMatchLength. + * + * E.g. for the two strings "aaabbbb" "aaabbbb" with pIn and the + * entry pointing at the first "b", the forward match length would be + * four (representing the "b" matches) and the backward match length would + * three (representing the "a" matches before the pointer). + */ LDM_hashEntry *HASH_getBestEntry(const LDM_hashTable *table, const hash_t hash, const U32 checksum, const BYTE *pIn, const BYTE *pEnd, const BYTE *pAnchor, - U64 *matchLength, + U64 *forwardMatchLength, U64 *backwardsMatchLength); +/** + * Return a hash of the value. + */ hash_t HASH_hashU32(U32 value); /** * Insert an LDM_hashEntry into the bucket corresponding to hash. + * + * An entry may be evicted in the process. */ void HASH_insert(LDM_hashTable *table, const hash_t hash, const LDM_hashEntry entry); @@ -41,6 +77,9 @@ void HASH_insert(LDM_hashTable *table, const hash_t hash, */ U32 HASH_getSize(const LDM_hashTable *table); +/** + * Destroy the table. + */ void HASH_destroyTable(LDM_hashTable *table); /** diff --git a/contrib/long_distance_matching/ldm_with_table.c b/contrib/long_distance_matching/ldm_integrated.c similarity index 94% rename from contrib/long_distance_matching/ldm_with_table.c rename to contrib/long_distance_matching/ldm_integrated.c index babfdf3f2..7733d4e92 100644 --- a/contrib/long_distance_matching/ldm_with_table.c +++ b/contrib/long_distance_matching/ldm_integrated.c @@ -33,8 +33,6 @@ //#define RUN_CHECKS -/* Hash table stuff */ - typedef U32 hash_t; typedef struct LDM_hashEntry { @@ -42,7 +40,6 @@ typedef struct LDM_hashEntry { U32 checksum; } LDM_hashEntry; -// TODO: Memory usage struct LDM_compressStats { U32 windowSizeLog, hashTableSizeLog; U32 numMatches; @@ -52,9 +49,6 @@ struct LDM_compressStats { U32 minOffset, maxOffset; - U32 numCollisions; - U32 numHashInserts; - U32 offsetHistogram[32]; }; @@ -85,8 +79,6 @@ struct LDM_CCtx { LDM_hashTable *hashTable; -// LDM_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; - const BYTE *lastPosHashed; /* Last position hashed */ hash_t lastHash; /* Hash corresponding to lastPosHashed */ U32 lastSum; @@ -109,11 +101,10 @@ struct LDM_CCtx { struct LDM_hashTable { U32 numBuckets; // Number of buckets - U32 numEntries; // Rename... + U32 numEntries; LDM_hashEntry *entries; BYTE *bucketOffsets; - // Position corresponding to offset=0 in LDM_hashEntry. }; /** @@ -354,32 +345,6 @@ static int intLog2(U32 x) { return ret; } -// Maybe we would eventually prefer to have linear rather than -// exponential buckets. -/** -void HASH_outputTableOffsetHistogram(const LDM_CCtx *cctx) { - U32 i = 0; - int buckets[32] = { 0 }; - - printf("\n"); - printf("Hash table histogram\n"); - for (; i < HASH_getSize(cctx->hashTable); i++) { - int offset = (cctx->ip - cctx->ibase) - - HASH_getEntryFromHash(cctx->hashTable, i)->offset; - buckets[intLog2(offset)]++; - } - - i = 0; - for (; i < 32; i++) { - printf("2^%*d: %10u %6.3f%%\n", 2, i, - buckets[i], - 100.0 * (double) buckets[i] / - (double) HASH_getSize(cctx->hashTable)); - } - printf("\n"); -} -*/ - void LDM_printCompressStats(const LDM_compressStats *stats) { int i = 0; printf("=====================\n"); @@ -508,7 +473,6 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->DEBUG_setNextHash = cctx->nextIp; #endif -// cctx->nextSum = getChecksum((const char *)cctx->nextIp, LDM_HASH_LENGTH); cctx->nextSum = updateChecksum( cctx->lastSum, LDM_HASH_LENGTH, cctx->lastPosHashed[0], @@ -517,7 +481,6 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->nextHash = checksumToHash(cctx->nextSum); #if LDM_LAG -// printf("LDM_LAG %zu\n", cctx->ip - cctx->lagIp); if (cctx->ip - cctx->ibase > LDM_LAG) { cctx->lagSum = updateChecksum( cctx->lagSum, LDM_HASH_LENGTH, @@ -547,10 +510,6 @@ static void putHashOfCurrentPositionFromHash( // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. // Note: this works only when cctx->step is 1. if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { - /** - const LDM_hashEntry entry = { cctx->ip - cctx->ibase , - MEM_read32(cctx->ip) }; - */ #if LDM_LAG // TODO: off by 1, but whatever if (cctx->lagIp - cctx->ibase > 0) { @@ -604,21 +563,6 @@ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { putHashOfCurrentPositionFromHash(cctx, hash, sum); } -U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { - const BYTE * const pStart = pIn; - while (pIn < pInLimit - 1) { - BYTE const diff = (*pMatch) ^ *(pIn); - if (!diff) { - pIn++; - pMatch++; - continue; - } - return (U32)(pIn - pStart); - } - return (U32)(pIn - pStart); -} - void LDM_outputConfiguration(void) { printf("=====================\n"); printf("Configuration\n"); @@ -640,6 +584,13 @@ void LDM_readHeader(const void *src, U64 *compressedSize, // ip += sizeof(U64); } +void LDM_writeHeader(void *memPtr, U64 compressedSize, + U64 decompressedSize) { + MEM_write64(memPtr, compressedSize); + MEM_write64((BYTE *)memPtr + 8, decompressedSize); +} + + void LDM_initializeCCtx(LDM_CCtx *cctx, const void *src, size_t srcSize, void *dst, size_t maxDstSize) { diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main-ldm.c index 9769f10e7..232c14a2f 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main-ldm.c @@ -12,13 +12,12 @@ #include "ldm.h" #include "zstd.h" -#define DEBUG //#define TEST /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. * - * TODO: This might seg fault if the compressed size is > the decompress + * 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. */ @@ -31,6 +30,7 @@ static int compress(const char *fname, const char *oname) { struct timeval tv1, tv2; double timeTaken; + /* Open the input file. */ if ((fdin = open(fname, O_RDONLY)) < 0) { perror("Error in file opening"); @@ -50,6 +50,7 @@ static int compress(const char *fname, const char *oname) { } maxCompressedSize = (statbuf.st_size + LDM_HEADER_SIZE); + // Handle case where compressed size is > decompressed size. // The compress function should check before writing or buffer writes. maxCompressedSize += statbuf.st_size / 255; @@ -79,21 +80,17 @@ static int compress(const char *fname, const char *oname) { compressedSize = LDM_HEADER_SIZE + LDM_compress(src, statbuf.st_size, dst + LDM_HEADER_SIZE, maxCompressedSize); + gettimeofday(&tv2, NULL); // Write compress and decompress size to header // TODO: should depend on LDM_DECOMPRESS_SIZE write32 - memcpy(dst, &compressedSize, 8); - memcpy(dst + 8, &(statbuf.st_size), 8); - -#ifdef DEBUG - printf("Compressed size: %zu\n", compressedSize); - printf("Decompressed size: %zu\n", (size_t)statbuf.st_size); -#endif + LDM_writeHeader(dst, compressedSize, statbuf.st_size); // Truncate file to compressedSize. ftruncate(fdout, compressedSize); + printf("%25s : %10lu -> %10lu - %s (%.2fx --- %.1f%%)\n", fname, (size_t)statbuf.st_size, (size_t)compressedSize, oname, (statbuf.st_size) / (double)compressedSize, @@ -102,7 +99,7 @@ static int compress(const char *fname, const char *oname) { timeTaken = (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 + (double) (tv2.tv_sec - tv1.tv_sec), - printf("Total compress time = %.3f seconds, Average compression speed: %.3f MB/s\n", + printf("Total compress time = %.3f seconds, Average scanning speed: %.3f MB/s\n", timeTaken, ((double)statbuf.st_size / (double) (1 << 20)) / timeTaken); From 6eefa3291195e3fc7f592484125ff3bc44ba3a50 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 24 Jul 2017 12:40:59 -0700 Subject: [PATCH 220/318] Deduplicate code --- contrib/long_distance_matching/Makefile | 6 +- contrib/long_distance_matching/ldm.c | 156 -------------- contrib/long_distance_matching/ldm.h | 16 +- contrib/long_distance_matching/ldm_64_hash.c | 198 ++---------------- contrib/long_distance_matching/ldm_common.c | 113 ++++++++++ .../long_distance_matching/ldm_integrated.c | 116 ---------- 6 files changed, 153 insertions(+), 452 deletions(-) create mode 100644 contrib/long_distance_matching/ldm_common.c diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index c8129f678..e1c31112d 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -27,13 +27,13 @@ default: all all: main-circular-buffer main-integrated main-64 -main-circular-buffer: circular_buffer_table.c ldm.c main-ldm.c +main-circular-buffer: ldm_common.c circular_buffer_table.c ldm.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ -main-64: ldm_64_hash.c main-ldm.c +main-64: ldm_common.c ldm_64_hash.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ -main-integrated: ldm_integrated.c main-ldm.c +main-integrated: ldm_common.c ldm_integrated.c main-ldm.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index a5594ff6d..9d3eda326 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -8,28 +8,16 @@ #include "ldm_hashtable.h" #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) #define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) -// Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG))) -#define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) - -#define ML_BITS 4 -#define ML_MASK ((1U< LDM_WINDOW_SIZE) { - return 0; - } - - for (; lengthLeft >= 4; lengthLeft -= 4) { - if (MEM_read32(curIn) != MEM_read32(curMatch)) { - return 0; - } - curIn += 4; - curMatch += 4; - } - return 1; -} -*/ - /** * Convert a sum computed from getChecksum to a hash value in the range * of the hash table. @@ -312,50 +279,6 @@ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { putHashOfCurrentPositionFromHash(cctx, hash, sum); } -/* -U32 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { - const BYTE * const pStart = pIn; - while (pIn < pInLimit - 1) { - BYTE const diff = (*pMatch) ^ *(pIn); - if (!diff) { - pIn++; - pMatch++; - continue; - } - return (U32)(pIn - pStart); - } - return (U32)(pIn - pStart); -} -*/ - -void LDM_outputConfiguration(void) { - printf("=====================\n"); - printf("Configuration\n"); - printf("Window size log: %d\n", LDM_WINDOW_SIZE_LOG); - printf("Min match, 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("LDM_LAG %d\n", LDM_LAG); - printf("=====================\n"); -} - -void LDM_readHeader(const void *src, U64 *compressedSize, - U64 *decompressedSize) { - const BYTE *ip = (const BYTE *)src; - *compressedSize = MEM_readLE64(ip); - ip += sizeof(U64); - *decompressedSize = MEM_readLE64(ip); - // ip += sizeof(U64); -} - -void LDM_writeHeader(void *memPtr, U64 compressedSize, - U64 decompressedSize) { - MEM_write64(memPtr, compressedSize); - MEM_write64((BYTE *)memPtr + 8, decompressedSize); -} - void LDM_initializeCCtx(LDM_CCtx *cctx, const void *src, size_t srcSize, void *dst, size_t maxDstSize) { @@ -587,85 +510,6 @@ size_t LDM_compress(const void *src, size_t srcSize, } } -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 - 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; - - // Inefficient for now. - while (match < cpy - offset && dctx.op < dctx.oend) { - *(dctx.op)++ = *match++; - } - } - return dctx.op - (BYTE *)dst; -} - void LDM_test(const BYTE *src) { (void)src; } diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index adbe35bfa..3078fb8cd 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -9,6 +9,11 @@ #define LDM_DECOMPRESSED_SIZE 8 #define LDM_HEADER_SIZE ((LDM_COMPRESSED_SIZE)+(LDM_DECOMPRESSED_SIZE)) +#define ML_BITS 4 +#define ML_MASK ((1U<> 2) #define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) -// Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#ifdef TMP_FORCE_HASH_ONLY - #define HASH_ONLY_EVERY_LOG 7 -#else - #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) - /* Hash table stuff. */ #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) -#define ML_BITS 4 -#define ML_MASK ((1U< pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) { pIn--; pMatch--; @@ -319,7 +304,6 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, } #ifdef TMP_EVICTION - void HASH_insert(LDM_hashTable *table, const hash_t hash, const LDM_hashEntry entry, LDM_CCtx *cctx) { @@ -381,9 +365,6 @@ void HASH_insert(LDM_hashTable *table, * * Insert forwards. If > tag, keep. Else evict. * - * - * - * */ @@ -428,7 +409,7 @@ void HASH_outputTableOccupancy(const LDM_hashTable *table) { } } - // TODO: repeat numBuckets as a check for now. + // The number of buckets is repeated as a check for now. printf("Num buckets, bucket size: %d (2^%d), %d\n", table->numBuckets, LDM_HASHLOG, HASH_BUCKET_SIZE); printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", @@ -498,31 +479,16 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { printf("=====================\n"); } -int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { - U32 lengthLeft = LDM_MIN_MATCH_LENGTH; - const BYTE *curIn = pIn; - const BYTE *curMatch = pMatch; - - if (pIn - pMatch > LDM_WINDOW_SIZE) { - return 0; - } - - for (; lengthLeft >= 4; lengthLeft -= 4) { - if (MEM_read32(curIn) != MEM_read32(curMatch)) { - return 0; - } - curIn += 4; - curMatch += 4; - } - return 1; -} - -// Upper LDM_HASHLOG bits. +/** + * Return the upper (most significant) LDM_HASHLOG bits. + */ static hash_t checksumToHash(U64 sum) { return sum >> (64 - LDM_HASHLOG); } -// 32 bits after LDM_HASH_LOG bits. +/** + * Return the 32 bits after the upper LDM_HASHLOG bits. + */ static U32 checksumFromHfHash(U64 hfHash) { return (hfHash >> (64 - 32 - LDM_HASHLOG)) & 0xFFFFFFFF; } @@ -534,9 +500,6 @@ static U32 lowerBitsFromHfHash(U64 hfHash) { // 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. -#ifdef TMP_SIMPLE_LOWER - return hfHash & HASH_ONLY_EVERY; -#else if (32 - LDM_HASHLOG < HASH_ONLY_EVERY_LOG) { return hfHash & HASH_ONLY_EVERY; } else { @@ -544,12 +507,20 @@ static U32 lowerBitsFromHfHash(U64 hfHash) { return (hfHash >> (32 - LDM_HASHLOG - HASH_ONLY_EVERY_LOG)) & HASH_ONLY_EVERY; } -#endif } #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 + CHECKSUM_CHAR_OFFSET)*(a^(k-1)) + ... + */ static U64 getChecksum(const BYTE *buf, U32 len) { U64 ret = 0; U32 i; @@ -575,8 +546,8 @@ static U64 ipow(U64 base, U64 exp) { static U64 updateChecksum(U64 sum, U32 len, BYTE toRemove, BYTE toAdd) { - // TODO: relying on compiler optimization here. - // The exponential can (should?) be calculated explicitly. + // TODO: this relies on compiler optimization. + // The exponential can be calculated explicitly as len is constant. sum -= ((toRemove + CHECKSUM_CHAR_OFFSET) * ipow(prime8bytes, len - 1)); sum *= prime8bytes; @@ -618,7 +589,6 @@ static void setNextHash(LDM_CCtx *cctx) { #endif #if LDM_LAG -// printf("LDM_LAG %zu\n", cctx->ip - cctx->lagIp); if (cctx->ip - cctx->ibase > LDM_LAG) { cctx->lagSum = updateChecksum( cctx->lagSum, LDM_HASH_LENGTH, @@ -647,7 +617,7 @@ static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hfHash) { // Note: this works only when cctx->step is 1. #if LDM_LAG if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { - // TODO: off by 1, but whatever. + // TODO: Off by one, but not important. if (cctx->lagIp - cctx->ibase > 0) { U32 hash = checksumToHash(cctx->lagSum); U32 sum = checksumFromHfHash(cctx->lagSum); @@ -724,50 +694,6 @@ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { putHashOfCurrentPositionFromHash(cctx, sum); } -U64 LDM_countMatchLength(const BYTE *pIn, const BYTE *pMatch, - const BYTE *pInLimit) { - const BYTE * const pStart = pIn; - while (pIn < pInLimit - 1) { - BYTE const diff = (*pMatch) ^ *(pIn); - if (!diff) { - pIn++; - pMatch++; - continue; - } - return (U64)(pIn - pStart); - } - return (U64)(pIn - pStart); -} - -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("=====================\n"); -} - -void LDM_readHeader(const void *src, U64 *compressedSize, - U64 *decompressedSize) { - const BYTE *ip = (const BYTE *)src; - *compressedSize = MEM_readLE64(ip); - ip += sizeof(U64); - *decompressedSize = MEM_readLE64(ip); - // ip += sizeof(U64); -} - -void LDM_writeHeader(void *memPtr, U64 compressedSize, - U64 decompressedSize) { - MEM_write64(memPtr, compressedSize); - MEM_write64((BYTE *)memPtr + 8, decompressedSize); -} - - void LDM_initializeCCtx(LDM_CCtx *cctx, const void *src, size_t srcSize, void *dst, size_t maxDstSize) { @@ -1013,86 +939,6 @@ size_t LDM_compress(const void *src, size_t srcSize, } } -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 - 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; - - // Inefficient for now. - while (match < cpy - offset && dctx.op < dctx.oend) { - *(dctx.op)++ = *match++; - } - } - return dctx.op - (BYTE *)dst; -} - // TODO: implement and test hash function void LDM_test(const BYTE *src) { const U32 diff = 100; diff --git a/contrib/long_distance_matching/ldm_common.c b/contrib/long_distance_matching/ldm_common.c new file mode 100644 index 000000000..673959dbe --- /dev/null +++ b/contrib/long_distance_matching/ldm_common.c @@ -0,0 +1,113 @@ +#include + +#include "ldm.h" + +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("=====================\n"); +} + +void LDM_readHeader(const void *src, U64 *compressedSize, + U64 *decompressedSize) { + const BYTE *ip = (const BYTE *)src; + *compressedSize = MEM_readLE64(ip); + ip += sizeof(U64); + *decompressedSize = MEM_readLE64(ip); + // ip += sizeof(U64); +} + +void LDM_writeHeader(void *memPtr, U64 compressedSize, + U64 decompressedSize) { + MEM_write64(memPtr, compressedSize); + MEM_write64((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 + 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; + + // Inefficient for now. + while (match < cpy - offset && dctx.op < dctx.oend) { + *(dctx.op)++ = *match++; + } + } + return dctx.op - (BYTE *)dst; +} + + diff --git a/contrib/long_distance_matching/ldm_integrated.c b/contrib/long_distance_matching/ldm_integrated.c index 7733d4e92..d51c1e9d3 100644 --- a/contrib/long_distance_matching/ldm_integrated.c +++ b/contrib/long_distance_matching/ldm_integrated.c @@ -11,19 +11,10 @@ #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) #define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) -// Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE) - (LDM_HASH_ENTRY_SIZE_LOG))) -#define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) - /* Hash table stuff. */ #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) -#define ML_BITS 4 -#define ML_MASK ((1U<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 - 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; - - // Inefficient for now. - while (match < cpy - offset && dctx.op < dctx.oend) { - *(dctx.op)++ = *match++; - } - } - return dctx.op - (BYTE *)dst; -} - // TODO: implement and test hash function void LDM_test(const BYTE *src) { (void)src; From 08a6e9a141025f4d41177fc77495c32b138d6e54 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 24 Jul 2017 13:22:00 -0700 Subject: [PATCH 221/318] Minor code cleanup --- contrib/long_distance_matching/ldm_64_hash.c | 291 +++++++++--------- contrib/long_distance_matching/ldm_common.c | 2 - .../long_distance_matching/ldm_integrated.c | 2 +- 3 files changed, 146 insertions(+), 149 deletions(-) diff --git a/contrib/long_distance_matching/ldm_64_hash.c b/contrib/long_distance_matching/ldm_64_hash.c index 95865f70d..06ddf5207 100644 --- a/contrib/long_distance_matching/ldm_64_hash.c +++ b/contrib/long_distance_matching/ldm_64_hash.c @@ -15,7 +15,7 @@ #define COMPUTE_STATS #define OUTPUT_CONFIGURATION -#define CHECKSUM_CHAR_OFFSET 1 +#define HASH_CHAR_OFFSET 10 // Take first match only. //#define ZSTD_SKIP @@ -24,8 +24,7 @@ static const U64 prime8bytes = 11400714785074694791ULL; -/* Hash table stuff */ - +// Type of the small hash used to index into the hash table. typedef U32 hash_t; typedef struct LDM_hashEntry { @@ -41,7 +40,6 @@ struct LDM_compressStats { U64 totalOffset; U32 minOffset, maxOffset; - U32 offsetHistogram[32]; U64 TMP_hashCount[1 << HASH_ONLY_EVERY_LOG]; @@ -56,8 +54,8 @@ struct LDM_compressStats { typedef struct LDM_hashTable LDM_hashTable; struct LDM_CCtx { - U64 isize; /* Input size */ - U64 maxOSize; /* Maximum output size */ + size_t isize; /* Input size */ + size_t maxOSize; /* Maximum output size */ const BYTE *ibase; /* Base of input */ const BYTE *ip; /* Current input position */ @@ -80,23 +78,21 @@ struct LDM_CCtx { LDM_hashTable *hashTable; -// LDM_hashEntry hashTable[LDM_HASHTABLESIZE_U32]; - const BYTE *lastPosHashed; /* Last position hashed */ - hash_t lastHash; /* Hash corresponding to lastPosHashed */ - U64 lastSum; + U64 lastHash; - const BYTE *nextIp; // TODO: this is redundant (ip + step) + const BYTE *nextIp; // TODO: this is redundant (ip + step) const BYTE *nextPosHashed; - U64 nextSum; + U64 nextHash; unsigned step; // ip step, should be 1. const BYTE *lagIp; - U64 lagSum; + U64 lagHash; - // DEBUG +#ifdef RUN_CHECKS const BYTE *DEBUG_setNextHash; +#endif }; struct LDM_hashTable { @@ -107,7 +103,6 @@ struct LDM_hashTable { BYTE *bucketOffsets; // A pointer (per bucket) to the next insert position. }; - /** * Create a hash table that can contain size elements. * The number of buckets is determined by size >> HASH_BUCKET_SIZE_LOG. @@ -126,70 +121,74 @@ static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { } static unsigned ZSTD_NbCommonBytes (register 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]; + 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; - _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; - const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ - 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 - } } + } 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 @@ -230,8 +229,8 @@ static size_t ZSTD_count(const BYTE *pIn, const BYTE *pMatch, * * We count only bytes where pMatch > pBaes and pIn > pAnchor. */ -size_t countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, - const BYTE *pMatch, const BYTE *pBase) { +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--; @@ -482,29 +481,29 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { /** * Return the upper (most significant) LDM_HASHLOG bits. */ -static hash_t checksumToHash(U64 sum) { - return sum >> (64 - LDM_HASHLOG); +static hash_t getSmallHash(U64 hash) { + return hash >> (64 - LDM_HASHLOG); } /** * Return the 32 bits after the upper LDM_HASHLOG bits. */ -static U32 checksumFromHfHash(U64 hfHash) { - return (hfHash >> (64 - 32 - LDM_HASHLOG)) & 0xFFFFFFFF; +static U32 getChecksum(U64 hash) { + return (hash >> (64 - 32 - LDM_HASHLOG)) & 0xFFFFFFFF; } #ifdef TMP_TAG_INSERT -static U32 lowerBitsFromHfHash(U64 hfHash) { +static U32 lowerBitsFromHfHash(U64 hash) { // The number of bits used so far is LDM_HASHLOG + 32. // So there are 32 - LDM_HASHLOG 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 - LDM_HASHLOG < HASH_ONLY_EVERY_LOG) { - return hfHash & HASH_ONLY_EVERY; + return hash & HASH_ONLY_EVERY; } else { // Otherwise shift by (32 - LDM_HASHLOG - HASH_ONLY_EVERY_LOG) bits first. - return (hfHash >> (32 - LDM_HASHLOG - HASH_ONLY_EVERY_LOG)) & + return (hash >> (32 - LDM_HASHLOG - HASH_ONLY_EVERY_LOG)) & HASH_ONLY_EVERY; } } @@ -519,14 +518,14 @@ static U32 lowerBitsFromHfHash(U64 hfHash) { * where the constant a is defined to be prime8bytes. * * The implementation adds an offset to each byte, so - * H(s) = (s_1 + CHECKSUM_CHAR_OFFSET)*(a^(k-1)) + ... + * H(s) = (s_1 + HASH_CHAR_OFFSET)*(a^(k-1)) + ... */ -static U64 getChecksum(const BYTE *buf, U32 len) { +static U64 getHash(const BYTE *buf, U32 len) { U64 ret = 0; U32 i; for (i = 0; i < len; i++) { ret *= prime8bytes; - ret += buf[i] + CHECKSUM_CHAR_OFFSET; + ret += buf[i] + HASH_CHAR_OFFSET; } return ret; @@ -544,20 +543,20 @@ static U64 ipow(U64 base, U64 exp) { return ret; } -static U64 updateChecksum(U64 sum, U32 len, - BYTE toRemove, BYTE toAdd) { +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. - sum -= ((toRemove + CHECKSUM_CHAR_OFFSET) * + hash -= ((toRemove + HASH_CHAR_OFFSET) * ipow(prime8bytes, len - 1)); - sum *= prime8bytes; - sum += toAdd + CHECKSUM_CHAR_OFFSET; - return sum; + hash *= prime8bytes; + hash += toAdd + HASH_CHAR_OFFSET; + return hash; } /** - * Update cctx->nextSum, cctx->nextHash, and cctx->nextPosHashed - * based on cctx->lastSum and cctx->lastPosHashed. + * 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. @@ -574,15 +573,15 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->DEBUG_setNextHash = cctx->nextIp; #endif - cctx->nextSum = updateChecksum( - cctx->lastSum, LDM_HASH_LENGTH, + cctx->nextHash = updateHash( + cctx->lastHash, LDM_HASH_LENGTH, cctx->lastPosHashed[0], cctx->lastPosHashed[LDM_HASH_LENGTH]); cctx->nextPosHashed = cctx->nextIp; #ifdef TMP_TAG_INSERT { - U32 hashEveryMask = lowerBitsFromHfHash(cctx->nextSum); + U32 hashEveryMask = lowerBitsFromHfHash(cctx->nextHash); cctx->stats.TMP_totalHashCount++; cctx->stats.TMP_hashCount[hashEveryMask]++; } @@ -590,18 +589,18 @@ static void setNextHash(LDM_CCtx *cctx) { #if LDM_LAG if (cctx->ip - cctx->ibase > LDM_LAG) { - cctx->lagSum = updateChecksum( - cctx->lagSum, LDM_HASH_LENGTH, + cctx->lagHash = updateHash( + cctx->lagHash, LDM_HASH_LENGTH, cctx->lagIp[0], cctx->lagIp[LDM_HASH_LENGTH]); cctx->lagIp++; } #endif #ifdef RUN_CHECKS - check = getChecksum(cctx->nextIp, LDM_HASH_LENGTH); + check = getHash(cctx->nextIp, LDM_HASH_LENGTH); - if (check != cctx->nextSum) { - printf("CHECK: setNextHash failed %llu %llu\n", check, cctx->nextSum); + if (check != cctx->nextHash) { + printf("CHECK: setNextHash failed %llu %llu\n", check, cctx->nextHash); } if ((cctx->nextIp - cctx->lastPosHashed) != 1) { @@ -612,58 +611,57 @@ static void setNextHash(LDM_CCtx *cctx) { #endif } -static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hfHash) { +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->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { // TODO: Off by one, but not important. if (cctx->lagIp - cctx->ibase > 0) { - U32 hash = checksumToHash(cctx->lagSum); - U32 sum = checksumFromHfHash(cctx->lagSum); - const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, sum }; + U32 smallHash = getSmallHash(cctx->lagHash); + U32 checksum = getChecksum(cctx->lagHash); + const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, checksum }; #ifdef TMP_EVICTION - HASH_insert(cctx->hashTable, hash, entry, cctx); + HASH_insert(cctx->hashTable, smallHash, entry, cctx); #else - HASH_insert(cctx->hashTable, hash, entry); + HASH_insert(cctx->hashTable, smallHash, entry); #endif } else { - U32 hash = checksumToHash(hfHash); - U32 sum = checksumFromHfHash(hfHash); + U32 smallHash = getSmallHash(hash); + U32 checksum = getChecksum(hash); - const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; + const LDM_hashEntry entry = { cctx->ip - cctx->ibase, checksum }; #ifdef TMP_EVICTION - HASH_insert(cctx->hashTable, hash, entry, cctx); + HASH_insert(cctx->hashTable, smallHash, entry, cctx); #else - HASH_insert(cctx->hashTable, hash, entry); + HASH_insert(cctx->hashTable, smallHash, entry); #endif } } #else #ifdef TMP_TAG_INSERT - U32 hashEveryMask = lowerBitsFromHfHash(hfHash); - // TODO: look at stats. + U32 hashEveryMask = lowerBitsFromHfHash(hash); if (hashEveryMask == HASH_ONLY_EVERY) { #else if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { #endif - U32 hash = checksumToHash(hfHash); - U32 sum = checksumFromHfHash(hfHash); - const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; + U32 smallHash = getSmallHash(hash); + U32 checksum = getChecksum(hash); + const LDM_hashEntry entry = { cctx->ip - cctx->ibase, checksum }; #ifdef TMP_EVICTION - HASH_insert(cctx->hashTable, hash, entry, cctx); + HASH_insert(cctx->hashTable, smallHash, entry, cctx); #else - HASH_insert(cctx->hashTable, hash, entry); + HASH_insert(cctx->hashTable, smallHash, entry); #endif } #endif cctx->lastPosHashed = cctx->ip; - cctx->lastSum = hfHash; + cctx->lastHash = hash; } /** - * Copy over the cctx->lastHash, cctx->lastSum, and cctx->lastPosHashed + * Copy over the cctx->lastHash, and cctx->lastPosHashed * fields from the "next" fields. * * This requires that cctx->ip == cctx->nextPosHashed. @@ -675,14 +673,14 @@ static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { cctx->ip - cctx->ibase); } #endif - putHashOfCurrentPositionFromHash(cctx, cctx->nextSum); + putHashOfCurrentPositionFromHash(cctx, cctx->nextHash); } /** * Insert hash of the current position into the hash table. */ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - U64 sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); + U64 hash = getHash(cctx->ip, LDM_HASH_LENGTH); #ifdef RUN_CHECKS if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { @@ -691,7 +689,7 @@ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { } #endif - putHashOfCurrentPositionFromHash(cctx, sum); + putHashOfCurrentPositionFromHash(cctx, hash); } void LDM_initializeCCtx(LDM_CCtx *cctx, @@ -726,7 +724,9 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->nextIp = cctx->ip + cctx->step; cctx->nextPosHashed = 0; +#ifdef RUN_CHECKS cctx->DEBUG_setNextHash = 0; +#endif } void LDM_destroyCCtx(LDM_CCtx *cctx) { @@ -748,16 +748,16 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, cctx->nextIp = cctx->ip + cctx->step; while (entry == NULL) { - hash_t h; U64 hash; - U32 sum; + hash_t smallHash; + U32 checksum; #ifdef TMP_TAG_INSERT U32 hashEveryMask; #endif setNextHash(cctx); - hash = cctx->nextSum; - h = checksumToHash(hash); - sum = checksumFromHfHash(hash); + hash = cctx->nextHash; + smallHash = getSmallHash(hash); + checksum = getChecksum(hash); #ifdef TMP_TAG_INSERT hashEveryMask = lowerBitsFromHfHash(hash); #endif @@ -770,11 +770,11 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, } #ifdef TMP_TAG_INSERT if (hashEveryMask == HASH_ONLY_EVERY) { - entry = HASH_getBestEntry(cctx, h, sum, + entry = HASH_getBestEntry(cctx, smallHash, checksum, forwardMatchLength, backwardMatchLength); } #else - entry = HASH_getBestEntry(cctx, h, sum, + entry = HASH_getBestEntry(cctx, smallHash, checksum, forwardMatchLength, backwardMatchLength); #endif @@ -850,15 +850,16 @@ size_t LDM_compress(const void *src, size_t srcSize, U64 backwardsMatchLength = 0; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); +#ifdef OUTPUT_CONFIGURATION LDM_outputConfiguration(); +#endif /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); #if LDM_LAG cctx.lagIp = cctx.ip; -// cctx.lagHash = cctx.lastHash; - cctx.lagSum = cctx.lastSum; + cctx.lagHash = cctx.lastHash; #endif /** * Find a match. @@ -918,8 +919,6 @@ size_t LDM_compress(const void *src, size_t srcSize, LDM_updateLastHashFromNextHash(&cctx); } - // HASH_outputTableOffsetHistogram(&cctx); - /* Encode the last literals (no more matches). */ { const U64 lastRun = cctx.iend - cctx.anchor; @@ -943,14 +942,14 @@ size_t LDM_compress(const void *src, size_t srcSize, void LDM_test(const BYTE *src) { const U32 diff = 100; const BYTE *pCur = src + diff; - U64 checksum = getChecksum(pCur, LDM_HASH_LENGTH); + U64 hash = getHash(pCur, LDM_HASH_LENGTH); for (; pCur < src + diff + 60; ++pCur) { - U64 nextSum = getChecksum(pCur + 1, LDM_HASH_LENGTH); - U64 updateSum = updateChecksum(checksum, LDM_HASH_LENGTH, - pCur[0], pCur[LDM_HASH_LENGTH]); - checksum = nextSum; - printf("%llu %llu\n", nextSum, updateSum); + U64 nextHash = getHash(pCur + 1, LDM_HASH_LENGTH); + U64 updatedHash = updateHash(hash, LDM_HASH_LENGTH, + pCur[0], pCur[LDM_HASH_LENGTH]); + hash = nextHash; + printf("%llu %llu\n", nextHash, updatedHash); } } diff --git a/contrib/long_distance_matching/ldm_common.c b/contrib/long_distance_matching/ldm_common.c index 673959dbe..1aa664f0a 100644 --- a/contrib/long_distance_matching/ldm_common.c +++ b/contrib/long_distance_matching/ldm_common.c @@ -109,5 +109,3 @@ size_t LDM_decompress(const void *src, size_t compressedSize, } return dctx.op - (BYTE *)dst; } - - diff --git a/contrib/long_distance_matching/ldm_integrated.c b/contrib/long_distance_matching/ldm_integrated.c index d51c1e9d3..6440c0092 100644 --- a/contrib/long_distance_matching/ldm_integrated.c +++ b/contrib/long_distance_matching/ldm_integrated.c @@ -17,7 +17,7 @@ #define COMPUTE_STATS #define OUTPUT_CONFIGURATION -#define CHECKSUM_CHAR_OFFSET 10 +#define CHECKSUM_CHAR_OFFSET 1 // Take first match only. //#define ZSTD_SKIP From d3d759301f0b6f90b2321f4b2d29120cc4ccd9b5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 13:47:39 -0700 Subject: [PATCH 222/318] changing position of endline for debug --- contrib/adaptive-compression/adapt.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index a90b59902..1b546f581 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -329,7 +329,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); - DEBUG(2, "wc %f\n\n", ctx->writeWaitCompressionCompletion); + DEBUG(2, "wc %f\n", ctx->writeWaitCompressionCompletion); createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; ctx->createWaitCompressionCompletion = 1; @@ -356,7 +356,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); ctx->compressionLevel -= boundChange; - DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n", boundChange); + DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n\n", boundChange); } else if (1-compressWaitWriteCompletion > threshold) { /* compress waiting on write */ @@ -364,7 +364,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; - DEBUG(2, "compress waiting on write, tried to increase compression level by %u\n", boundChange); + DEBUG(2, "compress waiting on write, tried to increase compression level by %u\n\n", boundChange); } else if (1-compressWaitCreateCompletion > threshold) { /* compress waiting on create*/ @@ -373,7 +373,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; - DEBUG(2, "compression waiting on create, tried to increase compression level by %u\n", boundChange); + DEBUG(2, "compression waiting on create, tried to increase compression level by %u\n\n", boundChange); } if (g_forceCompressionLevel) { @@ -517,7 +517,7 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(2, "compression completion %u %f\n", currJob, ctx->compressionCompletion); + DEBUG(3, "compression completion %u %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); } } while (remaining != 0); @@ -608,7 +608,7 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; - DEBUG(2, "write completion %u %f\n", currJob, ctx->writeCompletion); + DEBUG(3, "write completion %u %f\n", currJob, ctx->writeCompletion); pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); if (remaining == 0) break; @@ -750,7 +750,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA remaining -= ret; pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - DEBUG(2, "create completion %u %f\n", currJob, ctx->createCompletion); + DEBUG(3, "create completion %u %f\n", currJob, ctx->createCompletion); pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { From 8328f8192a61d4c4e140eff31f1bdaf034abb805 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 14:40:23 -0700 Subject: [PATCH 223/318] updating debug statements again --- contrib/adaptive-compression/adapt.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 1b546f581..09884c470 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -396,7 +396,7 @@ static void* compressionThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUG(3, "compressionThread(): waiting on job ready\n"); + DEBUG(2, "starting compression for job %u\n", currJob); { /* check if compression thread will have to wait */ @@ -413,7 +413,7 @@ static void* compressionThread(void* arg) if (willWaitForCreate || willWaitForWrite) { - DEBUG(2, "compression will wait for create or write\n"); + DEBUG(2, "compression will wait for create or write on job %u\n", currJob); pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->compressWaitCreateCompletion = ctx->createCompletion; @@ -534,7 +534,7 @@ static void* compressionThread(void* arg) DEBUG(3, "all jobs finished compressing\n"); break; } - + DEBUG(2, "finished compressing job %u\n", currJob); currJob++; } return arg; @@ -567,7 +567,7 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUG(3, "outputThread(): waiting on job compressed\n"); + DEBUG(2, "starting write for job %u\n", currJob); pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); @@ -638,6 +638,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); break; } + DEBUG(2, "finished writing job %u\n", currJob); currJob++; } @@ -738,6 +739,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA size_t const readBlockSize = 1 << 15; size_t remaining = FILE_CHUNK_SIZE; + DEBUG(2, "starting creation of job %u\n", currJob); while (remaining != 0 && !feof(srcFile)) { size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); if (ret != readBlockSize && !feof(srcFile)) { @@ -768,6 +770,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA return error; } } + DEBUG(2, "finished creating job %u\n", currJob); currJob++; if (feof(srcFile)) { DEBUG(3, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); From 0ee3f8c2f8b65a0261158ba9bb8335a0a6900b15 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 15:06:11 -0700 Subject: [PATCH 224/318] adding more debug --- contrib/adaptive-compression/adapt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 09884c470..ec40130a6 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -466,6 +466,7 @@ static void* compressionThread(void* arg) DEBUG(3, "compression level used: %u\n", cLevel); /* reset compressed size */ job->compressedSize = 0; + DEBUG(2, "calling ZSTD_compressBegin()\n"); /* begin compression */ { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); @@ -479,6 +480,7 @@ static void* compressionThread(void* arg) return arg; } } + DEBUG(2, "finished with ZSTD_compressBegin()\n"); do { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); From 4dc83ca64c9e5f0e87d08daf5b349e59ef97d5e1 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 15:14:58 -0700 Subject: [PATCH 225/318] compression thread should take measurements independently based on whether or not the create/write thread will actually bottleneck performance --- contrib/adaptive-compression/adapt.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ec40130a6..ef0da42f8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -412,14 +412,17 @@ static void* compressionThread(void* arg) pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); - if (willWaitForCreate || willWaitForWrite) { - DEBUG(2, "compression will wait for create or write on job %u\n", currJob); - + if (willWaitForCreate) { + DEBUG(2, "compression will wait for create on job %u\n", currJob); pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->compressWaitCreateCompletion = ctx->createCompletion; DEBUG(2, "create completion %f\n", ctx->compressWaitCreateCompletion); pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); + } + + if (willWaitForWrite) { + DEBUG(2, "compression will wait for write on job %u\n", currJob); pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(2, "write completion %f\n", ctx->compressWaitWriteCompletion); From 0295a27133e68f61ce398006a080353453cb2b40 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 24 Jul 2017 15:26:44 -0700 Subject: [PATCH 226/318] Experiment with not using a checksum --- contrib/long_distance_matching/ldm.c | 23 ++++ contrib/long_distance_matching/ldm.h | 20 +--- contrib/long_distance_matching/ldm_64_hash.c | 104 +++++++++++++++--- contrib/long_distance_matching/ldm_common.c | 13 --- .../long_distance_matching/ldm_integrated.c | 37 ++++++- 5 files changed, 151 insertions(+), 46 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 9d3eda326..fae35f9e4 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -10,6 +10,14 @@ #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) #define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) +#define LDM_HASH_ENTRY_SIZE_LOG 3 + +//#define HASH_ONLY_EVERY_LOG 7 +#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG))) + +#define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) + + #define COMPUTE_STATS #define OUTPUT_CONFIGURATION #define CHECKSUM_CHAR_OFFSET 10 @@ -510,6 +518,21 @@ size_t LDM_compress(const void *src, size_t srcSize, } } +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("=====================\n"); +} + + + void LDM_test(const BYTE *src) { (void)src; } diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 3078fb8cd..e1a005e34 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -32,23 +32,15 @@ #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) //These should be multiples of four (and perhaps set to the same value?). -#define LDM_MIN_MATCH_LENGTH 16 -#define LDM_HASH_LENGTH 16 +#define LDM_MIN_MATCH_LENGTH 64 +#define LDM_HASH_LENGTH 64 // Experimental. -//#define TMP_EVICTION -#define TMP_TAG_INSERT -//#define TMP_FORCE_HASH_ONLY +//#define TMP_EVICTION // Experiment with eviction policies. +#define TMP_TAG_INSERT // Insertion policy based on hash. -#define LDM_HASH_ENTRY_SIZE_LOG 3 - -// Insert every (HASH_ONLY_EVERY + 1) into the hash table. -#ifdef TMP_FORCE_HASH_ONLY - #define HASH_ONLY_EVERY_LOG 7 -#else - #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 USE_CHECKSUM 1 +//#define USE_CHECKSUM (HASH_BUCKET_SIZE_LOG) typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; diff --git a/contrib/long_distance_matching/ldm_64_hash.c b/contrib/long_distance_matching/ldm_64_hash.c index 06ddf5207..c74d71ff0 100644 --- a/contrib/long_distance_matching/ldm_64_hash.c +++ b/contrib/long_distance_matching/ldm_64_hash.c @@ -7,9 +7,20 @@ #include "ldm.h" #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) +#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) #define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) -/* Hash table stuff. */ +#if USE_CHECKSUM + #define LDM_HASH_ENTRY_SIZE_LOG 3 +#else + #define LDM_HASH_ENTRY_SIZE_LOG 2 +#endif + +//#define HASH_ONLY_EVERY_LOG 7 +#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG))) + +#define HASH_ONLY_EVERY ((1 << (HASH_ONLY_EVERY_LOG)) - 1) + #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) @@ -27,10 +38,16 @@ 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; @@ -39,6 +56,8 @@ struct LDM_compressStats { U64 totalLiteralLength; U64 totalOffset; + U32 matchLengthHistogram[32]; + U32 minOffset, maxOffset; U32 offsetHistogram[32]; @@ -262,12 +281,19 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, LDM_hashEntry *cur = bucket; LDM_hashEntry *bestEntry = NULL; U64 bestMatchLength = 0; +#if !(USE_CHECKSUM) + (void)checksum; +#endif for (; 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; @@ -448,12 +474,18 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { stats->minOffset, stats->maxOffset); printf("\n"); - printf("offset histogram: offset, num matches, %% of matches\n"); + printf("offset histogram | match length histogram\n"); + printf("offset/ML, num matches, %% of matches | num matches, %% of matches\n"); for (; i <= intLog2(stats->maxOffset); i++) { - printf("2^%*d: %10u %6.3f%%\n", 2, 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"); @@ -619,23 +651,32 @@ static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hash) { // TODO: Off by one, but not important. if (cctx->lagIp - cctx->ibase > 0) { U32 smallHash = getSmallHash(cctx->lagHash); + +# if USE_CHECKSUM U32 checksum = getChecksum(cctx->lagHash); const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, checksum }; -#ifdef TMP_EVICTION - HASH_insert(cctx->hashTable, smallHash, entry, cctx); -#else - HASH_insert(cctx->hashTable, smallHash, entry); -#endif - } else { - U32 smallHash = getSmallHash(hash); - U32 checksum = getChecksum(hash); +# else + const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase }; +# endif - const LDM_hashEntry entry = { cctx->ip - cctx->ibase, checksum }; -#ifdef TMP_EVICTION +# ifdef TMP_EVICTION HASH_insert(cctx->hashTable, smallHash, entry, cctx); -#else +# else HASH_insert(cctx->hashTable, smallHash, entry); -#endif +# endif + } else { +# if USE_CHECKSUM + U32 checksum = getChecksum(hash); + const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, checksum }; +# else + const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase }; +# endif + +# ifdef TMP_EVICTION + HASH_insert(cctx->hashTable, smallHash, entry, cctx); +# else + HASH_insert(cctx->hashTable, smallHash, entry); +# endif } } #else @@ -646,8 +687,12 @@ static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hash) { 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 #ifdef TMP_EVICTION HASH_insert(cctx->hashTable, smallHash, entry, cctx); #else @@ -711,8 +756,11 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, 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 cctx->stats.minOffset = UINT_MAX; cctx->stats.windowSizeLog = LDM_WINDOW_SIZE_LOG; cctx->stats.hashTableSizeLog = LDM_MEMORY_USAGE; @@ -755,6 +803,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, U32 hashEveryMask; #endif setNextHash(cctx); + hash = cctx->nextHash; smallHash = getSmallHash(hash); checksum = getChecksum(hash); @@ -770,6 +819,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, } #ifdef TMP_TAG_INSERT if (hashEveryMask == HASH_ONLY_EVERY) { + entry = HASH_getBestEntry(cctx, smallHash, checksum, forwardMatchLength, backwardMatchLength); } @@ -781,7 +831,9 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, if (entry != NULL) { *match = entry->offset + cctx->ibase; } + putHashOfCurrentPositionFromHash(cctx, hash); + } setNextHash(cctx); return 0; @@ -850,6 +902,7 @@ size_t LDM_compress(const void *src, size_t srcSize, U64 backwardsMatchLength = 0; LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); + #ifdef OUTPUT_CONFIGURATION LDM_outputConfiguration(); #endif @@ -869,6 +922,7 @@ size_t LDM_compress(const void *src, size_t srcSize, */ while (LDM_findBestMatch(&cctx, &match, &forwardMatchLength, &backwardsMatchLength) == 0) { + #ifdef COMPUTE_STATS cctx.stats.numMatches++; #endif @@ -898,6 +952,8 @@ size_t LDM_compress(const void *src, size_t srcSize, 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. @@ -938,6 +994,22 @@ size_t LDM_compress(const void *src, size_t srcSize, } } +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("=====================\n"); +} + + + // TODO: implement and test hash function void LDM_test(const BYTE *src) { const U32 diff = 100; diff --git a/contrib/long_distance_matching/ldm_common.c b/contrib/long_distance_matching/ldm_common.c index 1aa664f0a..1953656e3 100644 --- a/contrib/long_distance_matching/ldm_common.c +++ b/contrib/long_distance_matching/ldm_common.c @@ -2,19 +2,6 @@ #include "ldm.h" -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("=====================\n"); -} - void LDM_readHeader(const void *src, U64 *compressedSize, U64 *decompressedSize) { const BYTE *ip = (const BYTE *)src; diff --git a/contrib/long_distance_matching/ldm_integrated.c b/contrib/long_distance_matching/ldm_integrated.c index 6440c0092..b80a5c017 100644 --- a/contrib/long_distance_matching/ldm_integrated.c +++ b/contrib/long_distance_matching/ldm_integrated.c @@ -7,10 +7,16 @@ #include "ldm.h" #define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASH_ENTRY_SIZE_LOG 3 #define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2) #define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) +#define LDM_HASH_ENTRY_SIZE_LOG 3 +//#define HASH_ONLY_EVERY_LOG 7 +#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG))) + +#define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) + + /* Hash table stuff. */ #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) @@ -38,6 +44,8 @@ struct LDM_compressStats { U64 totalLiteralLength; U64 totalOffset; + U32 matchLengthHistogram[32]; + U32 minOffset, maxOffset; U32 offsetHistogram[32]; @@ -358,12 +366,18 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { stats->minOffset, stats->maxOffset); printf("\n"); - printf("offset histogram: offset, num matches, %% of matches\n"); + printf("offset histogram | match length histogram\n"); + printf("offset/ML, num matches, %% of matches | num matches, %% of matches\n"); for (; i <= intLog2(stats->maxOffset); i++) { - printf("2^%*d: %10u %6.3f%%\n", 2, 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"); @@ -742,6 +756,8 @@ size_t LDM_compress(const void *src, size_t srcSize, 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. @@ -784,6 +800,21 @@ size_t LDM_compress(const void *src, size_t srcSize, } } +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("=====================\n"); +} + + + // TODO: implement and test hash function void LDM_test(const BYTE *src) { (void)src; From df3754b6ed4f0f762f4702457eecc87f89593ec6 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 16:19:07 -0700 Subject: [PATCH 227/318] add quiet option, make progress bar default --- contrib/adaptive-compression/adapt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ef0da42f8..c4c46af5d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -31,7 +31,7 @@ static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; static UTIL_time_t g_startTime; static size_t g_streamedSize = 0; -static unsigned g_useProgressBar = 0; +static unsigned g_useProgressBar = 1; static UTIL_freq_t g_ticksPerSecond; static unsigned g_forceCompressionLevel = 0; @@ -903,6 +903,7 @@ static void help() PRINT(" -i# : provide initial compression level\n"); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); + PRINT(" -q : quiet mode -- do not show progress bar or other information\n"); } /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) @@ -953,6 +954,10 @@ int main(int argCount, const char* argv[]) case 'f': g_forceCompressionLevel = 1; break; + case 'q': + g_useProgressBar = 0; + g_displayLevel = 0; + break; default: DISPLAY("Error: invalid argument provided\n"); ret = 1; From 700758d67677836cc9208d868f49b9bc522b2552 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 16:26:20 -0700 Subject: [PATCH 228/318] added help statement for -p, switched it to hide progress bar now that progress bar is default --- contrib/adaptive-compression/adapt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index c4c46af5d..f6bcddd18 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -903,6 +903,7 @@ static void help() PRINT(" -i# : provide initial compression level\n"); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); + PRINT(" -p : hide progress bar\n"); PRINT(" -q : quiet mode -- do not show progress bar or other information\n"); } /* return 0 if successful, else return error */ @@ -945,7 +946,7 @@ int main(int argCount, const char* argv[]) help(); goto _main_exit; case 'p': - g_useProgressBar = 1; + g_useProgressBar = 0; break; case 'c': forceStdout = 1; From 6f1e260eddd08001631d6f8c5d760f9da6674e3a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 10:01:10 -0700 Subject: [PATCH 229/318] added mechanism for getting rid of spikes --- contrib/adaptive-compression/adapt.c | 38 +++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index f6bcddd18..32f54e67b 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -26,6 +26,7 @@ #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 0 #define MAX_COMPRESSION_LEVEL_CHANGE 2 +#define CONVERGENCE_LOWER_BOUND 3 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -77,6 +78,7 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; + unsigned convergenceCounter; double createWaitCompressionCompletion; double compressWaitCreateCompletion; double compressWaitWriteCompletion; @@ -213,6 +215,7 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->createCompletion = 1; ctx->writeCompletion = 1; ctx->compressionCompletion = 1; + ctx->convergenceCounter = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -323,6 +326,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double compressWaitWriteCompletion; double writeWaitCompressionCompletion; double const threshold = 0.00001; + unsigned const prevCompressionLevel = ctx->compressionLevel; DEBUG(2, "adapting compression level %u\n", ctx->compressionLevel); /* read and reset completion measurements */ @@ -347,7 +351,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; ctx->compressWaitCreateCompletion = 1; pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); - + DEBUG(2, "convergence counter: %u\n", ctx->convergenceCounter); /* adaptation logic */ if (1-createWaitCompressionCompletion > threshold || 1-writeWaitCompressionCompletion > threshold) { /* create or write waiting on compression */ @@ -355,7 +359,15 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); - ctx->compressionLevel -= boundChange; + if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { + /* reset convergence counter, might have been a spike */ + ctx->convergenceCounter = 0; + } + else if (boundChange != 0) { + ctx->compressionLevel -= boundChange; + ctx->convergenceCounter = 1; + } + DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n\n", boundChange); } else if (1-compressWaitWriteCompletion > threshold) { @@ -363,7 +375,14 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double const completion = compressWaitWriteCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - ctx->compressionLevel += boundChange; + if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { + ctx->convergenceCounter = 0; + } + else if (boundChange != 0) { + ctx->compressionLevel += boundChange; + ctx->convergenceCounter = 1; + } + DEBUG(2, "compress waiting on write, tried to increase compression level by %u\n\n", boundChange); } else if (1-compressWaitCreateCompletion > threshold) { @@ -372,10 +391,21 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double const completion = compressWaitCreateCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - ctx->compressionLevel += boundChange; + if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { + ctx->convergenceCounter = 0; + } + else if (boundChange != 0) { + ctx->compressionLevel += boundChange; + ctx->convergenceCounter = 1; + } + DEBUG(2, "compression waiting on create, tried to increase compression level by %u\n\n", boundChange); } + if (ctx->compressionLevel == prevCompressionLevel) { + ctx->convergenceCounter++; + } + if (g_forceCompressionLevel) { ctx->compressionLevel = g_compressionLevel; } From 85d7c919f6b85ef4deb82bc7e4c2cdbfc49b2e10 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 10:32:14 -0700 Subject: [PATCH 230/318] created independent function for controlling how completion relates to compression level change --- contrib/adaptive-compression/adapt.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 32f54e67b..87082be46 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -311,6 +311,19 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); } +static unsigned convertCompletionToChange(double completion) +{ + if (completion < 0.05) { + return 2; + } + else if (completion < 0.5) { + return 1; + } + else { + return 0; + } +} + /* * Compression level is changed depending on which part of the compression process is lagging * Currently, three theads exist for job creation, compression, and file writing respectively. @@ -357,7 +370,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* create or write waiting on compression */ /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); - unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ @@ -373,7 +386,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) else if (1-compressWaitWriteCompletion > threshold) { /* compress waiting on write */ double const completion = compressWaitWriteCompletion; - unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { ctx->convergenceCounter = 0; @@ -389,7 +402,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* compress waiting on create*/ /* use compressWaitCreateCompletion */ double const completion = compressWaitCreateCompletion; - unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { ctx->convergenceCounter = 0; From e02c79f8336f4057b9f01a5e9565e9269d701e01 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 11:16:27 -0700 Subject: [PATCH 231/318] started using decrease cooldown so that compression level would not decrease several times in a row --- contrib/adaptive-compression/adapt.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 87082be46..fac93d273 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -26,7 +26,8 @@ #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 0 #define MAX_COMPRESSION_LEVEL_CHANGE 2 -#define CONVERGENCE_LOWER_BOUND 3 +#define CONVERGENCE_LOWER_BOUND 5 +#define CLEVEL_DECREASE_COOLDOWN 5 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -79,6 +80,7 @@ typedef struct { unsigned allJobsCompleted; unsigned adaptParam; unsigned convergenceCounter; + unsigned cooldown; double createWaitCompressionCompletion; double compressWaitCreateCompletion; double compressWaitWriteCompletion; @@ -216,6 +218,7 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->writeCompletion = 1; ctx->compressionCompletion = 1; ctx->convergenceCounter = 0; + ctx->cooldown = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -365,8 +368,11 @@ static void adaptCompressionLevel(adaptCCtx* ctx) ctx->compressWaitCreateCompletion = 1; pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); DEBUG(2, "convergence counter: %u\n", ctx->convergenceCounter); + /* adaptation logic */ - if (1-createWaitCompressionCompletion > threshold || 1-writeWaitCompressionCompletion > threshold) { + if (ctx->cooldown) ctx->cooldown--; + + if ((1-createWaitCompressionCompletion > threshold || 1-writeWaitCompressionCompletion > threshold) && ctx->cooldown == 0) { /* create or write waiting on compression */ /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); @@ -378,6 +384,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) } else if (boundChange != 0) { ctx->compressionLevel -= boundChange; + ctx->cooldown = CLEVEL_DECREASE_COOLDOWN; ctx->convergenceCounter = 1; } From ae20d413daf35cac05fdb76cb20bbb3b1bf053a1 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 25 Jul 2017 12:52:01 -0700 Subject: [PATCH 232/318] [libzstd] Fix CHECK_V_F macros --- lib/compress/fse_compress.c | 2 +- lib/compress/huf_compress.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index 26e8052dd..3a03627cc 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -781,7 +781,7 @@ size_t FSE_compress_usingCTable (void* dst, size_t dstSize, size_t FSE_compressBound(size_t size) { return FSE_COMPRESSBOUND(size); } -#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return f +#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return e #define CHECK_F(f) { CHECK_V_F(_var_err__, f); } /* FSE_compress_wksp() : diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index beb4fdb60..953cb5f21 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -56,7 +56,7 @@ * Error Management ****************************************************************/ #define HUF_STATIC_ASSERT(c) { enum { HUF_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ -#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return f +#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return e #define CHECK_F(f) { CHECK_V_F(_var_err__, f); } From 310c12d07eb5a982fdcd2c025b5e3c98ec181c1b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 14:08:39 -0700 Subject: [PATCH 233/318] moved debug statements to a compiler flag --- contrib/adaptive-compression/Makefile | 3 +++ contrib/adaptive-compression/adapt.c | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index f2059a193..9bc19ee15 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -24,6 +24,9 @@ all: adapt datagen adapt: $(ZSTD_FILES) adapt.c $(CC) $(FLAGS) $^ -o $@ +adapt-debug: $(ZSTD_FILES) adapt.c + $(CC) $(FLAGS) -DDEBUG_MODE=2 $^ -o adapt + datagen : $(PRGDIR)/datagen.c datagencli.c $(CC) $(FLAGS) $^ -o $@$(EXT) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index fac93d273..d1fe753c9 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -29,7 +29,12 @@ #define CONVERGENCE_LOWER_BOUND 5 #define CLEVEL_DECREASE_COOLDOWN 5 +#ifndef DEBUG_MODE static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; +#else +static int g_displayLevel = DEBUG_MODE; +#endif + static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; static UTIL_time_t g_startTime; static size_t g_streamedSize = 0; @@ -949,7 +954,6 @@ static void help() PRINT("\n"); PRINT("Options:\n"); PRINT(" -oFILE : specify the output file name\n"); - PRINT(" -v : display debug information\n"); PRINT(" -i# : provide initial compression level\n"); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); @@ -984,9 +988,6 @@ int main(int argCount, const char* argv[]) argument += 2; outFilename = argument; break; - case 'v': - g_displayLevel++; - break; case 'i': argument += 2; g_compressionLevel = readU32FromChar(&argument); From 0882cd1981dd59dff606d379a4cf294785a5594a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 14:26:55 -0700 Subject: [PATCH 234/318] progress bar -- don't print num jobs, time elapsed shown in seconds --- contrib/adaptive-compression/adapt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index d1fe753c9..098f6fc90 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -600,7 +600,7 @@ static void* compressionThread(void* arg) return arg; } -static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) +static void displayProgress(unsigned cLevel, unsigned last) { if (!g_useProgressBar) return; UTIL_time_t currTime; @@ -608,7 +608,7 @@ static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0); double const sizeMB = (double)g_streamedSize / (1 << 20); double const avgCompRate = sizeMB * 1000 / timeElapsed; - fprintf(stderr, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); + fprintf(stderr, "\r| Comp. Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate); if (last) { fprintf(stderr, "\n"); } @@ -681,7 +681,7 @@ static void* outputThread(void* arg) } } DEBUG(3, "finished job write %u\n", currJob); - displayProgress(currJob, ctx->compressionLevel, job->lastJobPlusOne == currJob + 1); + displayProgress(ctx->compressionLevel, job->lastJobPlusOne == currJob + 1); DEBUG(3, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); ctx->jobWriteID++; From 5cfbf609a4d6afeaf7b2f101f9bf838b1d20f9e5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 14:31:48 -0700 Subject: [PATCH 235/318] removed old debug statements no longer being used --- contrib/adaptive-compression/adapt.c | 35 ---------------------------- 1 file changed, 35 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 098f6fc90..722ae243d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -504,10 +504,6 @@ static void* compressionThread(void* arg) ctx->compressionCompletion = 0; pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); - DEBUG(3, "compressionThread(): continuing after job ready\n"); - DEBUG(3, "DICTIONARY ENDED\n"); - DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); - /* adapt compression level */ if (currJob) adaptCompressionLevel(ctx); @@ -520,15 +516,12 @@ static void* compressionThread(void* arg) size_t remaining = job->src.size; size_t srcPos = 0; size_t dstPos = 0; - DEBUG(3, "cLevel used: %u\n", cLevel); - DEBUG(3, "compression level used: %u\n", cLevel); /* reset compressed size */ job->compressedSize = 0; DEBUG(2, "calling ZSTD_compressBegin()\n"); /* begin compression */ { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); - DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); @@ -542,8 +535,6 @@ static void* compressionThread(void* arg) do { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); - DEBUG(3, "remaining: %zu\n", remaining); - DEBUG(3, "actualBlockSize: %zu\n", actualBlockSize); /* continue compression */ if (currJob != 0 || blockNum != 0) { /* not first block of first job flush/overwrite the frame header */ @@ -557,9 +548,6 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } { - DEBUG(3, "write out ending: %d\n", (job->lastJobPlusOne == currJob + 1) && (remaining == actualBlockSize)); - DEBUG(3, "lastJobPlusOne %u\n", job->lastJobPlusOne); - DEBUG(3, "compressionBlockSize %zu\n", compressionBlockSize); size_t const ret = (job->lastJobPlusOne == currJob + 1 && remaining == actualBlockSize) ? ZSTD_compressEnd (ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize) : ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize); @@ -577,7 +565,6 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(3, "compression completion %u %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); } } while (remaining != 0); @@ -585,13 +572,10 @@ static void* compressionThread(void* arg) } pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); ctx->jobCompressedID++; - DEBUG(3, "signaling for job %u\n", currJob); pthread_cond_broadcast(&ctx->jobCompressed_cond.pCond); pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); - DEBUG(3, "finished job compression %u\n", currJob); if (job->lastJobPlusOne == currJob + 1 || ctx->threadError) { /* finished compressing all jobs */ - DEBUG(3, "all jobs finished compressing\n"); break; } DEBUG(2, "finished compressing job %u\n", currJob); @@ -633,7 +617,6 @@ static void* outputThread(void* arg) pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); /* write thread is waiting on compression thread */ ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; - DEBUG(3, "write thread waiting : writeWaitCompressionCompletion %f\n", ctx->writeWaitCompressionCompletion); DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion); pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -645,7 +628,6 @@ static void* outputThread(void* arg) ctx->writeCompletion = 0; pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); - DEBUG(3, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; size_t remaining = compressedSize; @@ -668,7 +650,6 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; - DEBUG(3, "write completion %u %f\n", currJob, ctx->writeCompletion); pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); if (remaining == 0) break; @@ -680,18 +661,14 @@ static void* outputThread(void* arg) } } } - DEBUG(3, "finished job write %u\n", currJob); displayProgress(ctx->compressionLevel, job->lastJobPlusOne == currJob + 1); - DEBUG(3, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); ctx->jobWriteID++; pthread_cond_signal(&ctx->jobWrite_cond.pCond); pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); - DEBUG(3, "unlocking job write mutex\n"); if (job->lastJobPlusOne == currJob + 1 || ctx->threadError) { /* finished with all jobs */ - DEBUG(3, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); ctx->allJobsCompleted = 1; pthread_cond_signal(&ctx->allJobsCompleted_cond.pCond); @@ -710,7 +687,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; - DEBUG(3, "createCompressionJob(): wait for job write\n"); /* wait until the job has been compressed */ @@ -719,8 +695,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); /* creation thread is waiting, take measurement of completion */ ctx->createWaitCompressionCompletion = ctx->compressionCompletion; - DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f\n", ctx->createWaitCompressionCompletion); - DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -730,9 +704,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 0; pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); - DEBUG(3, "createCompressionJob(): continuing after job write\n"); - - DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); job->compressionLevel = ctx->compressionLevel; job->src.size = srcSize; job->jobID = nextJob; @@ -745,13 +716,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) } job->dictSize = ctx->lastDictSize; - DEBUG(3, "finished job creation %u\n", nextJob); ctx->nextJobID++; - DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); /* if not on the last job, reuse data as dictionary in next job */ if (!last) { size_t const oldDictSize = ctx->lastDictSize; - DEBUG(3, "oldDictSize %zu\n", oldDictSize); memcpy(ctx->input.buffer.start, job->src.start + oldDictSize, srcSize); ctx->lastDictSize = srcSize; ctx->input.filled = srcSize; @@ -812,7 +780,6 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA remaining -= ret; pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - DEBUG(3, "create completion %u %f\n", currJob, ctx->createCompletion); pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { @@ -833,7 +800,6 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA DEBUG(2, "finished creating job %u\n", currJob); currJob++; if (feof(srcFile)) { - DEBUG(3, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); break; } } @@ -991,7 +957,6 @@ int main(int argCount, const char* argv[]) case 'i': argument += 2; g_compressionLevel = readU32FromChar(&argument); - DEBUG(3, "g_compressionLevel: %u\n", g_compressionLevel); break; case 'h': help(); From 31a9ed9883f8f4313fb5410a9bce9734ae77328d Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 14:53:40 -0700 Subject: [PATCH 236/318] updated const values, added more comments --- contrib/adaptive-compression/adapt.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 722ae243d..9ebe2f9f9 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -319,6 +319,7 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); } +/* map completion percentages to values for changing compression level */ static unsigned convertCompletionToChange(double completion) { if (completion < 0.05) { @@ -350,11 +351,11 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const prevCompressionLevel = ctx->compressionLevel; DEBUG(2, "adapting compression level %u\n", ctx->compressionLevel); - /* read and reset completion measurements */ + /* read and reset completion measurements */ pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); - DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); - DEBUG(2, "wc %f\n", ctx->writeWaitCompressionCompletion); + DEBUG(2, "createWaitCompressionCompletion %f\n", ctx->createWaitCompressionCompletion); + DEBUG(2, "writeWaitCompressionCompletion %f\n", ctx->writeWaitCompressionCompletion); createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; ctx->createWaitCompressionCompletion = 1; @@ -362,13 +363,13 @@ static void adaptCompressionLevel(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); - DEBUG(2, "cw %f\n", ctx->compressWaitWriteCompletion); + DEBUG(2, "compressWaitWriteCompletion %f\n", ctx->compressWaitWriteCompletion); compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; ctx->compressWaitWriteCompletion = 1; pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); - DEBUG(2, "cr %f\n", ctx->compressWaitCreateCompletion); + DEBUG(2, "compressWaitCreateCompletion %f\n", ctx->compressWaitCreateCompletion); compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; ctx->compressWaitCreateCompletion = 1; pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); @@ -439,8 +440,8 @@ static void adaptCompressionLevel(adaptCCtx* ctx) static size_t getUseableDictSize(unsigned compressionLevel) { ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, 0); - unsigned overlapLog = compressionLevel >= (unsigned)ZSTD_maxCLevel() ? 0 : 3; - size_t overlapSize = 1 << (params.cParams.windowLog - overlapLog); + unsigned const overlapLog = compressionLevel >= (unsigned)ZSTD_maxCLevel() ? 0 : 3; + size_t const overlapSize = 1 << (params.cParams.windowLog - overlapLog); return overlapSize; } @@ -450,7 +451,7 @@ static void* compressionThread(void* arg) unsigned currJob = 0; for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; - jobDescription* job = &ctx->jobs[currJobIndex]; + jobDescription* const job = &ctx->jobs[currJobIndex]; DEBUG(2, "starting compression for job %u\n", currJob); { @@ -610,7 +611,7 @@ static void* outputThread(void* arg) unsigned currJob = 0; for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; - jobDescription* job = &ctx->jobs[currJobIndex]; + jobDescription* const job = &ctx->jobs[currJobIndex]; DEBUG(2, "starting write for job %u\n", currJob); pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { @@ -686,7 +687,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) { unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; - jobDescription* job = &ctx->jobs[nextJobIndex]; + jobDescription* const job = &ctx->jobs[nextJobIndex]; /* wait until the job has been compressed */ @@ -736,6 +737,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadArg* otArg) { + /* early error check to exit */ if (!ctx || !srcFile || !otArg) { return 1; } From 629c30011880ed0f21d454993d53db7aae0cfdbc Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Tue, 25 Jul 2017 15:17:36 -0700 Subject: [PATCH 237/318] Rename and remove unneeded files --- contrib/long_distance_matching/Makefile | 13 +- .../circular_buffer_table.c | 256 ------------------ contrib/long_distance_matching/ldm.h | 14 +- .../{ldm_integrated.c => ldm_hash32.c} | 0 .../{ldm_64_hash.c => ldm_hash64.c} | 65 +++-- .../long_distance_matching/ldm_hashtable.h | 91 ------- .../{main-ldm.c => main.c} | 15 +- 7 files changed, 65 insertions(+), 389 deletions(-) delete mode 100644 contrib/long_distance_matching/circular_buffer_table.c rename contrib/long_distance_matching/{ldm_integrated.c => ldm_hash32.c} (100%) rename contrib/long_distance_matching/{ldm_64_hash.c => ldm_hash64.c} (96%) delete mode 100644 contrib/long_distance_matching/ldm_hashtable.h rename contrib/long_distance_matching/{main-ldm.c => main.c} (95%) diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index e1c31112d..292ce8517 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -1,5 +1,5 @@ # ################################################################ -# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. +# Copyright (c) 2016-present, Facebook, Inc. # All rights reserved. # # This source code is licensed under the BSD-style license found in the @@ -25,19 +25,16 @@ LDFLAGS += -lzstd default: all -all: main-circular-buffer main-integrated main-64 +all: main-hash32 main-hash64 -main-circular-buffer: ldm_common.c circular_buffer_table.c ldm.c main-ldm.c +main-hash64: ldm_common.c ldm_hash64.c main.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ -main-64: ldm_common.c ldm_64_hash.c main-ldm.c - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -main-integrated: ldm_common.c ldm_integrated.c main-ldm.c +main-hash32: ldm_common.c ldm_hash32.c main.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main-circular-buffer main-64 main-integrated + main-hash64 main-hash32 @echo Cleaning completed diff --git a/contrib/long_distance_matching/circular_buffer_table.c b/contrib/long_distance_matching/circular_buffer_table.c deleted file mode 100644 index 92ffc55bd..000000000 --- a/contrib/long_distance_matching/circular_buffer_table.c +++ /dev/null @@ -1,256 +0,0 @@ -#include -#include - -#include "ldm.h" -#include "ldm_hashtable.h" -#include "mem.h" - -// THe number of elements per hash bucket. -// HASH_BUCKET_SIZE_LOG is defined in ldm.h. -#define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) - -// The number of hash buckets. -#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) - -// If ZSTD_SKIP is defined, then the first entry is returned in HASH_getBestEntry -// (without looking at other entries in the bucket). -//#define ZSTD_SKIP - -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. - - const BYTE *offsetBase; // Corresponds to offset=0 in LDM_hashEntry. - U32 minMatchLength; - U32 maxWindowSize; -}; - -LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase, - U32 minMatchLength, U32 maxWindowSize) { - LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); - 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)); - table->offsetBase = offsetBase; - table->minMatchLength = minMatchLength; - table->maxWindowSize = maxWindowSize; - return table; -} - -static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { - return table->entries + (hash << HASH_BUCKET_SIZE_LOG); -} - -// From lib/compress/zstd_compress.c -static unsigned ZSTD_NbCommonBytes (register 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; - const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ - 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 - * Returns the number of bytes (consecutively) in common between pIn and pMatch - * up to pInLimit. - */ -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); -} - -/** - * Returns the number of bytes in common between pIn and pMatch, - * counting backwards, with pIn having a lower limit of pAnchor and - * pMatch having a lower limit of pBase. - */ -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; -} - -LDM_hashEntry *HASH_getBestEntry(const LDM_hashTable *table, - const hash_t hash, - const U32 checksum, - const BYTE *pIn, - const BYTE *pEnd, - const BYTE *pAnchor, - U64 *pForwardMatchLength, - U64 *pBackwardMatchLength) { - LDM_hashEntry *bucket = getBucket(table, hash); - LDM_hashEntry *cur = bucket; - LDM_hashEntry *bestEntry = NULL; - U64 bestMatchLength = 0; - for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { - const BYTE *pMatch = cur->offset + table->offsetBase; - - // Check checksum for faster check. - if (cur->checksum == checksum && pIn - pMatch <= table->maxWindowSize) { - U64 forwardMatchLength = ZSTD_count(pIn, pMatch, pEnd); - U64 backwardMatchLength, totalMatchLength; - - // Only take matches where the forwardMatchLength is large enough - // for speed. - if (forwardMatchLength < table->minMatchLength) { - continue; - } - backwardMatchLength = - countBackwardsMatch(pIn, pAnchor, cur->offset + table->offsetBase, - table->offsetBase); - - 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; -} - -hash_t HASH_hashU32(U32 value) { - return ((value * 2654435761U) >> (32 - LDM_HASHLOG)); -} - -void HASH_insert(LDM_hashTable *table, - const hash_t hash, const LDM_hashEntry entry) { - // Circular buffer. - *(getBucket(table, hash) + table->bucketOffsets[hash]) = entry; - table->bucketOffsets[hash]++; - table->bucketOffsets[hash] &= HASH_BUCKET_SIZE - 1; -} - -U32 HASH_getSize(const LDM_hashTable *table) { - return table->numBuckets; -} - -void HASH_destroyTable(LDM_hashTable *table) { - free(table->entries); - free(table->bucketOffsets); - free(table); -} - -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++; - } - } - - printf("Num buckets, bucket size: %d, %d\n", - table->numBuckets, HASH_BUCKET_SIZE); - printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n", - table->numEntries, ctr, - 100.0 * (double)(ctr) / table->numEntries); -} diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index e1a005e34..f9ad383e1 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -17,17 +17,22 @@ // THe number of bytes storing the offset. #define LDM_OFFSET_SIZE 4 +// ============================================================================= +// User parameters. +// ============================================================================= + // 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 +#define LDM_MEMORY_USAGE 25 // The number of entries in a hash bucket. -#define HASH_BUCKET_SIZE_LOG 0 // The maximum is 4 for now. +#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. #define LDM_WINDOW_SIZE_LOG 28 // Max value is 30 #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) @@ -37,10 +42,11 @@ // Experimental. //#define TMP_EVICTION // Experiment with eviction policies. -#define TMP_TAG_INSERT // Insertion policy based on hash. +#define INSERT_BY_TAG // Insertion policy based on hash. #define USE_CHECKSUM 1 -//#define USE_CHECKSUM (HASH_BUCKET_SIZE_LOG) + +// ============================================================================= typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; diff --git a/contrib/long_distance_matching/ldm_integrated.c b/contrib/long_distance_matching/ldm_hash32.c similarity index 100% rename from contrib/long_distance_matching/ldm_integrated.c rename to contrib/long_distance_matching/ldm_hash32.c diff --git a/contrib/long_distance_matching/ldm_64_hash.c b/contrib/long_distance_matching/ldm_hash64.c similarity index 96% rename from contrib/long_distance_matching/ldm_64_hash.c rename to contrib/long_distance_matching/ldm_hash64.c index c74d71ff0..e51ac57d3 100644 --- a/contrib/long_distance_matching/ldm_64_hash.c +++ b/contrib/long_distance_matching/ldm_hash64.c @@ -489,7 +489,7 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { (double) stats->numMatches); } printf("\n"); -#ifdef TMP_TAG_INSERT +#ifdef INSERT_BY_TAG /* printf("Lower bit distribution\n"); for (i = 0; i < (1 << HASH_ONLY_EVERY_LOG); i++) { @@ -524,7 +524,7 @@ static U32 getChecksum(U64 hash) { return (hash >> (64 - 32 - LDM_HASHLOG)) & 0xFFFFFFFF; } -#ifdef TMP_TAG_INSERT +#ifdef INSERT_BY_TAG static U32 lowerBitsFromHfHash(U64 hash) { // The number of bits used so far is LDM_HASHLOG + 32. // So there are 32 - LDM_HASHLOG bits left. @@ -611,7 +611,7 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->lastPosHashed[LDM_HASH_LENGTH]); cctx->nextPosHashed = cctx->nextIp; -#ifdef TMP_TAG_INSERT +#ifdef INSERT_BY_TAG { U32 hashEveryMask = lowerBitsFromHfHash(cctx->nextHash); cctx->stats.TMP_totalHashCount++; @@ -647,9 +647,13 @@ 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->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { - // TODO: Off by one, but not important. - if (cctx->lagIp - cctx->ibase > 0) { + if (cctx -> lagIp - cctx->ibase > 0) { +#ifdef 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 @@ -664,23 +668,32 @@ static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hash) { # else HASH_insert(cctx->hashTable, smallHash, entry); # endif - } else { -# if USE_CHECKSUM - U32 checksum = getChecksum(hash); - const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, checksum }; -# else - const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase }; -# endif + } + } else { +#ifdef 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); -# ifdef TMP_EVICTION +#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 + +#ifdef TMP_EVICTION HASH_insert(cctx->hashTable, smallHash, entry, cctx); -# else +#else HASH_insert(cctx->hashTable, smallHash, entry); -# endif +#endif } } #else -#ifdef TMP_TAG_INSERT +#ifdef INSERT_BY_TAG U32 hashEveryMask = lowerBitsFromHfHash(hash); if (hashEveryMask == HASH_ONLY_EVERY) { #else @@ -799,7 +812,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, U64 hash; hash_t smallHash; U32 checksum; -#ifdef TMP_TAG_INSERT +#ifdef INSERT_BY_TAG U32 hashEveryMask; #endif setNextHash(cctx); @@ -807,7 +820,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, hash = cctx->nextHash; smallHash = getSmallHash(hash); checksum = getChecksum(hash); -#ifdef TMP_TAG_INSERT +#ifdef INSERT_BY_TAG hashEveryMask = lowerBitsFromHfHash(hash); #endif @@ -817,7 +830,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, if (cctx->ip > cctx->imatchLimit) { return 1; } -#ifdef TMP_TAG_INSERT +#ifdef INSERT_BY_TAG if (hashEveryMask == HASH_ONLY_EVERY) { entry = HASH_getBestEntry(cctx, smallHash, checksum, @@ -1003,13 +1016,17 @@ void LDM_outputConfiguration(void) { 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("LDM_LAG: %d\n", LDM_LAG); + printf("USE_CHECKSUM: %d\n", USE_CHECKSUM); +#ifdef INSERT_BY_TAG + printf("INSERT_BY_TAG: %d\n", 1); +#else + printf("INSERT_BY_TAG: %d\n", 0); +#endif + printf("HASH_CHAR_OFFSET: %d\n", HASH_CHAR_OFFSET); printf("=====================\n"); } - - // TODO: implement and test hash function void LDM_test(const BYTE *src) { const U32 diff = 100; diff --git a/contrib/long_distance_matching/ldm_hashtable.h b/contrib/long_distance_matching/ldm_hashtable.h deleted file mode 100644 index 6093197dd..000000000 --- a/contrib/long_distance_matching/ldm_hashtable.h +++ /dev/null @@ -1,91 +0,0 @@ -/** - * A "hash" table used in LDM compression. - * - * This is not exactly a hash table in the sense that inserted entries - * are not guaranteed to remain in the hash table. - */ - -#ifndef LDM_HASHTABLE_H -#define LDM_HASHTABLE_H - -#include "mem.h" - -// The log size of LDM_hashEntry in bytes. -#define LDM_HASH_ENTRY_SIZE_LOG 3 - -typedef U32 hash_t; - -typedef struct LDM_hashEntry { - U32 offset; // Represents the offset of the entry from offsetBase. - U32 checksum; // A checksum to select entries with the same hash value. -} LDM_hashEntry; - -typedef struct LDM_hashTable LDM_hashTable; - -/** - * Create a table that can contain size elements. This does not necessarily - * correspond to the number of hash buckets. The number of hash buckets - * is size / (1 << HASH_BUCKET_SIZE_LOG) - * - * minMatchLength is the minimum match length required in HASH_getBestEntry. - * - * maxWindowSize is the maximum distance from pIn in HASH_getBestEntry. - * The window is defined to be (pIn - offsetBase - offset). - */ -LDM_hashTable *HASH_createTable(U32 size, const BYTE *offsetBase, - U32 minMatchLength, U32 maxWindowSize); - -/** - * Return the "best" entry from the table with the same hash and checksum. - * - * pIn: a pointer to the current input position. - * pEnd: a pointer to the maximum input position. - * pAnchor: a pointer to the minimum input position. - * - * This function computes the forward and backward match length from pIn - * and writes it to forwardMatchLength and backwardsMatchLength. - * - * E.g. for the two strings "aaabbbb" "aaabbbb" with pIn and the - * entry pointing at the first "b", the forward match length would be - * four (representing the "b" matches) and the backward match length would - * three (representing the "a" matches before the pointer). - */ -LDM_hashEntry *HASH_getBestEntry(const LDM_hashTable *table, - const hash_t hash, - const U32 checksum, - const BYTE *pIn, - const BYTE *pEnd, - const BYTE *pAnchor, - U64 *forwardMatchLength, - U64 *backwardsMatchLength); - -/** - * Return a hash of the value. - */ -hash_t HASH_hashU32(U32 value); - -/** - * Insert an LDM_hashEntry into the bucket corresponding to hash. - * - * An entry may be evicted in the process. - */ -void HASH_insert(LDM_hashTable *table, const hash_t hash, - const LDM_hashEntry entry); - -/** - * Return the number of distinct hash buckets. - */ -U32 HASH_getSize(const LDM_hashTable *table); - -/** - * Destroy the table. - */ -void HASH_destroyTable(LDM_hashTable *table); - -/** - * Prints the percentage of the hash table occupied (where occupied is defined - * as the entry being non-zero). - */ -void HASH_outputTableOccupancy(const LDM_hashTable *hashTable); - -#endif /* LDM_HASHTABLE_H */ diff --git a/contrib/long_distance_matching/main-ldm.c b/contrib/long_distance_matching/main.c similarity index 95% rename from contrib/long_distance_matching/main-ldm.c rename to contrib/long_distance_matching/main.c index 232c14a2f..cee5edbae 100644 --- a/contrib/long_distance_matching/main-ldm.c +++ b/contrib/long_distance_matching/main.c @@ -12,7 +12,7 @@ #include "ldm.h" #include "zstd.h" -//#define TEST +//#define DECOMPRESS_AND_VERIFY /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. @@ -91,9 +91,10 @@ static int compress(const char *fname, const char *oname) { ftruncate(fdout, compressedSize); - printf("%25s : %10lu -> %10lu - %s (%.2fx --- %.1f%%)\n", fname, - (size_t)statbuf.st_size, (size_t)compressedSize, oname, - (statbuf.st_size) / (double)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 + @@ -110,6 +111,7 @@ static int compress(const char *fname, const char *oname) { return 0; } +#ifdef DECOMPRESS /* 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. @@ -162,7 +164,6 @@ static int decompress(const char *fname, const char *oname) { src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE, dst, decompressedSize); printf("Ret size out: %zu\n", outSize); -// ftruncate(fdout, decompressedSize); close(fdin); close(fdout); @@ -207,6 +208,7 @@ static void verify(const char *inpFilename, const char *decFilename) { fclose(decFp); fclose(inpFp); } +#endif int main(int argc, const char *argv[]) { const char * const exeName = argv[0]; @@ -237,6 +239,7 @@ int main(int argc, const char *argv[]) { } } +#ifdef DECOMPRESS_AND_VERIFY /* Decompress */ { struct timeval tv1, tv2; @@ -252,6 +255,6 @@ int main(int argc, const char *argv[]) { } /* verify */ verify(inpFilename, decFilename); - +#endif return 0; } From 9a132707aff6ece4b77864db7078c5c123b7b421 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 15:26:26 -0700 Subject: [PATCH 238/318] changing time units to seconds --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 9ebe2f9f9..919502221 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -593,7 +593,7 @@ static void displayProgress(unsigned cLevel, unsigned last) double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, 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: %5.0f ms | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate); + fprintf(stderr, "\r| Comp. Level: %2u | Time Elapsed: %5.0f s | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate); if (last) { fprintf(stderr, "\n"); } From 8dbb07d822ec998fe92e485ab09deb6d26b6f6d5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 16:03:43 -0700 Subject: [PATCH 239/318] updated progress bar with better representation of time, added const --- contrib/adaptive-compression/adapt.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 919502221..ead68bf4a 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -439,7 +439,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) static size_t getUseableDictSize(unsigned compressionLevel) { - ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, 0); + ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, 0); unsigned const overlapLog = compressionLevel >= (unsigned)ZSTD_maxCLevel() ? 0 : 3; size_t const overlapSize = 1 << (params.cParams.windowLog - overlapLog); return overlapSize; @@ -447,7 +447,7 @@ static size_t getUseableDictSize(unsigned compressionLevel) static void* compressionThread(void* arg) { - adaptCCtx* ctx = (adaptCCtx*)arg; + adaptCCtx* const ctx = (adaptCCtx*)arg; unsigned currJob = 0; for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; @@ -593,7 +593,7 @@ static void displayProgress(unsigned cLevel, unsigned last) double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, 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: %5.0f s | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate); + 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"); } @@ -638,7 +638,6 @@ static void* outputThread(void* arg) return arg; } { - // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); size_t const blockSize = MAX(compressedSize >> 7, 1 << 10); size_t pos = 0; for ( ; ; ) { From 7cc74e0b27fbf6db0644d14b9861231c5f7486d8 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 16:55:16 -0700 Subject: [PATCH 240/318] adding more to readme --- contrib/adaptive-compression/README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/README.md b/contrib/adaptive-compression/README.md index 1d2613377..09923d190 100644 --- a/contrib/adaptive-compression/README.md +++ b/contrib/adaptive-compression/README.md @@ -1,2 +1,8 @@ -ZSTD_adapt is a compression tool targeted at optimizing performance across network connections. The tool aims at sensing network speeds and adapting compression level based on network or pipe speeds. -In many scenarios, using ZSTD without a properly adjusted compression level results in a pipe bottleneck, or a compressed file that could have been reduced in size. +###Summary + +`adapt` is a new compression tool targeted at optimizing performance across network connections. The tool aims at sensing network speeds and adapting compression level based on network or pipe speeds. +In situations where the compression level does not appropriately match the network/pipe speed, the compression may be bottlenecking the entire pipeline or the files may not be compressed as much as they potentially could be, therefore losing efficiency. It also becomes quite impractical to manually measure and set compression level, therefore the tool does it for you. + +###Using `adapt` + +In order to build and use the tool, you can simply run `make adapt` in the `adaptive-compression` directory under `contrib`. This will generate an executable available for use. From 0b18d21e03b9dd161cd27b8fd616dccb7f602e30 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 17:47:02 -0700 Subject: [PATCH 241/318] building on readme, added another help tip in the menu --- contrib/adaptive-compression/README.md | 17 +++++++++++++++++ contrib/adaptive-compression/adapt.c | 1 + 2 files changed, 18 insertions(+) diff --git a/contrib/adaptive-compression/README.md b/contrib/adaptive-compression/README.md index 09923d190..fadb071f9 100644 --- a/contrib/adaptive-compression/README.md +++ b/contrib/adaptive-compression/README.md @@ -6,3 +6,20 @@ In situations where the compression level does not appropriately match the netwo ###Using `adapt` In order to build and use the tool, you can simply run `make adapt` in the `adaptive-compression` directory under `contrib`. This will generate an executable available for use. + +###Options +`-oFILE` : write output to `FILE` + +`-i#` : provide initial compression level + +`-h` : display help/information + +`-f` : force the compression level to stay constant + +`-c` : force write to `stdout` + +`-p` : hide progress bar + +`-q` : quiet mode -- do not show progress bar or other information + +###Benchmarking / Test results diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ead68bf4a..16701e75d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -924,6 +924,7 @@ static void help() PRINT(" -i# : provide initial compression level\n"); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); + PRINT(" -c : force write to stdout\n"); PRINT(" -p : hide progress bar\n"); PRINT(" -q : quiet mode -- do not show progress bar or other information\n"); } From e9161637b28f3b9fb26398d5e85d74e8959ca2c8 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Tue, 25 Jul 2017 18:13:27 -0700 Subject: [PATCH 242/318] Allow parameters to be modified from a separate file --- contrib/long_distance_matching/Makefile | 8 +- contrib/long_distance_matching/ldm.c | 539 -------------------- contrib/long_distance_matching/ldm.h | 30 +- contrib/long_distance_matching/ldm_common.c | 5 +- contrib/long_distance_matching/ldm_hash32.c | 12 +- contrib/long_distance_matching/ldm_hash64.c | 164 +----- contrib/long_distance_matching/ldm_params.h | 10 + contrib/long_distance_matching/main.c | 8 +- 8 files changed, 47 insertions(+), 729 deletions(-) delete mode 100644 contrib/long_distance_matching/ldm.c create mode 100644 contrib/long_distance_matching/ldm_params.h diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 292ce8517..b1fd3a1ee 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -25,16 +25,16 @@ LDFLAGS += -lzstd default: all -all: main-hash32 main-hash64 +all: main-64 main-integrated -main-hash64: ldm_common.c ldm_hash64.c main.c +main-64: ldm_common.c ldm_hash64.c main.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ -main-hash32: ldm_common.c ldm_hash32.c main.c +main-integrated: ldm_common.c ldm_hash32.c main.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - main-hash64 main-hash32 + main-hash64 main-hash32 main-64 main-integrated @echo Cleaning completed diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c deleted file mode 100644 index fae35f9e4..000000000 --- a/contrib/long_distance_matching/ldm.c +++ /dev/null @@ -1,539 +0,0 @@ -#include -#include -#include -#include -#include - -#include "ldm.h" -#include "ldm_hashtable.h" - -#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE)) -#define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3) - -#define LDM_HASH_ENTRY_SIZE_LOG 3 - -//#define HASH_ONLY_EVERY_LOG 7 -#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG))) - -#define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) - - -#define COMPUTE_STATS -#define OUTPUT_CONFIGURATION -#define CHECKSUM_CHAR_OFFSET 10 - -//#define RUN_CHECKS - -typedef U32 checksum_t; - -struct LDM_compressStats { - U32 windowSizeLog, hashTableSizeLog; - U32 numMatches; - U64 totalMatchLength; - U64 totalLiteralLength; - U64 totalOffset; - - U32 minOffset, maxOffset; - - U32 offsetHistogram[32]; -}; - -struct LDM_CCtx { - U64 isize; /* Input size */ - U64 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 */ - hash_t lastHash; /* Hash corresponding to lastPosHashed */ - checksum_t lastSum; - - const BYTE *nextIp; // TODO: this is redundant (ip + step) - const BYTE *nextPosHashed; - hash_t nextHash; /* Hash corresponding to nextPosHashed */ - checksum_t nextSum; - - unsigned step; // ip step, should be 1. - - const BYTE *lagIp; - hash_t lagHash; - checksum_t lagSum; - - // DEBUG - const BYTE *DEBUG_setNextHash; -}; - -// TODO: This can be done more efficiently (but it is not that important as it -// is only used for computing stats). -static int intLog2(U32 x) { - int ret = 0; - while (x >>= 1) { - ret++; - } - return ret; -} - -void LDM_printCompressStats(const LDM_compressStats *stats) { - int i = 0; - 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: offset, num matches, %% of matches\n"); - - for (; i <= intLog2(stats->maxOffset); i++) { - printf("2^%*d: %10u %6.3f%%\n", 2, i, - stats->offsetHistogram[i], - 100.0 * (double) stats->offsetHistogram[i] / - (double) stats->numMatches); - } - printf("\n"); - printf("=====================\n"); -} - -/** - * Convert a sum computed from getChecksum to a hash value in the range - * of the hash table. - */ -static hash_t checksumToHash(U32 sum) { - return HASH_hashU32(sum); -} - -/** - * Computes a 32-bit checksum based on rsync's checksum. - * - * a(k,l) = \sum_{i = k}^l x_i (mod M) - * b(k,l) = \sum_{i = k}^l ((l - i + 1) * x_i) (mod M) - * checksum(k,l) = a(k,l) + 2^{16} * b(k,l) - */ -static checksum_t getChecksum(const BYTE *buf, U32 len) { - U32 i; - checksum_t s1, s2; - - s1 = s2 = 0; - for (i = 0; i < (len - 4); i += 4) { - s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + - (2 * buf[i + 2]) + (buf[i + 3]) + - (10 * CHECKSUM_CHAR_OFFSET); - s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3] + - + (4 * CHECKSUM_CHAR_OFFSET); - - } - for(; i < len; i++) { - s1 += buf[i] + CHECKSUM_CHAR_OFFSET; - s2 += s1; - } - return (s1 & 0xffff) + (s2 << 16); -} - -/** - * Update a checksum computed from getChecksum(data, len). - * - * The checksum can be updated along its ends as follows: - * a(k+1, l+1) = (a(k,l) - x_k + x_{l+1}) (mod M) - * b(k+1, l+1) = (b(k,l) - (l-k+1)*x_k + (a(k+1,l+1)) (mod M) - * - * Thus toRemove should correspond to data[0]. - */ -static checksum_t updateChecksum(checksum_t sum, U32 len, - BYTE toRemove, BYTE toAdd) { - U32 s1 = (sum & 0xffff) - toRemove + toAdd; - U32 s2 = (sum >> 16) - ((toRemove + CHECKSUM_CHAR_OFFSET) * len) + s1; - - return (s1 & 0xffff) + (s2 << 16); -} - -/** - * Update cctx->nextSum, cctx->nextHash, and cctx->nextPosHashed - * based on cctx->lastSum 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) { -#ifdef RUN_CHECKS - U32 check; - if ((cctx->nextIp - cctx->ibase != 1) && - (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { - printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, - cctx->DEBUG_setNextHash - cctx->ibase); - } - - cctx->DEBUG_setNextHash = cctx->nextIp; -#endif - - cctx->nextSum = updateChecksum( - cctx->lastSum, LDM_HASH_LENGTH, - cctx->lastPosHashed[0], - cctx->lastPosHashed[LDM_HASH_LENGTH]); - cctx->nextPosHashed = cctx->nextIp; - cctx->nextHash = checksumToHash(cctx->nextSum); - -#if LDM_LAG - if (cctx->ip - cctx->ibase > LDM_LAG) { - cctx->lagSum = updateChecksum( - cctx->lagSum, LDM_HASH_LENGTH, - cctx->lagIp[0], cctx->lagIp[LDM_HASH_LENGTH]); - cctx->lagIp++; - cctx->lagHash = checksumToHash(cctx->lagSum); - } -#endif - -#ifdef RUN_CHECKS - check = getChecksum(cctx->nextIp, LDM_HASH_LENGTH); - - if (check != cctx->nextSum) { - printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); - } - - if ((cctx->nextIp - cctx->lastPosHashed) != 1) { - printf("setNextHash: nextIp != lastPosHashed + 1. %zu %zu %zu\n", - cctx->nextIp - cctx->ibase, cctx->lastPosHashed - cctx->ibase, - cctx->ip - cctx->ibase); - } -#endif -} - -static void putHashOfCurrentPositionFromHash( - LDM_CCtx *cctx, hash_t hash, U32 checksum) { - // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. - // Note: this works only when cctx->step is 1. - if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { -#if LDM_LAG - // Off by 1, but whatever - if (cctx->lagIp - cctx->ibase > 0) { - const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, cctx->lagSum }; - HASH_insert(cctx->hashTable, cctx->lagHash, entry); - } else { - const LDM_hashEntry entry = { cctx->ip - cctx->ibase, checksum }; - HASH_insert(cctx->hashTable, hash, entry); - } -#else - const LDM_hashEntry entry = { cctx->ip - cctx->ibase, checksum }; - HASH_insert(cctx->hashTable, hash, entry); -#endif - } - - cctx->lastPosHashed = cctx->ip; - cctx->lastHash = hash; - cctx->lastSum = checksum; -} - -/** - * Copy over the cctx->lastHash, cctx->lastSum, and cctx->lastPosHashed - * fields from the "next" fields. - * - * This requires that cctx->ip == cctx->nextPosHashed. - */ -static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { -#ifdef RUN_CHECKS - if (cctx->ip != cctx->nextPosHashed) { - printf("CHECK failed: updateLastHashFromNextHash %zu\n", - cctx->ip - cctx->ibase); - } -#endif - putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); -} - -/** - * Insert hash of the current position into the hash table. - */ -static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - checksum_t sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); - hash_t hash = checksumToHash(sum); - -#ifdef RUN_CHECKS - if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { - printf("CHECK failed: putHashOfCurrentPosition %zu\n", - cctx->ip - cctx->ibase); - } -#endif - - putHashOfCurrentPositionFromHash(cctx, hash, sum); -} - -void 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)); - cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U64, cctx->ibase, - LDM_MIN_MATCH_LENGTH, LDM_WINDOW_SIZE); - - 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; - - cctx->DEBUG_setNextHash = 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). - * - * matchLength contains the forward length of the match. - */ -static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, - U64 *matchLength, U64 *backwardMatchLength) { - - LDM_hashEntry *entry = NULL; - cctx->nextIp = cctx->ip + cctx->step; - - while (entry == NULL) { - hash_t h; - checksum_t sum; - setNextHash(cctx); - h = cctx->nextHash; - sum = cctx->nextSum; - cctx->ip = cctx->nextIp; - cctx->nextIp += cctx->step; - - if (cctx->ip > cctx->imatchLimit) { - return 1; - } - - entry = HASH_getBestEntry(cctx->hashTable, h, sum, - cctx->ip, cctx->iend, - cctx->anchor, - matchLength, backwardMatchLength); - - if (entry != NULL) { - *match = entry->offset + cctx->ibase; - } - putHashOfCurrentPositionFromHash(cctx, h, sum); - } - setNextHash(cctx); - return 0; -} - -void LDM_encodeLiteralLengthAndLiterals( - LDM_CCtx *cctx, BYTE *pToken, const U64 literalLength) { - /* Encode the literal length. */ - if (literalLength >= RUN_MASK) { - int len = (int)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; - - LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); - LDM_outputConfiguration(); - - /* Hash the first position and put it into the hash table. */ - LDM_putHashOfCurrentPosition(&cctx); - -#if LDM_LAG - cctx.lagIp = cctx.ip; - cctx.lagHash = cctx.lastHash; - cctx.lagSum = cctx.lastSum; -#endif - /** - * 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) == 0) { -#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 U32 literalLength = cctx.ip - cctx.anchor; - const U32 offset = cctx.ip - match; - const U32 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)]++; -#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 U32 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("=====================\n"); -} - - - -void LDM_test(const BYTE *src) { - (void)src; -} - diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index f9ad383e1..b87a57bc8 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -2,6 +2,7 @@ #define LDM_H #include "mem.h" // from /lib/common/mem.h +#include "ldm_params.h" // The number of bytes storing the compressed and decompressed size // in the header. @@ -18,35 +19,38 @@ #define LDM_OFFSET_SIZE 4 // ============================================================================= -// User parameters. +// Modify parameters in ldm_params.h if "ldm_params.h" is included. // ============================================================================= +#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 25 + #define LDM_MEMORY_USAGE 25 // The number of entries in a hash bucket. -#define HASH_BUCKET_SIZE_LOG 3 // The maximum is 4 for now. + #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 + #define LDM_LAG 0 // The maximum window size. -#define LDM_WINDOW_SIZE_LOG 28 // Max value is 30 -#define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) + #define LDM_WINDOW_SIZE_LOG 28 // Max value is 30 //These should be multiples of four (and perhaps set to the same value?). -#define LDM_MIN_MATCH_LENGTH 64 -#define LDM_HASH_LENGTH 64 + #define LDM_MIN_MATCH_LENGTH 64 -// Experimental. -//#define TMP_EVICTION // Experiment with eviction policies. -#define INSERT_BY_TAG // Insertion policy based on hash. + #define INSERT_BY_TAG 1 // Insertion policy based on hash. -#define USE_CHECKSUM 1 + #define USE_CHECKSUM 1 +#endif // ============================================================================= +#define COMPUTE_STATS +#define OUTPUT_CONFIGURATION + +#define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) +#define LDM_HASH_LENGTH LDM_MIN_MATCH_LENGTH typedef struct LDM_compressStats LDM_compressStats; typedef struct LDM_CCtx LDM_CCtx; @@ -164,6 +168,4 @@ void LDM_writeHeader(void *memPtr, U64 compressedSize, */ void LDM_outputConfiguration(void); -void LDM_test(const BYTE *src); - #endif /* LDM_H */ diff --git a/contrib/long_distance_matching/ldm_common.c b/contrib/long_distance_matching/ldm_common.c index 1953656e3..26b716a1b 100644 --- a/contrib/long_distance_matching/ldm_common.c +++ b/contrib/long_distance_matching/ldm_common.c @@ -70,7 +70,8 @@ size_t LDM_decompress(const void *src, size_t compressedSize, dctx.ip += length; dctx.op = cpy; - //TODO : dynamic offset size + //TODO: dynamic offset size? + /* Encode the offset. */ offset = MEM_read32(dctx.ip); dctx.ip += LDM_OFFSET_SIZE; match = dctx.op - offset; @@ -89,7 +90,7 @@ size_t LDM_decompress(const void *src, size_t compressedSize, /* Copy match. */ cpy = dctx.op + length; - // Inefficient for now. + // TODO: this can be made more efficient. while (match < cpy - offset && dctx.op < dctx.oend) { *(dctx.op)++ = *match++; } diff --git a/contrib/long_distance_matching/ldm_hash32.c b/contrib/long_distance_matching/ldm_hash32.c index b80a5c017..94fa5e928 100644 --- a/contrib/long_distance_matching/ldm_hash32.c +++ b/contrib/long_distance_matching/ldm_hash32.c @@ -21,9 +21,7 @@ #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) -#define COMPUTE_STATS -#define OUTPUT_CONFIGURATION -#define CHECKSUM_CHAR_OFFSET 1 +#define CHECKSUM_CHAR_OFFSET 10 // Take first match only. //#define ZSTD_SKIP @@ -779,8 +777,6 @@ size_t LDM_compress(const void *src, size_t srcSize, LDM_updateLastHashFromNextHash(&cctx); } - // HASH_outputTableOffsetHistogram(&cctx); - /* Encode the last literals (no more matches). */ { const U32 lastRun = cctx.iend - cctx.anchor; @@ -815,9 +811,3 @@ void LDM_outputConfiguration(void) { -// TODO: implement and test hash function -void LDM_test(const BYTE *src) { - (void)src; -} - - diff --git a/contrib/long_distance_matching/ldm_hash64.c b/contrib/long_distance_matching/ldm_hash64.c index e51ac57d3..884f7b724 100644 --- a/contrib/long_distance_matching/ldm_hash64.c +++ b/contrib/long_distance_matching/ldm_hash64.c @@ -24,8 +24,6 @@ #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) #define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) -#define COMPUTE_STATS -#define OUTPUT_CONFIGURATION #define HASH_CHAR_OFFSET 10 // Take first match only. @@ -63,11 +61,6 @@ struct LDM_compressStats { U64 TMP_hashCount[1 << HASH_ONLY_EVERY_LOG]; U64 TMP_totalHashCount; - - U64 TMP_totalInWindow; - U64 TMP_totalInserts; - - U64 TMP_matchCount; }; typedef struct LDM_hashTable LDM_hashTable; @@ -328,91 +321,12 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, return NULL; } -#ifdef TMP_EVICTION -void HASH_insert(LDM_hashTable *table, - const hash_t hash, const LDM_hashEntry entry, - LDM_CCtx *cctx) { - // Overwrite based on part of checksum. - /* - LDM_hashEntry *toOverwrite = - getBucket(table, hash) + table->bucketOffsets[hash]; - const BYTE *pMatch = toOverwrite->offset + cctx->ibase; - if (toOverwrite->offset != 0 && - cctx->ip - pMatch <= LDM_WINDOW_SIZE) { - cctx->stats.TMP_totalInWindow++; - } - - cctx->stats.TMP_totalInserts++; - *(toOverwrite) = entry; - */ - - /* - int i; - LDM_hashEntry *bucket = getBucket(table, hash); - for (i = 0; i < HASH_BUCKET_SIZE; i++) { - if (bucket[i].checksum == entry.checksum) { - bucket[i] = entry; - cctx->stats.TMP_matchCount++; - return; - } - } - */ - - // Find entry beyond window size, replace. Else, random. - int i; - LDM_hashEntry *bucket = getBucket(table, hash); - for (i = 0; i < HASH_BUCKET_SIZE; i++) { - if (cctx->ip - cctx->ibase - bucket[i].offset > LDM_WINDOW_SIZE) { - bucket[i] = entry; - return; - } - } - - i = rand() & (HASH_BUCKET_SIZE - 1); - *(bucket + i) = entry; - - - /** - * Sliding buffer style pointer - * Keep old entry as temporary. If the old entry is outside the window, - * overwrite and we are done. - * - * Backwards (insert at x): - * x, a, b b, c c c c, d d d d d d d d - * x, d d d d d d d d, c c c c, b b, a - * - * Else, find something to evict. - * If old entry has more ones, it takes - * the next spot. <-- reversed order? - * - * If window size > LDM_WINDOW_SIZE, - * overwrite, - * - * Insert forwards. If > tag, keep. Else evict. - * - */ - - - /* - *(getBucket(table, hash) + table->bucketOffsets[hash]) = entry; - table->bucketOffsets[hash]++; - table->bucketOffsets[hash] &= HASH_BUCKET_SIZE - 1; - */ - -// U16 mask = entry.checksum & (HASH_BUCKET_SIZE - 1); -// *(getBucket(table, hash) + mask) = entry; -} - -#else - 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; } -#endif // TMP_EVICTION - U32 HASH_getSize(const LDM_hashTable *table) { return table->numBuckets; @@ -489,7 +403,7 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { (double) stats->numMatches); } printf("\n"); -#ifdef INSERT_BY_TAG +#if INSERT_BY_TAG /* printf("Lower bit distribution\n"); for (i = 0; i < (1 << HASH_ONLY_EVERY_LOG); i++) { @@ -500,13 +414,6 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { */ #endif -#ifdef TMP_EVICTION - printf("Evicted something in window: %llu %6.3f\n", - stats->TMP_totalInWindow, - 100.0 * (double)stats->TMP_totalInWindow / - (double)stats->TMP_totalInserts); - printf("Match count: %llu\n", stats->TMP_matchCount); -#endif printf("=====================\n"); } @@ -524,7 +431,7 @@ static U32 getChecksum(U64 hash) { return (hash >> (64 - 32 - LDM_HASHLOG)) & 0xFFFFFFFF; } -#ifdef INSERT_BY_TAG +#if INSERT_BY_TAG static U32 lowerBitsFromHfHash(U64 hash) { // The number of bits used so far is LDM_HASHLOG + 32. // So there are 32 - LDM_HASHLOG bits left. @@ -611,7 +518,7 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->lastPosHashed[LDM_HASH_LENGTH]); cctx->nextPosHashed = cctx->nextIp; -#ifdef INSERT_BY_TAG +#if INSERT_BY_TAG { U32 hashEveryMask = lowerBitsFromHfHash(cctx->nextHash); cctx->stats.TMP_totalHashCount++; @@ -648,7 +555,7 @@ static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hash) { // Note: this works only when cctx->step is 1. #if LDM_LAG if (cctx -> lagIp - cctx->ibase > 0) { -#ifdef INSERT_BY_TAG +#if INSERT_BY_TAG U32 hashEveryMask = lowerBitsFromHfHash(cctx->lagHash); if (hashEveryMask == HASH_ONLY_EVERY) { #else @@ -663,14 +570,11 @@ static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hash) { const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase }; # endif -# ifdef TMP_EVICTION - HASH_insert(cctx->hashTable, smallHash, entry, cctx); -# else HASH_insert(cctx->hashTable, smallHash, entry); -# endif } } else { -#ifdef INSERT_BY_TAG +#endif // LDM_LAG +#if INSERT_BY_TAG U32 hashEveryMask = lowerBitsFromHfHash(hash); if (hashEveryMask == HASH_ONLY_EVERY) { #else @@ -684,33 +588,9 @@ static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hash) { #else const LDM_hashEntry entry = { cctx->ip - cctx->ibase }; #endif - -#ifdef TMP_EVICTION - HASH_insert(cctx->hashTable, smallHash, entry, cctx); -#else HASH_insert(cctx->hashTable, smallHash, entry); -#endif } - } -#else -#ifdef 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 -#ifdef TMP_EVICTION - HASH_insert(cctx->hashTable, smallHash, entry, cctx); -#else - HASH_insert(cctx->hashTable, smallHash, entry); -#endif +#if LDM_LAG } #endif @@ -812,7 +692,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, U64 hash; hash_t smallHash; U32 checksum; -#ifdef INSERT_BY_TAG +#if INSERT_BY_TAG U32 hashEveryMask; #endif setNextHash(cctx); @@ -820,7 +700,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, hash = cctx->nextHash; smallHash = getSmallHash(hash); checksum = getChecksum(hash); -#ifdef INSERT_BY_TAG +#if INSERT_BY_TAG hashEveryMask = lowerBitsFromHfHash(hash); #endif @@ -830,7 +710,7 @@ static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match, if (cctx->ip > cctx->imatchLimit) { return 1; } -#ifdef INSERT_BY_TAG +#if INSERT_BY_TAG if (hashEveryMask == HASH_ONLY_EVERY) { entry = HASH_getBestEntry(cctx, smallHash, checksum, @@ -923,10 +803,8 @@ size_t LDM_compress(const void *src, size_t srcSize, /* Hash the first position and put it into the hash table. */ LDM_putHashOfCurrentPosition(&cctx); -#if LDM_LAG cctx.lagIp = cctx.ip; cctx.lagHash = cctx.lastHash; -#endif /** * Find a match. * If no more matches can be found (i.e. the length of the remaining input @@ -1018,28 +896,8 @@ void LDM_outputConfiguration(void) { 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); -#ifdef INSERT_BY_TAG - printf("INSERT_BY_TAG: %d\n", 1); -#else - printf("INSERT_BY_TAG: %d\n", 0); -#endif + printf("INSERT_BY_TAG: %d\n", INSERT_BY_TAG); printf("HASH_CHAR_OFFSET: %d\n", HASH_CHAR_OFFSET); printf("=====================\n"); } -// TODO: implement and test hash function -void LDM_test(const BYTE *src) { - const U32 diff = 100; - const BYTE *pCur = src + diff; - U64 hash = getHash(pCur, LDM_HASH_LENGTH); - - for (; pCur < src + diff + 60; ++pCur) { - U64 nextHash = getHash(pCur + 1, LDM_HASH_LENGTH); - U64 updatedHash = updateHash(hash, LDM_HASH_LENGTH, - pCur[0], pCur[LDM_HASH_LENGTH]); - hash = nextHash; - printf("%llu %llu\n", nextHash, updatedHash); - } -} - - diff --git a/contrib/long_distance_matching/ldm_params.h b/contrib/long_distance_matching/ldm_params.h new file mode 100644 index 000000000..0fcd30bd1 --- /dev/null +++ b/contrib/long_distance_matching/ldm_params.h @@ -0,0 +1,10 @@ +#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 diff --git a/contrib/long_distance_matching/main.c b/contrib/long_distance_matching/main.c index cee5edbae..bdd385cea 100644 --- a/contrib/long_distance_matching/main.c +++ b/contrib/long_distance_matching/main.c @@ -12,7 +12,7 @@ #include "ldm.h" #include "zstd.h" -//#define DECOMPRESS_AND_VERIFY +#define DECOMPRESS_AND_VERIFY /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. @@ -71,10 +71,6 @@ static int compress(const char *fname, const char *oname) { return 1; } -#ifdef TEST - LDM_test((const BYTE *)src); -#endif - gettimeofday(&tv1, NULL); compressedSize = LDM_HEADER_SIZE + @@ -111,7 +107,7 @@ static int compress(const char *fname, const char *oname) { return 0; } -#ifdef DECOMPRESS +#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. From be92a38d6a76ed987c44a4a364161190a8715b36 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 10:05:10 -0700 Subject: [PATCH 243/318] decrease completion requirements for change, move create thread wait, merge cases where compression thread should wait --- contrib/adaptive-compression/adapt.c | 54 +++++++++++----------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 16701e75d..1633b6bf8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -322,10 +322,10 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) /* map completion percentages to values for changing compression level */ static unsigned convertCompletionToChange(double completion) { - if (completion < 0.05) { + if (completion < 0.1) { return 2; } - else if (completion < 0.5) { + else if (completion < 0.65) { return 1; } else { @@ -396,9 +396,9 @@ static void adaptCompressionLevel(adaptCCtx* ctx) DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n\n", boundChange); } - else if (1-compressWaitWriteCompletion > threshold) { + else if (1-compressWaitWriteCompletion > threshold || 1-compressWaitCreateCompletion > threshold) { /* compress waiting on write */ - double const completion = compressWaitWriteCompletion; + double const completion = MIN(compressWaitWriteCompletion, compressWaitCreateCompletion); unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { @@ -406,26 +406,11 @@ static void adaptCompressionLevel(adaptCCtx* ctx) } else if (boundChange != 0) { ctx->compressionLevel += boundChange; + ctx->cooldown = 0; ctx->convergenceCounter = 1; } - DEBUG(2, "compress waiting on write, tried to increase compression level by %u\n\n", boundChange); - } - else if (1-compressWaitCreateCompletion > threshold) { - /* compress waiting on create*/ - /* use compressWaitCreateCompletion */ - double const completion = compressWaitCreateCompletion; - unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { - ctx->convergenceCounter = 0; - } - else if (boundChange != 0) { - ctx->compressionLevel += boundChange; - ctx->convergenceCounter = 1; - } - - DEBUG(2, "compression waiting on create, tried to increase compression level by %u\n\n", boundChange); + DEBUG(2, "compress waiting on write or create, tried to increase compression level by %u\n\n", boundChange); } if (ctx->compressionLevel == prevCompressionLevel) { @@ -689,17 +674,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) jobDescription* const job = &ctx->jobs[nextJobIndex]; - /* wait until the job has been compressed */ - pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); - while (nextJob - ctx->jobCompressedID >= ctx->numJobs && !ctx->threadError) { - pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); - /* creation thread is waiting, take measurement of completion */ - ctx->createWaitCompressionCompletion = ctx->compressionCompletion; - DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); - pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); - pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); - } - pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); /* reset create completion */ pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 0; @@ -767,8 +741,22 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA size_t pos = 0; size_t const readBlockSize = 1 << 15; size_t remaining = FILE_CHUNK_SIZE; - + unsigned const nextJob = ctx->nextJobID; DEBUG(2, "starting creation of job %u\n", currJob); + + + /* wait until the job has been compressed */ + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); + while (nextJob - ctx->jobCompressedID >= ctx->numJobs && !ctx->threadError) { + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); + /* creation thread is waiting, take measurement of completion */ + ctx->createWaitCompressionCompletion = ctx->compressionCompletion; + DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); + pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); + } + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); + while (remaining != 0 && !feof(srcFile)) { size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); if (ret != readBlockSize && !feof(srcFile)) { From 305d5ee70f41cb89f2d373abf703b09dfedcda61 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 10:20:29 -0700 Subject: [PATCH 244/318] change to >= convergence counter --- contrib/adaptive-compression/adapt.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 1633b6bf8..d75660806 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -384,33 +384,37 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); - if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { + if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; + DEBUG(2, "convergence counter reset, no change applied\n"); } else if (boundChange != 0) { ctx->compressionLevel -= boundChange; ctx->cooldown = CLEVEL_DECREASE_COOLDOWN; ctx->convergenceCounter = 1; - } - DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n\n", boundChange); + DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n\n", boundChange); + } } else if (1-compressWaitWriteCompletion > threshold || 1-compressWaitCreateCompletion > threshold) { /* compress waiting on write */ double const completion = MIN(compressWaitWriteCompletion, compressWaitCreateCompletion); unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { + if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { + /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; + DEBUG(2, "convergence counter reset, no change applied\n"); } else if (boundChange != 0) { ctx->compressionLevel += boundChange; ctx->cooldown = 0; ctx->convergenceCounter = 1; + + DEBUG(2, "compress waiting on write or create, tried to increase compression level by %u\n\n", boundChange); } - DEBUG(2, "compress waiting on write or create, tried to increase compression level by %u\n\n", boundChange); } if (ctx->compressionLevel == prevCompressionLevel) { From a959cc881a65b4ef250b4f963924c04c6760be0d Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 10:34:48 -0700 Subject: [PATCH 245/318] moved reset of completion to right after wait --- contrib/adaptive-compression/adapt.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index d75660806..237bb4306 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -678,10 +678,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) jobDescription* const job = &ctx->jobs[nextJobIndex]; - /* reset create completion */ - pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); - ctx->createCompletion = 0; - pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); job->compressionLevel = ctx->compressionLevel; job->src.size = srcSize; job->jobID = nextJob; @@ -761,6 +757,11 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); + /* reset create completion */ + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); + ctx->createCompletion = 0; + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); + while (remaining != 0 && !feof(srcFile)) { size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); if (ret != readBlockSize && !feof(srcFile)) { From 40759bade90d1ecb1bb52b55e2c7a3399e625998 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 26 Jul 2017 13:18:53 -0700 Subject: [PATCH 246/318] Add README and clean up code --- contrib/long_distance_matching/Makefile | 11 +- contrib/long_distance_matching/README.md | 39 + .../{ldm_hash64.c => ldm.c} | 109 +-- contrib/long_distance_matching/ldm.h | 87 +- contrib/long_distance_matching/ldm_hash32.c | 813 ------------------ contrib/long_distance_matching/ldm_params.h | 4 +- contrib/long_distance_matching/main.c | 13 +- 7 files changed, 134 insertions(+), 942 deletions(-) create mode 100644 contrib/long_distance_matching/README.md rename contrib/long_distance_matching/{ldm_hash64.c => ldm.c} (90%) delete mode 100644 contrib/long_distance_matching/ldm_hash32.c diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index b1fd3a1ee..8bc7ac478 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -25,16 +25,13 @@ LDFLAGS += -lzstd default: all -all: main-64 main-integrated - -main-64: ldm_common.c ldm_hash64.c main.c - $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -main-integrated: ldm_common.c ldm_hash32.c main.c +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 \ - main-hash64 main-hash32 main-64 main-integrated + ldm @echo Cleaning completed diff --git a/contrib/long_distance_matching/README.md b/contrib/long_distance_matching/README.md new file mode 100644 index 000000000..d9cb08951 --- /dev/null +++ b/contrib/long_distance_matching/README.md @@ -0,0 +1,39 @@ +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 descriped 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. + +### 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_hash64.c b/contrib/long_distance_matching/ldm.c similarity index 90% rename from contrib/long_distance_matching/ldm_hash64.c rename to contrib/long_distance_matching/ldm.c index 884f7b724..9a8438383 100644 --- a/contrib/long_distance_matching/ldm_hash64.c +++ b/contrib/long_distance_matching/ldm.c @@ -16,21 +16,21 @@ #define LDM_HASH_ENTRY_SIZE_LOG 2 #endif +// Force the "probability" of insertion to be some value. +// Entries are inserted into the table HASH_ONLY_EVERY + 1 times "on average". + //#define HASH_ONLY_EVERY_LOG 7 #define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG))) - #define HASH_ONLY_EVERY ((1 << (HASH_ONLY_EVERY_LOG)) - 1) #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) -#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(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 first match only. +// Take the first match in the hash bucket only. //#define ZSTD_SKIP -//#define RUN_CHECKS - static const U64 prime8bytes = 11400714785074694791ULL; // Type of the small hash used to index into the hash table. @@ -101,10 +101,6 @@ struct LDM_CCtx { const BYTE *lagIp; U64 lagHash; - -#ifdef RUN_CHECKS - const BYTE *DEBUG_setNextHash; -#endif }; struct LDM_hashTable { @@ -119,7 +115,7 @@ struct LDM_hashTable { * Create a hash table that can contain size elements. * The number of buckets is determined by size >> HASH_BUCKET_SIZE_LOG. */ -LDM_hashTable *HASH_createTable(U32 size) { +static LDM_hashTable *HASH_createTable(U32 size) { LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); table->numBuckets = size >> HASH_BUCKET_SIZE_LOG; table->numEntries = size; @@ -239,7 +235,7 @@ static size_t ZSTD_count(const BYTE *pIn, const BYTE *pMatch, /** * Count number of bytes that match backwards before pIn and pMatch. * - * We count only bytes where pMatch > pBaes and pIn > pAnchor. + * 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) { @@ -262,13 +258,12 @@ static size_t countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, * 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. - * */ -LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, - const hash_t hash, - const U32 checksum, - U64 *pForwardMatchLength, - U64 *pBackwardMatchLength) { +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 = bucket; @@ -321,24 +316,24 @@ LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, return NULL; } -void HASH_insert(LDM_hashTable *table, - const hash_t hash, const LDM_hashEntry entry) { +/** + * 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; } -U32 HASH_getSize(const LDM_hashTable *table) { - return table->numBuckets; -} - -void HASH_destroyTable(LDM_hashTable *table) { +static void HASH_destroyTable(LDM_hashTable *table) { free(table->entries); free(table->bucketOffsets); free(table); } -void HASH_outputTableOccupancy(const LDM_hashTable *table) { +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); @@ -350,7 +345,7 @@ void HASH_outputTableOccupancy(const LDM_hashTable *table) { // The number of buckets is repeated as a check for now. printf("Num buckets, bucket size: %d (2^%d), %d\n", - table->numBuckets, LDM_HASHLOG, HASH_BUCKET_SIZE); + 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); @@ -418,31 +413,32 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { } /** - * Return the upper (most significant) LDM_HASHLOG bits. + * Return the upper (most significant) NUM_HASH_BUCKETS_LOG bits. */ static hash_t getSmallHash(U64 hash) { - return hash >> (64 - LDM_HASHLOG); + return hash >> (64 - NUM_HASH_BUCKETS_LOG); } /** - * Return the 32 bits after the upper LDM_HASHLOG bits. + * Return the 32 bits after the upper NUM_HASH_BUCKETS_LOG bits. */ static U32 getChecksum(U64 hash) { - return (hash >> (64 - 32 - LDM_HASHLOG)) & 0xFFFFFFFF; + 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 LDM_HASHLOG + 32. - // So there are 32 - LDM_HASHLOG bits left. + // 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 - LDM_HASHLOG < HASH_ONLY_EVERY_LOG) { + if (32 - NUM_HASH_BUCKETS_LOG < HASH_ONLY_EVERY_LOG) { return hash & HASH_ONLY_EVERY; } else { - // Otherwise shift by (32 - LDM_HASHLOG - HASH_ONLY_EVERY_LOG) bits first. - return (hash >> (32 - LDM_HASHLOG - HASH_ONLY_EVERY_LOG)) & + // 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; } } @@ -501,17 +497,6 @@ static U64 updateHash(U64 hash, U32 len, * corresponds to cctx->nextIp - step. */ static void setNextHash(LDM_CCtx *cctx) { -#ifdef RUN_CHECKS - U64 check; - if ((cctx->nextIp - cctx->ibase != 1) && - (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { - printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, - cctx->DEBUG_setNextHash - cctx->ibase); - } - - cctx->DEBUG_setNextHash = cctx->nextIp; -#endif - cctx->nextHash = updateHash( cctx->lastHash, LDM_HASH_LENGTH, cctx->lastPosHashed[0], @@ -534,20 +519,6 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->lagIp++; } #endif - -#ifdef RUN_CHECKS - check = getHash(cctx->nextIp, LDM_HASH_LENGTH); - - if (check != cctx->nextHash) { - printf("CHECK: setNextHash failed %llu %llu\n", check, cctx->nextHash); - } - - if ((cctx->nextIp - cctx->lastPosHashed) != 1) { - printf("setNextHash: nextIp != lastPosHashed + 1. %zu %zu %zu\n", - cctx->nextIp - cctx->ibase, cctx->lastPosHashed - cctx->ibase, - cctx->ip - cctx->ibase); - } -#endif } static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hash) { @@ -605,12 +576,6 @@ static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hash) { * This requires that cctx->ip == cctx->nextPosHashed. */ static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { -#ifdef RUN_CHECKS - if (cctx->ip != cctx->nextPosHashed) { - printf("CHECK failed: updateLastHashFromNextHash %zu\n", - cctx->ip - cctx->ibase); - } -#endif putHashOfCurrentPositionFromHash(cctx, cctx->nextHash); } @@ -620,13 +585,6 @@ static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { U64 hash = getHash(cctx->ip, LDM_HASH_LENGTH); -#ifdef RUN_CHECKS - if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { - printf("CHECK failed: putHashOfCurrentPosition %zu\n", - cctx->ip - cctx->ibase); - } -#endif - putHashOfCurrentPositionFromHash(cctx, hash); } @@ -664,10 +622,6 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, cctx->step = 1; // Fixed to be 1 for now. Changing may break things. cctx->nextIp = cctx->ip + cctx->step; cctx->nextPosHashed = 0; - -#ifdef RUN_CHECKS - cctx->DEBUG_setNextHash = 0; -#endif } void LDM_destroyCCtx(LDM_CCtx *cctx) { @@ -805,6 +759,7 @@ size_t LDM_compress(const void *src, size_t srcSize, 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 diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index b87a57bc8..38d240152 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -2,7 +2,52 @@ #define LDM_H #include "mem.h" // from /lib/common/mem.h -#include "ldm_params.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 + +// ============================================================================= // The number of bytes storing the compressed and decompressed size // in the header. @@ -15,40 +60,9 @@ #define RUN_BITS (8-ML_BITS) #define RUN_MASK ((1U< -#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) - -#define LDM_HASH_ENTRY_SIZE_LOG 3 -//#define HASH_ONLY_EVERY_LOG 7 -#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG))) - -#define HASH_ONLY_EVERY ((1 << HASH_ONLY_EVERY_LOG) - 1) - - -/* Hash table stuff. */ -#define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) -#define LDM_HASHLOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG)) - -#define CHECKSUM_CHAR_OFFSET 10 - -// Take first match only. -//#define ZSTD_SKIP - -//#define RUN_CHECKS - -typedef U32 hash_t; - -typedef struct LDM_hashEntry { - U32 offset; - U32 checksum; -} LDM_hashEntry; - -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 { - U64 isize; /* Input size */ - U64 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 */ - hash_t lastHash; /* Hash corresponding to lastPosHashed */ - U32 lastSum; - - const BYTE *nextIp; // TODO: this is redundant (ip + step) - const BYTE *nextPosHashed; - hash_t nextHash; /* Hash corresponding to nextPosHashed */ - U32 nextSum; - - unsigned step; // ip step, should be 1. - - const BYTE *lagIp; - hash_t lagHash; - U32 lagSum; - - U64 numHashInserts; - // DEBUG - const BYTE *DEBUG_setNextHash; -}; - -struct LDM_hashTable { - U32 numBuckets; // Number of buckets - U32 numEntries; - LDM_hashEntry *entries; - - BYTE *bucketOffsets; -}; - -/** - * Create a hash table that can contain size elements. - * The number of buckets is determined by size >> HASH_BUCKET_SIZE_LOG. - */ -LDM_hashTable *HASH_createTable(U32 size) { - LDM_hashTable *table = malloc(sizeof(LDM_hashTable)); - 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)); - 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 (register 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; - const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ - 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 > pBaes and pIn > pAnchor. - */ -U32 countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor, - const BYTE *pMatch, const BYTE *pBase) { - U32 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. - * - */ -LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, - const hash_t hash, - const U32 checksum, - U32 *pForwardMatchLength, - U32 *pBackwardMatchLength) { - LDM_hashTable *table = cctx->hashTable; - LDM_hashEntry *bucket = getBucket(table, hash); - LDM_hashEntry *cur = bucket; - LDM_hashEntry *bestEntry = NULL; - U32 bestMatchLength = 0; - for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { - const BYTE *pMatch = cur->offset + cctx->ibase; - - // Check checksum for faster check. - if (cur->checksum == checksum && - cctx->ip - pMatch <= LDM_WINDOW_SIZE) { - U32 forwardMatchLength = ZSTD_count(cctx->ip, pMatch, cctx->iend); - U32 backwardMatchLength, totalMatchLength; - - // 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; -} - -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; -} - -U32 HASH_getSize(const LDM_hashTable *table) { - return table->numBuckets; -} - -void HASH_destroyTable(LDM_hashTable *table) { - free(table->entries); - free(table->bucketOffsets); - free(table); -} - -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++; - } - } - - printf("Num buckets, bucket size: %d, %d\n", - table->numBuckets, 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 (but it is not that important as it -// is only used for computing stats). -static int intLog2(U32 x) { - int ret = 0; - while (x >>= 1) { - ret++; - } - return ret; -} - -void LDM_printCompressStats(const LDM_compressStats *stats) { - int i = 0; - 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"); - - for (; i <= intLog2(stats->maxOffset); 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"); -} - -int LDM_isValidMatch(const BYTE *pIn, const BYTE *pMatch) { - U32 lengthLeft = LDM_MIN_MATCH_LENGTH; - const BYTE *curIn = pIn; - const BYTE *curMatch = pMatch; - - if (pIn - pMatch > LDM_WINDOW_SIZE) { - return 0; - } - - for (; lengthLeft >= 4; lengthLeft -= 4) { - if (MEM_read32(curIn) != MEM_read32(curMatch)) { - return 0; - } - curIn += 4; - curMatch += 4; - } - return 1; -} - -hash_t HASH_hashU32(U32 value) { - return ((value * 2654435761U) >> (32 - LDM_HASHLOG)); -} - -/** - * Convert a sum computed from getChecksum to a hash value in the range - * of the hash table. - */ -static hash_t checksumToHash(U32 sum) { - return HASH_hashU32(sum); -} - -/** - * Computes a checksum based on rsync's checksum. - * - * a(k,l) = \sum_{i = k}^l x_i (mod M) - * b(k,l) = \sum_{i = k}^l ((l - i + 1) * x_i) (mod M) - * checksum(k,l) = a(k,l) + 2^{16} * b(k,l) - */ -static U32 getChecksum(const BYTE *buf, U32 len) { - U32 i; - U32 s1, s2; - - s1 = s2 = 0; - for (i = 0; i < (len - 4); i += 4) { - s2 += (4 * (s1 + buf[i])) + (3 * buf[i + 1]) + - (2 * buf[i + 2]) + (buf[i + 3]) + - (10 * CHECKSUM_CHAR_OFFSET); - s1 += buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3] + - + (4 * CHECKSUM_CHAR_OFFSET); - - } - for(; i < len; i++) { - s1 += buf[i] + CHECKSUM_CHAR_OFFSET; - s2 += s1; - } - return (s1 & 0xffff) + (s2 << 16); -} - -/** - * Update a checksum computed from getChecksum(data, len). - * - * The checksum can be updated along its ends as follows: - * a(k+1, l+1) = (a(k,l) - x_k + x_{l+1}) (mod M) - * b(k+1, l+1) = (b(k,l) - (l-k+1)*x_k + (a(k+1,l+1)) (mod M) - * - * Thus toRemove should correspond to data[0]. - */ -static U32 updateChecksum(U32 sum, U32 len, - BYTE toRemove, BYTE toAdd) { - U32 s1 = (sum & 0xffff) - toRemove + toAdd; - U32 s2 = (sum >> 16) - ((toRemove + CHECKSUM_CHAR_OFFSET) * len) + s1; - - return (s1 & 0xffff) + (s2 << 16); -} - -/** - * Update cctx->nextSum, cctx->nextHash, and cctx->nextPosHashed - * based on cctx->lastSum 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) { -#ifdef RUN_CHECKS - U32 check; - if ((cctx->nextIp - cctx->ibase != 1) && - (cctx->nextIp - cctx->DEBUG_setNextHash != 1)) { - printf("CHECK debug fail: %zu %zu\n", cctx->nextIp - cctx->ibase, - cctx->DEBUG_setNextHash - cctx->ibase); - } - - cctx->DEBUG_setNextHash = cctx->nextIp; -#endif - - cctx->nextSum = updateChecksum( - cctx->lastSum, LDM_HASH_LENGTH, - cctx->lastPosHashed[0], - cctx->lastPosHashed[LDM_HASH_LENGTH]); - cctx->nextPosHashed = cctx->nextIp; - cctx->nextHash = checksumToHash(cctx->nextSum); - -#if LDM_LAG - if (cctx->ip - cctx->ibase > LDM_LAG) { - cctx->lagSum = updateChecksum( - cctx->lagSum, LDM_HASH_LENGTH, - cctx->lagIp[0], cctx->lagIp[LDM_HASH_LENGTH]); - cctx->lagIp++; - cctx->lagHash = checksumToHash(cctx->lagSum); - } -#endif - -#ifdef RUN_CHECKS - check = getChecksum(cctx->nextIp, LDM_HASH_LENGTH); - - if (check != cctx->nextSum) { - printf("CHECK: setNextHash failed %u %u\n", check, cctx->nextSum); - } - - if ((cctx->nextIp - cctx->lastPosHashed) != 1) { - printf("setNextHash: nextIp != lastPosHashed + 1. %zu %zu %zu\n", - cctx->nextIp - cctx->ibase, cctx->lastPosHashed - cctx->ibase, - cctx->ip - cctx->ibase); - } -#endif -} - -static void putHashOfCurrentPositionFromHash( - LDM_CCtx *cctx, hash_t hash, U32 sum) { - // Hash only every HASH_ONLY_EVERY times, based on cctx->ip. - // Note: this works only when cctx->step is 1. - if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) { -#if LDM_LAG - // TODO: off by 1, but whatever - if (cctx->lagIp - cctx->ibase > 0) { - const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, cctx->lagSum }; - HASH_insert(cctx->hashTable, cctx->lagHash, entry); - } else { - const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; - HASH_insert(cctx->hashTable, hash, entry); - } -#else - const LDM_hashEntry entry = { cctx->ip - cctx->ibase, sum }; - HASH_insert(cctx->hashTable, hash, entry); -#endif - } - - cctx->lastPosHashed = cctx->ip; - cctx->lastHash = hash; - cctx->lastSum = sum; -} - -/** - * Copy over the cctx->lastHash, cctx->lastSum, and cctx->lastPosHashed - * fields from the "next" fields. - * - * This requires that cctx->ip == cctx->nextPosHashed. - */ -static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) { -#ifdef RUN_CHECKS - if (cctx->ip != cctx->nextPosHashed) { - printf("CHECK failed: updateLastHashFromNextHash %zu\n", - cctx->ip - cctx->ibase); - } -#endif - putHashOfCurrentPositionFromHash(cctx, cctx->nextHash, cctx->nextSum); -} - -/** - * Insert hash of the current position into the hash table. - */ -static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { - U32 sum = getChecksum(cctx->ip, LDM_HASH_LENGTH); - hash_t hash = checksumToHash(sum); - -#ifdef RUN_CHECKS - if (cctx->nextPosHashed != cctx->ip && (cctx->ip != cctx->ibase)) { - printf("CHECK failed: putHashOfCurrentPosition %zu\n", - cctx->ip - cctx->ibase); - } -#endif - - putHashOfCurrentPositionFromHash(cctx, hash, sum); -} - -void 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)); - cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U64); - - 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; - - cctx->DEBUG_setNextHash = 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, - U32 *forwardMatchLength, U32 *backwardMatchLength) { - - LDM_hashEntry *entry = NULL; - cctx->nextIp = cctx->ip + cctx->step; - - while (entry == NULL) { - hash_t h; - U32 sum; - setNextHash(cctx); - h = cctx->nextHash; - sum = cctx->nextSum; - cctx->ip = cctx->nextIp; - cctx->nextIp += cctx->step; - - if (cctx->ip > cctx->imatchLimit) { - return 1; - } - - entry = HASH_getBestEntry(cctx, h, sum, - forwardMatchLength, backwardMatchLength); - - if (entry != NULL) { - *match = entry->offset + cctx->ibase; - } - putHashOfCurrentPositionFromHash(cctx, h, sum); - } - 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) { - unsigned 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; - U32 forwardMatchLength = 0; - U32 backwardsMatchLength = 0; - - LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); - LDM_outputConfiguration(); - - /* Hash the first position and put it into the hash table. */ - LDM_putHashOfCurrentPosition(&cctx); - -#if LDM_LAG - cctx.lagIp = cctx.ip; - cctx.lagHash = cctx.lastHash; - cctx.lagSum = cctx.lastSum; -#endif - /** - * 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) == 0) { -#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 U32 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("=====================\n"); -} - - - diff --git a/contrib/long_distance_matching/ldm_params.h b/contrib/long_distance_matching/ldm_params.h index 0fcd30bd1..a541581b0 100644 --- a/contrib/long_distance_matching/ldm_params.h +++ b/contrib/long_distance_matching/ldm_params.h @@ -1,5 +1,6 @@ #ifndef LDM_PARAMS_H #define LDM_PARAMS_H + #define LDM_MEMORY_USAGE 23 #define HASH_BUCKET_SIZE_LOG 3 #define LDM_LAG 0 @@ -7,4 +8,5 @@ #define LDM_MIN_MATCH_LENGTH 64 #define INSERT_BY_TAG 1 #define USE_CHECKSUM 1 -#endif + +#endif // LDM_PARAMS_H diff --git a/contrib/long_distance_matching/main.c b/contrib/long_distance_matching/main.c index bdd385cea..d55e01d32 100644 --- a/contrib/long_distance_matching/main.c +++ b/contrib/long_distance_matching/main.c @@ -12,11 +12,13 @@ #include "ldm.h" #include "zstd.h" -#define DECOMPRESS_AND_VERIFY +// #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. @@ -52,7 +54,7 @@ static int compress(const char *fname, const char *oname) { maxCompressedSize = (statbuf.st_size + LDM_HEADER_SIZE); // Handle case where compressed size is > decompressed size. - // The compress function should check before writing or buffer writes. + // TODO: The compress function should check before writing or buffer writes. maxCompressedSize += statbuf.st_size / 255; ftruncate(fdout, maxCompressedSize); @@ -64,7 +66,7 @@ static int compress(const char *fname, const char *oname) { return 1; } - /* mmap the output file */ + /* 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"); @@ -79,14 +81,12 @@ static int compress(const char *fname, const char *oname) { gettimeofday(&tv2, NULL); - // Write compress and decompress size to header - // TODO: should depend on LDM_DECOMPRESS_SIZE write32 + // 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", @@ -100,7 +100,6 @@ static int compress(const char *fname, const char *oname) { timeTaken, ((double)statbuf.st_size / (double) (1 << 20)) / timeTaken); - // Close files. close(fdin); close(fdout); From 6c1c1242fce69018710d30c785285f0cbaa850cc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 14:29:59 -0700 Subject: [PATCH 247/318] set the window log value before performing compression --- contrib/adaptive-compression/adapt.c | 16 ++++++++++------ contrib/adaptive-compression/test-correctness.sh | 5 +++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 237bb4306..5ff2e4181 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -513,12 +513,16 @@ static void* compressionThread(void* arg) { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); - size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); - if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { - DISPLAY("Error: something went wrong while starting compression\n"); - signalErrorToThreads(ctx); - return arg; + 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)) { + DISPLAY("Error: something went wrong while starting compression\n"); + signalErrorToThreads(ctx); + return arg; + } } } DEBUG(2, "finished with ZSTD_compressBegin()\n"); diff --git a/contrib/adaptive-compression/test-correctness.sh b/contrib/adaptive-compression/test-correctness.sh index 86d39ee4f..8ae6604af 100755 --- a/contrib/adaptive-compression/test-correctness.sh +++ b/contrib/adaptive-compression/test-correctness.sh @@ -237,4 +237,9 @@ rm tmp* zstd -d tmp.zst -o tmp2 diff -s -q tmp tmp2 rm tmp* + +echo -e "\ncorrectness tests -- window size test" +./datagen -s39 -g1GB | pv -L 25m -q | ./adapt -i1 | pv -q > tmp.zst +zstd -d tmp.zst +rm tmp* make clean From 715f36ca8165d3d89f974567c883dfb0e2bc4e36 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 15:52:15 -0700 Subject: [PATCH 248/318] added definitions for conversion constants, moved forced compression check to top of adaptCompressionLevel, used ZSTD_BLOCKSIZE_MAX --- contrib/adaptive-compression/adapt.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 5ff2e4181..05da8b7f7 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -28,6 +28,8 @@ #define MAX_COMPRESSION_LEVEL_CHANGE 2 #define CONVERGENCE_LOWER_BOUND 5 #define CLEVEL_DECREASE_COOLDOWN 5 +#define CHANGE_BY_TWO_THRESHOLD 0.1 +#define CHANGE_BY_ONE_THRESHOLD 0.65 #ifndef DEBUG_MODE static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; @@ -322,10 +324,10 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) /* map completion percentages to values for changing compression level */ static unsigned convertCompletionToChange(double completion) { - if (completion < 0.1) { + if (completion < CHANGE_BY_TWO_THRESHOLD) { return 2; } - else if (completion < 0.65) { + else if (completion < CHANGE_BY_ONE_THRESHOLD) { return 1; } else { @@ -350,6 +352,13 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double const threshold = 0.00001; unsigned const prevCompressionLevel = ctx->compressionLevel; + + if (g_forceCompressionLevel) { + ctx->compressionLevel = g_compressionLevel; + return; + } + + DEBUG(2, "adapting compression level %u\n", ctx->compressionLevel); /* read and reset completion measurements */ @@ -420,10 +429,6 @@ static void adaptCompressionLevel(adaptCCtx* ctx) if (ctx->compressionLevel == prevCompressionLevel) { ctx->convergenceCounter++; } - - if (g_forceCompressionLevel) { - ctx->compressionLevel = g_compressionLevel; - } } static size_t getUseableDictSize(unsigned compressionLevel) @@ -500,7 +505,7 @@ static void* compressionThread(void* arg) DEBUG(2, "job %u compressed with level %u\n", currJob, ctx->compressionLevel); /* compress the data */ { - size_t const compressionBlockSize = 1 << 17; /* 128 KB */ + size_t const compressionBlockSize = ZSTD_BLOCKSIZE_MAX; /* 128 KB */ unsigned const cLevel = ctx->compressionLevel; unsigned blockNum = 0; size_t remaining = job->src.size; From ab5a78547e62c9a80936e998f4db94fe43e6d232 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 16:40:05 -0700 Subject: [PATCH 249/318] fix leaky abstraction regarding measuring completion --- contrib/adaptive-compression/adapt.c | 57 ++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 05da8b7f7..a16ab40cc 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -367,20 +367,16 @@ static void adaptCompressionLevel(adaptCCtx* ctx) DEBUG(2, "writeWaitCompressionCompletion %f\n", ctx->writeWaitCompressionCompletion); createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; - ctx->createWaitCompressionCompletion = 1; - ctx->writeWaitCompressionCompletion = 1; pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); DEBUG(2, "compressWaitWriteCompletion %f\n", ctx->compressWaitWriteCompletion); compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; - ctx->compressWaitWriteCompletion = 1; pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); DEBUG(2, "compressWaitCreateCompletion %f\n", ctx->compressWaitCreateCompletion); compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; - ctx->compressWaitCreateCompletion = 1; pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); DEBUG(2, "convergence counter: %u\n", ctx->convergenceCounter); @@ -462,22 +458,28 @@ static void* compressionThread(void* arg) pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); if (willWaitForCreate) { DEBUG(2, "compression will wait for create on job %u\n", currJob); - pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->compressWaitCreateCompletion = ctx->createCompletion; DEBUG(2, "create completion %f\n", ctx->compressWaitCreateCompletion); - pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); } + else { + ctx->compressWaitCreateCompletion = 1; + } + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); + pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); if (willWaitForWrite) { DEBUG(2, "compression will wait for write on job %u\n", currJob); - pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(2, "write completion %f\n", ctx->compressWaitWriteCompletion); - pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); } + else { + ctx->compressWaitWriteCompletion = 1; + } + pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); } @@ -610,14 +612,27 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* const job = &ctx->jobs[currJobIndex]; + unsigned willWaitForCompress = 0; DEBUG(2, "starting write for job %u\n", currJob); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); - while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { - pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); + if (currJob + 1 > ctx->jobCompressedID) willWaitForCompress = 1; + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); + + + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); + if (willWaitForCompress) { /* write thread is waiting on compression thread */ ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion); - pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); + } + else { + ctx->writeWaitCompressionCompletion = 1; + } + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); + + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); + while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); @@ -751,17 +766,27 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA size_t const readBlockSize = 1 << 15; size_t remaining = FILE_CHUNK_SIZE; unsigned const nextJob = ctx->nextJobID; + unsigned willWaitForCompress = 0; DEBUG(2, "starting creation of job %u\n", currJob); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); + if (nextJob - ctx->jobCompressedID >= ctx->numJobs) willWaitForCompress = 1; + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); + + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); + if (willWaitForCompress) { + /* creation thread is waiting, take measurement of completion */ + ctx->createWaitCompressionCompletion = ctx->compressionCompletion; + DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); + } + else { + ctx->createWaitCompressionCompletion = 1; + } + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); /* wait until the job has been compressed */ pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (nextJob - ctx->jobCompressedID >= ctx->numJobs && !ctx->threadError) { - pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); - /* creation thread is waiting, take measurement of completion */ - ctx->createWaitCompressionCompletion = ctx->compressionCompletion; - DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); - pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); From 9eaf3d22d0818fc977b7df6af4ce78654613a97e Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 26 Jul 2017 16:43:25 -0700 Subject: [PATCH 250/318] Allow HASH_ONLY_EVERY_LOG to be configured in ldm.h --- contrib/long_distance_matching/Makefile | 2 +- contrib/long_distance_matching/ldm.c | 6 +++--- contrib/long_distance_matching/ldm.h | 11 +++++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile index 8bc7ac478..4193cb323 100644 --- a/contrib/long_distance_matching/Makefile +++ b/contrib/long_distance_matching/Makefile @@ -32,6 +32,6 @@ ldm: ldm_common.c ldm.c main.c clean: @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ - ldm + ldm @echo Cleaning completed diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 9a8438383..25bf5c838 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -16,11 +16,11 @@ #define LDM_HASH_ENTRY_SIZE_LOG 2 #endif -// Force the "probability" of insertion to be some value. // 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_LOG 7 -#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG))) #define HASH_ONLY_EVERY ((1 << (HASH_ONLY_EVERY_LOG)) - 1) #define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG)) diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 38d240152..af35130eb 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -3,7 +3,7 @@ #include "mem.h" // from /lib/common/mem.h -// #include "ldm_params.h" +//#include "ldm_params.h" // ============================================================================= // Modify the parameters in ldm_params.h if "ldm_params.h" is included. @@ -23,7 +23,7 @@ #define LDM_LAG 0 // The maximum window size when searching for matches. - // The maximum value is 30. + // The maximum value is 30 #define LDM_WINDOW_SIZE_LOG 28 // The minimum match length. @@ -47,6 +47,13 @@ // 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 From 2320e7378af2c62e576d4ce0fa4296580172177b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 17:02:47 -0700 Subject: [PATCH 251/318] remove unused variable, add documentation for context fields --- contrib/adaptive-compression/adapt.c | 38 +++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index a16ab40cc..5cf3b9708 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -24,7 +24,6 @@ #define MAX_PATH 256 #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 -#define DEFAULT_ADAPT_PARAM 0 #define MAX_COMPRESSION_LEVEL_CHANGE 2 #define CONVERGENCE_LOWER_BOUND 5 #define CLEVEL_DECREASE_COOLDOWN 5 @@ -81,20 +80,54 @@ typedef struct { unsigned numJobs; unsigned nextJobID; unsigned threadError; + + /* + * JobIDs for the next jobs to be created, compressed, and written + */ unsigned jobReadyID; unsigned jobCompressedID; unsigned jobWriteID; unsigned allJobsCompleted; - unsigned adaptParam; + + /* + * counter for how many jobs in a row the compression level has not changed + * if the counter becomes >= CONVERGENCE_LOWER_BOUND, the next time the + * compression level tries to change (by non-zero amount) resets the counter + * to 1 and does not apply the change + */ unsigned convergenceCounter; + + /* + * cooldown counter in order to prevent rapid successive decreases in compression level + * whenever compression level is decreased, cooldown is set to CLEVEL_DECREASE_COOLDOWN + * whenever adaptCompressionLevel() is called and cooldown != 0, it is decremented + * as long as cooldown != 0, the compression level cannot be decreased + */ unsigned cooldown; + + /* + * XWaitYCompletion + * Range from 0.0 to 1.0 + * if the value is not 1.0, then this implies that thread X waited on thread Y to finish + * and thread Y was XWaitYCompletion finished at the time of the wait (i.e. compressWaitWriteCompletion=0.5 + * implies that the compression thread waited on the write thread and it was only 50% finished writing a job) + */ double createWaitCompressionCompletion; double compressWaitCreateCompletion; double compressWaitWriteCompletion; double writeWaitCompressionCompletion; + + /* + * Completion values + * Range from 0.0 to 1.0 + * Jobs are divided into mini-chunks in order to measure completion + * these values are updated each time a thread finishes its operation on the + * mini-chunk (i.e. finishes writing out, compressing, etc. this mini-chunk). + */ double compressionCompletion; double writeCompletion; double createCompletion; + mutex_t jobCompressed_mutex; cond_t jobCompressed_cond; mutex_t jobReady_mutex; @@ -254,7 +287,6 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; - ctx->adaptParam = DEFAULT_ADAPT_PARAM; ctx->cctx = ZSTD_createCCtx(); if (!ctx->cctx) { From c105f605e66bd3bd985ceeaa660e7159e4d16298 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 27 Jul 2017 11:11:35 -0700 Subject: [PATCH 252/318] Update README --- contrib/long_distance_matching/README.md | 75 ++++++++++++++++++++++-- contrib/long_distance_matching/ldm.c | 22 ------- contrib/long_distance_matching/ldm.h | 3 + 3 files changed, 72 insertions(+), 28 deletions(-) diff --git a/contrib/long_distance_matching/README.md b/contrib/long_distance_matching/README.md index d9cb08951..e67bba714 100644 --- a/contrib/long_distance_matching/README.md +++ b/contrib/long_distance_matching/README.md @@ -28,12 +28,75 @@ The parameters are as follows and must all be defined: - `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 a 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 index 25bf5c838..ff9d94d07 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -58,9 +58,6 @@ struct LDM_compressStats { U32 minOffset, maxOffset; U32 offsetHistogram[32]; - - U64 TMP_hashCount[1 << HASH_ONLY_EVERY_LOG]; - U64 TMP_totalHashCount; }; typedef struct LDM_hashTable LDM_hashTable; @@ -398,17 +395,6 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { (double) stats->numMatches); } printf("\n"); -#if INSERT_BY_TAG -/* - printf("Lower bit distribution\n"); - for (i = 0; i < (1 << HASH_ONLY_EVERY_LOG); i++) { - printf("%5d %5llu %6.3f\n", i, stats->TMP_hashCount[i], - 100.0 * (double) stats->TMP_hashCount[i] / - (double) stats->TMP_totalHashCount); - } -*/ -#endif - printf("=====================\n"); } @@ -503,14 +489,6 @@ static void setNextHash(LDM_CCtx *cctx) { cctx->lastPosHashed[LDM_HASH_LENGTH]); cctx->nextPosHashed = cctx->nextIp; -#if INSERT_BY_TAG - { - U32 hashEveryMask = lowerBitsFromHfHash(cctx->nextHash); - cctx->stats.TMP_totalHashCount++; - cctx->stats.TMP_hashCount[hashEveryMask]++; - } -#endif - #if LDM_LAG if (cctx->ip - cctx->ibase > LDM_LAG) { cctx->lagHash = updateHash( diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index af35130eb..456ec5aa4 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -71,6 +71,9 @@ #define LDM_OFFSET_SIZE 4 #define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG)) + +// TODO: Match lengths that are too small do not use the hash table efficiently. +// There should be a minimum hash length given the hash table size. #define LDM_HASH_LENGTH LDM_MIN_MATCH_LENGTH typedef struct LDM_compressStats LDM_compressStats; From 627621839cf39310793fc5c2358985929497d42b Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 27 Jul 2017 15:37:37 -0700 Subject: [PATCH 253/318] Add checks in initialization code --- contrib/long_distance_matching/ldm.c | 33 ++++++++++++++++----- contrib/long_distance_matching/ldm.h | 9 ++++-- contrib/long_distance_matching/ldm_common.c | 20 +++++++++---- contrib/long_distance_matching/main.c | 18 ++++++++--- 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index ff9d94d07..c2cdb21ed 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -111,13 +111,25 @@ struct LDM_hashTable { /** * 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) { + free(table->bucketOffsets); + free(table->entries); + free(table); + return NULL; + } + return table; } @@ -566,9 +578,9 @@ static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) { putHashOfCurrentPositionFromHash(cctx, hash); } -void LDM_initializeCCtx(LDM_CCtx *cctx, - const void *src, size_t srcSize, - void *dst, size_t maxDstSize) { +size_t LDM_initializeCCtx(LDM_CCtx *cctx, + const void *src, size_t srcSize, + void *dst, size_t maxDstSize) { cctx->isize = srcSize; cctx->maxOSize = maxDstSize; @@ -590,16 +602,20 @@ void LDM_initializeCCtx(LDM_CCtx *cctx, #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) { @@ -726,7 +742,10 @@ size_t LDM_compress(const void *src, size_t srcSize, U64 forwardMatchLength = 0; U64 backwardsMatchLength = 0; - LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize); + if (LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize)) { + // Initialization failed. + return 0; + } #ifdef OUTPUT_CONFIGURATION LDM_outputConfiguration(); @@ -744,8 +763,8 @@ size_t LDM_compress(const void *src, size_t srcSize, * is less than the minimum match length), then stop searching for matches * and encode the final literals. */ - while (LDM_findBestMatch(&cctx, &match, &forwardMatchLength, - &backwardsMatchLength) == 0) { + while (!LDM_findBestMatch(&cctx, &match, &forwardMatchLength, + &backwardsMatchLength)) { #ifdef COMPUTE_STATS cctx.stats.numMatches++; diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h index 456ec5aa4..4adadbd0a 100644 --- a/contrib/long_distance_matching/ldm.h +++ b/contrib/long_distance_matching/ldm.h @@ -82,6 +82,7 @@ typedef struct LDM_DCtx LDM_DCtx; /** * Compresses src into dst. + * Returns the compressed size if successful, 0 otherwise. * * NB: This currently ignores maxDstSize and assumes enough space is available. * @@ -113,10 +114,12 @@ size_t LDM_compress(const void *src, size_t srcSize, * Initialize the compression context. * * Allocates memory for the hash table. + * + * Returns 0 if successful, 1 otherwise. */ -void LDM_initializeCCtx(LDM_CCtx *cctx, - const void *src, size_t srcSize, - void *dst, size_t maxDstSize); +size_t LDM_initializeCCtx(LDM_CCtx *cctx, + const void *src, size_t srcSize, + void *dst, size_t maxDstSize); /** * Frees up memory allocated in LDM_initializeCCtx(). diff --git a/contrib/long_distance_matching/ldm_common.c b/contrib/long_distance_matching/ldm_common.c index 26b716a1b..8b34f8ad4 100644 --- a/contrib/long_distance_matching/ldm_common.c +++ b/contrib/long_distance_matching/ldm_common.c @@ -2,19 +2,29 @@ #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); - ip += sizeof(U64); - *decompressedSize = MEM_readLE64(ip); - // ip += sizeof(U64); + *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_write64(memPtr, compressedSize); - MEM_write64((BYTE *)memPtr + 8, decompressedSize); + MEM_writeLE64(memPtr, compressedSize); + MEM_writeLE64((BYTE *)memPtr + 8, decompressedSize); } struct LDM_DCtx { diff --git a/contrib/long_distance_matching/main.c b/contrib/long_distance_matching/main.c index d55e01d32..72af54049 100644 --- a/contrib/long_distance_matching/main.c +++ b/contrib/long_distance_matching/main.c @@ -12,7 +12,7 @@ #include "ldm.h" #include "zstd.h" -// #define DECOMPRESS_AND_VERIFY +#define DECOMPRESS_AND_VERIFY /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. @@ -186,9 +186,18 @@ static int compare(FILE *fp0, FILE *fp1) { } /* Verify the input file is the same as the decompressed file. */ -static void verify(const char *inpFilename, const char *decFilename) { - FILE *inpFp = fopen(inpFilename, "rb"); - FILE *decFp = fopen(decFilename, "rb"); +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); { @@ -202,6 +211,7 @@ static void verify(const char *inpFilename, const char *decFilename) { fclose(decFp); fclose(inpFp); + return 0; } #endif From 1294a4a897d696e9a1f999f465527de31479596d Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 27 Jul 2017 15:49:46 -0700 Subject: [PATCH 254/318] Fix typo --- contrib/long_distance_matching/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/long_distance_matching/README.md b/contrib/long_distance_matching/README.md index e67bba714..771a6c3ce 100644 --- a/contrib/long_distance_matching/README.md +++ b/contrib/long_distance_matching/README.md @@ -1,6 +1,6 @@ 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 descriped in `ldm.h`. +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 @@ -28,7 +28,7 @@ The parameters are as follows and must all be defined: - `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 a even coverage of the window. +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 From 8fae41c412d99c8069ea1bd98ffcf50e2c99cd1e Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Thu, 27 Jul 2017 17:14:05 -0700 Subject: [PATCH 255/318] Return error code in verify() and minor code cleanup --- contrib/long_distance_matching/ldm.c | 50 ++++++++++++++------------- contrib/long_distance_matching/main.c | 12 ++++--- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index c2cdb21ed..4dccd0bfa 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -108,6 +108,12 @@ struct LDM_hashTable { 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. @@ -124,9 +130,7 @@ static LDM_hashTable *HASH_createTable(U32 size) { table->bucketOffsets = calloc(size >> HASH_BUCKET_SIZE_LOG, sizeof(BYTE)); if (!table->entries || !table->bucketOffsets) { - free(table->bucketOffsets); - free(table->entries); - free(table); + HASH_destroyTable(table); return NULL; } @@ -275,13 +279,13 @@ static LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx, U64 *pBackwardMatchLength) { LDM_hashTable *table = cctx->hashTable; LDM_hashEntry *bucket = getBucket(table, hash); - LDM_hashEntry *cur = bucket; + LDM_hashEntry *cur; LDM_hashEntry *bestEntry = NULL; U64 bestMatchLength = 0; #if !(USE_CHECKSUM) (void)checksum; #endif - for (; cur < bucket + HASH_BUCKET_SIZE; ++cur) { + for (cur = bucket; cur < bucket + HASH_BUCKET_SIZE; ++cur) { const BYTE *pMatch = cur->offset + cctx->ibase; // Check checksum for faster check. @@ -336,12 +340,6 @@ static void HASH_insert(LDM_hashTable *table, table->bucketOffsets[hash] &= HASH_BUCKET_SIZE - 1; } -static void HASH_destroyTable(LDM_hashTable *table) { - free(table->entries); - free(table->bucketOffsets); - free(table); -} - static void HASH_outputTableOccupancy(const LDM_hashTable *table) { U32 ctr = 0; LDM_hashEntry *cur = table->entries; @@ -360,8 +358,9 @@ static void HASH_outputTableOccupancy(const LDM_hashTable *table) { 100.0 * (double)(ctr) / table->numEntries); } -// TODO: This can be done more efficiently (but it is not that important as it -// is only used for computing stats). +// 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) { @@ -371,7 +370,6 @@ static int intLog2(U64 x) { } void LDM_printCompressStats(const LDM_compressStats *stats) { - int i = 0; printf("=====================\n"); printf("Compression statistics\n"); printf("Window size, hash table size (bytes): 2^%u, 2^%u\n", @@ -395,16 +393,20 @@ void LDM_printCompressStats(const LDM_compressStats *stats) { printf("offset histogram | match length histogram\n"); printf("offset/ML, num matches, %% of matches | num matches, %% of matches\n"); - for (; i <= intLog2(stats->maxOffset); 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); + { + 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"); diff --git a/contrib/long_distance_matching/main.c b/contrib/long_distance_matching/main.c index 72af54049..7c7086a59 100644 --- a/contrib/long_distance_matching/main.c +++ b/contrib/long_distance_matching/main.c @@ -12,7 +12,7 @@ #include "ldm.h" #include "zstd.h" -#define DECOMPRESS_AND_VERIFY +// #define DECOMPRESS_AND_VERIFY /* Compress file given by fname and output to oname. * Returns 0 if successful, error code otherwise. @@ -206,6 +206,7 @@ static int verify(const char *inpFilename, const char *decFilename) { printf("verify : OK\n"); } else { printf("verify : NG\n"); + return 1; } } @@ -239,7 +240,7 @@ int main(int argc, const char *argv[]) { /* Compress */ { if (compress(inpFilename, ldmFilename)) { - printf("Compress error"); + printf("Compress error\n"); return 1; } } @@ -250,7 +251,7 @@ int main(int argc, const char *argv[]) { struct timeval tv1, tv2; gettimeofday(&tv1, NULL); if (decompress(ldmFilename, decFilename)) { - printf("Decompress error"); + printf("Decompress error\n"); return 1; } gettimeofday(&tv2, NULL); @@ -259,7 +260,10 @@ int main(int argc, const char *argv[]) { (double) (tv2.tv_sec - tv1.tv_sec)); } /* verify */ - verify(inpFilename, decFilename); + if (verify(inpFilename, decFilename)) { + printf("Verification error\n"); + return 1; + } #endif return 0; } From ff54fced641bac595a4120108b563ee523650adc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 15:30:46 -0700 Subject: [PATCH 256/318] patched style errors, add ability to bound compression level variation --- contrib/adaptive-compression/adapt.c | 46 ++++++++++++++++++---------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 5cf3b9708..5e26a0db6 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -42,6 +42,8 @@ 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 = 22; typedef struct { void* start; @@ -420,7 +422,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); + unsigned const boundChange = ctx->compressionLevel >= g_minCLevel ? MIN(change, ctx->compressionLevel - g_minCLevel) : 0; if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; @@ -438,7 +440,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* compress waiting on write */ double const completion = MIN(compressWaitWriteCompletion, compressWaitCreateCompletion); unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); + unsigned const boundChange = g_maxCLevel >= ctx->compressionLevel ? MIN(change, g_maxCLevel - ctx->compressionLevel) : 0; if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; @@ -620,17 +622,19 @@ static void* compressionThread(void* arg) static void displayProgress(unsigned cLevel, unsigned last) { if (!g_useProgressBar) return; - UTIL_time_t currTime; - UTIL_getTime(&currTime); - double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, 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 { - fflush(stderr); + { + UTIL_time_t currTime; + UTIL_getTime(&currTime); + double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, 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 { + fflush(stderr); + } } } @@ -928,9 +932,9 @@ static int freeFileCompressionResources(fcResources* fcr) static int compressFilename(const char* const srcFilename, const char* const dstFilenameOrNull) { int ret = 0; + fcResources fcr = createFileCompressionResources(srcFilename, dstFilenameOrNull); UTIL_getTime(&g_startTime); g_streamedSize = 0; - fcResources fcr = createFileCompressionResources(srcFilename, dstFilenameOrNull); ret |= performCompression(fcr.ctx, fcr.srcFile, fcr.otArg); ret |= freeFileCompressionResources(&fcr); return ret; @@ -973,7 +977,7 @@ static unsigned readU32FromChar(const char** stringPtr) return result; } -static void help() +static void help(void) { PRINT("Usage:\n"); PRINT(" ./multi [options] [file(s)]\n"); @@ -986,6 +990,8 @@ static void help() PRINT(" -c : force write to stdout\n"); PRINT(" -p : hide progress bar\n"); PRINT(" -q : quiet mode -- do not show progress bar or other information\n"); + PRINT(" -l# : provide lower bound for compression level\n"); + PRINT(" -u# : provide upper bound for compression level\n"); } /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) @@ -993,10 +999,10 @@ int main(int argCount, const char* argv[]) const char* outFilename = NULL; const char** filenameTable = (const char**)malloc(argCount*sizeof(const char*)); unsigned filenameIdx = 0; - filenameTable[0] = stdinmark; unsigned forceStdout = 0; int ret = 0; int argNum; + filenameTable[0] = stdinmark; UTIL_initTimer(&g_ticksPerSecond); @@ -1036,6 +1042,14 @@ int main(int argCount, const char* argv[]) g_useProgressBar = 0; g_displayLevel = 0; break; + case 'l': + argument += 2; + g_minCLevel = readU32FromChar(&argument); + break; + case 'u': + argument += 2; + g_maxCLevel = readU32FromChar(&argument); + break; default: DISPLAY("Error: invalid argument provided\n"); ret = 1; From 0f4cb67b0050503aeb89a8c91e738c3d685cf89a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 15:55:02 -0700 Subject: [PATCH 257/318] add tests for compression bounds, fix another warning --- contrib/adaptive-compression/adapt.c | 5 +++-- contrib/adaptive-compression/test-correctness.sh | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 5e26a0db6..55f4148fb 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -621,10 +621,11 @@ static void* compressionThread(void* arg) static void displayProgress(unsigned cLevel, unsigned last) { + + UTIL_time_t currTime; + UTIL_getTime(&currTime); if (!g_useProgressBar) return; { - UTIL_time_t currTime; - UTIL_getTime(&currTime); double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0); double const sizeMB = (double)g_streamedSize / (1 << 20); double const avgCompRate = sizeMB * 1000 / timeElapsed; diff --git a/contrib/adaptive-compression/test-correctness.sh b/contrib/adaptive-compression/test-correctness.sh index 8ae6604af..3bea867b9 100755 --- a/contrib/adaptive-compression/test-correctness.sh +++ b/contrib/adaptive-compression/test-correctness.sh @@ -242,4 +242,11 @@ echo -e "\ncorrectness tests -- window size test" ./datagen -s39 -g1GB | pv -L 25m -q | ./adapt -i1 | pv -q > tmp.zst zstd -d tmp.zst rm tmp* + +echo -e "\ncorrectness tests -- testing bounds" +./datagen -s40 -g1GB | pv -L 25m -q | ./adapt -i1 -u4 | pv -q > tmp.zst +rm tmp* + +./datagen -s41 -g1GB | ./adapt -i14 -l4 > tmp.zst +rm tmp* make clean From 4d904ac800920286fc6d22b28e132577572cd4de Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 16:12:58 -0700 Subject: [PATCH 258/318] add flags for multithreading --- contrib/adaptive-compression/Makefile | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 9bc19ee15..0e081a670 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -6,6 +6,15 @@ ZSTDCOMP_FILES := $(ZSTDDIR)/compress/*.c ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/*.c ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) +# Define *.exe as extension for Windows systems +ifneq (,$(filter Windows%,$(OS))) +EXT =.exe +MULTITHREAD_LD = +else +EXT = +MULTITHREAD_LD = -pthread +endif + DEBUGFLAGS= -g -DZSTD_DEBUG=1 CPPFLAGS += -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) @@ -17,7 +26,7 @@ CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wredundant-decls CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) -FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) +FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(MULTITHREAD_LD) all: adapt datagen From 51788225db8d3a2fac58614d72e0dd918fa380fb Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 17:27:36 -0700 Subject: [PATCH 259/318] remove exe extension from makefile, reinclude pthread flag --- contrib/adaptive-compression/Makefile | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 0e081a670..b1c498c93 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -6,15 +6,7 @@ ZSTDCOMP_FILES := $(ZSTDDIR)/compress/*.c ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/*.c ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) -# Define *.exe as extension for Windows systems -ifneq (,$(filter Windows%,$(OS))) -EXT =.exe -MULTITHREAD_LD = -else -EXT = -MULTITHREAD_LD = -pthread -endif - +MULTITHREAD_LDFLAGS = -pthread DEBUGFLAGS= -g -DZSTD_DEBUG=1 CPPFLAGS += -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) @@ -26,7 +18,7 @@ CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wredundant-decls CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) -FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(MULTITHREAD_LD) +FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(MULTITHREAD_LDFLAGS) all: adapt datagen From cb9af53e773c1fd1df9765028c2ffec636838158 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 17:28:25 -0700 Subject: [PATCH 260/318] delete empty line --- contrib/adaptive-compression/adapt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 55f4148fb..755b7e976 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -621,7 +621,6 @@ static void* compressionThread(void* arg) static void displayProgress(unsigned cLevel, unsigned last) { - UTIL_time_t currTime; UTIL_getTime(&currTime); if (!g_useProgressBar) return; From e22b60cb76a09af2dc0365bca2ee20437003f2db Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 17:46:51 -0700 Subject: [PATCH 261/318] removed ternary operation, added assert statement, check to make sure initial compression level is within bounds --- contrib/adaptive-compression/adapt.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 755b7e976..3a57c3723 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -414,6 +414,8 @@ static void adaptCompressionLevel(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); DEBUG(2, "convergence counter: %u\n", ctx->convergenceCounter); + assert(g_minCLevel <= ctx->compressionLevel && g_maxCLevel >= ctx->compressionLevel); + /* adaptation logic */ if (ctx->cooldown) ctx->cooldown--; @@ -422,7 +424,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = ctx->compressionLevel >= g_minCLevel ? MIN(change, ctx->compressionLevel - g_minCLevel) : 0; + unsigned const boundChange = MIN(change, ctx->compressionLevel - g_minCLevel); if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; @@ -440,7 +442,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* compress waiting on write */ double const completion = MIN(compressWaitWriteCompletion, compressWaitCreateCompletion); unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = g_maxCLevel >= ctx->compressionLevel ? MIN(change, g_maxCLevel - ctx->compressionLevel) : 0; + unsigned const boundChange = MIN(change, g_maxCLevel - ctx->compressionLevel); if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; @@ -1000,6 +1002,7 @@ int main(int argCount, const char* argv[]) const char** filenameTable = (const char**)malloc(argCount*sizeof(const char*)); unsigned filenameIdx = 0; unsigned forceStdout = 0; + unsigned providedInitialCLevel = 0; int ret = 0; int argNum; filenameTable[0] = stdinmark; @@ -1024,6 +1027,7 @@ int main(int argCount, const char* argv[]) case 'i': argument += 2; g_compressionLevel = readU32FromChar(&argument); + providedInitialCLevel = 1; break; case 'h': help(); @@ -1062,6 +1066,20 @@ int main(int argCount, const char* argv[]) filenameTable[filenameIdx++] = argument; } + /* check initial, max, and min compression levels */ + { + unsigned const minMaxInconsistent = g_minCLevel > g_maxCLevel; + unsigned const initialNotInRange = g_minCLevel > g_compressionLevel || g_maxCLevel < g_compressionLevel; + if (minMaxInconsistent || (initialNotInRange && providedInitialCLevel)) { + DISPLAY("Error: provided compression level parameters are invalid\n"); + ret = 1; + goto _main_exit; + } + else if (initialNotInRange) { + g_compressionLevel = g_minCLevel; + } + } + /* error checking with number of files */ if (filenameIdx > 1 && (outFilename != NULL && strcmp(outFilename, stdoutmark))) { DISPLAY("Error: multiple input files provided, cannot use specified output file\n"); From f60cd3f99bccc482d89c1afac73b329a2d9adb29 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 31 Jul 2017 09:47:09 -0700 Subject: [PATCH 262/318] print defaults and range, remove EXT --- contrib/adaptive-compression/Makefile | 2 +- contrib/adaptive-compression/adapt.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index b1c498c93..87b61b417 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -29,7 +29,7 @@ adapt-debug: $(ZSTD_FILES) adapt.c $(CC) $(FLAGS) -DDEBUG_MODE=2 $^ -o adapt datagen : $(PRGDIR)/datagen.c datagencli.c - $(CC) $(FLAGS) $^ -o $@$(EXT) + $(CC) $(FLAGS) $^ -o $@ test-adapt-correctness: datagen adapt @./test-correctness.sh diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 3a57c3723..5cec227ec 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -986,14 +986,14 @@ static void help(void) PRINT("\n"); PRINT("Options:\n"); PRINT(" -oFILE : specify the output file name\n"); - PRINT(" -i# : provide initial compression level\n"); + PRINT(" -i# : provide initial compression level -- default %d, must be in the range [L, U] where L and U are bound values (see below for defaults)\n", DEFAULT_COMPRESSION_LEVEL); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); PRINT(" -c : force write to stdout\n"); PRINT(" -p : hide progress bar\n"); PRINT(" -q : quiet mode -- do not show progress bar or other information\n"); - PRINT(" -l# : provide lower bound for compression level\n"); - PRINT(" -u# : provide upper bound for compression level\n"); + PRINT(" -l# : provide lower bound for compression level -- default 1\n"); + PRINT(" -u# : provide upper bound for compression level -- default 22\n"); } /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) From 5adceeed0175e5312c9b68f00494ecd014aa81d6 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 31 Jul 2017 10:10:16 -0700 Subject: [PATCH 263/318] Allow queueSize=0 in pool.c and update poolTests --- lib/common/pool.c | 37 ++++++++++++++++++++++++++++++++----- tests/poolTests.c | 6 +++--- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index aeaca7e79..f51beccc5 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -13,6 +13,8 @@ #include /* malloc, calloc, free */ #include "pool.h" +#include + /* ====== Compiler specifics ====== */ #if defined(_MSC_VER) # pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */ @@ -34,11 +36,18 @@ struct POOL_ctx_s { pthread_t *threads; size_t numThreads; + size_t numThreadsBusy; + /* The queue is a circular buffer */ POOL_job *queue; size_t queueHead; size_t queueTail; size_t queueSize; + + size_t jobsQueued; + + size_t marker; + /* The mutex protects the queue */ pthread_mutex_t queueMutex; /* Condition variable for pushers to wait on when the queue is full */ @@ -60,21 +69,30 @@ static void* POOL_thread(void* opaque) { for (;;) { /* Lock the mutex and wait for a non-empty queue or until shutdown */ pthread_mutex_lock(&ctx->queueMutex); - while (ctx->queueHead == ctx->queueTail && !ctx->shutdown) { +// while (ctx->queueHead == ctx->queueTail && !ctx->shutdown) { + while (!ctx->jobsQueued && !ctx->shutdown && !ctx->marker) { pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex); } /* empty => shutting down: so stop */ - if (ctx->queueHead == ctx->queueTail) { + if (!ctx->jobsQueued && !ctx->marker) { pthread_mutex_unlock(&ctx->queueMutex); return opaque; } /* Pop a job off the queue */ - { POOL_job const job = ctx->queue[ctx->queueHead]; + { + POOL_job const job = ctx->queue[ctx->queueHead]; ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize; + ctx->jobsQueued--; + ctx->numThreadsBusy++; + ctx->marker = 0; /* Unlock the mutex, signal a pusher, and run the job */ pthread_mutex_unlock(&ctx->queueMutex); pthread_cond_signal(&ctx->queuePushCond); job.function(job.opaque); + + pthread_mutex_lock(&ctx->queueMutex); + ctx->numThreadsBusy--; + pthread_mutex_unlock(&ctx->queueMutex); } } /* Unreachable */ @@ -83,7 +101,7 @@ static void* POOL_thread(void* opaque) { POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { POOL_ctx *ctx; /* Check the parameters */ - if (!numThreads || !queueSize) { return NULL; } + if (!numThreads) { return NULL; } /* Allocate the context and zero initialize */ ctx = (POOL_ctx *)calloc(1, sizeof(POOL_ctx)); if (!ctx) { return NULL; } @@ -95,6 +113,9 @@ POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { ctx->queue = (POOL_job*) malloc(ctx->queueSize * sizeof(POOL_job)); ctx->queueHead = 0; ctx->queueTail = 0; + ctx->numThreadsBusy = 0; + ctx->jobsQueued = 0; + ctx->marker = 0; (void)pthread_mutex_init(&ctx->queueMutex, NULL); (void)pthread_cond_init(&ctx->queuePushCond, NULL); (void)pthread_cond_init(&ctx->queuePopCond, NULL); @@ -161,14 +182,20 @@ void POOL_add(void* ctxVoid, POOL_function function, void *opaque) { { POOL_job const job = {function, opaque}; /* Wait until there is space in the queue for the new job */ size_t newTail = (ctx->queueTail + 1) % ctx->queueSize; - while (ctx->queueHead == newTail && !ctx->shutdown) { + while (ctx->queueHead == newTail && !ctx->shutdown && + (ctx->queueSize > 1 || ctx->numThreadsBusy == ctx->numThreads || + ctx->marker)) { pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex); newTail = (ctx->queueTail + 1) % ctx->queueSize; } /* The queue is still going => there is space */ if (!ctx->shutdown) { + if (ctx->queueSize > 1) { + ctx->marker = 1; + } ctx->queue[ctx->queueTail] = job; ctx->queueTail = newTail; + ctx->jobsQueued++; } } pthread_mutex_unlock(&ctx->queueMutex); diff --git a/tests/poolTests.c b/tests/poolTests.c index adc5947df..09e6d6adf 100644 --- a/tests/poolTests.c +++ b/tests/poolTests.c @@ -54,7 +54,7 @@ int main(int argc, const char **argv) { size_t numThreads; for (numThreads = 1; numThreads <= 4; ++numThreads) { size_t queueSize; - for (queueSize = 1; queueSize <= 2; ++queueSize) { + for (queueSize = 0; queueSize <= 2; ++queueSize) { if (testOrder(numThreads, queueSize)) { printf("FAIL: testOrder\n"); return 1; @@ -64,7 +64,7 @@ int main(int argc, const char **argv) { printf("PASS: testOrder\n"); (void)argc; (void)argv; - return (POOL_create(0, 1) || POOL_create(1, 0)) ? printf("FAIL: testInvalid\n"), 1 - : printf("PASS: testInvalid\n"), 0; + return (POOL_create(0, 1)) ? printf("FAIL: testInvalid\n"), 1 + : printf("PASS: testInvalid\n"), 0; return 0; } From 9ea7df03de87a581b3f3be1eb6e555aece56888c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 31 Jul 2017 11:04:17 -0700 Subject: [PATCH 264/318] add install target in makefile --- contrib/adaptive-compression/Makefile | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 87b61b417..c64fce954 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -46,3 +46,31 @@ clean: @$(RM) -f tests/*.zst @$(RM) -f tests/tmp* @echo "finished cleaning" + +#----------------------------------------------------------------------------- +# make install is validated only for Linux, OSX, BSD, Hurd and Solaris targets +#----------------------------------------------------------------------------- +ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS)) + +ifneq (,$(filter $(shell uname),SunOS)) +INSTALL ?= ginstall +else +INSTALL ?= install +endif + +PREFIX ?= /usr/local +DESTDIR ?= +BINDIR ?= $(PREFIX)/bin + +INSTALL_PROGRAM ?= $(INSTALL) -m 755 + +install: adapt + @echo Installing binaries + @$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)/ + @$(INSTALL_PROGRAM) adapt $(DESTDIR)$(BINDIR)/zstd-adaptive + @echo zstd-adaptive installation completed + +uninstall: + @$(RM) $(DESTDIR)$(BINDIR)/zstd-adaptive + @echo zstd-adaptive programs successfully uninstalled +endif From 0295737ad770ebeafd02f93cf61e1446a2cd4913 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 31 Jul 2017 13:43:03 -0700 Subject: [PATCH 265/318] change signal to broadcast for jobCompressed condition varaible since multiple threads waiting --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 5cec227ec..eeb4c2ea9 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -333,7 +333,7 @@ static void signalErrorToThreads(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); - pthread_cond_signal(&ctx->jobCompressed_cond.pCond); + pthread_cond_broadcast(&ctx->jobCompressed_cond.pCond); pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); From 1d76da1d87d0908f9b24d865b064ab812e491e79 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Tue, 1 Aug 2017 12:24:55 -0700 Subject: [PATCH 266/318] Replace marker with queueEmpty variable and update pool.h comment --- lib/common/pool.c | 35 +++++++++++++++-------------------- lib/common/pool.h | 1 - 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index f51beccc5..e25b1d75e 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -13,8 +13,6 @@ #include /* malloc, calloc, free */ #include "pool.h" -#include - /* ====== Compiler specifics ====== */ #if defined(_MSC_VER) # pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */ @@ -36,17 +34,16 @@ struct POOL_ctx_s { pthread_t *threads; size_t numThreads; - size_t numThreadsBusy; - /* The queue is a circular buffer */ POOL_job *queue; size_t queueHead; size_t queueTail; size_t queueSize; - size_t jobsQueued; - - size_t marker; + /* The number of threads working on jobs */ + size_t numThreadsBusy; + /* Indicates if the queue is empty */ + int queueEmpty; /* The mutex protects the queue */ pthread_mutex_t queueMutex; @@ -69,12 +66,11 @@ static void* POOL_thread(void* opaque) { for (;;) { /* Lock the mutex and wait for a non-empty queue or until shutdown */ pthread_mutex_lock(&ctx->queueMutex); -// while (ctx->queueHead == ctx->queueTail && !ctx->shutdown) { - while (!ctx->jobsQueued && !ctx->shutdown && !ctx->marker) { + while (ctx->queueEmpty && !ctx->shutdown) { pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex); } /* empty => shutting down: so stop */ - if (!ctx->jobsQueued && !ctx->marker) { + if (ctx->queueEmpty) { pthread_mutex_unlock(&ctx->queueMutex); return opaque; } @@ -82,9 +78,8 @@ static void* POOL_thread(void* opaque) { { POOL_job const job = ctx->queue[ctx->queueHead]; ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize; - ctx->jobsQueued--; ctx->numThreadsBusy++; - ctx->marker = 0; + ctx->queueEmpty = ctx->queueHead == ctx->queueTail; /* Unlock the mutex, signal a pusher, and run the job */ pthread_mutex_unlock(&ctx->queueMutex); pthread_cond_signal(&ctx->queuePushCond); @@ -114,8 +109,7 @@ POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { ctx->queueHead = 0; ctx->queueTail = 0; ctx->numThreadsBusy = 0; - ctx->jobsQueued = 0; - ctx->marker = 0; + ctx->queueEmpty = 1; (void)pthread_mutex_init(&ctx->queueMutex, NULL); (void)pthread_cond_init(&ctx->queuePushCond, NULL); (void)pthread_cond_init(&ctx->queuePopCond, NULL); @@ -180,22 +174,23 @@ void POOL_add(void* ctxVoid, POOL_function function, void *opaque) { pthread_mutex_lock(&ctx->queueMutex); { POOL_job const job = {function, opaque}; - /* Wait until there is space in the queue for the new job */ + + // Wait until there is space in the queue for the new job. + // If the ctx->queueSize is 1 (the pool was created with an + // intended queueSize of 0) and there is no job already waiting, + // wait until there is a thread free for the new job. size_t newTail = (ctx->queueTail + 1) % ctx->queueSize; while (ctx->queueHead == newTail && !ctx->shutdown && (ctx->queueSize > 1 || ctx->numThreadsBusy == ctx->numThreads || - ctx->marker)) { + !ctx->queueEmpty)) { pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex); newTail = (ctx->queueTail + 1) % ctx->queueSize; } /* The queue is still going => there is space */ if (!ctx->shutdown) { - if (ctx->queueSize > 1) { - ctx->marker = 1; - } + ctx->queueEmpty = 0; ctx->queue[ctx->queueTail] = job; ctx->queueTail = newTail; - ctx->jobsQueued++; } } pthread_mutex_unlock(&ctx->queueMutex); diff --git a/lib/common/pool.h b/lib/common/pool.h index 957100f46..ed2711950 100644 --- a/lib/common/pool.h +++ b/lib/common/pool.h @@ -22,7 +22,6 @@ typedef struct POOL_ctx_s POOL_ctx; * Create a thread pool with at most `numThreads` threads. * `numThreads` must be at least 1. * The maximum number of queued jobs before blocking is `queueSize`. - * `queueSize` must be at least 1. * @return : POOL_ctx pointer on success, else NULL. */ POOL_ctx *POOL_create(size_t numThreads, size_t queueSize); From 69ef22c0ac9766be283ad6e568defba2633d0d6f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 1 Aug 2017 17:36:13 -0700 Subject: [PATCH 267/318] added detach statements to prevent resource leak --- contrib/adaptive-compression/adapt.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index eeb4c2ea9..eef6c9326 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -785,6 +785,11 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA signalErrorToThreads(ctx); return 1; } + else if (pthread_detach(out)) { + DISPLAY("Error: could not detach output thread\n"); + signalErrorToThreads(ctx); + return 1; + } } /* create compression thread */ @@ -795,6 +800,11 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA signalErrorToThreads(ctx); return 1; } + else if (pthread_detach(compression)) { + DISPLAY("Error: could not detach compression thread\n"); + signalErrorToThreads(ctx); + return 1; + } } { unsigned currJob = 0; From 73ba58955fa238d23bc7ba483c10b722af5e8c3b Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Tue, 1 Aug 2017 20:12:06 -0700 Subject: [PATCH 268/318] Signal after finishing job when queueSize=0 --- lib/common/pool.c | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index e25b1d75e..e140f1e88 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -66,6 +66,7 @@ static void* POOL_thread(void* opaque) { for (;;) { /* Lock the mutex and wait for a non-empty queue or until shutdown */ pthread_mutex_lock(&ctx->queueMutex); + while (ctx->queueEmpty && !ctx->shutdown) { pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex); } @@ -82,12 +83,20 @@ static void* POOL_thread(void* opaque) { ctx->queueEmpty = ctx->queueHead == ctx->queueTail; /* Unlock the mutex, signal a pusher, and run the job */ pthread_mutex_unlock(&ctx->queueMutex); - pthread_cond_signal(&ctx->queuePushCond); + + if (ctx->queueSize > 1) { + pthread_cond_signal(&ctx->queuePushCond); + } + job.function(job.opaque); - pthread_mutex_lock(&ctx->queueMutex); - ctx->numThreadsBusy--; - pthread_mutex_unlock(&ctx->queueMutex); + /* If the intended queue size was 0, signal after finishing job */ + if (ctx->queueSize == 1) { + pthread_mutex_lock(&ctx->queueMutex); + ctx->numThreadsBusy--; + pthread_mutex_unlock(&ctx->queueMutex); + pthread_cond_signal(&ctx->queuePushCond); + } } } /* Unreachable */ @@ -168,6 +177,21 @@ size_t POOL_sizeof(POOL_ctx *ctx) { + ctx->numThreads * sizeof(pthread_t); } +/** + * Returns 1 if the queue is full and 0 otherwise. + * + * If the queueSize is 1 (the pool was created with an intended queueSize of 0), + * then a queue is empty if there is a thread free and no job is waiting. + */ +static int isQueueFull(POOL_ctx const* ctx) { + if (ctx->queueSize > 1) { + return ctx->queueHead == ((ctx->queueTail + 1) % ctx->queueSize); + } else { + return ctx->numThreadsBusy == ctx->numThreads || + !ctx->queueEmpty; + } +} + void POOL_add(void* ctxVoid, POOL_function function, void *opaque) { POOL_ctx* const ctx = (POOL_ctx*)ctxVoid; if (!ctx) { return; } @@ -175,22 +199,15 @@ void POOL_add(void* ctxVoid, POOL_function function, void *opaque) { pthread_mutex_lock(&ctx->queueMutex); { POOL_job const job = {function, opaque}; - // Wait until there is space in the queue for the new job. - // If the ctx->queueSize is 1 (the pool was created with an - // intended queueSize of 0) and there is no job already waiting, - // wait until there is a thread free for the new job. - size_t newTail = (ctx->queueTail + 1) % ctx->queueSize; - while (ctx->queueHead == newTail && !ctx->shutdown && - (ctx->queueSize > 1 || ctx->numThreadsBusy == ctx->numThreads || - !ctx->queueEmpty)) { + /* Wait until there is space in the queue for the new job */ + while (isQueueFull(ctx) && !ctx->shutdown) { pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex); - newTail = (ctx->queueTail + 1) % ctx->queueSize; } /* The queue is still going => there is space */ if (!ctx->shutdown) { ctx->queueEmpty = 0; ctx->queue[ctx->queueTail] = job; - ctx->queueTail = newTail; + ctx->queueTail = (ctx->queueTail + 1) % ctx->queueSize; } } pthread_mutex_unlock(&ctx->queueMutex); From 8be7bba08c1ceca678726aa32b3d537af0ecbf21 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 2 Aug 2017 10:27:33 -0700 Subject: [PATCH 269/318] added mutex for compression level to avoid data race --- contrib/adaptive-compression/adapt.c | 46 ++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index eef6c9326..7cbf2c99a 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -59,7 +59,6 @@ typedef struct { typedef struct { buffer_t src; buffer_t dst; - unsigned compressionLevel; unsigned jobID; unsigned lastJobPlusOne; size_t compressedSize; @@ -78,7 +77,6 @@ typedef struct { typedef struct { unsigned compressionLevel; - unsigned numActiveThreads; unsigned numJobs; unsigned nextJobID; unsigned threadError; @@ -141,6 +139,7 @@ typedef struct { mutex_t compressionCompletion_mutex; mutex_t createCompletion_mutex; mutex_t writeCompletion_mutex; + mutex_t compressionLevel_mutex; size_t lastDictSize; inBuff_t input; jobDescription* jobs; @@ -202,6 +201,7 @@ static int freeCCtx(adaptCCtx* ctx) error |= destroyMutex(&ctx->compressionCompletion_mutex); error |= destroyMutex(&ctx->createCompletion_mutex); error |= destroyMutex(&ctx->writeCompletion_mutex); + error |= destroyMutex(&ctx->compressionLevel_mutex); error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); free(ctx->input.buffer.start); if (ctx->jobs){ @@ -243,6 +243,7 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) pthreadError |= initMutex(&ctx->compressionCompletion_mutex); pthreadError |= initMutex(&ctx->createCompletion_mutex); pthreadError |= initMutex(&ctx->writeCompletion_mutex); + pthreadError |= initMutex(&ctx->compressionLevel_mutex); if (pthreadError) return pthreadError; } ctx->numJobs = numJobs; @@ -384,16 +385,22 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double compressWaitWriteCompletion; double writeWaitCompressionCompletion; double const threshold = 0.00001; - unsigned const prevCompressionLevel = ctx->compressionLevel; + unsigned prevCompressionLevel; + + pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex); + prevCompressionLevel = ctx->compressionLevel; + pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex); if (g_forceCompressionLevel) { + pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex); ctx->compressionLevel = g_compressionLevel; + pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex); return; } - DEBUG(2, "adapting compression level %u\n", ctx->compressionLevel); + DEBUG(2, "adapting compression level %u\n", prevCompressionLevel); /* read and reset completion measurements */ pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); @@ -414,7 +421,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); DEBUG(2, "convergence counter: %u\n", ctx->convergenceCounter); - assert(g_minCLevel <= ctx->compressionLevel && g_maxCLevel >= ctx->compressionLevel); + assert(g_minCLevel <= prevCompressionLevel && g_maxCLevel >= prevCompressionLevel); /* adaptation logic */ if (ctx->cooldown) ctx->cooldown--; @@ -424,14 +431,16 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = MIN(change, ctx->compressionLevel - g_minCLevel); + unsigned const boundChange = MIN(change, prevCompressionLevel - g_minCLevel); if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; DEBUG(2, "convergence counter reset, no change applied\n"); } else if (boundChange != 0) { + pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex); ctx->compressionLevel -= boundChange; + pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex); ctx->cooldown = CLEVEL_DECREASE_COOLDOWN; ctx->convergenceCounter = 1; @@ -442,14 +451,16 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* compress waiting on write */ double const completion = MIN(compressWaitWriteCompletion, compressWaitCreateCompletion); unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = MIN(change, g_maxCLevel - ctx->compressionLevel); + unsigned const boundChange = MIN(change, g_maxCLevel - prevCompressionLevel); if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; DEBUG(2, "convergence counter reset, no change applied\n"); } else if (boundChange != 0) { + pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex); ctx->compressionLevel += boundChange; + pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex); ctx->cooldown = 0; ctx->convergenceCounter = 1; @@ -458,9 +469,11 @@ static void adaptCompressionLevel(adaptCCtx* ctx) } + pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex); if (ctx->compressionLevel == prevCompressionLevel) { ctx->convergenceCounter++; } + pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex); } static size_t getUseableDictSize(unsigned compressionLevel) @@ -540,15 +553,23 @@ static void* compressionThread(void* arg) /* adapt compression level */ if (currJob) adaptCompressionLevel(ctx); + pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex); DEBUG(2, "job %u compressed with level %u\n", currJob, ctx->compressionLevel); + pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex); + /* compress the data */ { size_t const compressionBlockSize = ZSTD_BLOCKSIZE_MAX; /* 128 KB */ - unsigned const cLevel = ctx->compressionLevel; + unsigned cLevel; unsigned blockNum = 0; size_t remaining = job->src.size; size_t srcPos = 0; size_t dstPos = 0; + + pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex); + cLevel = ctx->compressionLevel; + pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex); + /* reset compressed size */ job->compressedSize = 0; DEBUG(2, "calling ZSTD_compressBegin()\n"); @@ -712,7 +733,13 @@ static void* outputThread(void* arg) } } } - displayProgress(ctx->compressionLevel, job->lastJobPlusOne == currJob + 1); + { + unsigned cLevel; + pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex); + cLevel = ctx->compressionLevel; + pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex); + displayProgress(cLevel, job->lastJobPlusOne == currJob + 1); + } pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); ctx->jobWriteID++; pthread_cond_signal(&ctx->jobWrite_cond.pCond); @@ -740,7 +767,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) jobDescription* const job = &ctx->jobs[nextJobIndex]; - job->compressionLevel = ctx->compressionLevel; job->src.size = srcSize; job->jobID = nextJob; if (last) job->lastJobPlusOne = nextJob + 1; From 1e366f9dea1855c4ee8bd4bb8b4b41e486c95e6c Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 2 Aug 2017 11:27:50 -0700 Subject: [PATCH 270/318] Add test for deadlock --- tests/poolTests.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/poolTests.c b/tests/poolTests.c index 09e6d6adf..d5c37aee0 100644 --- a/tests/poolTests.c +++ b/tests/poolTests.c @@ -2,6 +2,7 @@ #include "threading.h" #include #include +#include #define ASSERT_TRUE(p) \ do { \ @@ -50,6 +51,26 @@ int testOrder(size_t numThreads, size_t queueSize) { return 0; } +void waitFn(void *opaque) { + (void)opaque; + usleep(100); +} + +/* Tests for deadlock */ +int testWait(size_t numThreads, size_t queueSize) { + struct data data; + POOL_ctx *ctx = POOL_create(numThreads, queueSize); + ASSERT_TRUE(ctx); + { + size_t i; + for (i = 0; i < 16; ++i) { + POOL_add(ctx, &waitFn, &data); + } + } + POOL_free(ctx); + return 0; +} + int main(int argc, const char **argv) { size_t numThreads; for (numThreads = 1; numThreads <= 4; ++numThreads) { @@ -59,6 +80,10 @@ int main(int argc, const char **argv) { printf("FAIL: testOrder\n"); return 1; } + if (testWait(numThreads, queueSize)) { + printf("FAIL: testWait\n"); + return 1; + } } } printf("PASS: testOrder\n"); From 01237e3b358bc99a5b877d2c1fbeed184c8412e4 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 3 Aug 2017 15:13:49 -0700 Subject: [PATCH 271/318] changed multi to zstd-adaptive in the help menu --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 7cbf2c99a..e1ca3e2ae 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -1018,7 +1018,7 @@ static unsigned readU32FromChar(const char** stringPtr) static void help(void) { PRINT("Usage:\n"); - PRINT(" ./multi [options] [file(s)]\n"); + PRINT(" zstd-adaptive [options] [file(s)]\n"); PRINT("\n"); PRINT("Options:\n"); PRINT(" -oFILE : specify the output file name\n"); From 7393b49fbd7937b1264d6bcbf28d5bc0d75abdcc Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 4 Aug 2017 16:57:03 -0700 Subject: [PATCH 272/318] [linux-kernel] Update patches for v4 --- contrib/linux-kernel/0000-cover-letter.patch | 45 +- .../0001-lib-Add-xxhash-module.patch | 4 +- .../0002-lib-Add-zstd-modules.patch | 21 +- .../0003-btrfs-Add-zstd-support.patch | 39 +- .../0004-squashfs-Add-zstd-support.patch | 30 +- .../0005-crypto-Add-zstd-support.patch | 425 ++++++++++++++++++ ...0006-squashfs-tools-Add-zstd-support.patch | 423 +++++++++++++++++ contrib/linux-kernel/fs/btrfs/zstd.c | 23 +- .../linux-kernel/fs/squashfs/zstd_wrapper.c | 11 +- contrib/linux-kernel/lib/zstd/decompress.c | 2 + contrib/linux-kernel/lib/zstd/zstd_internal.h | 2 +- 11 files changed, 946 insertions(+), 79 deletions(-) create mode 100644 contrib/linux-kernel/0005-crypto-Add-zstd-support.patch create mode 100644 contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch diff --git a/contrib/linux-kernel/0000-cover-letter.patch b/contrib/linux-kernel/0000-cover-letter.patch index 33a5189b8..f72b7614e 100644 --- a/contrib/linux-kernel/0000-cover-letter.patch +++ b/contrib/linux-kernel/0000-cover-letter.patch @@ -1,7 +1,7 @@ -From 0cd63464d182bb9708f8b25f7da3dc8e5ec6b4fa Mon Sep 17 00:00:00 2001 +From a276288db937088d00b975ad9c36278fa46c8cf7 Mon Sep 17 00:00:00 2001 From: Nick Terrell -Date: Thu, 20 Jul 2017 13:18:30 -0700 -Subject: [PATCH v3 0/4] Add xxhash and zstd modules +Date: Fri, 4 Aug 2017 12:47:29 -0700 +Subject: [PATCH v4 0/5] Add xxhash and zstd modules Hi all, @@ -16,27 +16,39 @@ Nick Terrell Changelog: v1 -> v2: -- Make pointer in lib/xxhash.c:394 non-const (1/4) -- Use div_u64() for division of u64s (2/4) +- Make pointer in lib/xxhash.c:394 non-const (1/5) +- Use div_u64() for division of u64s (2/5) - Reduce stack usage of ZSTD_compressSequences(), ZSTD_buildSeqTable(), ZSTD_decompressSequencesLong(), FSE_buildDTable(), FSE_decompress_wksp(), HUF_writeCTable(), HUF_readStats(), HUF_readCTable(), - HUF_compressWeights(), HUF_readDTableX2(), and HUF_readDTableX4() (2/4) -- No zstd function uses more than 400 B of stack space (2/4) + HUF_compressWeights(), HUF_readDTableX2(), and HUF_readDTableX4() (2/5) +- No zstd function uses more than 400 B of stack space (2/5) v2 -> v3: - Work around gcc-7 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388 - (2/4) -- Fix bug in dictionary compression from upstream commit cc1522351f (2/4) -- Port upstream BtrFS commits e1ddce71d6, 389a6cfc2a, and 6acafd1eff (3/4) -- Change default compression level for BtrFS to 3 (3/4) + (2/5) +- Fix bug in dictionary compression from upstream commit cc1522351f (2/5) +- Port upstream BtrFS commits e1ddce71d6, 389a6cfc2a, and 6acafd1eff (3/5) +- Change default compression level for BtrFS to 3 (3/5) -Nick Terrell (4): +v3 -> v4: +- Fix compiler warnings (2/5) +- Add missing includes (3/5) +- Fix minor linter warnings (3/5, 4/5) +- Add crypto patch (5/5) + +Nick Terrell (5): lib: Add xxhash module lib: Add zstd modules btrfs: Add zstd support squashfs: Add zstd support + crypto: Add zstd support + crypto/Kconfig | 9 + + crypto/Makefile | 1 + + crypto/testmgr.c | 10 + + crypto/testmgr.h | 71 + + crypto/zstd.c | 265 ++++ fs/btrfs/Kconfig | 2 + fs/btrfs/Makefile | 2 +- fs/btrfs/compression.c | 1 + @@ -47,13 +59,13 @@ Nick Terrell (4): fs/btrfs/props.c | 6 + fs/btrfs/super.c | 12 +- fs/btrfs/sysfs.c | 2 + - fs/btrfs/zstd.c | 435 ++++++ + fs/btrfs/zstd.c | 432 ++++++ fs/squashfs/Kconfig | 14 + fs/squashfs/Makefile | 1 + fs/squashfs/decompressor.c | 7 + fs/squashfs/decompressor.h | 4 + fs/squashfs/squashfs_fs.h | 1 + - fs/squashfs/zstd_wrapper.c | 150 ++ + fs/squashfs/zstd_wrapper.c | 149 ++ include/linux/xxhash.h | 236 +++ include/linux/zstd.h | 1157 +++++++++++++++ include/uapi/linux/btrfs.h | 8 +- @@ -63,7 +75,7 @@ Nick Terrell (4): lib/zstd/Makefile | 18 + lib/zstd/bitstream.h | 374 +++++ lib/zstd/compress.c | 3479 ++++++++++++++++++++++++++++++++++++++++++++ - lib/zstd/decompress.c | 2526 ++++++++++++++++++++++++++++++++ + lib/zstd/decompress.c | 2528 ++++++++++++++++++++++++++++++++ lib/zstd/entropy_common.c | 243 ++++ lib/zstd/error_private.h | 53 + lib/zstd/fse.h | 575 ++++++++ @@ -76,7 +88,8 @@ Nick Terrell (4): lib/zstd/zstd_common.c | 75 + lib/zstd/zstd_internal.h | 250 ++++ lib/zstd/zstd_opt.h | 1014 +++++++++++++ - 39 files changed, 14382 insertions(+), 12 deletions(-) + 44 files changed, 14736 insertions(+), 12 deletions(-) + create mode 100644 crypto/zstd.c create mode 100644 fs/btrfs/zstd.c create mode 100644 fs/squashfs/zstd_wrapper.c create mode 100644 include/linux/xxhash.h diff --git a/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch b/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch index f86731cec..21425425d 100644 --- a/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch +++ b/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch @@ -1,7 +1,7 @@ -From fc7f26acbabda35f1c61dfc357dbb207dc8ed23d Mon Sep 17 00:00:00 2001 +From 587f1ba6e78cc5b0d3e26971290aef36ff66f378 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 17 Jul 2017 17:07:18 -0700 -Subject: [PATCH v3 1/4] lib: Add xxhash module +Subject: [PATCH v4 1/5] lib: Add xxhash module Adds xxhash kernel module with xxh32 and xxh64 hashes. xxhash is an extremely fast non-cryptographic hash algorithm for checksumming. diff --git a/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch b/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch index 268307cf1..59467dbc2 100644 --- a/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch +++ b/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch @@ -1,7 +1,7 @@ -From 686a6149b98250d66b5951e3ae05e79063e9de98 Mon Sep 17 00:00:00 2001 +From c7f952ce985f652fe1f2c9266f39cd87b470fd8a Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 17 Jul 2017 17:08:19 -0700 -Subject: [PATCH v3 2/4] lib: Add zstd modules +Subject: [PATCH v4 2/5] lib: Add zstd modules Add zstd compression and decompression kernel modules. zstd offers a wide varity of compression speed and quality trade-offs. @@ -114,13 +114,16 @@ v2 -> v3: - Work around gcc-7 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388 - Fix bug in dictionary compression from upstream commit cc1522351f +v3 -> v4: +- Fix minor compiler warnings + include/linux/zstd.h | 1157 +++++++++++++++ lib/Kconfig | 8 + lib/Makefile | 2 + lib/zstd/Makefile | 18 + lib/zstd/bitstream.h | 374 +++++ lib/zstd/compress.c | 3479 +++++++++++++++++++++++++++++++++++++++++++++ - lib/zstd/decompress.c | 2526 ++++++++++++++++++++++++++++++++ + lib/zstd/decompress.c | 2528 ++++++++++++++++++++++++++++++++ lib/zstd/entropy_common.c | 243 ++++ lib/zstd/error_private.h | 53 + lib/zstd/fse.h | 575 ++++++++ @@ -133,7 +136,7 @@ v2 -> v3: lib/zstd/zstd_common.c | 75 + lib/zstd/zstd_internal.h | 250 ++++ lib/zstd/zstd_opt.h | 1014 +++++++++++++ - 19 files changed, 12994 insertions(+) + 19 files changed, 12996 insertions(+) create mode 100644 include/linux/zstd.h create mode 100644 lib/zstd/Makefile create mode 100644 lib/zstd/bitstream.h @@ -5238,10 +5241,10 @@ index 0000000..d60ab7d +MODULE_DESCRIPTION("Zstd Compressor"); diff --git a/lib/zstd/decompress.c b/lib/zstd/decompress.c new file mode 100644 -index 0000000..62449ae +index 0000000..b178467 --- /dev/null +++ b/lib/zstd/decompress.c -@@ -0,0 +1,2526 @@ +@@ -0,0 +1,2528 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. @@ -6242,6 +6245,8 @@ index 0000000..62449ae + BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ + FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ + ++ seq.match = NULL; ++ + return seq; +} + @@ -11996,7 +12001,7 @@ index 0000000..a282624 +} diff --git a/lib/zstd/zstd_internal.h b/lib/zstd/zstd_internal.h new file mode 100644 -index 0000000..f0ba474 +index 0000000..44e8f100 --- /dev/null +++ b/lib/zstd/zstd_internal.h @@ -0,0 +1,250 @@ @@ -12128,7 +12133,7 @@ index 0000000..f0ba474 +/*-******************************************* +* Shared functions to include for inlining +*********************************************/ -+static void ZSTD_copy8(void *dst, const void *src) { ++ZSTD_STATIC void ZSTD_copy8(void *dst, const void *src) { + memcpy(dst, src, 8); +} +/*! ZSTD_wildcopy() : diff --git a/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch b/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch index 5578fa383..9fdcdf03e 100644 --- a/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch +++ b/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch @@ -1,7 +1,7 @@ -From b0ef8fc63c9ca251ceca632f53aa1de8f1f17772 Mon Sep 17 00:00:00 2001 +From 6ade5bc08dcfa2bce2b4801e47edf783dcb7ca43 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 17 Jul 2017 17:08:39 -0700 -Subject: [PATCH v3 3/4] btrfs: Add zstd support +Subject: [PATCH v4 3/5] btrfs: Add zstd support Add zstd compression and decompression support to BtrFS. zstd at its fastest level compresses almost as well as zlib, while offering much @@ -67,6 +67,10 @@ v2 -> v3: - Port upstream BtrFS commits e1ddce71d6, 389a6cfc2a, and 6acafd1eff - Change default compression level for BtrFS to 3 +v3 -> v4: +- Add missing includes, which fixes the aarch64 build +- Fix minor linter warnings + fs/btrfs/Kconfig | 2 + fs/btrfs/Makefile | 2 +- fs/btrfs/compression.c | 1 + @@ -77,9 +81,9 @@ v2 -> v3: fs/btrfs/props.c | 6 + fs/btrfs/super.c | 12 +- fs/btrfs/sysfs.c | 2 + - fs/btrfs/zstd.c | 435 +++++++++++++++++++++++++++++++++++++++++++++ + fs/btrfs/zstd.c | 432 +++++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/btrfs.h | 8 +- - 12 files changed, 471 insertions(+), 12 deletions(-) + 12 files changed, 468 insertions(+), 12 deletions(-) create mode 100644 fs/btrfs/zstd.c diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig @@ -277,10 +281,10 @@ index c2d5f35..2b6d37c 100644 BTRFS_FEAT_ATTR_PTR(raid56), diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c new file mode 100644 -index 0000000..1822068 +index 0000000..607ce47 --- /dev/null +++ b/fs/btrfs/zstd.c -@@ -0,0 +1,435 @@ +@@ -0,0 +1,432 @@ +/* + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. @@ -293,20 +297,16 @@ index 0000000..1822068 + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public -+ * License along with this program; if not, write to the -+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, -+ * Boston, MA 021110-1307, USA. + */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include +#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include +#include +#include "compression.h" + @@ -316,7 +316,8 @@ index 0000000..1822068 + +static ZSTD_parameters zstd_get_btrfs_parameters(size_t src_len) +{ -+ ZSTD_parameters params = ZSTD_getParams(ZSTD_BTRFS_DEFAULT_LEVEL, src_len, 0); ++ ZSTD_parameters params = ZSTD_getParams(ZSTD_BTRFS_DEFAULT_LEVEL, ++ src_len, 0); + + if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG) + params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG; diff --git a/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch b/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch index 02bd10733..d27522b5d 100644 --- a/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch +++ b/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch @@ -1,7 +1,7 @@ -From 0cd63464d182bb9708f8b25f7da3dc8e5ec6b4fa Mon Sep 17 00:00:00 2001 -From: Nick Terrell +From 6e1c54639deca96465b973ad80e34ff7fc789573 Mon Sep 17 00:00:00 2001 +From: Sean Purcell Date: Mon, 17 Jul 2017 17:08:59 -0700 -Subject: [PATCH v3 4/4] squashfs: Add zstd support +Subject: [PATCH v4 4/5] squashfs: Add zstd support Add zstd compression and decompression support to SquashFS. zstd is a great fit for SquashFS because it can compress at ratios approaching xz, @@ -42,16 +42,19 @@ taking over the submission process. zstd source repository: https://github.com/facebook/zstd -Cc: Sean Purcell +Signed-off-by: Sean Purcell Signed-off-by: Nick Terrell --- +v3 -> v4: +- Fix minor linter warnings + fs/squashfs/Kconfig | 14 +++++ fs/squashfs/Makefile | 1 + fs/squashfs/decompressor.c | 7 +++ fs/squashfs/decompressor.h | 4 ++ fs/squashfs/squashfs_fs.h | 1 + - fs/squashfs/zstd_wrapper.c | 150 +++++++++++++++++++++++++++++++++++++++++++++ - 6 files changed, 177 insertions(+) + fs/squashfs/zstd_wrapper.c | 149 +++++++++++++++++++++++++++++++++++++++++++++ + 6 files changed, 176 insertions(+) create mode 100644 fs/squashfs/zstd_wrapper.c diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig @@ -140,10 +143,10 @@ index 506f4ba..24d12fd 100644 __le32 s_magic; diff --git a/fs/squashfs/zstd_wrapper.c b/fs/squashfs/zstd_wrapper.c new file mode 100644 -index 0000000..8cb7c76 +index 0000000..d70efa8 --- /dev/null +++ b/fs/squashfs/zstd_wrapper.c -@@ -0,0 +1,150 @@ +@@ -0,0 +1,149 @@ +/* + * Squashfs - a compressed read only filesystem for Linux + * @@ -160,10 +163,6 @@ index 0000000..8cb7c76 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -+ * + * zstd_wrapper.c + */ + @@ -187,6 +186,7 @@ index 0000000..8cb7c76 +static void *zstd_init(struct squashfs_sb_info *msblk, void *buff) +{ + struct workspace *wksp = kmalloc(sizeof(*wksp), GFP_KERNEL); ++ + if (wksp == NULL) + goto failed; + wksp->mem_size = ZSTD_DStreamWorkspaceBound(max_t(size_t, @@ -239,6 +239,7 @@ index 0000000..8cb7c76 + do { + if (in_buf.pos == in_buf.size && k < b) { + int avail = min(length, msblk->devblksize - offset); ++ + length -= avail; + in_buf.src = bh[k]->b_data + offset; + in_buf.size = avail; @@ -249,8 +250,9 @@ index 0000000..8cb7c76 + if (out_buf.pos == out_buf.size) { + out_buf.dst = squashfs_next_page(output); + if (out_buf.dst == NULL) { -+ /* shouldn't run out of pages before stream is -+ * done */ ++ /* Shouldn't run out of pages ++ * before stream is done. ++ */ + squashfs_finish_page(output); + goto out; + } diff --git a/contrib/linux-kernel/0005-crypto-Add-zstd-support.patch b/contrib/linux-kernel/0005-crypto-Add-zstd-support.patch new file mode 100644 index 000000000..fac772f07 --- /dev/null +++ b/contrib/linux-kernel/0005-crypto-Add-zstd-support.patch @@ -0,0 +1,425 @@ +From a276288db937088d00b975ad9c36278fa46c8cf7 Mon Sep 17 00:00:00 2001 +From: Nick Terrell +Date: Wed, 2 Aug 2017 18:02:13 -0700 +Subject: [PATCH v4 5/5] crypto: Add zstd support + +Adds zstd support to crypto and scompress. Only supports the default +level. + +Signed-off-by: Nick Terrell +--- + crypto/Kconfig | 9 ++ + crypto/Makefile | 1 + + crypto/testmgr.c | 10 +++ + crypto/testmgr.h | 71 +++++++++++++++ + crypto/zstd.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 356 insertions(+) + create mode 100644 crypto/zstd.c + +diff --git a/crypto/Kconfig b/crypto/Kconfig +index caa770e..4fc3936 100644 +--- a/crypto/Kconfig ++++ b/crypto/Kconfig +@@ -1662,6 +1662,15 @@ config CRYPTO_LZ4HC + help + This is the LZ4 high compression mode algorithm. + ++config CRYPTO_ZSTD ++ tristate "Zstd compression algorithm" ++ select CRYPTO_ALGAPI ++ select CRYPTO_ACOMP2 ++ select ZSTD_COMPRESS ++ select ZSTD_DECOMPRESS ++ help ++ This is the zstd algorithm. ++ + comment "Random Number Generation" + + config CRYPTO_ANSI_CPRNG +diff --git a/crypto/Makefile b/crypto/Makefile +index d41f033..b22e1e8 100644 +--- a/crypto/Makefile ++++ b/crypto/Makefile +@@ -133,6 +133,7 @@ obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o + obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o + obj-$(CONFIG_CRYPTO_USER_API_RNG) += algif_rng.o + obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o ++obj-$(CONFIG_CRYPTO_ZSTD) += zstd.o + + ecdh_generic-y := ecc.o + ecdh_generic-y += ecdh.o +diff --git a/crypto/testmgr.c b/crypto/testmgr.c +index 7125ba3..8a124d3 100644 +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -3603,6 +3603,16 @@ static const struct alg_test_desc alg_test_descs[] = { + .decomp = __VECS(zlib_deflate_decomp_tv_template) + } + } ++ }, { ++ .alg = "zstd", ++ .test = alg_test_comp, ++ .fips_allowed = 1, ++ .suite = { ++ .comp = { ++ .comp = __VECS(zstd_comp_tv_template), ++ .decomp = __VECS(zstd_decomp_tv_template) ++ } ++ } + } + }; + +diff --git a/crypto/testmgr.h b/crypto/testmgr.h +index 6ceb0e2..e6b5920 100644 +--- a/crypto/testmgr.h ++++ b/crypto/testmgr.h +@@ -34631,4 +34631,75 @@ static const struct comp_testvec lz4hc_decomp_tv_template[] = { + }, + }; + ++static const struct comp_testvec zstd_comp_tv_template[] = { ++ { ++ .inlen = 68, ++ .outlen = 39, ++ .input = "The algorithm is zstd. " ++ "The algorithm is zstd. " ++ "The algorithm is zstd.", ++ .output = "\x28\xb5\x2f\xfd\x00\x50\xf5\x00\x00\xb8\x54\x68\x65" ++ "\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x20\x69\x73" ++ "\x20\x7a\x73\x74\x64\x2e\x20\x01\x00\x55\x73\x36\x01" ++ , ++ }, ++ { ++ .inlen = 244, ++ .outlen = 151, ++ .input = "zstd, short for Zstandard, is a fast lossless " ++ "compression algorithm, targeting real-time " ++ "compression scenarios at zlib-level and better " ++ "compression ratios. The zstd compression library " ++ "provides in-memory compression and decompression " ++ "functions.", ++ .output = "\x28\xb5\x2f\xfd\x00\x50\x75\x04\x00\x42\x4b\x1e\x17" ++ "\x90\x81\x31\x00\xf2\x2f\xe4\x36\xc9\xef\x92\x88\x32" ++ "\xc9\xf2\x24\x94\xd8\x68\x9a\x0f\x00\x0c\xc4\x31\x6f" ++ "\x0d\x0c\x38\xac\x5c\x48\x03\xcd\x63\x67\xc0\xf3\xad" ++ "\x4e\x90\xaa\x78\xa0\xa4\xc5\x99\xda\x2f\xb6\x24\x60" ++ "\xe2\x79\x4b\xaa\xb6\x6b\x85\x0b\xc9\xc6\x04\x66\x86" ++ "\xe2\xcc\xe2\x25\x3f\x4f\x09\xcd\xb8\x9d\xdb\xc1\x90" ++ "\xa9\x11\xbc\x35\x44\x69\x2d\x9c\x64\x4f\x13\x31\x64" ++ "\xcc\xfb\x4d\x95\x93\x86\x7f\x33\x7f\x1a\xef\xe9\x30" ++ "\xf9\x67\xa1\x94\x0a\x69\x0f\x60\xcd\xc3\xab\x99\xdc" ++ "\x42\xed\x97\x05\x00\x33\xc3\x15\x95\x3a\x06\xa0\x0e" ++ "\x20\xa9\x0e\x82\xb9\x43\x45\x01", ++ }, ++}; ++ ++static const struct comp_testvec zstd_decomp_tv_template[] = { ++ { ++ .inlen = 43, ++ .outlen = 68, ++ .input = "\x28\xb5\x2f\xfd\x04\x50\xf5\x00\x00\xb8\x54\x68\x65" ++ "\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x20\x69\x73" ++ "\x20\x7a\x73\x74\x64\x2e\x20\x01\x00\x55\x73\x36\x01" ++ "\x6b\xf4\x13\x35", ++ .output = "The algorithm is zstd. " ++ "The algorithm is zstd. " ++ "The algorithm is zstd.", ++ }, ++ { ++ .inlen = 155, ++ .outlen = 244, ++ .input = "\x28\xb5\x2f\xfd\x04\x50\x75\x04\x00\x42\x4b\x1e\x17" ++ "\x90\x81\x31\x00\xf2\x2f\xe4\x36\xc9\xef\x92\x88\x32" ++ "\xc9\xf2\x24\x94\xd8\x68\x9a\x0f\x00\x0c\xc4\x31\x6f" ++ "\x0d\x0c\x38\xac\x5c\x48\x03\xcd\x63\x67\xc0\xf3\xad" ++ "\x4e\x90\xaa\x78\xa0\xa4\xc5\x99\xda\x2f\xb6\x24\x60" ++ "\xe2\x79\x4b\xaa\xb6\x6b\x85\x0b\xc9\xc6\x04\x66\x86" ++ "\xe2\xcc\xe2\x25\x3f\x4f\x09\xcd\xb8\x9d\xdb\xc1\x90" ++ "\xa9\x11\xbc\x35\x44\x69\x2d\x9c\x64\x4f\x13\x31\x64" ++ "\xcc\xfb\x4d\x95\x93\x86\x7f\x33\x7f\x1a\xef\xe9\x30" ++ "\xf9\x67\xa1\x94\x0a\x69\x0f\x60\xcd\xc3\xab\x99\xdc" ++ "\x42\xed\x97\x05\x00\x33\xc3\x15\x95\x3a\x06\xa0\x0e" ++ "\x20\xa9\x0e\x82\xb9\x43\x45\x01\xaa\x6d\xda\x0d", ++ .output = "zstd, short for Zstandard, is a fast lossless " ++ "compression algorithm, targeting real-time " ++ "compression scenarios at zlib-level and better " ++ "compression ratios. The zstd compression library " ++ "provides in-memory compression and decompression " ++ "functions.", ++ }, ++}; + #endif /* _CRYPTO_TESTMGR_H */ +diff --git a/crypto/zstd.c b/crypto/zstd.c +new file mode 100644 +index 0000000..9a76b3e +--- /dev/null ++++ b/crypto/zstd.c +@@ -0,0 +1,265 @@ ++/* ++ * Cryptographic API. ++ * ++ * Copyright (c) 2017-present, Facebook, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define ZSTD_DEF_LEVEL 3 ++ ++struct zstd_ctx { ++ ZSTD_CCtx *cctx; ++ ZSTD_DCtx *dctx; ++ void *cwksp; ++ void *dwksp; ++}; ++ ++static ZSTD_parameters zstd_params(void) ++{ ++ return ZSTD_getParams(ZSTD_DEF_LEVEL, 0, 0); ++} ++ ++static int zstd_comp_init(struct zstd_ctx *ctx) ++{ ++ int ret = 0; ++ const ZSTD_parameters params = zstd_params(); ++ const size_t wksp_size = ZSTD_CCtxWorkspaceBound(params.cParams); ++ ++ ctx->cwksp = vzalloc(wksp_size); ++ if (!ctx->cwksp) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ctx->cctx = ZSTD_initCCtx(ctx->cwksp, wksp_size); ++ if (!ctx->cctx) { ++ ret = -EINVAL; ++ goto out_free; ++ } ++out: ++ return ret; ++out_free: ++ vfree(ctx->cwksp); ++ goto out; ++} ++ ++static int zstd_decomp_init(struct zstd_ctx *ctx) ++{ ++ int ret = 0; ++ const size_t wksp_size = ZSTD_DCtxWorkspaceBound(); ++ ++ ctx->dwksp = vzalloc(wksp_size); ++ if (!ctx->dwksp) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ctx->dctx = ZSTD_initDCtx(ctx->dwksp, wksp_size); ++ if (!ctx->dctx) { ++ ret = -EINVAL; ++ goto out_free; ++ } ++out: ++ return ret; ++out_free: ++ vfree(ctx->dwksp); ++ goto out; ++} ++ ++static void zstd_comp_exit(struct zstd_ctx *ctx) ++{ ++ vfree(ctx->cwksp); ++ ctx->cwksp = NULL; ++ ctx->cctx = NULL; ++} ++ ++static void zstd_decomp_exit(struct zstd_ctx *ctx) ++{ ++ vfree(ctx->dwksp); ++ ctx->dwksp = NULL; ++ ctx->dctx = NULL; ++} ++ ++static int __zstd_init(void *ctx) ++{ ++ int ret; ++ ++ ret = zstd_comp_init(ctx); ++ if (ret) ++ return ret; ++ ret = zstd_decomp_init(ctx); ++ if (ret) ++ zstd_comp_exit(ctx); ++ return ret; ++} ++ ++static void *zstd_alloc_ctx(struct crypto_scomp *tfm) ++{ ++ int ret; ++ struct zstd_ctx *ctx; ++ ++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return ERR_PTR(-ENOMEM); ++ ++ ret = __zstd_init(ctx); ++ if (ret) { ++ kfree(ctx); ++ return ERR_PTR(ret); ++ } ++ ++ return ctx; ++} ++ ++static int zstd_init(struct crypto_tfm *tfm) ++{ ++ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ return __zstd_init(ctx); ++} ++ ++static void __zstd_exit(void *ctx) ++{ ++ zstd_comp_exit(ctx); ++ zstd_decomp_exit(ctx); ++} ++ ++static void zstd_free_ctx(struct crypto_scomp *tfm, void *ctx) ++{ ++ __zstd_exit(ctx); ++ kzfree(ctx); ++} ++ ++static void zstd_exit(struct crypto_tfm *tfm) ++{ ++ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ __zstd_exit(ctx); ++} ++ ++static int __zstd_compress(const u8 *src, unsigned int slen, ++ u8 *dst, unsigned int *dlen, void *ctx) ++{ ++ size_t out_len; ++ struct zstd_ctx *zctx = ctx; ++ const ZSTD_parameters params = zstd_params(); ++ ++ out_len = ZSTD_compressCCtx(zctx->cctx, dst, *dlen, src, slen, params); ++ if (ZSTD_isError(out_len)) ++ return -EINVAL; ++ *dlen = out_len; ++ return 0; ++} ++ ++static int zstd_compress(struct crypto_tfm *tfm, const u8 *src, ++ unsigned int slen, u8 *dst, unsigned int *dlen) ++{ ++ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ return __zstd_compress(src, slen, dst, dlen, ctx); ++} ++ ++static int zstd_scompress(struct crypto_scomp *tfm, const u8 *src, ++ unsigned int slen, u8 *dst, unsigned int *dlen, ++ void *ctx) ++{ ++ return __zstd_compress(src, slen, dst, dlen, ctx); ++} ++ ++static int __zstd_decompress(const u8 *src, unsigned int slen, ++ u8 *dst, unsigned int *dlen, void *ctx) ++{ ++ size_t out_len; ++ struct zstd_ctx *zctx = ctx; ++ ++ out_len = ZSTD_decompressDCtx(zctx->dctx, dst, *dlen, src, slen); ++ if (ZSTD_isError(out_len)) ++ return -EINVAL; ++ *dlen = out_len; ++ return 0; ++} ++ ++static int zstd_decompress(struct crypto_tfm *tfm, const u8 *src, ++ unsigned int slen, u8 *dst, unsigned int *dlen) ++{ ++ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ return __zstd_decompress(src, slen, dst, dlen, ctx); ++} ++ ++static int zstd_sdecompress(struct crypto_scomp *tfm, const u8 *src, ++ unsigned int slen, u8 *dst, unsigned int *dlen, ++ void *ctx) ++{ ++ return __zstd_decompress(src, slen, dst, dlen, ctx); ++} ++ ++static struct crypto_alg alg = { ++ .cra_name = "zstd", ++ .cra_flags = CRYPTO_ALG_TYPE_COMPRESS, ++ .cra_ctxsize = sizeof(struct zstd_ctx), ++ .cra_module = THIS_MODULE, ++ .cra_init = zstd_init, ++ .cra_exit = zstd_exit, ++ .cra_u = { .compress = { ++ .coa_compress = zstd_compress, ++ .coa_decompress = zstd_decompress } } ++}; ++ ++static struct scomp_alg scomp = { ++ .alloc_ctx = zstd_alloc_ctx, ++ .free_ctx = zstd_free_ctx, ++ .compress = zstd_scompress, ++ .decompress = zstd_sdecompress, ++ .base = { ++ .cra_name = "zstd", ++ .cra_driver_name = "zstd-scomp", ++ .cra_module = THIS_MODULE, ++ } ++}; ++ ++static int __init zstd_mod_init(void) ++{ ++ int ret; ++ ++ ret = crypto_register_alg(&alg); ++ if (ret) ++ return ret; ++ ++ ret = crypto_register_scomp(&scomp); ++ if (ret) ++ crypto_unregister_alg(&alg); ++ ++ return ret; ++} ++ ++static void __exit zstd_mod_fini(void) ++{ ++ crypto_unregister_alg(&alg); ++ crypto_unregister_scomp(&scomp); ++} ++ ++module_init(zstd_mod_init); ++module_exit(zstd_mod_fini); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Zstd Compression Algorithm"); ++MODULE_ALIAS_CRYPTO("zstd"); +-- +2.9.3 + diff --git a/contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch b/contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch new file mode 100644 index 000000000..49e240fb1 --- /dev/null +++ b/contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch @@ -0,0 +1,423 @@ +From 0ec6ae4b2c69fcf27785e389391b0add474efd8c Mon Sep 17 00:00:00 2001 +From: Sean Purcell +Date: Thu, 3 Aug 2017 17:47:03 -0700 +Subject: [PATCH v4] squashfs-tools: Add zstd support + +This patch adds zstd support to squashfs-tools. It works with zstd +versions >= 1.0.0. It was originally written by Sean Purcell. + +Signed-off-by: Sean Purcell +Signed-off-by: Nick Terrell +--- + squashfs-tools/Makefile | 21 ++++ + squashfs-tools/compressor.c | 8 ++ + squashfs-tools/squashfs_fs.h | 3 +- + squashfs-tools/zstd_wrapper.c | 254 ++++++++++++++++++++++++++++++++++++++++++ + squashfs-tools/zstd_wrapper.h | 48 ++++++++ + 5 files changed, 333 insertions(+), 1 deletion(-) + create mode 100644 squashfs-tools/zstd_wrapper.c + create mode 100644 squashfs-tools/zstd_wrapper.h + +diff --git a/squashfs-tools/Makefile b/squashfs-tools/Makefile +index 52d2582..8e82e09 100644 +--- a/squashfs-tools/Makefile ++++ b/squashfs-tools/Makefile +@@ -75,6 +75,19 @@ GZIP_SUPPORT = 1 + #LZMA_SUPPORT = 1 + #LZMA_DIR = ../../../../LZMA/lzma465 + ++ ++########### Building ZSTD support ############ ++# ++# The ZSTD library is supported ++# ZSTD homepage: http://zstd.net ++# ZSTD source repository: https://github.com/facebook/zstd ++# ++# To build configure the tools using cmake to build shared libraries, ++# install and uncomment ++# the ZSTD_SUPPORT line below. ++# ++#ZSTD_SUPPORT = 1 ++ + ######## Specifying default compression ######## + # + # The next line specifies which compression algorithm is used by default +@@ -177,6 +190,14 @@ LIBS += -llz4 + COMPRESSORS += lz4 + endif + ++ifeq ($(ZSTD_SUPPORT),1) ++CFLAGS += -DZSTD_SUPPORT ++MKSQUASHFS_OBJS += zstd_wrapper.o ++UNSQUASHFS_OBJS += zstd_wrapper.o ++LIBS += -lzstd ++COMPRESSORS += zstd ++endif ++ + ifeq ($(XATTR_SUPPORT),1) + ifeq ($(XATTR_DEFAULT),1) + CFLAGS += -DXATTR_SUPPORT -DXATTR_DEFAULT +diff --git a/squashfs-tools/compressor.c b/squashfs-tools/compressor.c +index 525e316..02b5e90 100644 +--- a/squashfs-tools/compressor.c ++++ b/squashfs-tools/compressor.c +@@ -65,6 +65,13 @@ static struct compressor xz_comp_ops = { + extern struct compressor xz_comp_ops; + #endif + ++#ifndef ZSTD_SUPPORT ++static struct compressor zstd_comp_ops = { ++ ZSTD_COMPRESSION, "zstd" ++}; ++#else ++extern struct compressor zstd_comp_ops; ++#endif + + static struct compressor unknown_comp_ops = { + 0, "unknown" +@@ -77,6 +84,7 @@ struct compressor *compressor[] = { + &lzo_comp_ops, + &lz4_comp_ops, + &xz_comp_ops, ++ &zstd_comp_ops, + &unknown_comp_ops + }; + +diff --git a/squashfs-tools/squashfs_fs.h b/squashfs-tools/squashfs_fs.h +index 791fe12..1f2e8b0 100644 +--- a/squashfs-tools/squashfs_fs.h ++++ b/squashfs-tools/squashfs_fs.h +@@ -24,7 +24,7 @@ + * squashfs_fs.h + */ + +-#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE ++#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE + #define SQUASHFS_MAJOR 4 + #define SQUASHFS_MINOR 0 + #define SQUASHFS_MAGIC 0x73717368 +@@ -277,6 +277,7 @@ typedef long long squashfs_inode; + #define LZO_COMPRESSION 3 + #define XZ_COMPRESSION 4 + #define LZ4_COMPRESSION 5 ++#define ZSTD_COMPRESSION 6 + + struct squashfs_super_block { + unsigned int s_magic; +diff --git a/squashfs-tools/zstd_wrapper.c b/squashfs-tools/zstd_wrapper.c +new file mode 100644 +index 0000000..0989f0f +--- /dev/null ++++ b/squashfs-tools/zstd_wrapper.c +@@ -0,0 +1,254 @@ ++/* ++ * Copyright (c) 2017 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * zstd_wrapper.c ++ * ++ * Support for ZSTD compression http://zstd.net ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "squashfs_fs.h" ++#include "zstd_wrapper.h" ++#include "compressor.h" ++ ++static int compression_level = ZSTD_DEFAULT_COMPRESSION_LEVEL; ++ ++/* ++ * This function is called by the options parsing code in mksquashfs.c ++ * to parse any -X compressor option. ++ * ++ * This function returns: ++ * >=0 (number of additional args parsed) on success ++ * -1 if the option was unrecognised, or ++ * -2 if the option was recognised, but otherwise bad in ++ * some way (e.g. invalid parameter) ++ * ++ * Note: this function sets internal compressor state, but does not ++ * pass back the results of the parsing other than success/failure. ++ * The zstd_dump_options() function is called later to get the options in ++ * a format suitable for writing to the filesystem. ++ */ ++static int zstd_options(char *argv[], int argc) ++{ ++ if (strcmp(argv[0], "-Xcompression-level") == 0) { ++ if (argc < 2) { ++ fprintf(stderr, "zstd: -Xcompression-level missing " ++ "compression level\n"); ++ fprintf(stderr, "zstd: -Xcompression-level it should " ++ "be 1 <= n <= %d\n", ZSTD_maxCLevel()); ++ goto failed; ++ } ++ ++ compression_level = atoi(argv[1]); ++ if (compression_level < 1 || ++ compression_level > ZSTD_maxCLevel()) { ++ fprintf(stderr, "zstd: -Xcompression-level invalid, it " ++ "should be 1 <= n <= %d\n", ZSTD_maxCLevel()); ++ goto failed; ++ } ++ ++ return 1; ++ } ++ ++ return -1; ++failed: ++ return -2; ++} ++ ++/* ++ * This function is called by mksquashfs to dump the parsed ++ * compressor options in a format suitable for writing to the ++ * compressor options field in the filesystem (stored immediately ++ * after the superblock). ++ * ++ * This function returns a pointer to the compression options structure ++ * to be stored (and the size), or NULL if there are no compression ++ * options. ++ */ ++static void *zstd_dump_options(int block_size, int *size) ++{ ++ static struct zstd_comp_opts comp_opts; ++ ++ /* don't return anything if the options are all default */ ++ if (compression_level == ZSTD_DEFAULT_COMPRESSION_LEVEL) ++ return NULL; ++ ++ comp_opts.compression_level = compression_level; ++ ++ SQUASHFS_INSWAP_COMP_OPTS(&comp_opts); ++ ++ *size = sizeof(comp_opts); ++ return &comp_opts; ++} ++ ++/* ++ * This function is a helper specifically for the append mode of ++ * mksquashfs. Its purpose is to set the internal compressor state ++ * to the stored compressor options in the passed compressor options ++ * structure. ++ * ++ * In effect this function sets up the compressor options ++ * to the same state they were when the filesystem was originally ++ * generated, this is to ensure on appending, the compressor uses ++ * the same compression options that were used to generate the ++ * original filesystem. ++ * ++ * Note, even if there are no compressor options, this function is still ++ * called with an empty compressor structure (size == 0), to explicitly ++ * set the default options, this is to ensure any user supplied ++ * -X options on the appending mksquashfs command line are over-ridden. ++ * ++ * This function returns 0 on sucessful extraction of options, and -1 on error. ++ */ ++static int zstd_extract_options(int block_size, void *buffer, int size) ++{ ++ struct zstd_comp_opts *comp_opts = buffer; ++ ++ if (size == 0) { ++ /* Set default values */ ++ compression_level = ZSTD_DEFAULT_COMPRESSION_LEVEL; ++ return 0; ++ } ++ ++ /* we expect a comp_opts structure of sufficient size to be present */ ++ if (size < sizeof(*comp_opts)) ++ goto failed; ++ ++ SQUASHFS_INSWAP_COMP_OPTS(comp_opts); ++ ++ if (comp_opts->compression_level < 1 || ++ comp_opts->compression_level > ZSTD_maxCLevel()) { ++ fprintf(stderr, "zstd: bad compression level in compression " ++ "options structure\n"); ++ goto failed; ++ } ++ ++ compression_level = comp_opts->compression_level; ++ ++ return 0; ++ ++failed: ++ fprintf(stderr, "zstd: error reading stored compressor options from " ++ "filesystem!\n"); ++ ++ return -1; ++} ++ ++void zstd_display_options(void *buffer, int size) ++{ ++ struct zstd_comp_opts *comp_opts = buffer; ++ ++ /* we expect a comp_opts structure of sufficient size to be present */ ++ if (size < sizeof(*comp_opts)) ++ goto failed; ++ ++ SQUASHFS_INSWAP_COMP_OPTS(comp_opts); ++ ++ if (comp_opts->compression_level < 1 || ++ comp_opts->compression_level > ZSTD_maxCLevel()) { ++ fprintf(stderr, "zstd: bad compression level in compression " ++ "options structure\n"); ++ goto failed; ++ } ++ ++ printf("\tcompression-level %d\n", comp_opts->compression_level); ++ ++ return; ++ ++failed: ++ fprintf(stderr, "zstd: error reading stored compressor options from " ++ "filesystem!\n"); ++} ++ ++/* ++ * This function is called by mksquashfs to initialise the ++ * compressor, before compress() is called. ++ * ++ * This function returns 0 on success, and -1 on error. ++ */ ++static int zstd_init(void **strm, int block_size, int datablock) ++{ ++ ZSTD_CCtx *cctx = ZSTD_createCCtx(); ++ ++ if (!cctx) { ++ fprintf(stderr, "zstd: failed to allocate compression " ++ "context!\n"); ++ return -1; ++ } ++ ++ *strm = cctx; ++ return 0; ++} ++ ++static int zstd_compress(void *strm, void *dest, void *src, int size, ++ int block_size, int *error) ++{ ++ const size_t res = ZSTD_compressCCtx((ZSTD_CCtx*)strm, dest, block_size, ++ src, size, compression_level); ++ ++ if (ZSTD_isError(res)) { ++ /* FIXME: ++ * zstd does not expose stable error codes. The error enum may ++ * change between versions. Until upstream zstd stablizes the ++ * error codes, we have no way of knowing why the error occurs. ++ * zstd shouldn't fail to compress any input unless there isn't ++ * enough output space. We assume that is the cause and return ++ * the special error code for not enough output space. ++ */ ++ return 0; ++ } ++ ++ return (int)res; ++} ++ ++static int zstd_uncompress(void *dest, void *src, int size, int outsize, ++ int *error) ++{ ++ const size_t res = ZSTD_decompress(dest, outsize, src, size); ++ ++ if (ZSTD_isError(res)) { ++ fprintf(stderr, "\t%d %d\n", outsize, size); ++ ++ *error = (int)ZSTD_getErrorCode(res); ++ return -1; ++ } ++ ++ return (int)res; ++} ++ ++static void zstd_usage(void) ++{ ++ fprintf(stderr, "\t -Xcompression-level \n"); ++ fprintf(stderr, "\t\t should be 1 .. %d (default " ++ "%d)\n", ZSTD_maxCLevel(), ZSTD_DEFAULT_COMPRESSION_LEVEL); ++} ++ ++struct compressor zstd_comp_ops = { ++ .init = zstd_init, ++ .compress = zstd_compress, ++ .uncompress = zstd_uncompress, ++ .options = zstd_options, ++ .dump_options = zstd_dump_options, ++ .extract_options = zstd_extract_options, ++ .display_options = zstd_display_options, ++ .usage = zstd_usage, ++ .id = ZSTD_COMPRESSION, ++ .name = "zstd", ++ .supported = 1 ++}; +diff --git a/squashfs-tools/zstd_wrapper.h b/squashfs-tools/zstd_wrapper.h +new file mode 100644 +index 0000000..4fbef0a +--- /dev/null ++++ b/squashfs-tools/zstd_wrapper.h +@@ -0,0 +1,48 @@ ++#ifndef ZSTD_WRAPPER_H ++#define ZSTD_WRAPPER_H ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2017 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * zstd_wrapper.h ++ * ++ */ ++ ++#ifndef linux ++#define __BYTE_ORDER BYTE_ORDER ++#define __BIG_ENDIAN BIG_ENDIAN ++#define __LITTLE_ENDIAN LITTLE_ENDIAN ++#else ++#include ++#endif ++ ++#if __BYTE_ORDER == __BIG_ENDIAN ++extern unsigned int inswap_le16(unsigned short); ++extern unsigned int inswap_le32(unsigned int); ++ ++#define SQUASHFS_INSWAP_COMP_OPTS(s) { \ ++ (s)->compression_level = inswap_le32((s)->compression_level); \ ++} ++#else ++#define SQUASHFS_INSWAP_COMP_OPTS(s) ++#endif ++ ++/* Default compression */ ++#define ZSTD_DEFAULT_COMPRESSION_LEVEL 15 ++ ++struct zstd_comp_opts { ++ int compression_level; ++}; ++#endif +-- +2.9.3 + diff --git a/contrib/linux-kernel/fs/btrfs/zstd.c b/contrib/linux-kernel/fs/btrfs/zstd.c index 182206872..607ce47b4 100644 --- a/contrib/linux-kernel/fs/btrfs/zstd.c +++ b/contrib/linux-kernel/fs/btrfs/zstd.c @@ -10,20 +10,16 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 021110-1307, USA. */ -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include "compression.h" @@ -33,7 +29,8 @@ static ZSTD_parameters zstd_get_btrfs_parameters(size_t src_len) { - ZSTD_parameters params = ZSTD_getParams(ZSTD_BTRFS_DEFAULT_LEVEL, src_len, 0); + ZSTD_parameters params = ZSTD_getParams(ZSTD_BTRFS_DEFAULT_LEVEL, + src_len, 0); if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG) params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG; diff --git a/contrib/linux-kernel/fs/squashfs/zstd_wrapper.c b/contrib/linux-kernel/fs/squashfs/zstd_wrapper.c index 8cb7c76d6..d70efa8b2 100644 --- a/contrib/linux-kernel/fs/squashfs/zstd_wrapper.c +++ b/contrib/linux-kernel/fs/squashfs/zstd_wrapper.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * * zstd_wrapper.c */ @@ -41,6 +37,7 @@ struct workspace { static void *zstd_init(struct squashfs_sb_info *msblk, void *buff) { struct workspace *wksp = kmalloc(sizeof(*wksp), GFP_KERNEL); + if (wksp == NULL) goto failed; wksp->mem_size = ZSTD_DStreamWorkspaceBound(max_t(size_t, @@ -93,6 +90,7 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm, do { if (in_buf.pos == in_buf.size && k < b) { int avail = min(length, msblk->devblksize - offset); + length -= avail; in_buf.src = bh[k]->b_data + offset; in_buf.size = avail; @@ -103,8 +101,9 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm, if (out_buf.pos == out_buf.size) { out_buf.dst = squashfs_next_page(output); if (out_buf.dst == NULL) { - /* shouldn't run out of pages before stream is - * done */ + /* Shouldn't run out of pages + * before stream is done. + */ squashfs_finish_page(output); goto out; } diff --git a/contrib/linux-kernel/lib/zstd/decompress.c b/contrib/linux-kernel/lib/zstd/decompress.c index 62449ae05..b17846725 100644 --- a/contrib/linux-kernel/lib/zstd/decompress.c +++ b/contrib/linux-kernel/lib/zstd/decompress.c @@ -998,6 +998,8 @@ static seq_t ZSTD_decodeSequence(seqState_t *seqState) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ + seq.match = NULL; + return seq; } diff --git a/contrib/linux-kernel/lib/zstd/zstd_internal.h b/contrib/linux-kernel/lib/zstd/zstd_internal.h index f0ba47442..44e8f1001 100644 --- a/contrib/linux-kernel/lib/zstd/zstd_internal.h +++ b/contrib/linux-kernel/lib/zstd/zstd_internal.h @@ -126,7 +126,7 @@ static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG; /*-******************************************* * Shared functions to include for inlining *********************************************/ -static void ZSTD_copy8(void *dst, const void *src) { +ZSTD_STATIC void ZSTD_copy8(void *dst, const void *src) { memcpy(dst, src, 8); } /*! ZSTD_wildcopy() : From 308047eb5dbb1659961835fce90db4883ed386a6 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 3 Aug 2017 14:05:01 -0700 Subject: [PATCH 273/318] Fix compression failure on incompressible data If the destination buffer is the minimum allowed size in `ZSTD_compressSequences()` (2^17), then if the block isn't compressible compression might fail with `dstSize_tooSmall`, when it should instead emit a raw uncompressed block. Additionally, `ZSTD_compressLiterals()` implicitly called `ZSTD_noCompressLiterals()` if Huffman compression failed. Make that explicit. --- lib/compress/zstd_compress.c | 52 +++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index a73763d24..a70a66684 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -942,7 +942,7 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t * entropy, else { entropy->hufCTable_repeatMode = HUF_repeat_check; } /* now have a table to reuse */ } - if ((cLitSize==0) | (cLitSize >= srcSize - minGain)) { + if ((cLitSize==0) | (cLitSize >= srcSize - minGain) | ERR_isError(cLitSize)) { entropy->hufCTable_repeatMode = HUF_repeat_none; return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } @@ -1156,11 +1156,10 @@ MEM_STATIC size_t ZSTD_encodeSequences(void* dst, size_t dstCapacity, } } -MEM_STATIC size_t ZSTD_compressSequences (seqStore_t* seqStorePtr, +MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, ZSTD_entropyCTables_t* entropy, ZSTD_compressionParameters const* cParams, - void* dst, size_t dstCapacity, - size_t srcSize) + void* dst, size_t dstCapacity) { const int longOffsets = cParams->windowLog > STREAM_ACCUMULATOR_MIN; U32 count[MaxSeq+1]; @@ -1195,7 +1194,7 @@ MEM_STATIC size_t ZSTD_compressSequences (seqStore_t* seqStorePtr, if (nbSeq < 0x7F) *op++ = (BYTE)nbSeq; else if (nbSeq < LONGNBSEQ) 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) goto _check_compressibility; + if (nbSeq==0) return op - ostart; /* seqHead : flags for FSE encoding type */ seqHead = op++; @@ -1244,23 +1243,40 @@ MEM_STATIC size_t ZSTD_compressSequences (seqStore_t* seqStorePtr, op += streamSize; } + return op - ostart; +} - /* check compressibility */ -_check_compressibility: - { size_t const minGain = ZSTD_minGain(srcSize); - size_t const maxCSize = srcSize - minGain; - if ((size_t)(op-ostart) >= maxCSize) { - 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; - } } +MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, + ZSTD_entropyCTables_t* entropy, + ZSTD_compressionParameters const* cParams, + void* dst, size_t dstCapacity, + size_t srcSize) +{ + size_t const cSize = ZSTD_compressSequences_internal(seqStorePtr, entropy, cParams, + dst, dstCapacity); + size_t const minGain = ZSTD_minGain(srcSize); + size_t const maxCSize = srcSize - minGain; + /* 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. + */ + int const uncompressibleError = cSize == ERROR(dstSize_tooSmall) && srcSize <= dstCapacity; + + if (ZSTD_isError(cSize) && !uncompressibleError) + return cSize; + /* Check compressibility */ + 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; + } + assert(!ZSTD_isError(cSize)); /* confirm repcodes */ { int i; for (i=0; irep[i] = seqStorePtr->repToConfirm[i]; } - - return op - ostart; + return cSize; } From e1abc2a3677c89bd300f96081b07e8c34e69aafd Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Mon, 7 Aug 2017 11:43:37 -0700 Subject: [PATCH 274/318] Switch the sleep function to UTIL_sleepMilli --- tests/poolTests.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/poolTests.c b/tests/poolTests.c index d5c37aee0..9e11281ba 100644 --- a/tests/poolTests.c +++ b/tests/poolTests.c @@ -1,8 +1,8 @@ #include "pool.h" #include "threading.h" +#include "util.h" #include #include -#include #define ASSERT_TRUE(p) \ do { \ @@ -53,7 +53,7 @@ int testOrder(size_t numThreads, size_t queueSize) { void waitFn(void *opaque) { (void)opaque; - usleep(100); + UTIL_sleepMilli(1); } /* Tests for deadlock */ From abe12b339994a15cd176df65ceb3a5345e2d47aa Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 28 Jul 2017 11:54:28 -0700 Subject: [PATCH 275/318] [libzstd] Fix bug in Huffman decompresser The zstd format specification doesn't enforce that Huffman compressed literals (including the table) have to be smaller than the uncompressed literals. The compressor will never Huffman compress literals if the compressed size is larger than the uncompressed size. The decompresser doesn't accept Huffman compressed literals with 4 streams whose compressed size is at least as large as the uncompressed size. * Make the decompresser accept Huffman compressed literals whose size increases. * Add a test case that exposes the bug. The compressed file has to be statically generated, since the compressor won't normally produce files that expose the bug. --- lib/decompress/huf_decompress.c | 6 +++--- tests/files/huffman-compressed-larger | Bin 0 -> 143 bytes tests/playTests.sh | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 tests/files/huffman-compressed-larger diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 2a1b70ea5..0a47a3d74 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -917,11 +917,11 @@ static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, qu * 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 */ +* Assumption : 0 < cSrcSize, dstSize <= 128 KB */ U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize) { /* decoder timing evaluation */ - U32 const Q = (U32)(cSrcSize * 16 / dstSize); /* Q < 16 since dstSize > cSrcSize */ + 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); @@ -977,7 +977,7 @@ size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, { /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); - if ((cSrcSize >= dstSize) || (cSrcSize <= 1)) return ERROR(corruption_detected); /* invalid */ + if (cSrcSize == 0) return ERROR(corruption_detected); { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); return algoNb ? HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize): diff --git a/tests/files/huffman-compressed-larger b/tests/files/huffman-compressed-larger new file mode 100644 index 0000000000000000000000000000000000000000..f594f1ae9816a52054935aab96eec94c4ffe14b7 GIT binary patch literal 143 zcmdPcs{fZIK$L|ctxZmLLJ_MmLxavzMg|5327!B9*1g_!Z=NR$149D?LxUKDID;62 z4cnWd+vT|{HGdgQ)<1GEK!Jya@c{d;4wE_?385^JMKc_X+jeyj%ok_$6x literal 0 HcmV?d00001 diff --git a/tests/playTests.sh b/tests/playTests.sh index 77853b1a4..bc8584e7a 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -386,6 +386,13 @@ $ZSTD -t tmpSplit.* && die "bad file not detected !" ./datagen | $ZSTD -c | $ZSTD -t + +$ECHO "\n**** golden files tests **** " + +$ZSTD -t -r files +$ZSTD -c -r files | $ZSTD -t + + $ECHO "\n**** benchmark mode tests **** " $ECHO "bench one file" From e100a311ebb88e59f1bf155f0a31f22ba04e172b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 7 Aug 2017 13:11:07 -0700 Subject: [PATCH 276/318] removed direct assignment of 22, used ZSTD_maxCLevel() instead --- contrib/adaptive-compression/adapt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index eeb4c2ea9..40ebb0722 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -43,7 +43,7 @@ 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 = 22; +static unsigned g_maxCLevel; typedef struct { void* start; @@ -993,7 +993,7 @@ static void help(void) PRINT(" -p : hide progress bar\n"); PRINT(" -q : quiet mode -- do not show progress bar or other information\n"); PRINT(" -l# : provide lower bound for compression level -- default 1\n"); - PRINT(" -u# : provide upper bound for compression level -- default 22\n"); + PRINT(" -u# : provide upper bound for compression level -- default %u\n", ZSTD_maxCLevel()); } /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) @@ -1006,6 +1006,7 @@ int main(int argCount, const char* argv[]) int ret = 0; int argNum; filenameTable[0] = stdinmark; + g_maxCLevel = ZSTD_maxCLevel(); UTIL_initTimer(&g_ticksPerSecond); From 9ba97182d174a38928731096947ab12748d71249 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 8 Aug 2017 12:32:26 -0700 Subject: [PATCH 277/318] [CI] Add gcc7build test --- Makefile | 5 +++++ circle.yml | 6 +++--- lib/decompress/zstd_decompress.c | 10 ++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 5e887364a..1dceec88e 100644 --- a/Makefile +++ b/Makefile @@ -146,6 +146,11 @@ gcc6build: clean gcc-6 -v CC=gcc-6 $(MAKE) all MOREFLAGS="-Werror" +.PHONY: gcc7build +gcc7build: clean + gcc-7 -v + CC=gcc-7 $(MAKE) all MOREFLAGS="-Werror" + .PHONY: clangbuild clangbuild: clean clang -v diff --git a/circle.yml b/circle.yml index 218e33bfc..8c2bd30d3 100644 --- a/circle.yml +++ b/circle.yml @@ -3,7 +3,7 @@ dependencies: - sudo dpkg --add-architecture i386 - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; sudo apt-get -y -qq update - sudo apt-get -y install gcc-powerpc-linux-gnu gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross - - sudo apt-get -y install libstdc++-6-dev clang gcc g++ gcc-5 gcc-6 zlib1g-dev liblzma-dev + - sudo apt-get -y install libstdc++-7-dev clang gcc g++ gcc-5 gcc-6 gcc-7 zlib1g-dev liblzma-dev - sudo apt-get -y install linux-libc-dev:i386 libc6-dev-i386 test: @@ -45,7 +45,7 @@ test: parallel: true - ? | if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make ppc64build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then true && make clean; fi #could add another test here + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gcc7build && make clean; fi #could add another test here : parallel: true - ? | @@ -64,7 +64,7 @@ test: #- gcc -v; make -C tests test32 MOREFLAGS="-I/usr/include/x86_64-linux-gnu" && make clean #- make uasan && make clean #- make asan32 && make clean - #- make -C tests test32 CC=clang MOREFLAGS="-g -fsanitize=address -I/usr/include/x86_64-linux-gnu" + #- make -C tests test32 CC=clang MOREFLAGS="-g -fsanitize=address -I/usr/include/x86_64-linux-gnu" # Valgrind tests #- CFLAGS="-O1 -g" make -C zlibWrapper valgrindTest && make clean #- make -C tests valgrindTest && make clean diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 92e80c1ac..159b7b15b 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1731,7 +1731,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c return 0; } dctx->expected = 0; /* not necessary to copy more */ - + /* fall-through */ case ZSTDds_decodeFrameHeader: assert(src != NULL); memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_prefix, src, dctx->expected); @@ -2391,7 +2391,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB zds->outBuffSize = neededOutSize; } } zds->streamStage = zdss_read; - /* pass-through */ + /* fall-through */ case zdss_read: DEBUGLOG(5, "stage zdss_read"); @@ -2416,8 +2416,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; - /* pass-through */ - + /* fall-through */ case zdss_load: { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds); size_t const toLoad = neededInSize - zds->inPos; /* should always be <= remaining space within inBuff */ @@ -2439,8 +2438,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB zds->outEnd = zds->outStart + decodedSize; } } zds->streamStage = zdss_flush; - /* pass-through */ - + /* 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); From 8b6702a00d833043fde5ebc73e6f064866594c70 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 8 Aug 2017 16:27:10 -0700 Subject: [PATCH 278/318] [linux-kernel] Update patches for v5 --- contrib/linux-kernel/0000-cover-letter.patch | 22 +++-- .../0001-lib-Add-xxhash-module.patch | 4 +- .../0002-lib-Add-zstd-modules.patch | 86 ++++++++++++------- .../0003-btrfs-Add-zstd-support.patch | 4 +- .../0004-squashfs-Add-zstd-support.patch | 24 ++++-- .../0005-crypto-Add-zstd-support.patch | 17 ++-- ...0006-squashfs-tools-Add-zstd-support.patch | 43 ++++------ contrib/linux-kernel/README.md | 8 +- .../linux-kernel/fs/squashfs/zstd_wrapper.c | 8 +- contrib/linux-kernel/lib/zstd/compress.c | 47 +++++----- contrib/linux-kernel/lib/zstd/zstd_internal.h | 17 +++- 11 files changed, 164 insertions(+), 116 deletions(-) diff --git a/contrib/linux-kernel/0000-cover-letter.patch b/contrib/linux-kernel/0000-cover-letter.patch index f72b7614e..d57ef27e7 100644 --- a/contrib/linux-kernel/0000-cover-letter.patch +++ b/contrib/linux-kernel/0000-cover-letter.patch @@ -1,7 +1,7 @@ -From a276288db937088d00b975ad9c36278fa46c8cf7 Mon Sep 17 00:00:00 2001 +From 308795a7713ca6fcd468b60fba9a2fca99cee6a0 Mon Sep 17 00:00:00 2001 From: Nick Terrell -Date: Fri, 4 Aug 2017 12:47:29 -0700 -Subject: [PATCH v4 0/5] Add xxhash and zstd modules +Date: Tue, 8 Aug 2017 19:20:25 -0700 +Subject: [PATCH v5 0/5] Add xxhash and zstd modules Hi all, @@ -37,6 +37,12 @@ v3 -> v4: - Fix minor linter warnings (3/5, 4/5) - Add crypto patch (5/5) +v4 -> v5: +- Fix rare compression bug from upstream commit 308047eb5d (2/5) +- Fix bug introduced in v3 when working around the gcc-7 bug (2/5) +- Fix ZSTD_DStream initialization code in squashfs (4/5) +- Fix patch documentation for patches written by Sean Purcell (4/5) + Nick Terrell (5): lib: Add xxhash module lib: Add zstd modules @@ -65,7 +71,7 @@ Nick Terrell (5): fs/squashfs/decompressor.c | 7 + fs/squashfs/decompressor.h | 4 + fs/squashfs/squashfs_fs.h | 1 + - fs/squashfs/zstd_wrapper.c | 149 ++ + fs/squashfs/zstd_wrapper.c | 151 ++ include/linux/xxhash.h | 236 +++ include/linux/zstd.h | 1157 +++++++++++++++ include/uapi/linux/btrfs.h | 8 +- @@ -74,9 +80,9 @@ Nick Terrell (5): lib/xxhash.c | 500 +++++++ lib/zstd/Makefile | 18 + lib/zstd/bitstream.h | 374 +++++ - lib/zstd/compress.c | 3479 ++++++++++++++++++++++++++++++++++++++++++++ + lib/zstd/compress.c | 3484 ++++++++++++++++++++++++++++++++++++++++++++ lib/zstd/decompress.c | 2528 ++++++++++++++++++++++++++++++++ - lib/zstd/entropy_common.c | 243 ++++ + lib/zstd/entropy_common.c | 243 +++ lib/zstd/error_private.h | 53 + lib/zstd/fse.h | 575 ++++++++ lib/zstd/fse_compress.c | 795 ++++++++++ @@ -86,9 +92,9 @@ Nick Terrell (5): lib/zstd/huf_decompress.c | 960 ++++++++++++ lib/zstd/mem.h | 151 ++ lib/zstd/zstd_common.c | 75 + - lib/zstd/zstd_internal.h | 250 ++++ + lib/zstd/zstd_internal.h | 263 ++++ lib/zstd/zstd_opt.h | 1014 +++++++++++++ - 44 files changed, 14736 insertions(+), 12 deletions(-) + 44 files changed, 14756 insertions(+), 12 deletions(-) create mode 100644 crypto/zstd.c create mode 100644 fs/btrfs/zstd.c create mode 100644 fs/squashfs/zstd_wrapper.c diff --git a/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch b/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch index 21425425d..83f09924f 100644 --- a/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch +++ b/contrib/linux-kernel/0001-lib-Add-xxhash-module.patch @@ -1,7 +1,7 @@ -From 587f1ba6e78cc5b0d3e26971290aef36ff66f378 Mon Sep 17 00:00:00 2001 +From a4b1ffb6e89bbccd519f9afa0910635668436105 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 17 Jul 2017 17:07:18 -0700 -Subject: [PATCH v4 1/5] lib: Add xxhash module +Subject: [PATCH v5 1/5] lib: Add xxhash module Adds xxhash kernel module with xxh32 and xxh64 hashes. xxhash is an extremely fast non-cryptographic hash algorithm for checksumming. diff --git a/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch b/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch index 59467dbc2..eb8b8b288 100644 --- a/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch +++ b/contrib/linux-kernel/0002-lib-Add-zstd-modules.patch @@ -1,7 +1,7 @@ -From c7f952ce985f652fe1f2c9266f39cd87b470fd8a Mon Sep 17 00:00:00 2001 +From b7f044163968d724be55bf4841fd80babe036dc2 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 17 Jul 2017 17:08:19 -0700 -Subject: [PATCH v4 2/5] lib: Add zstd modules +Subject: [PATCH v5 2/5] lib: Add zstd modules Add zstd compression and decompression kernel modules. zstd offers a wide varity of compression speed and quality trade-offs. @@ -117,12 +117,16 @@ v2 -> v3: v3 -> v4: - Fix minor compiler warnings +v4 -> v5: +- Fix rare compression bug from upstream commit 308047eb5d +- Fix bug introduced in v3 when working around the gcc-7 bug + include/linux/zstd.h | 1157 +++++++++++++++ lib/Kconfig | 8 + lib/Makefile | 2 + lib/zstd/Makefile | 18 + lib/zstd/bitstream.h | 374 +++++ - lib/zstd/compress.c | 3479 +++++++++++++++++++++++++++++++++++++++++++++ + lib/zstd/compress.c | 3484 +++++++++++++++++++++++++++++++++++++++++++++ lib/zstd/decompress.c | 2528 ++++++++++++++++++++++++++++++++ lib/zstd/entropy_common.c | 243 ++++ lib/zstd/error_private.h | 53 + @@ -134,9 +138,9 @@ v3 -> v4: lib/zstd/huf_decompress.c | 960 +++++++++++++ lib/zstd/mem.h | 151 ++ lib/zstd/zstd_common.c | 75 + - lib/zstd/zstd_internal.h | 250 ++++ + lib/zstd/zstd_internal.h | 263 ++++ lib/zstd/zstd_opt.h | 1014 +++++++++++++ - 19 files changed, 12996 insertions(+) + 19 files changed, 13014 insertions(+) create mode 100644 include/linux/zstd.h create mode 100644 lib/zstd/Makefile create mode 100644 lib/zstd/bitstream.h @@ -1756,10 +1760,10 @@ index 0000000..a826b99 +#endif /* BITSTREAM_H_MODULE */ diff --git a/lib/zstd/compress.c b/lib/zstd/compress.c new file mode 100644 -index 0000000..d60ab7d +index 0000000..f9166cf --- /dev/null +++ b/lib/zstd/compress.c -@@ -0,0 +1,3479 @@ +@@ -0,0 +1,3484 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. @@ -2345,7 +2349,7 @@ index 0000000..d60ab7d + mlCodeTable[seqStorePtr->longLengthPos] = MaxML; +} + -+ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCapacity, size_t srcSize) ++ZSTD_STATIC size_t ZSTD_compressSequences_internal(ZSTD_CCtx *zc, void *dst, size_t dstCapacity) +{ + const int longOffsets = zc->params.cParams.windowLog > STREAM_ACCUMULATOR_MIN; + const seqStore_t *seqStorePtr = &(zc->seqStore); @@ -2398,7 +2402,7 @@ index 0000000..d60ab7d + else + op[0] = 0xFF, ZSTD_writeLE16(op + 1, (U16)(nbSeq - LONGNBSEQ)), op += 3; + if (nbSeq == 0) -+ goto _check_compressibility; ++ return op - ostart; + + /* seqHead : flags for FSE encoding type */ + seqHead = op++; @@ -2588,28 +2592,33 @@ index 0000000..d60ab7d + op += streamSize; + } + } -+ -+/* check compressibility */ -+_check_compressibility: -+ { -+ size_t const minGain = ZSTD_minGain(srcSize); -+ size_t const maxCSize = srcSize - minGain; -+ if ((size_t)(op - ostart) >= maxCSize) { -+ zc->flagStaticHufTable = HUF_repeat_none; -+ return 0; -+ } -+ } -+ -+ /* confirm repcodes */ -+ { -+ int i; -+ for (i = 0; i < ZSTD_REP_NUM; i++) -+ zc->rep[i] = zc->repToConfirm[i]; -+ } -+ + return op - ostart; +} + ++ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCapacity, size_t srcSize) ++{ ++ size_t const cSize = ZSTD_compressSequences_internal(zc, dst, dstCapacity); ++ size_t const minGain = ZSTD_minGain(srcSize); ++ size_t const maxCSize = srcSize - minGain; ++ /* 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. ++ */ ++ int const uncompressibleError = cSize == ERROR(dstSize_tooSmall) && srcSize <= dstCapacity; ++ int i; ++ ++ if (ZSTD_isError(cSize) && !uncompressibleError) ++ return cSize; ++ if (cSize >= maxCSize || uncompressibleError) { ++ zc->flagStaticHufTable = HUF_repeat_none; ++ return 0; ++ } ++ /* confirm repcodes */ ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ zc->rep[i] = zc->repToConfirm[i]; ++ return cSize; ++} ++ +/*! ZSTD_storeSeq() : + Store a sequence (literal length, literals, offset code and match length code) into seqStore_t. + `offsetCode` : distance to match, or 0 == repCode. @@ -12001,10 +12010,10 @@ index 0000000..a282624 +} diff --git a/lib/zstd/zstd_internal.h b/lib/zstd/zstd_internal.h new file mode 100644 -index 0000000..44e8f100 +index 0000000..1a79fab --- /dev/null +++ b/lib/zstd/zstd_internal.h -@@ -0,0 +1,250 @@ +@@ -0,0 +1,263 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. @@ -12141,8 +12150,21 @@ index 0000000..44e8f100 +#define WILDCOPY_OVERLENGTH 8 +ZSTD_STATIC void ZSTD_wildcopy(void *dst, const void *src, ptrdiff_t length) +{ -+ if (length > 0) -+ memcpy(dst, src, length); ++ const BYTE* ip = (const BYTE*)src; ++ BYTE* op = (BYTE*)dst; ++ BYTE* const oend = op + length; ++ /* Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388. ++ * Avoid the bad case where the loop only runs once by handling the ++ * special case separately. This doesn't trigger the bug because it ++ * doesn't involve pointer/integer overflow. ++ */ ++ if (length <= 8) ++ return ZSTD_copy8(dst, src); ++ do { ++ ZSTD_copy8(op, ip); ++ op += 8; ++ ip += 8; ++ } while (op < oend); +} + +/*-******************************************* diff --git a/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch b/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch index 9fdcdf03e..edc7839a0 100644 --- a/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch +++ b/contrib/linux-kernel/0003-btrfs-Add-zstd-support.patch @@ -1,7 +1,7 @@ -From 6ade5bc08dcfa2bce2b4801e47edf783dcb7ca43 Mon Sep 17 00:00:00 2001 +From 8a9dddfbf6551afea73911e367dd4be64d62b9fd Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 17 Jul 2017 17:08:39 -0700 -Subject: [PATCH v4 3/5] btrfs: Add zstd support +Subject: [PATCH v5 3/5] btrfs: Add zstd support Add zstd compression and decompression support to BtrFS. zstd at its fastest level compresses almost as well as zlib, while offering much diff --git a/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch b/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch index d27522b5d..36cdf71df 100644 --- a/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch +++ b/contrib/linux-kernel/0004-squashfs-Add-zstd-support.patch @@ -1,7 +1,7 @@ -From 6e1c54639deca96465b973ad80e34ff7fc789573 Mon Sep 17 00:00:00 2001 +From 46bf8f6d30d6ddf2446c110f122482b5e5e16933 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Mon, 17 Jul 2017 17:08:59 -0700 -Subject: [PATCH v4 4/5] squashfs: Add zstd support +Subject: [PATCH v5 4/5] squashfs: Add zstd support Add zstd compression and decompression support to SquashFS. zstd is a great fit for SquashFS because it can compress at ratios approaching xz, @@ -48,13 +48,17 @@ Signed-off-by: Nick Terrell v3 -> v4: - Fix minor linter warnings +v4 -> v5: +- Fix ZSTD_DStream initialization code in squashfs +- Fix patch documentation to reflect that Sean Purcell is the author + fs/squashfs/Kconfig | 14 +++++ fs/squashfs/Makefile | 1 + fs/squashfs/decompressor.c | 7 +++ fs/squashfs/decompressor.h | 4 ++ fs/squashfs/squashfs_fs.h | 1 + - fs/squashfs/zstd_wrapper.c | 149 +++++++++++++++++++++++++++++++++++++++++++++ - 6 files changed, 176 insertions(+) + fs/squashfs/zstd_wrapper.c | 151 +++++++++++++++++++++++++++++++++++++++++++++ + 6 files changed, 178 insertions(+) create mode 100644 fs/squashfs/zstd_wrapper.c diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig @@ -143,10 +147,10 @@ index 506f4ba..24d12fd 100644 __le32 s_magic; diff --git a/fs/squashfs/zstd_wrapper.c b/fs/squashfs/zstd_wrapper.c new file mode 100644 -index 0000000..d70efa8 +index 0000000..eeaabf8 --- /dev/null +++ b/fs/squashfs/zstd_wrapper.c -@@ -0,0 +1,149 @@ +@@ -0,0 +1,151 @@ +/* + * Squashfs - a compressed read only filesystem for Linux + * @@ -181,6 +185,7 @@ index 0000000..d70efa8 +struct workspace { + void *mem; + size_t mem_size; ++ size_t window_size; +}; + +static void *zstd_init(struct squashfs_sb_info *msblk, void *buff) @@ -189,8 +194,9 @@ index 0000000..d70efa8 + + if (wksp == NULL) + goto failed; -+ wksp->mem_size = ZSTD_DStreamWorkspaceBound(max_t(size_t, -+ msblk->block_size, SQUASHFS_METADATA_SIZE)); ++ wksp->window_size = max_t(size_t, ++ msblk->block_size, SQUASHFS_METADATA_SIZE); ++ wksp->mem_size = ZSTD_DStreamWorkspaceBound(wksp->window_size); + wksp->mem = vmalloc(wksp->mem_size); + if (wksp->mem == NULL) + goto failed; @@ -226,7 +232,7 @@ index 0000000..d70efa8 + ZSTD_inBuffer in_buf = { NULL, 0, 0 }; + ZSTD_outBuffer out_buf = { NULL, 0, 0 }; + -+ stream = ZSTD_initDStream(wksp->mem_size, wksp->mem, wksp->mem_size); ++ stream = ZSTD_initDStream(wksp->window_size, wksp->mem, wksp->mem_size); + + if (!stream) { + ERROR("Failed to initialize zstd decompressor\n"); diff --git a/contrib/linux-kernel/0005-crypto-Add-zstd-support.patch b/contrib/linux-kernel/0005-crypto-Add-zstd-support.patch index fac772f07..971b06345 100644 --- a/contrib/linux-kernel/0005-crypto-Add-zstd-support.patch +++ b/contrib/linux-kernel/0005-crypto-Add-zstd-support.patch @@ -1,7 +1,7 @@ -From a276288db937088d00b975ad9c36278fa46c8cf7 Mon Sep 17 00:00:00 2001 +From 308795a7713ca6fcd468b60fba9a2fca99cee6a0 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 2 Aug 2017 18:02:13 -0700 -Subject: [PATCH v4 5/5] crypto: Add zstd support +Subject: [PATCH v5 5/5] crypto: Add zstd support Adds zstd support to crypto and scompress. Only supports the default level. @@ -23,7 +23,7 @@ index caa770e..4fc3936 100644 @@ -1662,6 +1662,15 @@ config CRYPTO_LZ4HC help This is the LZ4 high compression mode algorithm. - + +config CRYPTO_ZSTD + tristate "Zstd compression algorithm" + select CRYPTO_ALGAPI @@ -34,7 +34,7 @@ index caa770e..4fc3936 100644 + This is the zstd algorithm. + comment "Random Number Generation" - + config CRYPTO_ANSI_CPRNG diff --git a/crypto/Makefile b/crypto/Makefile index d41f033..b22e1e8 100644 @@ -45,7 +45,7 @@ index d41f033..b22e1e8 100644 obj-$(CONFIG_CRYPTO_USER_API_RNG) += algif_rng.o obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o +obj-$(CONFIG_CRYPTO_ZSTD) += zstd.o - + ecdh_generic-y := ecc.o ecdh_generic-y += ecdh.o diff --git a/crypto/testmgr.c b/crypto/testmgr.c @@ -68,7 +68,7 @@ index 7125ba3..8a124d3 100644 + } } }; - + diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 6ceb0e2..e6b5920 100644 --- a/crypto/testmgr.h @@ -76,7 +76,7 @@ index 6ceb0e2..e6b5920 100644 @@ -34631,4 +34631,75 @@ static const struct comp_testvec lz4hc_decomp_tv_template[] = { }, }; - + +static const struct comp_testvec zstd_comp_tv_template[] = { + { + .inlen = 68, @@ -420,6 +420,5 @@ index 0000000..9a76b3e +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Zstd Compression Algorithm"); +MODULE_ALIAS_CRYPTO("zstd"); --- +-- 2.9.3 - diff --git a/contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch b/contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch index 49e240fb1..b38930fdc 100644 --- a/contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch +++ b/contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch @@ -1,7 +1,7 @@ -From 0ec6ae4b2c69fcf27785e389391b0add474efd8c Mon Sep 17 00:00:00 2001 +From cc08b43a31fed1289c2027d5090999da569457f1 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 3 Aug 2017 17:47:03 -0700 -Subject: [PATCH v4] squashfs-tools: Add zstd support +Subject: [PATCH v5] squashfs-tools: Add zstd support This patch adds zstd support to squashfs-tools. It works with zstd versions >= 1.0.0. It was originally written by Sean Purcell. @@ -9,12 +9,17 @@ versions >= 1.0.0. It was originally written by Sean Purcell. Signed-off-by: Sean Purcell Signed-off-by: Nick Terrell --- +v4 -> v5: +- Fix patch documentation to reflect that Sean Purcell is the author +- Don't strip trailing whitespace of unreleated code +- Make zstd_display_options() static + squashfs-tools/Makefile | 21 ++++ squashfs-tools/compressor.c | 8 ++ - squashfs-tools/squashfs_fs.h | 3 +- + squashfs-tools/squashfs_fs.h | 1 + squashfs-tools/zstd_wrapper.c | 254 ++++++++++++++++++++++++++++++++++++++++++ squashfs-tools/zstd_wrapper.h | 48 ++++++++ - 5 files changed, 333 insertions(+), 1 deletion(-) + 5 files changed, 332 insertions(+) create mode 100644 squashfs-tools/zstd_wrapper.c create mode 100644 squashfs-tools/zstd_wrapper.h @@ -25,7 +30,7 @@ index 52d2582..8e82e09 100644 @@ -75,6 +75,19 @@ GZIP_SUPPORT = 1 #LZMA_SUPPORT = 1 #LZMA_DIR = ../../../../LZMA/lzma465 - + + +########### Building ZSTD support ############ +# @@ -45,7 +50,7 @@ index 52d2582..8e82e09 100644 @@ -177,6 +190,14 @@ LIBS += -llz4 COMPRESSORS += lz4 endif - + +ifeq ($(ZSTD_SUPPORT),1) +CFLAGS += -DZSTD_SUPPORT +MKSQUASHFS_OBJS += zstd_wrapper.o @@ -64,7 +69,7 @@ index 525e316..02b5e90 100644 @@ -65,6 +65,13 @@ static struct compressor xz_comp_ops = { extern struct compressor xz_comp_ops; #endif - + +#ifndef ZSTD_SUPPORT +static struct compressor zstd_comp_ops = { + ZSTD_COMPRESSION, "zstd" @@ -72,7 +77,7 @@ index 525e316..02b5e90 100644 +#else +extern struct compressor zstd_comp_ops; +#endif - + static struct compressor unknown_comp_ops = { 0, "unknown" @@ -77,6 +84,7 @@ struct compressor *compressor[] = { @@ -82,31 +87,22 @@ index 525e316..02b5e90 100644 + &zstd_comp_ops, &unknown_comp_ops }; - + diff --git a/squashfs-tools/squashfs_fs.h b/squashfs-tools/squashfs_fs.h -index 791fe12..1f2e8b0 100644 +index 791fe12..afca918 100644 --- a/squashfs-tools/squashfs_fs.h +++ b/squashfs-tools/squashfs_fs.h -@@ -24,7 +24,7 @@ - * squashfs_fs.h - */ - --#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE -+#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE - #define SQUASHFS_MAJOR 4 - #define SQUASHFS_MINOR 0 - #define SQUASHFS_MAGIC 0x73717368 @@ -277,6 +277,7 @@ typedef long long squashfs_inode; #define LZO_COMPRESSION 3 #define XZ_COMPRESSION 4 #define LZ4_COMPRESSION 5 +#define ZSTD_COMPRESSION 6 - + struct squashfs_super_block { unsigned int s_magic; diff --git a/squashfs-tools/zstd_wrapper.c b/squashfs-tools/zstd_wrapper.c new file mode 100644 -index 0000000..0989f0f +index 0000000..dcab75a --- /dev/null +++ b/squashfs-tools/zstd_wrapper.c @@ -0,0 +1,254 @@ @@ -262,7 +258,7 @@ index 0000000..0989f0f + return -1; +} + -+void zstd_display_options(void *buffer, int size) ++static void zstd_display_options(void *buffer, int size) +{ + struct zstd_comp_opts *comp_opts = buffer; + @@ -418,6 +414,5 @@ index 0000000..4fbef0a + int compression_level; +}; +#endif --- +-- 2.9.3 - diff --git a/contrib/linux-kernel/README.md b/contrib/linux-kernel/README.md index 1b58304f2..86552b8bd 100644 --- a/contrib/linux-kernel/README.md +++ b/contrib/linux-kernel/README.md @@ -1,7 +1,7 @@ # Linux Kernel Patch There are four pieces, the `xxhash` kernel module, the `zstd_compress` and `zstd_decompress` kernel modules, the BtrFS patch, and the SquashFS patch. -The patches are based off of the linux kernel master branch (version 4.10). +The patches are based off of the linux kernel master branch. ## xxHash kernel module @@ -42,7 +42,7 @@ The patches are based off of the linux kernel master branch (version 4.10). Benchmarks run on a Ubuntu 14.04 with 2 cores and 4 GiB of RAM. The VM is running on a Macbook Pro with a 3.1 GHz Intel Core i7 processor, 16 GB of ram, and a SSD. -The kernel running was built from the master branch with the patch (version 4.10). +The kernel running was built from the master branch with the patch. The compression benchmark is copying 10 copies of the unzipped [silesia corpus](http://mattmahoney.net/dc/silesia.html) into a BtrFS @@ -69,14 +69,14 @@ See `btrfs-benchmark.sh` for details. * The patch is located in `squashfs.diff` * Additionally `fs/squashfs/zstd_wrapper.c` is provided as a source for convenience. -* The patch has been tested on a 4.10 kernel. +* The patch has been tested on the master branch of the kernel. ### Benchmarks Benchmarks run on a Ubuntu 14.04 with 2 cores and 4 GiB of RAM. The VM is running on a Macbook Pro with a 3.1 GHz Intel Core i7 processor, 16 GB of ram, and a SSD. -The kernel running was built from the master branch with the patch (version 4.10). +The kernel running was built from the master branch with the patch. The compression benchmark is the file tree from the SquashFS archive found in the Ubuntu 16.10 desktop image (ubuntu-16.10-desktop-amd64.iso). diff --git a/contrib/linux-kernel/fs/squashfs/zstd_wrapper.c b/contrib/linux-kernel/fs/squashfs/zstd_wrapper.c index d70efa8b2..eeaabf881 100644 --- a/contrib/linux-kernel/fs/squashfs/zstd_wrapper.c +++ b/contrib/linux-kernel/fs/squashfs/zstd_wrapper.c @@ -32,6 +32,7 @@ struct workspace { void *mem; size_t mem_size; + size_t window_size; }; static void *zstd_init(struct squashfs_sb_info *msblk, void *buff) @@ -40,8 +41,9 @@ static void *zstd_init(struct squashfs_sb_info *msblk, void *buff) if (wksp == NULL) goto failed; - wksp->mem_size = ZSTD_DStreamWorkspaceBound(max_t(size_t, - msblk->block_size, SQUASHFS_METADATA_SIZE)); + wksp->window_size = max_t(size_t, + msblk->block_size, SQUASHFS_METADATA_SIZE); + wksp->mem_size = ZSTD_DStreamWorkspaceBound(wksp->window_size); wksp->mem = vmalloc(wksp->mem_size); if (wksp->mem == NULL) goto failed; @@ -77,7 +79,7 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm, ZSTD_inBuffer in_buf = { NULL, 0, 0 }; ZSTD_outBuffer out_buf = { NULL, 0, 0 }; - stream = ZSTD_initDStream(wksp->mem_size, wksp->mem, wksp->mem_size); + stream = ZSTD_initDStream(wksp->window_size, wksp->mem, wksp->mem_size); if (!stream) { ERROR("Failed to initialize zstd decompressor\n"); diff --git a/contrib/linux-kernel/lib/zstd/compress.c b/contrib/linux-kernel/lib/zstd/compress.c index d60ab7d4f..f9166cf4f 100644 --- a/contrib/linux-kernel/lib/zstd/compress.c +++ b/contrib/linux-kernel/lib/zstd/compress.c @@ -583,7 +583,7 @@ void ZSTD_seqToCodes(const seqStore_t *seqStorePtr) mlCodeTable[seqStorePtr->longLengthPos] = MaxML; } -ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCapacity, size_t srcSize) +ZSTD_STATIC size_t ZSTD_compressSequences_internal(ZSTD_CCtx *zc, void *dst, size_t dstCapacity) { const int longOffsets = zc->params.cParams.windowLog > STREAM_ACCUMULATOR_MIN; const seqStore_t *seqStorePtr = &(zc->seqStore); @@ -636,7 +636,7 @@ ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCa else op[0] = 0xFF, ZSTD_writeLE16(op + 1, (U16)(nbSeq - LONGNBSEQ)), op += 3; if (nbSeq == 0) - goto _check_compressibility; + return op - ostart; /* seqHead : flags for FSE encoding type */ seqHead = op++; @@ -826,28 +826,33 @@ ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCa op += streamSize; } } - -/* check compressibility */ -_check_compressibility: - { - size_t const minGain = ZSTD_minGain(srcSize); - size_t const maxCSize = srcSize - minGain; - if ((size_t)(op - ostart) >= maxCSize) { - zc->flagStaticHufTable = HUF_repeat_none; - return 0; - } - } - - /* confirm repcodes */ - { - int i; - for (i = 0; i < ZSTD_REP_NUM; i++) - zc->rep[i] = zc->repToConfirm[i]; - } - return op - ostart; } +ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCapacity, size_t srcSize) +{ + size_t const cSize = ZSTD_compressSequences_internal(zc, dst, dstCapacity); + size_t const minGain = ZSTD_minGain(srcSize); + size_t const maxCSize = srcSize - minGain; + /* 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. + */ + int const uncompressibleError = cSize == ERROR(dstSize_tooSmall) && srcSize <= dstCapacity; + int i; + + if (ZSTD_isError(cSize) && !uncompressibleError) + return cSize; + if (cSize >= maxCSize || uncompressibleError) { + zc->flagStaticHufTable = HUF_repeat_none; + return 0; + } + /* confirm repcodes */ + for (i = 0; i < ZSTD_REP_NUM; i++) + zc->rep[i] = zc->repToConfirm[i]; + return cSize; +} + /*! ZSTD_storeSeq() : Store a sequence (literal length, literals, offset code and match length code) into seqStore_t. `offsetCode` : distance to match, or 0 == repCode. diff --git a/contrib/linux-kernel/lib/zstd/zstd_internal.h b/contrib/linux-kernel/lib/zstd/zstd_internal.h index 44e8f1001..1a79fab9e 100644 --- a/contrib/linux-kernel/lib/zstd/zstd_internal.h +++ b/contrib/linux-kernel/lib/zstd/zstd_internal.h @@ -134,8 +134,21 @@ ZSTD_STATIC void ZSTD_copy8(void *dst, const void *src) { #define WILDCOPY_OVERLENGTH 8 ZSTD_STATIC void ZSTD_wildcopy(void *dst, const void *src, ptrdiff_t length) { - if (length > 0) - memcpy(dst, src, length); + const BYTE* ip = (const BYTE*)src; + BYTE* op = (BYTE*)dst; + BYTE* const oend = op + length; + /* Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388. + * Avoid the bad case where the loop only runs once by handling the + * special case separately. This doesn't trigger the bug because it + * doesn't involve pointer/integer overflow. + */ + if (length <= 8) + return ZSTD_copy8(dst, src); + do { + ZSTD_copy8(op, ip); + op += 8; + ip += 8; + } while (op < oend); } /*-******************************************* From fc9046958721176de642b369e64acffc6f54015b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 10 Aug 2017 16:11:59 -0700 Subject: [PATCH 279/318] updated program name print statement --- contrib/adaptive-compression/adapt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index b5c158145..e449e2a5f 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -1015,10 +1015,10 @@ static unsigned readU32FromChar(const char** stringPtr) return result; } -static void help(void) +static void help(const char* progPath) { PRINT("Usage:\n"); - PRINT(" zstd-adaptive [options] [file(s)]\n"); + PRINT(" %s [options] [file(s)]\n", progPath); PRINT("\n"); PRINT("Options:\n"); PRINT(" -oFILE : specify the output file name\n"); @@ -1067,7 +1067,7 @@ int main(int argCount, const char* argv[]) providedInitialCLevel = 1; break; case 'h': - help(); + help(argv[0]); goto _main_exit; case 'p': g_useProgressBar = 0; From 736a28d835799495fa1e9a0092edcaf3912a770f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 11 Aug 2017 14:34:49 -0700 Subject: [PATCH 280/318] reduce educational decoder to single frame decompression --- doc/educational_decoder/zstd_decompress.c | 102 +++------------------- 1 file changed, 13 insertions(+), 89 deletions(-) diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index 7c8d8114d..93c346312 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -28,6 +28,7 @@ size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len, /// Get the decompressed size of an input stream so memory can be allocated in /// advance /// Returns -1 if the size can't be determined +/// Assumes decompression of a single frame size_t ZSTD_get_decompressed_size(const void *const src, const size_t src_len); /******* UTILITY MACROS AND TYPES *********************************************/ @@ -396,9 +397,9 @@ size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len, // Multiple frames can be appended into a single file or stream. A frame is // totally independent, has a defined beginning and end, and a set of // parameters which tells the decoder how to decompress it." - while (IO_istream_len(&in) > 0) { - decode_frame(&out, &in, &parsed_dict); - } + + /* this decoder assumes decompression of a single frame */ + decode_frame(&out, &in, &parsed_dict); free_dictionary(&parsed_dict); @@ -424,30 +425,6 @@ static void decompress_data(frame_context_t *const ctx, ostream_t *const out, static void decode_frame(ostream_t *const out, istream_t *const in, const dictionary_t *const dict) { const u32 magic_number = IO_read_bits(in, 32); - - // Skippable frame - // - // "Magic_Number - // - // 4 Bytes, little-endian format. Value : 0x184D2A5?, which means any value - // from 0x184D2A50 to 0x184D2A5F. All 16 values are valid to identify a - // skippable frame." - if ((magic_number & ~0xFU) == 0x184D2A50U) { - // "Skippable frames allow the insertion of user-defined data into a - // flow of concatenated frames. Its design is pretty straightforward, - // with the sole objective to allow the decoder to quickly skip over - // user-defined data and continue decoding. - // - // Skippable frames defined in this specification are compatible with - // LZ4 ones." - const size_t frame_size = IO_read_bits(in, 32); - - // skip over frame - IO_advance_input(in, frame_size); - - return; - } - // Zstandard frame // // "Magic_Number @@ -460,8 +437,8 @@ static void decode_frame(ostream_t *const out, istream_t *const in, return; } - // not a real frame - ERROR("Invalid magic number"); + // not a real frame or a skippable frame + ERROR("Tried to decode non-ZSTD frame"); } /// Decode a frame that contains compressed data. Not all frames do as there @@ -1420,28 +1397,17 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, /******* END SEQUENCE EXECUTION ***********************************************/ /******* OUTPUT SIZE COUNTING *************************************************/ -static void traverse_frame(const frame_header_t *const header, istream_t *const in); - /// Get the decompressed size of an input stream so memory can be allocated in /// advance. -/// This is more complex than the implementation in the reference -/// implementation, as this API allows for the decompression of multiple -/// concatenated frames. +/// This implementation assumes `src` points to a single ZSTD-compressed frame size_t ZSTD_get_decompressed_size(const void *src, const size_t src_len) { istream_t in = IO_make_istream(src, src_len); - size_t dst_size = 0; - // Each frame header only gives us the size of its frame, so iterate over - // all - // frames - while (IO_istream_len(&in) > 0) { + // get decompressed size from ZSTD frame header + { const u32 magic_number = IO_read_bits(&in, 32); - if ((magic_number & ~0xFU) == 0x184D2A50U) { - // skippable frame, this has no impact on output size - const size_t frame_size = IO_read_bits(&in, 32); - IO_advance_input(&in, frame_size); - } else if (magic_number == 0xFD2FB528U) { + if (magic_number == 0xFD2FB528U) { // ZSTD frame frame_header_t header; parse_frame_header(&header, &in); @@ -1451,54 +1417,13 @@ size_t ZSTD_get_decompressed_size(const void *src, const size_t src_len) { return -1; } - dst_size += header.frame_content_size; - - // Consume the input from the frame to reach the start of the next - traverse_frame(&header, &in); + return header.frame_content_size; } else { - // not a real frame - ERROR("Invalid magic number"); + // not a real frame or skippable frame + ERROR("ZSTD frame magic number did not match"); } } - - return dst_size; } - -/// Iterate over each block in a frame to find the end of it, to get to the -/// start of the next frame -static void traverse_frame(const frame_header_t *const header, istream_t *const in) { - int last_block = 0; - - do { - // Parse the block header - last_block = IO_read_bits(in, 1); - const int block_type = IO_read_bits(in, 2); - const size_t block_len = IO_read_bits(in, 21); - - switch (block_type) { - case 0: // Raw block, block_len bytes - IO_advance_input(in, block_len); - break; - case 1: // RLE block, 1 byte - IO_advance_input(in, 1); - break; - case 2: // Compressed block, compressed size is block_len - IO_advance_input(in, block_len); - break; - case 3: - // Reserved block type - CORRUPTION(); - break; - default: - IMPOSSIBLE(); - } - } while (!last_block); - - if (header->content_checksum_flag) { - IO_advance_input(in, 4); - } -} - /******* END OUTPUT SIZE COUNTING *********************************************/ /******* DICTIONARY PARSING ***************************************************/ @@ -2355,4 +2280,3 @@ static void FSE_copy_dtable(FSE_dtable *const dst, const FSE_dtable *const src) memcpy(dst->new_state_base, src->new_state_base, size * sizeof(u16)); } /******* END FSE PRIMITIVES ***************************************************/ - From d0dc675596e5d8caa337494b4a3857389b7a448e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 11 Aug 2017 14:35:13 -0700 Subject: [PATCH 281/318] add makefile --- doc/educational_decoder/Makefile | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 doc/educational_decoder/Makefile diff --git a/doc/educational_decoder/Makefile b/doc/educational_decoder/Makefile new file mode 100644 index 000000000..27b184154 --- /dev/null +++ b/doc/educational_decoder/Makefile @@ -0,0 +1,21 @@ +HARNESS_FILES=*.c + +MULTITHREAD_LDFLAGS = -pthread +DEBUGFLAGS= -g -DZSTD_DEBUG=1 +CPPFLAGS += -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ + -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) +CFLAGS ?= -O3 +CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ + -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ + -Wstrict-prototypes -Wundef -Wformat-security \ + -Wvla -Wformat=2 -Winit-self -Wfloat-equal -Wwrite-strings \ + -Wredundant-decls +CFLAGS += $(DEBUGFLAGS) +CFLAGS += $(MOREFLAGS) +FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(MULTITHREAD_LDFLAGS) + +harness: $(HARNESS_FILES) + $(CC) $(FLAGS) $^ -o $@ + +clean: + @$(RM) -f harness From 9f67e8652e6e3703b9794672df768e842535bc8e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 11 Aug 2017 14:41:44 -0700 Subject: [PATCH 282/318] fixed warnings shown by compiler --- doc/educational_decoder/harness.c | 3 +-- doc/educational_decoder/zstd_decompress.c | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/educational_decoder/harness.c b/doc/educational_decoder/harness.c index 683278dfc..31e1c2924 100644 --- a/doc/educational_decoder/harness.c +++ b/doc/educational_decoder/harness.c @@ -87,7 +87,7 @@ int main(int argc, char **argv) { } size_t decompressed_size = ZSTD_get_decompressed_size(input, input_size); - if (decompressed_size == -1) { + if (decompressed_size == (size_t)-1) { decompressed_size = MAX_COMPRESSION_RATIO * input_size; fprintf(stderr, "WARNING: Compressed data does not contain " "decompressed size, going to assume the compression " @@ -117,4 +117,3 @@ int main(int argc, char **argv) { free(dict); input = output = dict = NULL; } - diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index 93c346312..236ad58aa 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -1374,7 +1374,7 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, // than the offset // ex: if the output so far was "abc", a command with offset=3 and // match_length=6 would produce "abcabcabc" as the new output - for (size_t i = 0; i < match_length; i++) { + for (size_t j = 0; j < match_length; j++) { *write_ptr = *(write_ptr - offset); write_ptr++; } @@ -2117,7 +2117,7 @@ static void FSE_init_dtable(FSE_dtable *const dtable, } // Now we can fill baseline and num bits - for (int i = 0; i < size; i++) { + for (size_t i = 0; i < size; i++) { u8 symbol = dtable->symbols[i]; u16 next_state_desc = state_desc[symbol]++; // Fills in the table appropriately, next_state_desc increases by symbol From bd308d806b8949928afab440e6148fdc728b4bd9 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 11 Aug 2017 14:42:15 -0700 Subject: [PATCH 283/318] remove debug symbols when cleaning, added a simple test --- doc/educational_decoder/Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/educational_decoder/Makefile b/doc/educational_decoder/Makefile index 27b184154..451c2351b 100644 --- a/doc/educational_decoder/Makefile +++ b/doc/educational_decoder/Makefile @@ -19,3 +19,11 @@ harness: $(HARNESS_FILES) clean: @$(RM) -f harness + @$(RM) -rf harness.dSYM + +test: harness + @zstd README.md -o tmp.zst + @./harness tmp.zst tmp + @diff -s tmp README.md + @$(RM) -f tmp* + @make clean From bfc6db8d6aabe8e7b0470128620d7db091fc0580 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 11 Aug 2017 17:53:37 -0700 Subject: [PATCH 284/318] exposed dictionary functions/types --- doc/educational_decoder/Makefile | 5 +++ doc/educational_decoder/zstd_decompress.c | 34 ++++------------- doc/educational_decoder/zstd_decompress.h | 46 ++++++++++++++++++++--- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/doc/educational_decoder/Makefile b/doc/educational_decoder/Makefile index 451c2351b..ace1294f8 100644 --- a/doc/educational_decoder/Makefile +++ b/doc/educational_decoder/Makefile @@ -26,4 +26,9 @@ test: harness @./harness tmp.zst tmp @diff -s tmp README.md @$(RM) -f tmp* + @zstd --train harness.c zstd_decompress.c zstd_decompress.h README.md + @zstd -D dictionary README.md -o tmp.zst + @./harness tmp.zst tmp dictionary + @diff -s tmp README.md + @$(RM) -f tmp* dictionary @make clean diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index 236ad58aa..fe8770924 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -14,22 +14,7 @@ #include #include #include - -/// Zstandard decompression functions. -/// `dst` must point to a space at least as large as the reconstructed output. -size_t ZSTD_decompress(void *const dst, const size_t dst_len, - const void *const src, const size_t src_len); -/// If `dict != NULL` and `dict_len >= 8`, does the same thing as -/// `ZSTD_decompress` but uses the provided dict -size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len, - const void *const src, const size_t src_len, - const void *const dict, const size_t dict_len); - -/// Get the decompressed size of an input stream so memory can be allocated in -/// advance -/// Returns -1 if the size can't be determined -/// Assumes decompression of a single frame -size_t ZSTD_get_decompressed_size(const void *const src, const size_t src_len); +#include "zstd_decompress.h" /******* UTILITY MACROS AND TYPES *********************************************/ // Max block size decompressed size is 128 KB and literal blocks can't be @@ -308,7 +293,7 @@ typedef struct { /// The decoded contents of a dictionary so that it doesn't have to be repeated /// for each frame that uses it -typedef struct { +struct dictionary_s { // Entropy tables HUF_dtable literals_dtable; FSE_dtable ll_dtable; @@ -323,7 +308,7 @@ typedef struct { u64 previous_offsets[3]; u32 dictionary_id; -} dictionary_t; +}; /// A tuple containing the parts necessary to decode and execute a ZSTD sequence /// command @@ -368,10 +353,6 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, const sequence_command_t *const sequences, const size_t num_sequences); -// Parse a provided dictionary blob for use in decompression -static void parse_dictionary(dictionary_t *const dict, const u8 *src, - size_t src_len); -static void free_dictionary(dictionary_t *const dict); /******* END ZSTD HELPER STRUCTS AND PROTOTYPES *******************************/ size_t ZSTD_decompress(void *const dst, const size_t dst_len, @@ -387,7 +368,7 @@ size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len, memset(&parsed_dict, 0, sizeof(dictionary_t)); // dict_len < 8 is not a valid dictionary if (dict && dict_len > 8) { - parse_dictionary(&parsed_dict, (const u8 *)dict, dict_len); + parse_dictionary(&parsed_dict, dict, dict_len); } istream_t in = IO_make_istream(src, src_len); @@ -1430,14 +1411,15 @@ size_t ZSTD_get_decompressed_size(const void *src, const size_t src_len) { static void init_dictionary_content(dictionary_t *const dict, istream_t *const in); -static void parse_dictionary(dictionary_t *const dict, const u8 *src, +void parse_dictionary(dictionary_t *const dict, const void *src, size_t src_len) { + const u8 *byte_src = (const u8 *)src; memset(dict, 0, sizeof(dictionary_t)); if (src_len < 8) { INP_SIZE(); } - istream_t in = IO_make_istream(src, src_len); + istream_t in = IO_make_istream(byte_src, src_len); const u32 magic_number = IO_read_bits(&in, 32); if (magic_number != 0xEC30A437) { @@ -1495,7 +1477,7 @@ static void init_dictionary_content(dictionary_t *const dict, } /// Free an allocated dictionary -static void free_dictionary(dictionary_t *const dict) { +void free_dictionary(dictionary_t *const dict) { HUF_free_dtable(&dict->literals_dtable); FSE_free_dtable(&dict->ll_dtable); FSE_free_dtable(&dict->of_dtable); diff --git a/doc/educational_decoder/zstd_decompress.h b/doc/educational_decoder/zstd_decompress.h index 16f4da3eb..2ac903075 100644 --- a/doc/educational_decoder/zstd_decompress.h +++ b/doc/educational_decoder/zstd_decompress.h @@ -7,10 +7,44 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -size_t ZSTD_decompress(void *const dst, const size_t dst_len, - const void *const src, const size_t src_len); -size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len, - const void *const src, const size_t src_len, - const void *const dict, const size_t dict_len); -size_t ZSTD_get_decompressed_size(const void *const src, const size_t src_len); +/******* DECOMPRESSION FUNCTIONS **********************************************/ +/// Zstandard decompression functions. +/// `dst` must point to a space at least as large as the reconstructed output. +size_t ZSTD_decompress(void *const dst, const size_t dst_len, + const void *const src, const size_t src_len); + +/// If `dict != NULL` and `dict_len >= 8`, does the same thing as +/// `ZSTD_decompress` but uses the provided dict +size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len, + const void *const src, const size_t src_len, + const void *const dict, const size_t dict_len); + +/// Get the decompressed size of an input stream so memory can be allocated in +/// advance +/// Returns -1 if the size can't be determined +/// Assumes decompression of a single frame +size_t ZSTD_get_decompressed_size(const void *const src, const size_t src_len); +/******* END DECOMPRESSION FUNCTIONS ******************************************/ + +/******* DICTIONARY MANAGEMENT ***********************************************/ +/* + * Contains the parsed contents of a dictionary + * This includes Huffman and FSE tables used for decoding and data on offsets + */ +typedef struct dictionary_s dictionary_t; + +/* + * Parse a provided dictionary blob for use in decompression + * `src` -- must point to memory space representing the dictionary + * `src_len` -- must provide the dictionary size + * `dict` -- will contain the parsed contents of the dictionary and + * can be used for decompression + */ +void parse_dictionary(dictionary_t *const dict, const void *src, + size_t src_len); +/* + * Free internal Huffman tables, FSE tables, and dictionary content + */ +void free_dictionary(dictionary_t *const dict); +/******* END DICTIONARY MANAGEMENT *******************************************/ From 7ef9c6f4b2675d4297f2ad4c2a3291a56d33f6c1 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 11 Aug 2017 18:40:19 -0700 Subject: [PATCH 285/318] made separate API for dictionary management --- doc/educational_decoder/harness.c | 8 +++++- doc/educational_decoder/zstd_decompress.c | 31 +++++++++++++---------- doc/educational_decoder/zstd_decompress.h | 16 +++++++++--- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/doc/educational_decoder/harness.c b/doc/educational_decoder/harness.c index 31e1c2924..43d6df7fc 100644 --- a/doc/educational_decoder/harness.c +++ b/doc/educational_decoder/harness.c @@ -106,9 +106,15 @@ int main(int argc, char **argv) { return 1; } + dictionary_t* parsed_dict = create_dictionary(); + if (dict) { + parse_dictionary(parsed_dict, dict, dict_size); + } size_t decompressed = ZSTD_decompress_with_dict(output, decompressed_size, - input, input_size, dict, dict_size); + input, input_size, parsed_dict); + + free_dictionary(parsed_dict); write_file(argv[2], output, decompressed); diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index fe8770924..e22df1c10 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -357,19 +357,16 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, size_t ZSTD_decompress(void *const dst, const size_t dst_len, const void *const src, const size_t src_len) { - return ZSTD_decompress_with_dict(dst, dst_len, src, src_len, NULL, 0); + dictionary_t* uninit_dict = create_dictionary(); + size_t const decomp_size = ZSTD_decompress_with_dict(dst, dst_len, src, + src_len, uninit_dict); + free_dictionary(uninit_dict); + return decomp_size; } size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len, const void *const src, const size_t src_len, - const void *const dict, - const size_t dict_len) { - dictionary_t parsed_dict; - memset(&parsed_dict, 0, sizeof(dictionary_t)); - // dict_len < 8 is not a valid dictionary - if (dict && dict_len > 8) { - parse_dictionary(&parsed_dict, dict, dict_len); - } + dictionary_t* parsed_dict) { istream_t in = IO_make_istream(src, src_len); ostream_t out = IO_make_ostream(dst, dst_len); @@ -380,9 +377,7 @@ size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len, // parameters which tells the decoder how to decompress it." /* this decoder assumes decompression of a single frame */ - decode_frame(&out, &in, &parsed_dict); - - free_dictionary(&parsed_dict); + decode_frame(&out, &in, parsed_dict); return out.ptr - (u8 *)dst; } @@ -1408,6 +1403,16 @@ size_t ZSTD_get_decompressed_size(const void *src, const size_t src_len) { /******* END OUTPUT SIZE COUNTING *********************************************/ /******* DICTIONARY PARSING ***************************************************/ +#define DICT_SIZE_ERROR() ERROR("Dictionary size cannot be less than 8 bytes") + +dictionary_t* create_dictionary() { + dictionary_t* dict = calloc(1, sizeof(dictionary_t)); + if (!dict) { + BAD_ALLOC(); + } + return dict; +} + static void init_dictionary_content(dictionary_t *const dict, istream_t *const in); @@ -1416,7 +1421,7 @@ void parse_dictionary(dictionary_t *const dict, const void *src, const u8 *byte_src = (const u8 *)src; memset(dict, 0, sizeof(dictionary_t)); if (src_len < 8) { - INP_SIZE(); + DICT_SIZE_ERROR(); } istream_t in = IO_make_istream(byte_src, src_len); diff --git a/doc/educational_decoder/zstd_decompress.h b/doc/educational_decoder/zstd_decompress.h index 2ac903075..41009909b 100644 --- a/doc/educational_decoder/zstd_decompress.h +++ b/doc/educational_decoder/zstd_decompress.h @@ -7,6 +7,13 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +/******* EXPOSED TYPES ********************************************************/ +/* +* Contains the parsed contents of a dictionary +* This includes Huffman and FSE tables used for decoding and data on offsets +*/ +typedef struct dictionary_s dictionary_t; +/******* END EXPOSED TYPES ****************************************************/ /******* DECOMPRESSION FUNCTIONS **********************************************/ /// Zstandard decompression functions. @@ -18,7 +25,7 @@ size_t ZSTD_decompress(void *const dst, const size_t dst_len, /// `ZSTD_decompress` but uses the provided dict size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len, const void *const src, const size_t src_len, - const void *const dict, const size_t dict_len); + dictionary_t* parsed_dict); /// Get the decompressed size of an input stream so memory can be allocated in /// advance @@ -29,10 +36,10 @@ size_t ZSTD_get_decompressed_size(const void *const src, const size_t src_len); /******* DICTIONARY MANAGEMENT ***********************************************/ /* - * Contains the parsed contents of a dictionary - * This includes Huffman and FSE tables used for decoding and data on offsets + * Return a valid dictionary_t pointer for use with dictionary initialization + * or decompression */ -typedef struct dictionary_s dictionary_t; +dictionary_t* create_dictionary(); /* * Parse a provided dictionary blob for use in decompression @@ -43,6 +50,7 @@ typedef struct dictionary_s dictionary_t; */ void parse_dictionary(dictionary_t *const dict, const void *src, size_t src_len); + /* * Free internal Huffman tables, FSE tables, and dictionary content */ From b9d4f4fb7426978ef58ddb4dff0adc1b8f845e86 Mon Sep 17 00:00:00 2001 From: Roman Gershman Date: Sun, 13 Aug 2017 13:29:42 +0300 Subject: [PATCH 286/318] Fix ZSTD_estimateDStreamSize function after ZSTD_DStream and ZSTD_DCtx were merged --- 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 159b7b15b..851d0d080 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2244,7 +2244,7 @@ size_t ZSTD_estimateDStreamSize(size_t windowSize) size_t const blockSize = MIN(windowSize, ZSTD_BLOCKSIZE_MAX); size_t const inBuffSize = blockSize; /* no block can be larger */ size_t const outBuffSize = windowSize + blockSize + (WILDCOPY_OVERLENGTH * 2); - return sizeof(ZSTD_DStream) + ZSTD_estimateDCtxSize() + inBuffSize + outBuffSize; + return ZSTD_estimateDCtxSize() + inBuffSize + outBuffSize; } ZSTDLIB_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize) From 0fb7b23fccada5d5411b3667b87f5f6a1f79a6e4 Mon Sep 17 00:00:00 2001 From: codicodi Date: Mon, 14 Aug 2017 14:03:46 +0200 Subject: [PATCH 287/318] fix typo in lz4 support code --- programs/fileio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/fileio.c b/programs/fileio.c index 1dd8008e8..e729e37ec 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -1616,7 +1616,7 @@ static unsigned long long FIO_decompressLz4Frame(dRess_t* ress, /* Write Block */ if (decodedBytes) { if (fwrite(ress->dstBuffer, 1, decodedBytes, ress->dstFile) != decodedBytes) { - DISPLAYLEVEL(1, "zstd: %s \n", strerr(errno)); + DISPLAYLEVEL(1, "zstd: %s \n", strerror(errno)); decodingError = 1; break; } filesize += decodedBytes; From 38f4e433814158dde6039317188793d637e29310 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 14 Aug 2017 09:41:04 -0700 Subject: [PATCH 288/318] added error checking for dictionary initialized with null src --- doc/educational_decoder/zstd_decompress.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index e22df1c10..961d27a5b 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -1404,6 +1404,7 @@ size_t ZSTD_get_decompressed_size(const void *src, const size_t src_len) { /******* DICTIONARY PARSING ***************************************************/ #define DICT_SIZE_ERROR() ERROR("Dictionary size cannot be less than 8 bytes") +#define NULL_SRC() ERROR("Tried to create dictionary with pointer to null src"); dictionary_t* create_dictionary() { dictionary_t* dict = calloc(1, sizeof(dictionary_t)); @@ -1420,6 +1421,9 @@ void parse_dictionary(dictionary_t *const dict, const void *src, size_t src_len) { const u8 *byte_src = (const u8 *)src; memset(dict, 0, sizeof(dictionary_t)); + if (src == NULL) { /* cannot initialize dictionary with null src */ + NULL_SRC(); + } if (src_len < 8) { DICT_SIZE_ERROR(); } From 93c1309fd432aece3daef715d4f9677864b011c0 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 14 Aug 2017 13:08:30 -0700 Subject: [PATCH 289/318] added free to free_dictionary() --- doc/educational_decoder/zstd_decompress.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index 961d27a5b..f45091b16 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -1495,6 +1495,8 @@ void free_dictionary(dictionary_t *const dict) { free(dict->content); memset(dict, 0, sizeof(dictionary_t)); + + free(dict); } /******* END DICTIONARY PARSING ***********************************************/ From b6d6be58c9b377f48e9d6959c1f9f8a3cf73b21f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 14 Aug 2017 14:05:16 -0700 Subject: [PATCH 290/318] created separate function for copying literals during sequence execution --- doc/educational_decoder/zstd_decompress.c | 37 ++++++++++++++--------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index f45091b16..5b88e4c97 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -353,6 +353,9 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, const sequence_command_t *const sequences, const size_t num_sequences); +static u32 copy_literals(sequence_command_t seq, istream_t *litstream, + ostream_t *const out); + /******* END ZSTD HELPER STRUCTS AND PROTOTYPES *******************************/ size_t ZSTD_decompress(void *const dst, const size_t dst_len, @@ -1256,23 +1259,10 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, for (size_t i = 0; i < num_sequences; i++) { const sequence_command_t seq = sequences[i]; - { - // If the sequence asks for more literals than are left, the - // sequence must be corrupted - if (seq.literal_length > IO_istream_len(&litstream)) { - CORRUPTION(); - } - - u8 *const write_ptr = IO_write_bytes(out, seq.literal_length); - const u8 *const read_ptr = - IO_read_bytes(&litstream, seq.literal_length); - // Copy literals to output - memcpy(write_ptr, read_ptr, seq.literal_length); - - total_output += seq.literal_length; + const u32 literals_size = copy_literals(seq, &litstream, out); + total_output += literals_size; } - size_t offset; // Offsets are special, we need to handle the repeat offsets @@ -1370,6 +1360,23 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, ctx->current_total_output = total_output; } + +static u32 copy_literals(const sequence_command_t seq, istream_t *litstream, + ostream_t *const out) { + // If the sequence asks for more literals than are left, the + // sequence must be corrupted + if (seq.literal_length > IO_istream_len(litstream)) { + CORRUPTION(); + } + + u8 *const write_ptr = IO_write_bytes(out, seq.literal_length); + const u8 *const read_ptr = + IO_read_bytes(litstream, seq.literal_length); + // Copy literals to output + memcpy(write_ptr, read_ptr, seq.literal_length); + + return seq.literal_length; +} /******* END SEQUENCE EXECUTION ***********************************************/ /******* OUTPUT SIZE COUNTING *************************************************/ From d3e57db0bd1b1e801986be02a34aeb9416892df2 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 14 Aug 2017 14:20:12 -0700 Subject: [PATCH 291/318] created separate function for offset computation --- doc/educational_decoder/zstd_decompress.c | 96 ++++++++++++----------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index 5b88e4c97..1a999fd92 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -356,6 +356,8 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, static u32 copy_literals(sequence_command_t seq, istream_t *litstream, ostream_t *const out); +static size_t compute_offset(sequence_command_t seq, u64 *const offset_hist); + /******* END ZSTD HELPER STRUCTS AND PROTOTYPES *******************************/ size_t ZSTD_decompress(void *const dst, const size_t dst_len, @@ -1263,51 +1265,7 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, const u32 literals_size = copy_literals(seq, &litstream, out); total_output += literals_size; } - size_t offset; - - // Offsets are special, we need to handle the repeat offsets - if (seq.offset <= 3) { - // "The first 3 values define a repeated offset and we will call - // them Repeated_Offset1, Repeated_Offset2, and Repeated_Offset3. - // They are sorted in recency order, with Repeated_Offset1 meaning - // 'most recent one'". - - // Use 0 indexing for the array - u32 idx = seq.offset - 1; - if (seq.literal_length == 0) { - // "There is an exception though, when current sequence's - // literals length is 0. In this case, repeated offsets are - // shifted by one, so Repeated_Offset1 becomes Repeated_Offset2, - // Repeated_Offset2 becomes Repeated_Offset3, and - // Repeated_Offset3 becomes Repeated_Offset1 - 1_byte." - idx++; - } - - if (idx == 0) { - offset = offset_hist[0]; - } else { - // If idx == 3 then literal length was 0 and the offset was 3, - // as per the exception listed above - offset = idx < 3 ? offset_hist[idx] : offset_hist[0] - 1; - - // If idx == 1 we don't need to modify offset_hist[2], since - // we're using the second-most recent code - if (idx > 1) { - offset_hist[2] = offset_hist[1]; - } - offset_hist[1] = offset_hist[0]; - offset_hist[0] = offset; - } - } else { - // When it's not a repeat offset: - // "if (Offset_Value > 3) offset = Offset_Value - 3;" - offset = seq.offset - 3; - - // Shift back history - offset_hist[2] = offset_hist[1]; - offset_hist[1] = offset_hist[0]; - offset_hist[0] = offset; - } + size_t offset = compute_offset(seq, offset_hist); size_t match_length = seq.match_length; @@ -1377,6 +1335,54 @@ static u32 copy_literals(const sequence_command_t seq, istream_t *litstream, return seq.literal_length; } + +static size_t compute_offset(sequence_command_t seq, u64 *const offset_hist) { + size_t offset; + // Offsets are special, we need to handle the repeat offsets + if (seq.offset <= 3) { + // "The first 3 values define a repeated offset and we will call + // them Repeated_Offset1, Repeated_Offset2, and Repeated_Offset3. + // They are sorted in recency order, with Repeated_Offset1 meaning + // 'most recent one'". + + // Use 0 indexing for the array + u32 idx = seq.offset - 1; + if (seq.literal_length == 0) { + // "There is an exception though, when current sequence's + // literals length is 0. In this case, repeated offsets are + // shifted by one, so Repeated_Offset1 becomes Repeated_Offset2, + // Repeated_Offset2 becomes Repeated_Offset3, and + // Repeated_Offset3 becomes Repeated_Offset1 - 1_byte." + idx++; + } + + if (idx == 0) { + offset = offset_hist[0]; + } else { + // If idx == 3 then literal length was 0 and the offset was 3, + // as per the exception listed above + offset = idx < 3 ? offset_hist[idx] : offset_hist[0] - 1; + + // If idx == 1 we don't need to modify offset_hist[2], since + // we're using the second-most recent code + if (idx > 1) { + offset_hist[2] = offset_hist[1]; + } + offset_hist[1] = offset_hist[0]; + offset_hist[0] = offset; + } + } else { + // When it's not a repeat offset: + // "if (Offset_Value > 3) offset = Offset_Value - 3;" + offset = seq.offset - 3; + + // Shift back history + offset_hist[2] = offset_hist[1]; + offset_hist[1] = offset_hist[0]; + offset_hist[0] = offset; + } + return offset; +} /******* END SEQUENCE EXECUTION ***********************************************/ /******* OUTPUT SIZE COUNTING *************************************************/ From 9d56c2127942ed89f8fab2128b0a25422735374f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 14 Aug 2017 15:06:03 -0700 Subject: [PATCH 292/318] added separate function for executing match copy command --- doc/educational_decoder/zstd_decompress.c | 78 +++++++++++++---------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index 1a999fd92..cbfeaa166 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -358,6 +358,10 @@ static u32 copy_literals(sequence_command_t seq, istream_t *litstream, static size_t compute_offset(sequence_command_t seq, u64 *const offset_hist); +static void execute_match_copy(frame_context_t *const ctx, size_t offset, + size_t match_length, size_t total_output, + ostream_t *const out); + /******* END ZSTD HELPER STRUCTS AND PROTOTYPES *******************************/ size_t ZSTD_decompress(void *const dst, const size_t dst_len, @@ -1269,41 +1273,9 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, size_t match_length = seq.match_length; - u8 *write_ptr = IO_write_bytes(out, match_length); - if (total_output <= ctx->header.window_size) { - // In this case offset might go back into the dictionary - if (offset > total_output + ctx->dict_content_len) { - // The offset goes beyond even the dictionary - CORRUPTION(); - } + execute_match_copy(ctx, offset, match_length, total_output, out); - if (offset > total_output) { - // "The rest of the dictionary is its content. The content act - // as a "past" in front of data to compress or decompress, so it - // can be referenced in sequence commands." - const size_t dict_copy = - MIN(offset - total_output, match_length); - const size_t dict_offset = - ctx->dict_content_len - (offset - total_output); - - memcpy(write_ptr, ctx->dict_content + dict_offset, dict_copy); - write_ptr += dict_copy; - match_length -= dict_copy; - } - } else if (offset > ctx->header.window_size) { - CORRUPTION(); - } - - // We must copy byte by byte because the match length might be larger - // than the offset - // ex: if the output so far was "abc", a command with offset=3 and - // match_length=6 would produce "abcabcabc" as the new output - for (size_t j = 0; j < match_length; j++) { - *write_ptr = *(write_ptr - offset); - write_ptr++; - } - - total_output += seq.match_length; + total_output += match_length; } // Copy any leftover literals @@ -1383,6 +1355,44 @@ static size_t compute_offset(sequence_command_t seq, u64 *const offset_hist) { } return offset; } + +static void execute_match_copy(frame_context_t *const ctx, size_t offset, + size_t match_length, size_t total_output, + ostream_t *const out) { + u8 *write_ptr = IO_write_bytes(out, match_length); + if (total_output <= ctx->header.window_size) { + // In this case offset might go back into the dictionary + if (offset > total_output + ctx->dict_content_len) { + // The offset goes beyond even the dictionary + CORRUPTION(); + } + + if (offset > total_output) { + // "The rest of the dictionary is its content. The content act + // as a "past" in front of data to compress or decompress, so it + // can be referenced in sequence commands." + const size_t dict_copy = + MIN(offset - total_output, match_length); + const size_t dict_offset = + ctx->dict_content_len - (offset - total_output); + + memcpy(write_ptr, ctx->dict_content + dict_offset, dict_copy); + write_ptr += dict_copy; + match_length -= dict_copy; + } + } else if (offset > ctx->header.window_size) { + CORRUPTION(); + } + + // We must copy byte by byte because the match length might be larger + // than the offset + // ex: if the output so far was "abc", a command with offset=3 and + // match_length=6 would produce "abcabcabc" as the new output + for (size_t j = 0; j < match_length; j++) { + *write_ptr = *(write_ptr - offset); + write_ptr++; + } +} /******* END SEQUENCE EXECUTION ***********************************************/ /******* OUTPUT SIZE COUNTING *************************************************/ From 6aebcfa0bca90b98ba5a4dd718c0d5e45f31a975 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 14 Aug 2017 15:11:01 -0700 Subject: [PATCH 293/318] added comments for new functions --- doc/educational_decoder/zstd_decompress.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index cbfeaa166..f2e9a95a3 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -353,11 +353,18 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, const sequence_command_t *const sequences, const size_t num_sequences); +// Copies literals and returns the total literal length that was copied static u32 copy_literals(sequence_command_t seq, istream_t *litstream, ostream_t *const out); +// Given an offset code from a sequence command (either an actual offset value +// or an index for previous offset), computes the correct offset and udpates +// the offset history static size_t compute_offset(sequence_command_t seq, u64 *const offset_hist); +// Given an offset, match length, and total output, as well as the frame +// context for the dictionary, determines if the dictionary is used and +// executes the copy operation static void execute_match_copy(frame_context_t *const ctx, size_t offset, size_t match_length, size_t total_output, ostream_t *const out); From 8d3f18af2c73523bc4751512eeb4dc88cd412b96 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 14 Aug 2017 17:51:51 -0700 Subject: [PATCH 294/318] renamed IO functions for clarity --- doc/educational_decoder/zstd_decompress.c | 44 +++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index f2e9a95a3..0fa30b274 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -94,10 +94,10 @@ static inline size_t IO_istream_len(const istream_t *const in); /// Advances the stream by `len` bytes, and returns a pointer to the chunk that /// was skipped. The stream must be byte aligned. -static inline const u8 *IO_read_bytes(istream_t *const in, size_t len); +static inline const u8 *IO_get_read_ptr(istream_t *const in, size_t len); /// Advances the stream by `len` bytes, and returns a pointer to the chunk that /// was skipped so it can be written to. -static inline u8 *IO_write_bytes(ostream_t *const out, size_t len); +static inline u8 *IO_get_write_ptr(ostream_t *const out, size_t len); /// Advance the inner state by `len` bytes. The stream must be byte aligned. static inline void IO_advance_input(istream_t *const in, size_t len); @@ -641,8 +641,8 @@ static void decompress_data(frame_context_t *const ctx, ostream_t *const out, case 0: { // "Raw_Block - this is an uncompressed block. Block_Size is the // number of bytes to read and copy." - const u8 *const read_ptr = IO_read_bytes(in, block_len); - u8 *const write_ptr = IO_write_bytes(out, block_len); + const u8 *const read_ptr = IO_get_read_ptr(in, block_len); + u8 *const write_ptr = IO_get_write_ptr(out, block_len); // Copy the raw data into the output memcpy(write_ptr, read_ptr, block_len); @@ -654,8 +654,8 @@ static void decompress_data(frame_context_t *const ctx, ostream_t *const out, // "RLE_Block - this is a single byte, repeated N times. In which // case, Block_Size is the size to regenerate, while the // "compressed" block is just 1 byte (the byte to repeat)." - const u8 *const read_ptr = IO_read_bytes(in, 1); - u8 *const write_ptr = IO_write_bytes(out, block_len); + const u8 *const read_ptr = IO_get_read_ptr(in, 1); + u8 *const write_ptr = IO_get_write_ptr(out, block_len); // Copy `block_len` copies of `read_ptr[0]` to the output memset(write_ptr, read_ptr[0], block_len); @@ -801,13 +801,13 @@ static size_t decode_literals_simple(istream_t *const in, u8 **const literals, switch (block_type) { case 0: { // "Raw_Literals_Block - Literals are stored uncompressed." - const u8 *const read_ptr = IO_read_bytes(in, size); + const u8 *const read_ptr = IO_get_read_ptr(in, size); memcpy(*literals, read_ptr, size); break; } case 1: { // "RLE_Literals_Block - Literals consist of a single byte value repeated N times." - const u8 *const read_ptr = IO_read_bytes(in, 1); + const u8 *const read_ptr = IO_get_read_ptr(in, 1); memset(*literals, read_ptr[0], size); break; } @@ -918,7 +918,7 @@ static void decode_huf_table(HUF_dtable *const dtable, istream_t *const in) { num_symbs = header - 127; const size_t bytes = (num_symbs + 1) / 2; - const u8 *const weight_src = IO_read_bytes(in, bytes); + const u8 *const weight_src = IO_get_read_ptr(in, bytes); for (int i = 0; i < num_symbs; i++) { // "They are encoded forward, 2 @@ -1126,7 +1126,7 @@ static void decompress_sequences(frame_context_t *const ctx, istream_t *in, } const size_t len = IO_istream_len(in); - const u8 *const src = IO_read_bytes(in, len); + const u8 *const src = IO_get_read_ptr(in, len); // "After writing the last bit containing information, the compressor writes // a single 1-bit and then fills the byte with 0-7 0 bits of padding." @@ -1231,7 +1231,7 @@ static void decode_seq_table(FSE_dtable *const table, istream_t *const in, } case seq_rle: { // "RLE_Mode : it's a single code, repeated Number_of_Sequences times." - const u8 symb = IO_read_bytes(in, 1)[0]; + const u8 symb = IO_get_read_ptr(in, 1)[0]; FSE_init_dtable_rle(table, symb); break; } @@ -1288,8 +1288,8 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, // Copy any leftover literals { size_t len = IO_istream_len(&litstream); - u8 *const write_ptr = IO_write_bytes(out, len); - const u8 *const read_ptr = IO_read_bytes(&litstream, len); + u8 *const write_ptr = IO_get_write_ptr(out, len); + const u8 *const read_ptr = IO_get_read_ptr(&litstream, len); memcpy(write_ptr, read_ptr, len); total_output += len; @@ -1306,9 +1306,9 @@ static u32 copy_literals(const sequence_command_t seq, istream_t *litstream, CORRUPTION(); } - u8 *const write_ptr = IO_write_bytes(out, seq.literal_length); + u8 *const write_ptr = IO_get_write_ptr(out, seq.literal_length); const u8 *const read_ptr = - IO_read_bytes(litstream, seq.literal_length); + IO_get_read_ptr(litstream, seq.literal_length); // Copy literals to output memcpy(write_ptr, read_ptr, seq.literal_length); @@ -1366,7 +1366,7 @@ static size_t compute_offset(sequence_command_t seq, u64 *const offset_hist) { static void execute_match_copy(frame_context_t *const ctx, size_t offset, size_t match_length, size_t total_output, ostream_t *const out) { - u8 *write_ptr = IO_write_bytes(out, match_length); + u8 *write_ptr = IO_get_write_ptr(out, match_length); if (total_output <= ctx->header.window_size) { // In this case offset might go back into the dictionary if (offset > total_output + ctx->dict_content_len) { @@ -1510,7 +1510,7 @@ static void init_dictionary_content(dictionary_t *const dict, BAD_ALLOC(); } - const u8 *const content = IO_read_bytes(in, dict->content_size); + const u8 *const content = IO_get_read_ptr(in, dict->content_size); memcpy(dict->content, content, dict->content_size); } @@ -1605,7 +1605,7 @@ static inline size_t IO_istream_len(const istream_t *const in) { /// Returns a pointer where `len` bytes can be read, and advances the internal /// state. The stream must be byte aligned. -static inline const u8 *IO_read_bytes(istream_t *const in, size_t len) { +static inline const u8 *IO_get_read_ptr(istream_t *const in, size_t len) { if (len > in->len) { INP_SIZE(); } @@ -1619,7 +1619,7 @@ static inline const u8 *IO_read_bytes(istream_t *const in, size_t len) { return ptr; } /// Returns a pointer to write `len` bytes to, and advances the internal state -static inline u8 *IO_write_bytes(ostream_t *const out, size_t len) { +static inline u8 *IO_get_write_ptr(ostream_t *const out, size_t len) { if (len > out->len) { OUT_SIZE(); } @@ -1658,7 +1658,7 @@ static inline istream_t IO_make_istream(const u8 *in, size_t len) { /// `in` must be byte aligned static inline istream_t IO_make_sub_istream(istream_t *const in, size_t len) { // Consume `len` bytes of the parent stream - const u8 *const ptr = IO_read_bytes(in, len); + const u8 *const ptr = IO_get_read_ptr(in, len); // Make a substream using the pointer to those `len` bytes return IO_make_istream(ptr, len); @@ -1762,7 +1762,7 @@ static size_t HUF_decompress_1stream(const HUF_dtable *const dtable, if (len == 0) { INP_SIZE(); } - const u8 *const src = IO_read_bytes(in, len); + const u8 *const src = IO_get_read_ptr(in, len); // "Each bitstream must be read backward, that is starting from the end down // to the beginning. Therefore it's necessary to know the size of each @@ -2013,7 +2013,7 @@ static size_t FSE_decompress_interleaved2(const FSE_dtable *const dtable, if (len == 0) { INP_SIZE(); } - const u8 *const src = IO_read_bytes(in, len); + const u8 *const src = IO_get_read_ptr(in, len); // "Each bitstream must be read backward, that is starting from the end down // to the beginning. Therefore it's necessary to know the size of each From 565e925eb7a48a4fe4cda899b00b27863e046ebb Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 14 Aug 2017 17:20:50 -0700 Subject: [PATCH 295/318] [libzstd] Fix FORCE_INLINE macro --- lib/common/compiler.h | 76 ++++++++++++++++++++++++++++++++ lib/common/fse_decompress.c | 24 +--------- lib/common/xxhash.c | 50 ++++++++++++--------- lib/common/zstd_internal.h | 33 +------------- lib/compress/fse_compress.c | 22 +-------- lib/compress/zstd_compress.c | 16 +++---- lib/compress/zstd_opt.h | 18 ++++---- lib/decompress/huf_decompress.c | 24 ++-------- lib/decompress/zstd_decompress.c | 17 ++----- 9 files changed, 132 insertions(+), 148 deletions(-) create mode 100644 lib/common/compiler.h diff --git a/lib/common/compiler.h b/lib/common/compiler.h new file mode 100644 index 000000000..69eb7b910 --- /dev/null +++ b/lib/common/compiler.h @@ -0,0 +1,76 @@ +#ifndef ZSTD_COMPILER_H +#define ZSTD_COMPILER_H + +/*-******************************************************* +* Compiler specifics +*********************************************************/ +/* force inlining */ +#if defined (__GNUC__) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# define INLINE_KEYWORD inline +#else +# define INLINE_KEYWORD +#endif + +#if defined(__GNUC__) +# define FORCE_INLINE_ATTR __attribute__((always_inline)) +#elif defined(_MSC_VER) +# define FORCE_INLINE_ATTR __forceinline +#else +# define FORCE_INLINE_ATTR +#endif + +/** + * FORCE_INLINE_TEMPLATE is used to define C "templates", which take constant + * parameters. They must be inlined for the compiler to elimininate the constant + * branches. + */ +#define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR +/** + * HINT_INLINE is used to help the compiler generate better code. It is *not* + * used for "templates", so it can be tweaked based on the compilers + * performance. + * + * gcc-4.8 and gcc-4.9 have been shown to benefit from leaving off the + * always_inline attribute. + * + * clang up to 5.0.0 (trunk) benefit tremendously from the always_inline + * attribute. + */ +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 && __GNUC__ < 5 +# define HINT_INLINE static INLINE_KEYWORD +#else +# define HINT_INLINE static INLINE_KEYWORD FORCE_INLINE_ATTR +#endif + +/* force no inlining */ +#ifdef _MSC_VER +# define FORCE_NOINLINE static __declspec(noinline) +#else +# ifdef __GNUC__ +# define FORCE_NOINLINE static __attribute__((__noinline__)) +# else +# define FORCE_NOINLINE static +# 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 */ +# define PREFETCH(ptr) _mm_prefetch((const char*)ptr, _MM_HINT_T0) +#elif defined(__GNUC__) +# define PREFETCH(ptr) __builtin_prefetch(ptr, 0, 0) +#else +# define PREFETCH(ptr) /* disabled */ +#endif + +/* disable warnings */ +#ifdef _MSC_VER /* Visual Studio */ +# include /* For Visual 2005 */ +# pragma warning(disable : 4100) /* disable: C4100: unreferenced formal parameter */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */ +# pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ +# pragma warning(disable : 4324) /* disable: C4324: padded structure */ +#endif + +#endif /* ZSTD_COMPILER_H */ diff --git a/lib/common/fse_decompress.c b/lib/common/fse_decompress.c index 8474a4c07..6bcc6b20a 100644 --- a/lib/common/fse_decompress.c +++ b/lib/common/fse_decompress.c @@ -33,33 +33,13 @@ ****************************************************************** */ -/* ************************************************************** -* Compiler specifics -****************************************************************/ -#ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline -# include /* For Visual 2005 */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ -#else -# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif -# else -# define FORCE_INLINE static -# endif /* __STDC_VERSION__ */ -#endif - - /* ************************************************************** * Includes ****************************************************************/ #include /* malloc, free, qsort */ #include /* memcpy, memset */ #include "bitstream.h" +#include "compiler.h" #define FSE_STATIC_LINKING_ONLY #include "fse.h" @@ -216,7 +196,7 @@ size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits) return 0; } -FORCE_INLINE size_t FSE_decompress_usingDTable_generic( +FORCE_INLINE_TEMPLATE size_t FSE_decompress_usingDTable_generic( void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt, const unsigned fast) diff --git a/lib/common/xxhash.c b/lib/common/xxhash.c index eb44222c5..9d9c0e963 100644 --- a/lib/common/xxhash.c +++ b/lib/common/xxhash.c @@ -113,19 +113,25 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcp /* ************************************* * Compiler Specific Options ***************************************/ -#ifdef _MSC_VER /* Visual Studio */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# define FORCE_INLINE static __forceinline +#if defined (__GNUC__) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# define INLINE_KEYWORD inline #else -# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif -# else -# define FORCE_INLINE static -# endif /* __STDC_VERSION__ */ +# define INLINE_KEYWORD +#endif + +#if defined(__GNUC__) +# define FORCE_INLINE_ATTR __attribute__((always_inline)) +#elif defined(_MSC_VER) +# define FORCE_INLINE_ATTR __forceinline +#else +# define FORCE_INLINE_ATTR +#endif + +#define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR + + +#ifdef _MSC_VER +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif @@ -248,7 +254,7 @@ typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; *****************************/ typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; -FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +FORCE_INLINE_TEMPLATE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) { if (align==XXH_unaligned) return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); @@ -256,7 +262,7 @@ FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_a return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); } -FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) +FORCE_INLINE_TEMPLATE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); } @@ -266,7 +272,7 @@ static U32 XXH_readBE32(const void* ptr) return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); } -FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +FORCE_INLINE_TEMPLATE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) { if (align==XXH_unaligned) return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); @@ -274,7 +280,7 @@ FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_a return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); } -FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) +FORCE_INLINE_TEMPLATE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) { return XXH_readLE64_align(ptr, endian, XXH_unaligned); } @@ -335,7 +341,7 @@ static U32 XXH32_round(U32 seed, U32 input) return seed; } -FORCE_INLINE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align) +FORCE_INLINE_TEMPLATE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align) { const BYTE* p = (const BYTE*)input; const BYTE* bEnd = p + len; @@ -435,7 +441,7 @@ static U64 XXH64_mergeRound(U64 acc, U64 val) return acc; } -FORCE_INLINE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align) +FORCE_INLINE_TEMPLATE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align) { const BYTE* p = (const BYTE*)input; const BYTE* const bEnd = p + len; @@ -584,7 +590,7 @@ XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long } -FORCE_INLINE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) +FORCE_INLINE_TEMPLATE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) { const BYTE* p = (const BYTE*)input; const BYTE* const bEnd = p + len; @@ -654,7 +660,7 @@ XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* -FORCE_INLINE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) +FORCE_INLINE_TEMPLATE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) { const BYTE * p = (const BYTE*)state->mem32; const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize; @@ -704,7 +710,7 @@ XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in) /* **** XXH64 **** */ -FORCE_INLINE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) +FORCE_INLINE_TEMPLATE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) { const BYTE* p = (const BYTE*)input; const BYTE* const bEnd = p + len; @@ -771,7 +777,7 @@ XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* -FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) +FORCE_INLINE_TEMPLATE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) { const BYTE * p = (const BYTE*)state->mem64; const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize; diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 1621bca61..66ea832e1 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -10,42 +10,11 @@ #ifndef ZSTD_CCOMMON_H_MODULE #define ZSTD_CCOMMON_H_MODULE -/*-******************************************************* -* Compiler specifics -*********************************************************/ -#ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline -# include /* For Visual 2005 */ -# pragma warning(disable : 4100) /* disable: C4100: unreferenced formal parameter */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */ -# pragma warning(disable : 4324) /* disable: C4324: padded structure */ -#else -# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif -# else -# define FORCE_INLINE static -# endif /* __STDC_VERSION__ */ -#endif - -#ifdef _MSC_VER -# define FORCE_NOINLINE static __declspec(noinline) -#else -# ifdef __GNUC__ -# define FORCE_NOINLINE static __attribute__((__noinline__)) -# else -# define FORCE_NOINLINE static -# endif -#endif - /*-************************************* * Dependencies ***************************************/ +#include "compiler.h" #include "mem.h" #include "error_private.h" #define ZSTD_STATIC_LINKING_ONLY diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index 3a03627cc..50a130250 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -32,27 +32,6 @@ - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ -/* ************************************************************** -* Compiler specifics -****************************************************************/ -#ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline -# include /* For Visual 2005 */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ -#else -# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif -# else -# define FORCE_INLINE static -# endif /* __STDC_VERSION__ */ -#endif - - /* ************************************************************** * Includes ****************************************************************/ @@ -60,6 +39,7 @@ #include /* memcpy, memset */ #include /* printf (debug) */ #include "bitstream.h" +#include "compiler.h" #define FSE_STATIC_LINKING_ONLY #include "fse.h" diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index a70a66684..0a7a98a96 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1483,7 +1483,7 @@ static void ZSTD_fillHashTable (ZSTD_CCtx* zc, const void* end, const U32 mls) } -FORCE_INLINE +FORCE_INLINE_TEMPLATE void ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, const void* src, size_t srcSize, const U32 mls) @@ -1726,7 +1726,7 @@ static void ZSTD_fillDoubleHashTable (ZSTD_CCtx* cctx, const void* end, const U3 } -FORCE_INLINE +FORCE_INLINE_TEMPLATE void ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx, const void* src, size_t srcSize, const U32 mls) @@ -2284,7 +2284,7 @@ static size_t ZSTD_BtFindBestMatch_selectMLS_extDict ( /* Update chains up to ip (excluded) Assumption : always within prefix (i.e. not within extDict) */ -FORCE_INLINE +FORCE_INLINE_TEMPLATE U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls) { U32* const hashTable = zc->hashTable; @@ -2308,7 +2308,7 @@ U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls) /* inlining is important to hardwire a hot branch (template emulation) */ -FORCE_INLINE +FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_generic ( ZSTD_CCtx* zc, /* Index table will be updated */ const BYTE* const ip, const BYTE* const iLimit, @@ -2360,7 +2360,7 @@ size_t ZSTD_HcFindBestMatch_generic ( } -FORCE_INLINE size_t ZSTD_HcFindBestMatch_selectMLS ( +FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_selectMLS ( ZSTD_CCtx* zc, const BYTE* ip, const BYTE* const iLimit, size_t* offsetPtr, @@ -2377,7 +2377,7 @@ FORCE_INLINE size_t ZSTD_HcFindBestMatch_selectMLS ( } -FORCE_INLINE size_t ZSTD_HcFindBestMatch_extDict_selectMLS ( +FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_extDict_selectMLS ( ZSTD_CCtx* zc, const BYTE* ip, const BYTE* const iLimit, size_t* offsetPtr, @@ -2397,7 +2397,7 @@ FORCE_INLINE size_t ZSTD_HcFindBestMatch_extDict_selectMLS ( /* ******************************* * Common parser - lazy strategy *********************************/ -FORCE_INLINE +FORCE_INLINE_TEMPLATE void ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx, const void* src, size_t srcSize, const U32 searchMethod, const U32 depth) @@ -2559,7 +2559,7 @@ static void ZSTD_compressBlock_greedy(ZSTD_CCtx* ctx, const void* src, size_t sr } -FORCE_INLINE +FORCE_INLINE_TEMPLATE void ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx, const void* src, size_t srcSize, const U32 searchMethod, const U32 depth) diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index 53e806eb7..e0af5bfa5 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -22,7 +22,7 @@ /*-************************************* * Price functions for optimal parser ***************************************/ -FORCE_INLINE void ZSTD_setLog2Prices(optState_t* optPtr) +static void ZSTD_setLog2Prices(optState_t* optPtr) { optPtr->log2matchLengthSum = ZSTD_highbit32(optPtr->matchLengthSum+1); optPtr->log2litLengthSum = ZSTD_highbit32(optPtr->litLengthSum+1); @@ -32,7 +32,7 @@ FORCE_INLINE void ZSTD_setLog2Prices(optState_t* optPtr) } -MEM_STATIC void ZSTD_rescaleFreqs(optState_t* optPtr, const BYTE* src, size_t srcSize) +static void ZSTD_rescaleFreqs(optState_t* optPtr, const BYTE* src, size_t srcSize) { unsigned u; @@ -96,7 +96,7 @@ MEM_STATIC void ZSTD_rescaleFreqs(optState_t* optPtr, const BYTE* src, size_t sr } -FORCE_INLINE U32 ZSTD_getLiteralPrice(optState_t* optPtr, U32 litLength, const BYTE* literals) +static U32 ZSTD_getLiteralPrice(optState_t* optPtr, U32 litLength, const BYTE* literals) { U32 price, u; @@ -137,7 +137,7 @@ FORCE_INLINE U32 ZSTD_getLiteralPrice(optState_t* optPtr, U32 litLength, const B } -FORCE_INLINE U32 ZSTD_getPrice(optState_t* optPtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength, const int ultra) +FORCE_INLINE_TEMPLATE U32 ZSTD_getPrice(optState_t* optPtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength, const int ultra) { /* offset */ U32 price; @@ -159,7 +159,7 @@ FORCE_INLINE U32 ZSTD_getPrice(optState_t* optPtr, U32 litLength, const BYTE* li } -MEM_STATIC void ZSTD_updatePrice(optState_t* optPtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength) +static void ZSTD_updatePrice(optState_t* optPtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength) { U32 u; @@ -203,7 +203,7 @@ MEM_STATIC void ZSTD_updatePrice(optState_t* optPtr, U32 litLength, const BYTE* /* function safe only for comparisons */ -MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length) +static U32 ZSTD_readMINMATCH(const void* memPtr, U32 length) { switch (length) { @@ -219,7 +219,7 @@ 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) */ -FORCE_INLINE +static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_CCtx* zc, const BYTE* ip) { U32* const hashTable3 = zc->hashTable3; @@ -412,7 +412,7 @@ static U32 ZSTD_BtGetAllMatches_selectMLS_extDict ( /*-******************************* * Optimal parser *********************************/ -FORCE_INLINE +FORCE_INLINE_TEMPLATE void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, const void* src, size_t srcSize, const int ultra) { @@ -662,7 +662,7 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ } -FORCE_INLINE +FORCE_INLINE_TEMPLATE void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, const void* src, size_t srcSize, const int ultra) { diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 0a47a3d74..6f6c3d4f0 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -32,30 +32,12 @@ - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ -/* ************************************************************** -* Compiler specifics -****************************************************************/ -#ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -#else -# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif -# else -# define FORCE_INLINE static -# endif /* __STDC_VERSION__ */ -#endif - - /* ************************************************************** * Dependencies ****************************************************************/ #include /* memcpy, memset */ #include "bitstream.h" /* BIT_* */ +#include "compiler.h" #include "fse.h" /* header compression */ #define HUF_STATIC_LINKING_ONLY #include "huf.h" @@ -180,7 +162,7 @@ static BYTE HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, con if (MEM_64bits()) \ HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) -FORCE_INLINE size_t HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog) +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; @@ -639,7 +621,7 @@ static U32 HUF_decodeLastSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DE if (MEM_64bits()) \ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) -FORCE_INLINE size_t HUF_decodeStreamX4(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, const HUF_DEltX4* const dt, const U32 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; diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 159b7b15b..db58a0092 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -53,15 +53,6 @@ # include "zstd_legacy.h" #endif -#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 */ -# define ZSTD_PREFETCH(ptr) _mm_prefetch((const char*)ptr, _MM_HINT_T0) -#elif defined(__GNUC__) -# define ZSTD_PREFETCH(ptr) __builtin_prefetch(ptr, 0, 0) -#else -# define ZSTD_PREFETCH(ptr) /* disabled */ -#endif - /*-************************************* * Errors @@ -953,7 +944,7 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState) } -FORCE_INLINE +HINT_INLINE size_t ZSTD_execSequence(BYTE* op, BYTE* const oend, seq_t sequence, const BYTE** litPtr, const BYTE* const litLimit, @@ -1097,7 +1088,7 @@ static size_t ZSTD_decompressSequences( } -FORCE_INLINE seq_t ZSTD_decodeSequenceLong_generic(seqState_t* seqState, int const longOffsets) +FORCE_INLINE_TEMPLATE seq_t ZSTD_decodeSequenceLong_generic(seqState_t* seqState, int const longOffsets) { seq_t seq; @@ -1197,7 +1188,7 @@ static seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, unsigned const window } } -FORCE_INLINE +HINT_INLINE size_t ZSTD_execSequenceLong(BYTE* op, BYTE* const oend, seq_t sequence, const BYTE** litPtr, const BYTE* const litLimit, @@ -1333,7 +1324,7 @@ static size_t ZSTD_decompressSequencesLong( seq_t const sequence = ZSTD_decodeSequenceLong(&seqState, windowSize32); size_t const oneSeqSize = ZSTD_execSequenceLong(op, oend, sequences[(seqNb-ADVANCED_SEQS) & STOSEQ_MASK], &litPtr, litEnd, base, vBase, dictEnd); if (ZSTD_isError(oneSeqSize)) return oneSeqSize; - ZSTD_PREFETCH(sequence.match); + PREFETCH(sequence.match); sequences[seqNb&STOSEQ_MASK] = sequence; op += oneSeqSize; } From 57e2df665155c094d02a7c811748d0f36bec208d Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 14 Aug 2017 22:43:36 -0700 Subject: [PATCH 296/318] [kernel] Update squashfs-tools patch --- ...0006-squashfs-tools-Add-zstd-support.patch | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch b/contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch index b38930fdc..ca638f264 100644 --- a/contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch +++ b/contrib/linux-kernel/0006-squashfs-tools-Add-zstd-support.patch @@ -1,7 +1,7 @@ -From cc08b43a31fed1289c2027d5090999da569457f1 Mon Sep 17 00:00:00 2001 +From 57a3cf95b276946559f9e044c7352c11303bb9c1 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 3 Aug 2017 17:47:03 -0700 -Subject: [PATCH v5] squashfs-tools: Add zstd support +Subject: [PATCH v6] squashfs-tools: Add zstd support This patch adds zstd support to squashfs-tools. It works with zstd versions >= 1.0.0. It was originally written by Sean Purcell. @@ -14,20 +14,23 @@ v4 -> v5: - Don't strip trailing whitespace of unreleated code - Make zstd_display_options() static - squashfs-tools/Makefile | 21 ++++ +v5 -> v6: +- Fix build instructions in Makefile + + squashfs-tools/Makefile | 20 ++++ squashfs-tools/compressor.c | 8 ++ squashfs-tools/squashfs_fs.h | 1 + squashfs-tools/zstd_wrapper.c | 254 ++++++++++++++++++++++++++++++++++++++++++ squashfs-tools/zstd_wrapper.h | 48 ++++++++ - 5 files changed, 332 insertions(+) + 5 files changed, 331 insertions(+) create mode 100644 squashfs-tools/zstd_wrapper.c create mode 100644 squashfs-tools/zstd_wrapper.h diff --git a/squashfs-tools/Makefile b/squashfs-tools/Makefile -index 52d2582..8e82e09 100644 +index 52d2582..22fc559 100644 --- a/squashfs-tools/Makefile +++ b/squashfs-tools/Makefile -@@ -75,6 +75,19 @@ GZIP_SUPPORT = 1 +@@ -75,6 +75,18 @@ GZIP_SUPPORT = 1 #LZMA_SUPPORT = 1 #LZMA_DIR = ../../../../LZMA/lzma465 @@ -38,16 +41,15 @@ index 52d2582..8e82e09 100644 +# ZSTD homepage: http://zstd.net +# ZSTD source repository: https://github.com/facebook/zstd +# -+# To build configure the tools using cmake to build shared libraries, -+# install and uncomment -+# the ZSTD_SUPPORT line below. ++# To build using the ZSTD library - install the library and uncomment the ++# ZSTD_SUPPORT line below. +# +#ZSTD_SUPPORT = 1 + ######## Specifying default compression ######## # # The next line specifies which compression algorithm is used by default -@@ -177,6 +190,14 @@ LIBS += -llz4 +@@ -177,6 +189,14 @@ LIBS += -llz4 COMPRESSORS += lz4 endif @@ -415,4 +417,4 @@ index 0000000..4fbef0a +}; +#endif -- -2.9.3 +2.9.5 From 07c6ff588ed1af5066bf0584b5013d48eda880fe Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 15 Aug 2017 11:23:28 -0700 Subject: [PATCH 297/318] [FSE][HUF] Inline error checks Caught by Clang's optimization remarks. --- lib/common/fse_decompress.c | 1 + lib/compress/fse_compress.c | 2 ++ lib/compress/huf_compress.c | 2 ++ lib/decompress/huf_decompress.c | 2 ++ 4 files changed, 7 insertions(+) diff --git a/lib/common/fse_decompress.c b/lib/common/fse_decompress.c index 8474a4c07..1a1977989 100644 --- a/lib/common/fse_decompress.c +++ b/lib/common/fse_decompress.c @@ -62,6 +62,7 @@ #include "bitstream.h" #define FSE_STATIC_LINKING_ONLY #include "fse.h" +#include "error_private.h" /* ************************************************************** diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index 3a03627cc..05ccc3fd5 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -62,11 +62,13 @@ #include "bitstream.h" #define FSE_STATIC_LINKING_ONLY #include "fse.h" +#include "error_private.h" /* ************************************************************** * Error Management ****************************************************************/ +#define FSE_isError ERR_isError #define FSE_STATIC_ASSERT(c) { enum { FSE_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 953cb5f21..2a47c1820 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -50,11 +50,13 @@ #include "fse.h" /* header compression */ #define HUF_STATIC_LINKING_ONLY #include "huf.h" +#include "error_private.h" /* ************************************************************** * Error Management ****************************************************************/ +#define HUF_isError ERR_isError #define HUF_STATIC_ASSERT(c) { enum { HUF_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ #define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return e #define CHECK_F(f) { CHECK_V_F(_var_err__, f); } diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 0a47a3d74..39530a64f 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -59,11 +59,13 @@ #include "fse.h" /* header compression */ #define HUF_STATIC_LINKING_ONLY #include "huf.h" +#include "error_private.h" /* ************************************************************** * Error Management ****************************************************************/ +#define HUF_isError ERR_isError #define HUF_STATIC_ASSERT(c) { enum { HUF_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ From 733ca51360d05a6927db69302b22b54ac8749c8e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 15 Aug 2017 10:14:38 -0700 Subject: [PATCH 298/318] Updating README.md --- contrib/adaptive-compression/README.md | 82 +++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/contrib/adaptive-compression/README.md b/contrib/adaptive-compression/README.md index fadb071f9..0929b16ba 100644 --- a/contrib/adaptive-compression/README.md +++ b/contrib/adaptive-compression/README.md @@ -1,16 +1,32 @@ -###Summary +### Summary -`adapt` is a new compression tool targeted at optimizing performance across network connections. The tool aims at sensing network speeds and adapting compression level based on network or pipe speeds. -In situations where the compression level does not appropriately match the network/pipe speed, the compression may be bottlenecking the entire pipeline or the files may not be compressed as much as they potentially could be, therefore losing efficiency. It also becomes quite impractical to manually measure and set compression level, therefore the tool does it for you. +`adapt` is a new compression tool targeted at optimizing performance across network connections and pipelines. The tool is aimed at sensing network speeds and adapting compression level based on network or pipe speeds. +In situations where the compression level does not appropriately match the network/pipe speed, compression may be bottlenecking the entire pipeline or the files may not be compressed as much as they potentially could be, therefore losing efficiency. It also becomes quite impractical to manually measure and set an optimalcompression level (which could potentially change over time). -###Using `adapt` +### Using `adapt` -In order to build and use the tool, you can simply run `make adapt` in the `adaptive-compression` directory under `contrib`. This will generate an executable available for use. +In order to build and use the tool, you can simply run `make adapt` in the `adaptive-compression` directory under `contrib`. This will generate an executable available for use. Another possible method of installation is running `make install`, which will create and install the binary as the command `zstd-adaptive`. -###Options +Similar to many other compression utilities, `zstd-adaptive` can be invoked by using the following format: + +`zstd-adaptive [options] [file(s)]` + +Supported options for the above format are described below. + +`zstd-adaptive` also supports reading from `stdin` and writing to `stdout`, which is potentially more useful. By default, if no files are given, `zstd-adaptive` reads from and writes to standard I/O. Therefore, you can simply insert it within a pipeline like so: + +`cat FILE | zstd-adaptive | ssh "cat - > tmp.zst"` + +If a file is provided, it is also possible to force writing to stdout using the `-c` flag like so: + +`zstd-adaptive -c FILE | ssh "cat - > tmp.zst"` + +Several options described below can be used to control the behavior of `zstd-adaptive`. More specifically, using the `-l#` and `-u#` flags will will set upper and lower bounds so that the compression level will always be within that range. The `-i#` flag can also be used to change the initial compression level. If an initial compression level is not provided, the initial compression level will be chosen such that it is within the appropriate range (it becomes equal to the lower bound). + +### Options `-oFILE` : write output to `FILE` -`-i#` : provide initial compression level +`-i#` : provide initial compression level (must within the appropriate bounds) `-h` : display help/information @@ -22,4 +38,54 @@ In order to build and use the tool, you can simply run `make adapt` in the `adap `-q` : quiet mode -- do not show progress bar or other information -###Benchmarking / Test results +`-l#` : set a lower bound on the compression level (default is 1) + +`-u#` : set an upper bound on the compression level (default is 22) +### Benchmarking / Test results +#### Artificial Tests +These artificial tests were run by using the `pv` command line utility in order to limit pipe speeds (25 MB/s read and 5 MB/s write limits were chosen to mimic severe throughput constraints). A 40 GB backup file was sent through a pipeline, compressed, and written out to a file. Compression time, size, and ratio were computed. Data for `zstd -15` was excluded from these tests because the test runs quite long. + + + + +
      25 MB/s read limit
      + +| Compressor Name | Ratio | Compressed Size | Compression Time | +|:----------------|------:|----------------:|-----------------:| +| zstd -3 | 2.108 | 20.718 GB | 29m 48.530s | +| zstd-adaptive | 2.230 | 19.581 GB | 29m 48.798s | + +
      + + + + +
      5 MB/s write limit
      + +| Compressor Name | Ratio | Compressed Size | Compression Time | +|:----------------|------:|----------------:|-----------------:| +| zstd -3 | 2.108 | 20.718 GB | 1h 10m 43.076s | +| zstd-adaptive | 2.249 | 19.412 GB | 1h 06m 15.577s | + +
      + +The commands used for this test generally followed the form: + +`cat FILE | pv -L 25m -q | COMPRESSION | pv -q > tmp.zst # impose 25 MB/s read limit` + +`cat FILE | pv -q | COMPRESSION | pv -L 5m -q > tmp.zst # impose 5 MB/s write limit` + +#### SSH Tests + +The following tests were performed by piping a relatively large backup file (approximately 80 GB) through compression and over SSH to be stored on a server. The test data includes statistics for time and compressed size on `zstd` at several compression levels, as well as `zstd-adaptive`. The data highlights the potential advantages that `zstd-adaptive` has over using a low static compression level and the negative imapcts that using an excessively high static compression level can have on +pipe throughput. + +| Compressor Name | Ratio | Compressed Size | Compression Time | +|:----------------|------:|----------------:|-----------------:| +| zstd -3 | 2.212 | 32.426 GB | 1h 17m 59.756s | +| zstd -15 | 2.374 | 30.213 GB | 2h 56m 59.441s | +| zstd-adaptive | 2.315 | 30.993 GB | 1h 18m 52.860s | + +The commands used for this test generally followed the form: + +`cat FILE | COMPRESSION | ssh dev "cat - > tmp.zst"` From bef5eda8d9d9179af6165eaff512bc843cc96fc2 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 16 Aug 2017 11:11:52 -0700 Subject: [PATCH 299/318] const vars, change copy_literals() to only take size_t literal_length --- doc/educational_decoder/harness.c | 2 +- doc/educational_decoder/zstd_decompress.c | 28 +++++++++++------------ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/doc/educational_decoder/harness.c b/doc/educational_decoder/harness.c index 43d6df7fc..982e066e2 100644 --- a/doc/educational_decoder/harness.c +++ b/doc/educational_decoder/harness.c @@ -106,7 +106,7 @@ int main(int argc, char **argv) { return 1; } - dictionary_t* parsed_dict = create_dictionary(); + dictionary_t* const parsed_dict = create_dictionary(); if (dict) { parse_dictionary(parsed_dict, dict, dict_size); } diff --git a/doc/educational_decoder/zstd_decompress.c b/doc/educational_decoder/zstd_decompress.c index 0fa30b274..af10db528 100644 --- a/doc/educational_decoder/zstd_decompress.c +++ b/doc/educational_decoder/zstd_decompress.c @@ -354,7 +354,7 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, const size_t num_sequences); // Copies literals and returns the total literal length that was copied -static u32 copy_literals(sequence_command_t seq, istream_t *litstream, +static u32 copy_literals(const size_t seq, istream_t *litstream, ostream_t *const out); // Given an offset code from a sequence command (either an actual offset value @@ -1273,12 +1273,13 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, for (size_t i = 0; i < num_sequences; i++) { const sequence_command_t seq = sequences[i]; { - const u32 literals_size = copy_literals(seq, &litstream, out); + const u32 literals_size = copy_literals(seq.literal_length, &litstream, out); total_output += literals_size; } - size_t offset = compute_offset(seq, offset_hist); - size_t match_length = seq.match_length; + size_t const offset = compute_offset(seq, offset_hist); + + size_t const match_length = seq.match_length; execute_match_copy(ctx, offset, match_length, total_output, out); @@ -1288,31 +1289,28 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out, // Copy any leftover literals { size_t len = IO_istream_len(&litstream); - u8 *const write_ptr = IO_get_write_ptr(out, len); - const u8 *const read_ptr = IO_get_read_ptr(&litstream, len); - memcpy(write_ptr, read_ptr, len); - + copy_literals(len, &litstream, out); total_output += len; } ctx->current_total_output = total_output; } -static u32 copy_literals(const sequence_command_t seq, istream_t *litstream, +static u32 copy_literals(const size_t literal_length, istream_t *litstream, ostream_t *const out) { // If the sequence asks for more literals than are left, the // sequence must be corrupted - if (seq.literal_length > IO_istream_len(litstream)) { - CORRUPTION(); + if (literal_length > IO_istream_len(litstream)) { + CORRUPTION(); } - u8 *const write_ptr = IO_get_write_ptr(out, seq.literal_length); + u8 *const write_ptr = IO_get_write_ptr(out, literal_length); const u8 *const read_ptr = - IO_get_read_ptr(litstream, seq.literal_length); + IO_get_read_ptr(litstream, literal_length); // Copy literals to output - memcpy(write_ptr, read_ptr, seq.literal_length); + memcpy(write_ptr, read_ptr, literal_length); - return seq.literal_length; + return literal_length; } static size_t compute_offset(sequence_command_t seq, u64 *const offset_hist) { From 3f54d788e906279f1e13aba6a43f67c2c6d7b83c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 18 Aug 2017 15:15:31 -0700 Subject: [PATCH 300/318] removed --list from cli help (-h), reported by Agostino Sarubbo (@asarubbo) (#800) redundant with shorter -l. both -l and --list do the same thing, and are documented in man page. --- programs/zstdcli.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index b1268c1f3..6817ebc45 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -88,12 +88,12 @@ static FILE* g_displayOut; **************************************/ static int usage(const char* programName) { - DISPLAY( "Usage :\n"); - DISPLAY( " %s [args] [FILE(s)] [-o file]\n", programName); + DISPLAY( "Usage : \n"); + DISPLAY( " %s [args] [FILE(s)] [-o file] \n", programName); DISPLAY( "\n"); - DISPLAY( "FILE : a filename\n"); + DISPLAY( "FILE : a filename \n"); DISPLAY( " with no FILE, or when FILE is - , read standard input\n"); - DISPLAY( "Arguments :\n"); + DISPLAY( "Arguments : \n"); #ifndef ZSTD_NOCOMPRESS DISPLAY( " -# : # compression level (1-%d, default:%d) \n", ZSTDCLI_CLEVEL_MAX, ZSTDCLI_CLEVEL_DEFAULT); #endif @@ -105,7 +105,7 @@ static int usage(const char* programName) DISPLAY( " -f : overwrite output without prompting and (de)compress links \n"); DISPLAY( "--rm : remove source file(s) after successful de/compression \n"); DISPLAY( " -k : preserve source file(s) (default) \n"); - DISPLAY( " -h/-H : display help/long help and exit\n"); + DISPLAY( " -h/-H : display help/long help and exit \n"); return 0; } @@ -114,12 +114,12 @@ static int usage_advanced(const char* programName) DISPLAY(WELCOME_MESSAGE); usage(programName); DISPLAY( "\n"); - DISPLAY( "Advanced arguments :\n"); - DISPLAY( " -V : display Version number and exit\n"); + DISPLAY( "Advanced arguments : \n"); + DISPLAY( " -V : display Version number and exit \n"); DISPLAY( " -v : verbose mode; specify multiple times to increase verbosity\n"); DISPLAY( " -q : suppress warnings; specify twice to suppress errors too\n"); DISPLAY( " -c : force write to standard output, even if it is the console\n"); - DISPLAY( " -l : print information about zstd compressed files.\n"); + 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()); #ifdef ZSTD_MULTITHREAD @@ -151,11 +151,10 @@ static int usage_advanced(const char* programName) #endif #endif DISPLAY( " -M# : Set a memory usage limit for decompression \n"); - DISPLAY( "--list : list information about a zstd compressed file \n"); DISPLAY( "-- : All arguments after \"--\" are treated as files \n"); #ifndef ZSTD_NODICT DISPLAY( "\n"); - DISPLAY( "Dictionary builder :\n"); + DISPLAY( "Dictionary builder : \n"); DISPLAY( "--train ## : create a dictionary from a training set of files \n"); 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); @@ -165,12 +164,12 @@ static int usage_advanced(const char* programName) #endif #ifndef ZSTD_NOBENCH DISPLAY( "\n"); - DISPLAY( "Benchmark arguments :\n"); + DISPLAY( "Benchmark arguments : \n"); DISPLAY( " -b# : benchmark file(s), using # compression level (default : 1) \n"); DISPLAY( " -e# : test all compression levels from -bX to # (default: 1)\n"); - DISPLAY( " -i# : minimum evaluation time in seconds (default : 3s)\n"); + DISPLAY( " -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"); + DISPLAY( "--priority=rt : set process priority to real-time \n"); #endif return 0; } From c523c93b262daf0e94af6b71feff68758c13cfe2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 18 Aug 2017 15:57:53 -0700 Subject: [PATCH 301/318] improved and fixed --list command, original patches by @ib (#772) accepts all skippable frame identifiers. display in MB or KB, depending on frame size. fixed combination of skippable and zstd frames. --- NEWS | 1 + programs/fileio.c | 95 ++++++++++++++++++++--------------------------- 2 files changed, 42 insertions(+), 54 deletions(-) diff --git a/NEWS b/NEWS index b3c2613fe..641551d9f 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ v1.3.1 perf: substantially decreased memory usage in Multi-threading mode, thanks to reports by Tino Reichardt perf: Multi-threading supports up to 256 threads. Cap at 256 when more are requested (#760) +cli : improved and fixed --list command, by @ib (#772) build: fix Visual compilation for non x86/x64 targets, reported by Greg Slazinski (#718) API exp : breaking change : ZSTD_getframeHeader() provides more information API exp : breaking change : pinned down values of error codes diff --git a/programs/fileio.c b/programs/fileio.c index e729e37ec..e6fa61f62 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -947,6 +947,7 @@ static int getFileInfo(fileInfo_t* info, const char* inFileName){ return 3; } info->compressedSize = (unsigned long long)UTIL_getFileSize(inFileName); + /* begin analyzing frame */ for ( ; ; ) { BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; @@ -966,37 +967,31 @@ static int getFileInfo(fileInfo_t* info, const char* inFileName){ break; } } - { - U32 const magicNumber = MEM_readLE32(headerBuffer); + { U32 const magicNumber = MEM_readLE32(headerBuffer); + /* Zstandard frame */ if (magicNumber == ZSTD_MAGICNUMBER) { U64 const frameContentSize = ZSTD_getFrameContentSize(headerBuffer, numBytesRead); if (frameContentSize == ZSTD_CONTENTSIZE_ERROR || frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN) { info->decompUnavailable = 1; - } - else { + } else { info->decompressedSize += frameContentSize; } - { - /* move to the end of the frame header */ - size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead); + /* move to the end of the frame header */ + { size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead); if (ZSTD_isError(headerSize)) { DISPLAY("Error: could not determine frame header size\n"); detectError = 1; break; } - { - int const ret = fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR); + { int const ret = fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR); if (ret != 0) { DISPLAY("Error: could not move to end of frame header\n"); detectError = 1; break; - } - } - } + } } } /* skip the rest of the blocks in the frame */ - { - int lastBlock = 0; + { int lastBlock = 0; do { BYTE blockHeaderBuffer[3]; U32 blockHeader; @@ -1009,24 +1004,20 @@ static int getFileInfo(fileInfo_t* info, const char* inFileName){ } blockHeader = MEM_readLE24(blockHeaderBuffer); lastBlock = blockHeader & 1; - blockSize = blockHeader >> 3; - { - int const ret = fseek(srcFile, blockSize, SEEK_CUR); + blockSize = blockHeader >> 3; /* Warning : does not work when block is RLE type */ + { int const ret = fseek(srcFile, blockSize, SEEK_CUR); if (ret != 0) { DISPLAY("Error: could not skip to end of block\n"); detectError = 1; break; - } - } + } } } while (lastBlock != 1); - if (detectError) { - break; - } + if (detectError) break; } - { - /* check if checksum is used */ - BYTE const frameHeaderDescriptor = headerBuffer[4]; + + /* check if checksum is used */ + { BYTE const frameHeaderDescriptor = headerBuffer[4]; int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2; if (contentChecksumFlag) { int const ret = fseek(srcFile, 4, SEEK_CUR); @@ -1035,64 +1026,60 @@ static int getFileInfo(fileInfo_t* info, const char* inFileName){ DISPLAY("Error: could not skip past checksum\n"); detectError = 1; break; - } - } - } + } } } info->numActualFrames++; } - else if (magicNumber == ZSTD_MAGIC_SKIPPABLE_START) { - BYTE frameSizeBuffer[4]; - size_t const readBytes = fread(frameSizeBuffer, 1, 4, srcFile); - if (readBytes != 4) { - DISPLAY("There was an error reading skippable frame size"); + /* Skippable frame */ + else if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { + U32 const frameSize = MEM_readLE32(headerBuffer + 4); + long const seek = -numBytesRead + 8 + frameSize; + int const ret = LONG_SEEK(srcFile, seek, SEEK_CUR); + if (ret != 0) { + DISPLAY("Error: could not find end of skippable frame\n"); detectError = 1; break; } - { - U32 const frameSize = MEM_readLE32(frameSizeBuffer); - int const ret = LONG_SEEK(srcFile, frameSize, SEEK_CUR); - if (ret != 0) { - DISPLAY("Error: could not find end of skippable frame\n"); - detectError = 1; - break; - } - } info->numSkippableFrames++; } + /* unknown content */ else { detectError = 2; break; } } - } + } /* end analyzing frame */ fclose(srcFile); return detectError; } static void displayInfo(const char* inFileName, fileInfo_t* info, int displayLevel){ - double const compressedSizeMB = (double)info->compressedSize/(1 MB); - double const decompressedSizeMB = (double)info->decompressedSize/(1 MB); + unsigned const unit = info->compressedSize < (1 MB) ? (1 KB) : (1 MB); + const char* const unitStr = info->compressedSize < (1 MB) ? "KB" : "MB"; + double const compressedSizeUnit = (double)info->compressedSize / unit; + double const decompressedSizeUnit = (double)info->decompressedSize / unit; double const ratio = (info->compressedSize == 0) ? 0 : ((double)info->decompressedSize)/info->compressedSize; const char* const checkString = (info->usesCheck ? "XXH64" : "None"); if (displayLevel <= 2) { if (!info->decompUnavailable) { DISPLAYOUT("Skippable Non-Skippable Compressed Uncompressed Ratio Check Filename\n"); - DISPLAYOUT("%9d %13d %7.2f MB %9.2f MB %5.3f %5s %s\n", - info->numSkippableFrames, info->numActualFrames, compressedSizeMB, decompressedSizeMB, + DISPLAYOUT("%9d %13d %7.2f %2s %9.2f %2s %5.3f %5s %s\n", + info->numSkippableFrames, info->numActualFrames, + compressedSizeUnit, unitStr, decompressedSizeUnit, unitStr, ratio, checkString, inFileName); - } - else { + } else { DISPLAYOUT("Skippable Non-Skippable Compressed Check Filename\n"); DISPLAYOUT("%9d %13d %7.2f MB %5s %s\n", - info->numSkippableFrames, info->numActualFrames, compressedSizeMB, checkString, inFileName); + info->numSkippableFrames, info->numActualFrames, + compressedSizeUnit, checkString, inFileName); } - } - else{ + } else { DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames); DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames); - DISPLAYOUT("Compressed Size: %.2f MB (%llu B)\n", compressedSizeMB, info->compressedSize); + DISPLAYOUT("Compressed Size: %.2f %2s (%llu B)\n", + compressedSizeUnit, unitStr, info->compressedSize); if (!info->decompUnavailable) { - DISPLAYOUT("Decompressed Size: %.2f MB (%llu B)\n", decompressedSizeMB, info->decompressedSize); + DISPLAYOUT("Decompressed Size: %.2f %2s (%llu B)\n", + decompressedSizeUnit, unitStr, info->decompressedSize); DISPLAYOUT("Ratio: %.4f\n", ratio); } DISPLAYOUT("Check: %s\n", checkString); From 88d2f72df97c95ef61ede9d620cabe49afb4c968 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 18 Aug 2017 16:18:20 -0700 Subject: [PATCH 302/318] fixed --list command in presence of special blocks block type RLE is special, compressed size is always 1. block type 3 is "reserved", aka not supported. --- programs/fileio.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index e6fa61f62..3db6af734 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -994,23 +994,29 @@ static int getFileInfo(fileInfo_t* info, const char* inFileName){ { int lastBlock = 0; do { BYTE blockHeaderBuffer[3]; - U32 blockHeader; - int blockSize; size_t const readBytes = fread(blockHeaderBuffer, 1, 3, srcFile); if (readBytes != 3) { DISPLAY("There was a problem reading the block header\n"); detectError = 1; break; } - blockHeader = MEM_readLE24(blockHeaderBuffer); - lastBlock = blockHeader & 1; - blockSize = blockHeader >> 3; /* Warning : does not work when block is RLE type */ - { int const ret = fseek(srcFile, blockSize, SEEK_CUR); - if (ret != 0) { - DISPLAY("Error: could not skip to end of block\n"); + { U32 const blockHeader = MEM_readLE24(blockHeaderBuffer); + U32 const blockTypeID = (blockHeader >> 1) & 3; + U32 const isRLE = (blockTypeID == 1); + U32 const isWrongBlock = (blockTypeID == 3); + long const blockSize = isRLE ? 1 : (long)(blockHeader >> 3); + if (isWrongBlock) { + DISPLAY("Error: unsupported block type \n"); detectError = 1; break; - } } + } + lastBlock = blockHeader & 1; + { int const ret = fseek(srcFile, blockSize, SEEK_CUR); + if (ret != 0) { + DISPLAY("Error: could not skip to end of block\n"); + detectError = 1; + break; + } } } } while (lastBlock != 1); if (detectError) break; From 4f73b3b55d83b082aad180816a5d336eff33a243 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 18 Aug 2017 16:32:08 -0700 Subject: [PATCH 303/318] added GPLv2 license and removed PATENTS clause --- CONTRIBUTING.md | 2 +- COPYING | 339 ++++++++++++++++++++++++++++++++++++++++++++++++ PATENTS | 33 ----- README.md | 4 +- 4 files changed, 342 insertions(+), 36 deletions(-) create mode 100644 COPYING delete mode 100644 PATENTS diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index edf5b7f47..dd013f808 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,4 +39,4 @@ outlined on that page and do not file a public issue. ## License By contributing to Zstandard, you agree that your contributions will be licensed -under the [LICENSE](LICENSE) file in the root directory of this source tree. +under both the [LICENSE](LICENSE) file and the [COPYING](COPYING) file in the root directory of this source tree. diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..ecbc05937 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. \ No newline at end of file diff --git a/PATENTS b/PATENTS deleted file mode 100644 index 15b4a2ea5..000000000 --- a/PATENTS +++ /dev/null @@ -1,33 +0,0 @@ -Additional Grant of Patent Rights Version 2 - -"Software" means the Zstandard software distributed by Facebook, Inc. - -Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software -("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable -(subject to the termination provision below) license under any Necessary -Claims, to make, have made, use, sell, offer to sell, import, and otherwise -transfer the Software. For avoidance of doubt, no license is granted under -Facebook’s rights in any patent claims that are infringed by (i) modifications -to the Software made by you or any third party or (ii) the Software in -combination with any software or other technology. - -The license granted hereunder will terminate, automatically and without notice, -if you (or any of your subsidiaries, corporate affiliates or agents) initiate -directly or indirectly, or take a direct financial interest in, any Patent -Assertion: (i) against Facebook or any of its subsidiaries or corporate -affiliates, (ii) against any party if such Patent Assertion arises in whole or -in part from any software, technology, product or service of Facebook or any of -its subsidiaries or corporate affiliates, or (iii) against any party relating -to the Software. Notwithstanding the foregoing, if Facebook or any of its -subsidiaries or corporate affiliates files a lawsuit alleging patent -infringement against you in the first instance, and you respond by filing a -patent infringement counterclaim in that lawsuit against that party that is -unrelated to the Software, the license granted hereunder will not terminate -under section (i) of this paragraph due to such counterclaim. - -A "Necessary Claim" is a claim of a patent owned by Facebook that is -necessarily infringed by the Software standing alone. - -A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, -or contributory infringement or inducement to infringe any patent, including a -cross-claim or counterclaim. diff --git a/README.md b/README.md index f37be4542..377ae0843 100644 --- a/README.md +++ b/README.md @@ -134,12 +134,12 @@ Going into `build` directory, you will find additional possibilities : ### Status -Zstandard is currently deployed within Facebook. It is used daily to compress and decompress very large amounts of data in multiple formats and use cases. +Zstandard is currently deployed within Facebook. It is used continuously to compress large amounts of data in multiple formats and use cases. Zstandard is considered safe for production environments. ### License -Zstandard is [BSD-licensed](LICENSE). We also provide an [additional patent grant](PATENTS). +Zstandard is dual-licensed under [BSD](LICENSE) and [GPLv2](COPYING). ### Contributing From 32fb407c9da004dc61756bfac565b712ef26c62a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 18 Aug 2017 16:52:05 -0700 Subject: [PATCH 304/318] updated a bunch of headers for the new license --- examples/dictionary_compression.c | 9 +++++---- examples/dictionary_decompression.c | 9 +++++---- examples/multiple_streaming_compression.c | 9 +++++---- examples/simple_compression.c | 9 +++++---- examples/simple_decompression.c | 9 +++++---- examples/streaming_compression.c | 9 +++++---- examples/streaming_decompression.c | 9 +++++---- lib/common/compiler.h | 9 +++++++++ lib/common/error_private.c | 8 ++++---- lib/common/error_private.h | 8 ++++---- lib/common/mem.h | 8 ++++---- lib/common/pool.c | 10 +++++----- lib/common/pool.h | 11 ++++++----- lib/common/threading.h | 1 - lib/common/zstd_common.c | 8 ++++---- lib/common/zstd_errors.h | 8 ++++---- lib/common/zstd_internal.h | 8 ++++---- lib/compress/zstd_compress.c | 8 ++++---- lib/compress/zstd_opt.h | 10 +++++----- lib/compress/zstdmt_compress.c | 8 ++++---- lib/compress/zstdmt_compress.h | 8 ++++---- lib/decompress/zstd_decompress.c | 8 ++++---- lib/deprecated/zbuff.h | 8 ++++---- lib/deprecated/zbuff_common.c | 9 ++++----- lib/deprecated/zbuff_compress.c | 8 ++++---- lib/deprecated/zbuff_decompress.c | 8 ++++---- lib/dictBuilder/cover.c | 8 ++++---- lib/dictBuilder/zdict.c | 8 ++++---- lib/dictBuilder/zdict.h | 8 ++++---- lib/legacy/zstd_legacy.h | 8 ++++---- lib/legacy/zstd_v01.c | 8 ++++---- lib/legacy/zstd_v01.h | 8 ++++---- lib/legacy/zstd_v02.c | 8 ++++---- lib/legacy/zstd_v02.h | 8 ++++---- lib/legacy/zstd_v03.c | 8 ++++---- lib/legacy/zstd_v03.h | 8 ++++---- lib/legacy/zstd_v04.c | 8 ++++---- lib/legacy/zstd_v04.h | 8 ++++---- lib/legacy/zstd_v05.c | 8 ++++---- lib/legacy/zstd_v05.h | 8 ++++---- lib/legacy/zstd_v06.c | 8 ++++---- lib/legacy/zstd_v06.h | 8 ++++---- lib/legacy/zstd_v07.c | 8 ++++---- lib/legacy/zstd_v07.h | 8 ++++---- lib/zstd.h | 7 +++---- programs/bench.c | 8 ++++---- programs/bench.h | 8 ++++---- programs/datagen.c | 8 ++++---- programs/datagen.h | 10 ++++++---- programs/dibio.c | 8 ++++---- programs/dibio.h | 8 ++++---- programs/fileio.c | 8 ++++---- programs/fileio.h | 8 ++++---- programs/platform.h | 12 +++++------- programs/util.h | 12 +++++------- programs/zstdcli.c | 8 ++++---- tests/datagencli.c | 8 ++++---- tests/decodecorpus.c | 10 +++++----- tests/fullbench.c | 8 ++++---- tests/fuzzer.c | 8 ++++---- tests/invalidDictionaries.c | 9 +++++++++ tests/legacy.c | 11 +++++------ tests/longmatch.c | 10 ++++++++++ tests/namespaceTest.c | 8 ++++---- tests/paramgrill.c | 8 ++++---- tests/poolTests.c | 10 ++++++++++ tests/roundTripCrash.c | 8 ++++---- tests/symbols.c | 10 ++++++++++ tests/zbufftest.c | 8 ++++---- tests/zstreamtest.c | 8 ++++---- zlibWrapper/examples/zwrapbench.c | 10 +++++----- zlibWrapper/gzcompatibility.h | 16 ++++++++-------- zlibWrapper/gzlib.c | 2 +- zlibWrapper/gzread.c | 4 ++-- zlibWrapper/gzwrite.c | 4 ++-- zlibWrapper/zstd_zlibwrapper.c | 10 +++++----- zlibWrapper/zstd_zlibwrapper.h | 18 +++++++++--------- 77 files changed, 352 insertions(+), 302 deletions(-) diff --git a/examples/dictionary_compression.c b/examples/dictionary_compression.c index adcc3b4d5..17acec98d 100644 --- a/examples/dictionary_compression.c +++ b/examples/dictionary_compression.c @@ -1,9 +1,10 @@ -/** - * Copyright 2016-present, Yann Collet, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the license found in the - * LICENSE-examples file in the root directory of this source tree. + * 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). */ diff --git a/examples/dictionary_decompression.c b/examples/dictionary_decompression.c index ef739c189..345c968c3 100644 --- a/examples/dictionary_decompression.c +++ b/examples/dictionary_decompression.c @@ -1,9 +1,10 @@ -/** - * Copyright 2016-present, Yann Collet, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the license found in the - * LICENSE-examples file in the root directory of this source tree. + * 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). */ diff --git a/examples/multiple_streaming_compression.c b/examples/multiple_streaming_compression.c index 61699104c..7bfa133ee 100644 --- a/examples/multiple_streaming_compression.c +++ b/examples/multiple_streaming_compression.c @@ -1,9 +1,10 @@ -/** - * Copyright 2016-present, Yann Collet, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the license found in the - * LICENSE-examples file in the root directory of this source tree. + * 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). */ diff --git a/examples/simple_compression.c b/examples/simple_compression.c index ab1131475..95853faa6 100644 --- a/examples/simple_compression.c +++ b/examples/simple_compression.c @@ -1,9 +1,10 @@ -/** - * Copyright 2016-present, Yann Collet, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the license found in the - * LICENSE-examples file in the root directory of this source tree. + * 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). */ diff --git a/examples/simple_decompression.c b/examples/simple_decompression.c index 4b7ea59e5..9e9fcc9ed 100644 --- a/examples/simple_decompression.c +++ b/examples/simple_decompression.c @@ -1,9 +1,10 @@ -/** - * Copyright 2016-present, Yann Collet, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the license found in the - * LICENSE-examples file in the root directory of this source tree. + * 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). */ #include // malloc, exit diff --git a/examples/streaming_compression.c b/examples/streaming_compression.c index 24ad15bd6..ac7ee7687 100644 --- a/examples/streaming_compression.c +++ b/examples/streaming_compression.c @@ -1,9 +1,10 @@ -/** - * Copyright 2016-present, Yann Collet, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the license found in the - * LICENSE-examples file in the root directory of this source tree. + * 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). */ diff --git a/examples/streaming_decompression.c b/examples/streaming_decompression.c index bb2d80987..76dd85169 100644 --- a/examples/streaming_decompression.c +++ b/examples/streaming_decompression.c @@ -1,9 +1,10 @@ -/** - * Copyright 2016-present, Yann Collet, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the license found in the - * LICENSE-examples file in the root directory of this source tree. + * 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). */ diff --git a/lib/common/compiler.h b/lib/common/compiler.h index 69eb7b910..d7225c443 100644 --- a/lib/common/compiler.h +++ b/lib/common/compiler.h @@ -1,3 +1,12 @@ +/* + * 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). + */ + #ifndef ZSTD_COMPILER_H #define ZSTD_COMPILER_H diff --git a/lib/common/error_private.c b/lib/common/error_private.c index c3a386214..b5b14b509 100644 --- a/lib/common/error_private.c +++ b/lib/common/error_private.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ /* The purpose of this file is to have a single list of error strings embedded in binary */ diff --git a/lib/common/error_private.h b/lib/common/error_private.h index 1bc2e4954..9dd9a87cf 100644 --- a/lib/common/error_private.h +++ b/lib/common/error_private.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ /* Note : this module is expected to remain private, do not expose it */ diff --git a/lib/common/mem.h b/lib/common/mem.h index 528286eff..df85404fb 100644 --- a/lib/common/mem.h +++ b/lib/common/mem.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef MEM_H_MODULE diff --git a/lib/common/pool.c b/lib/common/pool.c index e140f1e88..bcea21ca0 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -1,10 +1,10 @@ -/** - * Copyright (c) 2016-present, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/common/pool.h b/lib/common/pool.h index ed2711950..264c5c9ca 100644 --- a/lib/common/pool.h +++ b/lib/common/pool.h @@ -1,11 +1,12 @@ -/** - * Copyright (c) 2016-present, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ + #ifndef POOL_H #define POOL_H diff --git a/lib/common/threading.h b/lib/common/threading.h index ee7864555..ab09977a8 100644 --- a/lib/common/threading.h +++ b/lib/common/threading.h @@ -1,4 +1,3 @@ - /** * Copyright (c) 2016 Tino Reichardt * All rights reserved. diff --git a/lib/common/zstd_common.c b/lib/common/zstd_common.c index f68167238..08384cabf 100644 --- a/lib/common/zstd_common.c +++ b/lib/common/zstd_common.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/common/zstd_errors.h b/lib/common/zstd_errors.h index a645a9e8e..a69387b71 100644 --- a/lib/common/zstd_errors.h +++ b/lib/common/zstd_errors.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTD_ERRORS_H_398273423 diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 66ea832e1..261052860 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTD_CCOMMON_H_MODULE diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 0a7a98a96..0322c03eb 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index e0af5bfa5..ae24732c7 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -1,10 +1,10 @@ -/** - * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 234ced9de..02bb7c593 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 843a240aa..0f0fc2b03 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTDMT_COMPRESS_H diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 6941140a3..d2bc545e5 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/deprecated/zbuff.h b/lib/deprecated/zbuff.h index f62091976..e6ea84ad3 100644 --- a/lib/deprecated/zbuff.h +++ b/lib/deprecated/zbuff.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ /* *************************************************************** diff --git a/lib/deprecated/zbuff_common.c b/lib/deprecated/zbuff_common.c index 9fff6eb20..2de45bec1 100644 --- a/lib/deprecated/zbuff_common.c +++ b/lib/deprecated/zbuff_common.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ /*-************************************* @@ -23,4 +23,3 @@ unsigned ZBUFF_isError(size_t errorCode) { return ERR_isError(errorCode); } /*! ZBUFF_getErrorName() : * provides error code string from function result (useful for debugging) */ const char* ZBUFF_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); } - diff --git a/lib/deprecated/zbuff_compress.c b/lib/deprecated/zbuff_compress.c index 5a37a0027..4444e95d8 100644 --- a/lib/deprecated/zbuff_compress.c +++ b/lib/deprecated/zbuff_compress.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/deprecated/zbuff_decompress.c b/lib/deprecated/zbuff_decompress.c index d9c155e08..a819d7f40 100644 --- a/lib/deprecated/zbuff_decompress.c +++ b/lib/deprecated/zbuff_decompress.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c index 38376d08b..3d445ae8b 100644 --- a/lib/dictBuilder/cover.c +++ b/lib/dictBuilder/cover.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ /* ***************************************************************************** diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 113d205fc..c2871c2cc 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index 7bfbb351a..3d72a465e 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef DICTBUILDER_H_001 diff --git a/lib/legacy/zstd_legacy.h b/lib/legacy/zstd_legacy.h index 3c9798f88..6342192ee 100644 --- a/lib/legacy/zstd_legacy.h +++ b/lib/legacy/zstd_legacy.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTD_LEGACY_H diff --git a/lib/legacy/zstd_v01.c b/lib/legacy/zstd_v01.c index cf5354d6a..45f421ae6 100644 --- a/lib/legacy/zstd_v01.c +++ b/lib/legacy/zstd_v01.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/legacy/zstd_v01.h b/lib/legacy/zstd_v01.h index 13cb3acfd..a91c6a133 100644 --- a/lib/legacy/zstd_v01.h +++ b/lib/legacy/zstd_v01.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTD_V01_H_28739879432 diff --git a/lib/legacy/zstd_v02.c b/lib/legacy/zstd_v02.c index 3cf8f4778..dc1ec0e7c 100644 --- a/lib/legacy/zstd_v02.c +++ b/lib/legacy/zstd_v02.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/legacy/zstd_v02.h b/lib/legacy/zstd_v02.h index d14f0293c..63cb3b8d5 100644 --- a/lib/legacy/zstd_v02.h +++ b/lib/legacy/zstd_v02.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTD_V02_H_4174539423 diff --git a/lib/legacy/zstd_v03.c b/lib/legacy/zstd_v03.c index f438330a4..8257de7e6 100644 --- a/lib/legacy/zstd_v03.c +++ b/lib/legacy/zstd_v03.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/legacy/zstd_v03.h b/lib/legacy/zstd_v03.h index 07f7597bb..e38e0109b 100644 --- a/lib/legacy/zstd_v03.h +++ b/lib/legacy/zstd_v03.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTD_V03_H_298734209782 diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c index 2ba75a875..951561a6c 100644 --- a/lib/legacy/zstd_v04.c +++ b/lib/legacy/zstd_v04.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/legacy/zstd_v04.h b/lib/legacy/zstd_v04.h index 1b5439d39..a7d662330 100644 --- a/lib/legacy/zstd_v04.h +++ b/lib/legacy/zstd_v04.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTD_V04_H_91868324769238 diff --git a/lib/legacy/zstd_v05.c b/lib/legacy/zstd_v05.c index bd3e8c113..4a1d4d4bd 100644 --- a/lib/legacy/zstd_v05.c +++ b/lib/legacy/zstd_v05.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/legacy/zstd_v05.h b/lib/legacy/zstd_v05.h index 8ce662fd9..a333bd127 100644 --- a/lib/legacy/zstd_v05.h +++ b/lib/legacy/zstd_v05.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTDv05_H diff --git a/lib/legacy/zstd_v06.c b/lib/legacy/zstd_v06.c index 3fb47c2bf..a285a0901 100644 --- a/lib/legacy/zstd_v06.c +++ b/lib/legacy/zstd_v06.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/legacy/zstd_v06.h b/lib/legacy/zstd_v06.h index 10c9c7725..ee043a179 100644 --- a/lib/legacy/zstd_v06.h +++ b/lib/legacy/zstd_v06.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTDv06_H diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c index 6669b71ce..ad392e90b 100644 --- a/lib/legacy/zstd_v07.c +++ b/lib/legacy/zstd_v07.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/lib/legacy/zstd_v07.h b/lib/legacy/zstd_v07.h index cc95c661b..68d18e963 100644 --- a/lib/legacy/zstd_v07.h +++ b/lib/legacy/zstd_v07.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTDv07_H_235446 diff --git a/lib/zstd.h b/lib/zstd.h index dadfe74c1..13b4563fd 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -2,11 +2,10 @@ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ - #if defined (__cplusplus) extern "C" { #endif diff --git a/programs/bench.c b/programs/bench.c index f9493e3b0..2b48a4663 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/programs/bench.h b/programs/bench.h index 77a527f8f..5f8d61a25 100644 --- a/programs/bench.h +++ b/programs/bench.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/programs/datagen.c b/programs/datagen.c index d0116b972..b1da8e78b 100644 --- a/programs/datagen.c +++ b/programs/datagen.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/programs/datagen.h b/programs/datagen.h index 094056b69..5b1b7c47c 100644 --- a/programs/datagen.h +++ b/programs/datagen.h @@ -1,11 +1,13 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ + + #ifndef DATAGEN_H #define DATAGEN_H diff --git a/programs/dibio.c b/programs/dibio.c index 31cde5c95..ab2dc285a 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/programs/dibio.h b/programs/dibio.h index 84f7d5802..0227239b2 100644 --- a/programs/dibio.h +++ b/programs/dibio.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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 library is designed for a single-threaded console application. diff --git a/programs/fileio.c b/programs/fileio.c index 3db6af734..ee98f5d7b 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/programs/fileio.h b/programs/fileio.h index 9d9167df9..8008e97dd 100644 --- a/programs/fileio.h +++ b/programs/fileio.h @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/programs/platform.h b/programs/platform.h index 74412cde3..fb2e9b173 100644 --- a/programs/platform.h +++ b/programs/platform.h @@ -1,12 +1,10 @@ -/** - * platform.h - compiler and OS detection - * - * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef PLATFORM_H_MODULE diff --git a/programs/util.h b/programs/util.h index dd971e0f8..7b553661c 100644 --- a/programs/util.h +++ b/programs/util.h @@ -1,12 +1,10 @@ -/** - * util.h - utility functions - * - * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef UTIL_H_MODULE diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 6817ebc45..3547a5ef5 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/tests/datagencli.c b/tests/datagencli.c index 8a81939d1..bf9601f20 100644 --- a/tests/datagencli.c +++ b/tests/datagencli.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/tests/decodecorpus.c b/tests/decodecorpus.c index eaf074578..23166bd67 100644 --- a/tests/decodecorpus.c +++ b/tests/decodecorpus.c @@ -1,10 +1,10 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #include diff --git a/tests/fullbench.c b/tests/fullbench.c index 45ef2b6a3..78a70940f 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 046c67ea8..0c13a6e48 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/tests/invalidDictionaries.c b/tests/invalidDictionaries.c index fe8b23b5e..83fe439d4 100644 --- a/tests/invalidDictionaries.c +++ b/tests/invalidDictionaries.c @@ -1,3 +1,12 @@ +/* + * 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). + */ + #include #include "zstd.h" diff --git a/tests/legacy.c b/tests/legacy.c index e84e31273..962b2c9c3 100644 --- a/tests/legacy.c +++ b/tests/legacy.c @@ -1,10 +1,10 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ /* @@ -226,4 +226,3 @@ const char* const EXPECTED = "snowden is snowed in / he's now then in his snow den / when does the snow end?\n" "goodbye little dog / you dug some holes in your day / they'll be hard to fill.\n" "when life shuts a door, / just open it. it’s a door. / that is how doors work.\n"; - diff --git a/tests/longmatch.c b/tests/longmatch.c index 61b81b359..ef79337f5 100644 --- a/tests/longmatch.c +++ b/tests/longmatch.c @@ -1,3 +1,13 @@ +/* + * 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). + */ + + #include #include #include diff --git a/tests/namespaceTest.c b/tests/namespaceTest.c index dd63186d1..6f6c74fd6 100644 --- a/tests/namespaceTest.c +++ b/tests/namespaceTest.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/tests/paramgrill.c b/tests/paramgrill.c index da06ccb52..ed13e1dac 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/tests/poolTests.c b/tests/poolTests.c index 9e11281ba..f3d5c382a 100644 --- a/tests/poolTests.c +++ b/tests/poolTests.c @@ -1,3 +1,13 @@ +/* + * 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). + */ + + #include "pool.h" #include "threading.h" #include "util.h" diff --git a/tests/roundTripCrash.c b/tests/roundTripCrash.c index 77c6737ee..0b478f6ca 100644 --- a/tests/roundTripCrash.c +++ b/tests/roundTripCrash.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ /* diff --git a/tests/symbols.c b/tests/symbols.c index 8920187f3..f08542dbf 100644 --- a/tests/symbols.c +++ b/tests/symbols.c @@ -1,3 +1,13 @@ +/* + * 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). + */ + + #include #include "zstd_errors.h" #define ZSTD_STATIC_LINKING_ONLY diff --git a/tests/zbufftest.c b/tests/zbufftest.c index 601aa808d..fe08fdab5 100644 --- a/tests/zbufftest.c +++ b/tests/zbufftest.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 3e551e331..dd044342e 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -1,10 +1,10 @@ -/** +/* * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c index 1fc2117f6..050c9db63 100644 --- a/zlibWrapper/examples/zwrapbench.c +++ b/zlibWrapper/examples/zwrapbench.c @@ -1,10 +1,10 @@ -/** - * Copyright (c) 2016-present, Yann Collet, Przemyslaw Skibinski, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/zlibWrapper/gzcompatibility.h b/zlibWrapper/gzcompatibility.h index e2ec1addb..ac9020acc 100644 --- a/zlibWrapper/gzcompatibility.h +++ b/zlibWrapper/gzcompatibility.h @@ -1,10 +1,10 @@ -/** - * Copyright (c) 2016-present, Przemyslaw Skibinski, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ @@ -12,9 +12,9 @@ #if ZLIB_VERNUM <= 0x1240 ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); -ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); - + #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else @@ -38,7 +38,7 @@ struct gzFile_s { #if ZLIB_VERNUM <= 0x1270 #if defined(_WIN32) && !defined(Z_SOLO) -# include /* for wchar_t */ +# include /* for wchar_t */ ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, const char *mode)); #endif diff --git a/zlibWrapper/gzlib.c b/zlibWrapper/gzlib.c index aa94206a8..8235cff4f 100644 --- a/zlibWrapper/gzlib.c +++ b/zlibWrapper/gzlib.c @@ -1,5 +1,5 @@ /* gzlib.c contains minimal changes required to be compiled with zlibWrapper: - * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ + * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ /* gzlib.c -- zlib functions common to reading and writing gzip files * Copyright (C) 2004-2017 Mark Adler diff --git a/zlibWrapper/gzread.c b/zlibWrapper/gzread.c index d37aaa1d4..88fc06c77 100644 --- a/zlibWrapper/gzread.c +++ b/zlibWrapper/gzread.c @@ -1,6 +1,6 @@ /* gzread.c contains minimal changes required to be compiled with zlibWrapper: - * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ - + * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ + /* gzread.c -- zlib functions for reading gzip files * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler * For conditions of distribution and use, see http://www.zlib.net/zlib_license.html diff --git a/zlibWrapper/gzwrite.c b/zlibWrapper/gzwrite.c index bcda4774a..d1250b900 100644 --- a/zlibWrapper/gzwrite.c +++ b/zlibWrapper/gzwrite.c @@ -1,6 +1,6 @@ /* gzwrite.c contains minimal changes required to be compiled with zlibWrapper: - * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ - + * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ + /* gzwrite.c -- zlib functions for writing gzip files * Copyright (C) 2004-2017 Mark Adler * For conditions of distribution and use, see http://www.zlib.net/zlib_license.html diff --git a/zlibWrapper/zstd_zlibwrapper.c b/zlibWrapper/zstd_zlibwrapper.c index ade3b88cd..272369a28 100644 --- a/zlibWrapper/zstd_zlibwrapper.c +++ b/zlibWrapper/zstd_zlibwrapper.c @@ -1,10 +1,10 @@ -/** - * Copyright (c) 2016-present, Przemyslaw Skibinski, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ diff --git a/zlibWrapper/zstd_zlibwrapper.h b/zlibWrapper/zstd_zlibwrapper.h index 0ebd87612..f8f368009 100644 --- a/zlibWrapper/zstd_zlibwrapper.h +++ b/zlibWrapper/zstd_zlibwrapper.h @@ -1,10 +1,10 @@ -/** - * Copyright (c) 2016-present, Przemyslaw Skibinski, Facebook, Inc. +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. * All rights reserved. * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. + * 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). */ #ifndef ZSTD_ZLIBWRAPPER_H @@ -32,7 +32,7 @@ const char * zstdVersion(void); /*** COMPRESSION ***/ /* ZWRAP_useZSTDcompression() enables/disables zstd compression during runtime. By default zstd compression is disabled. To enable zstd compression please use one of the methods: - - compilation with the additional option -DZWRAP_USE_ZSTD=1 + - compilation with the additional option -DZWRAP_USE_ZSTD=1 - using '#define ZWRAP_USE_ZSTD 1' in source code before '#include "zstd_zlibwrapper.h"' - calling ZWRAP_useZSTDcompression(1) All above-mentioned methods will enable zstd compression for all threads. @@ -45,13 +45,13 @@ int ZWRAP_isUsingZSTDcompression(void); /* Changes a pledged source size for a given compression stream. It will change ZSTD compression parameters what may improve compression speed and/or ratio. The function should be called just after deflateInit() or deflateReset() and before deflate() or deflateSetDictionary(). - It's only helpful when data is compressed in blocks. - There will be no change in case of deflateInit() or deflateReset() immediately followed by deflate(strm, Z_FINISH) + It's only helpful when data is compressed in blocks. + There will be no change in case of deflateInit() or deflateReset() immediately followed by deflate(strm, Z_FINISH) as this case is automatically detected. */ int ZWRAP_setPledgedSrcSize(z_streamp strm, unsigned long long pledgedSrcSize); /* Similar to deflateReset but preserves dictionary set using deflateSetDictionary. - It should improve compression speed because there will be less calls to deflateSetDictionary + It should improve compression speed because there will be less calls to deflateSetDictionary When using zlib compression this method redirects to deflateReset. */ int ZWRAP_deflateReset_keepDict(z_streamp strm); From f207b39f5597120b37a04b3974d5865bbc239806 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 18 Aug 2017 17:06:12 -0700 Subject: [PATCH 305/318] blindfix for Windows conversion warning long type is 32-bits on Windows 64, while it's 64-bits on Unix. 64-to-32 shortening conversion for long is a specific Windows issue. --- programs/fileio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/fileio.c b/programs/fileio.c index 3db6af734..9f9b72fb0 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -1038,7 +1038,7 @@ static int getFileInfo(fileInfo_t* info, const char* inFileName){ /* Skippable frame */ else if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { U32 const frameSize = MEM_readLE32(headerBuffer + 4); - long const seek = -numBytesRead + 8 + frameSize; + long const seek = (long)(8 + frameSize - numBytesRead); int const ret = LONG_SEEK(srcFile, seek, SEEK_CUR); if (ret != 0) { DISPLAY("Error: could not find end of skippable frame\n"); From 166645e7b39502493ce6df1f8025726e5cbee2d3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 18 Aug 2017 18:30:41 -0700 Subject: [PATCH 306/318] fixed zstd-compress file-information is dependent on decompression functions. it should only be enabled when ZSTD_NODECOMPRESS is not set. also : added zstd-compress compilation test into `make shortest` --- Makefile | 1 + programs/Makefile | 24 ++- programs/fileio.c | 450 +++++++++++++++++++++++---------------------- programs/zstdcli.c | 16 +- 4 files changed, 261 insertions(+), 230 deletions(-) diff --git a/Makefile b/Makefile index d88104a0a..a72f99fcb 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,7 @@ zlibwrapper: .PHONY: test shortest test shortest: + $(MAKE) -C $(PRGDIR) allVariants $(MAKE) -C $(TESTDIR) $@ .PHONY: examples diff --git a/programs/Makefile b/programs/Makefile index 2460a091f..ee623e52f 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -132,12 +132,15 @@ else LZ4_MSG := $(NO_LZ4_MSG) endif -.PHONY: default all clean clean_decomp_o install uninstall generate_res - +.PHONY: default default: zstd-release +.PHONY: all all: zstd +.PHONY: allVariants +allVariants: zstd zstd-compress + $(ZSTDDECOMP_O): CFLAGS += $(ALIGN_LOOP) zstd zstd4 : CPPFLAGS += $(THREAD_CPP) $(ZLIBCPP) $(LZMACPP) @@ -156,6 +159,7 @@ ifneq (,$(filter Windows%,$(OS))) endif $(CC) $(FLAGS) $^ $(RES_FILE) -o zstd$(EXT) $(LDFLAGS) +.PHONY: zstd-release zstd-release: DEBUGFLAGS := zstd-release: zstd @@ -166,7 +170,7 @@ ifneq (,$(filter Windows%,$(OS))) endif $(CC) -m32 $(FLAGS) $^ $(RES32_FILE) -o $@$(EXT) -zstd-nolegacy : clean_decomp_o +zstd-nolegacy : $(MAKE) zstd ZSTD_LEGACY_SUPPORT=0 zstd-nomt : THREAD_CPP := @@ -211,9 +215,11 @@ zstd-compress: $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) zstdcli.c fileio.c # zstd is now built with Multi-threading by default zstdmt: zstd +.PHONY: generate_res generate_res: windres/generate_res.bat +.PHONY: clean clean: $(MAKE) -C $(ZSTDDIR) clean @$(RM) $(ZSTDDIR)/decompress/*.o $(ZSTDDIR)/decompress/zstd_decompress.gcda @@ -222,20 +228,20 @@ clean: *.gcda default.profraw have_zlib$(EXT) @echo Cleaning completed -clean_decomp_o: - @$(RM) $(ZSTDDECOMP_O) - MD2ROFF = ronn MD2ROFF_FLAGS = --roff --warnings --manual="User Commands" --organization="zstd $(ZSTD_VERSION)" zstd.1: zstd.1.md cat $^ | $(MD2ROFF) $(MD2ROFF_FLAGS) | sed -n '/^\.\\\".*/!p' > $@ +.PHONY: man man: zstd.1 +.PHONY: clean-man clean-man: rm zstd.1 +.PHONY: preview-man preview-man: clean-man man man ./zstd.1 @@ -244,6 +250,10 @@ preview-man: clean-man man #----------------------------------------------------------------------------- ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS)) +.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 + ifneq (,$(filter $(shell uname),SunOS)) INSTALL ?= ginstall else @@ -264,6 +274,7 @@ INSTALL_PROGRAM ?= $(INSTALL) -m 755 INSTALL_SCRIPT ?= $(INSTALL) -m 755 INSTALL_MAN ?= $(INSTALL) -m 644 +.PHONY: install install: zstd @echo Installing binaries @$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)/ $(DESTDIR)$(MANDIR)/ @@ -279,6 +290,7 @@ install: zstd @ln -sf zstd.1 $(DESTDIR)$(MANDIR)/unzstd.1 @echo zstd installation completed +.PHONY: uninstall uninstall: @$(RM) $(DESTDIR)$(BINDIR)/zstdgrep @$(RM) $(DESTDIR)$(BINDIR)/zstdless diff --git a/programs/fileio.c b/programs/fileio.c index 9f9b72fb0..eb75c68ad 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -925,223 +925,6 @@ int FIO_compressFilename(const char* dstFileName, const char* srcFileName, return result; } -typedef struct { - int numActualFrames; - int numSkippableFrames; - unsigned long long decompressedSize; - int decompUnavailable; - unsigned long long compressedSize; - int usesCheck; -} fileInfo_t; - -/* - * Reads information from file, stores in *info - * if successful, returns 0, returns 1 for frame analysis error, returns 2 for file not compressed with zstd - * returns 3 for cases in which file could not be opened. - */ -static int getFileInfo(fileInfo_t* info, const char* inFileName){ - int detectError = 0; - FILE* const srcFile = FIO_openSrcFile(inFileName); - if (srcFile == NULL) { - DISPLAY("Error: could not open source file %s\n", inFileName); - return 3; - } - info->compressedSize = (unsigned long long)UTIL_getFileSize(inFileName); - - /* begin analyzing frame */ - for ( ; ; ) { - BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; - size_t const numBytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), srcFile); - if (numBytesRead < ZSTD_frameHeaderSize_min) { - if (feof(srcFile) && numBytesRead == 0 && info->compressedSize > 0) { - break; - } - else if (feof(srcFile)) { - DISPLAY("Error: reached end of file with incomplete frame\n"); - detectError = 2; - break; - } - else { - DISPLAY("Error: did not reach end of file but ran out of frames\n"); - detectError = 1; - break; - } - } - { U32 const magicNumber = MEM_readLE32(headerBuffer); - /* Zstandard frame */ - if (magicNumber == ZSTD_MAGICNUMBER) { - U64 const frameContentSize = ZSTD_getFrameContentSize(headerBuffer, numBytesRead); - if (frameContentSize == ZSTD_CONTENTSIZE_ERROR || frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN) { - info->decompUnavailable = 1; - } else { - info->decompressedSize += frameContentSize; - } - /* move to the end of the frame header */ - { size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead); - if (ZSTD_isError(headerSize)) { - DISPLAY("Error: could not determine frame header size\n"); - detectError = 1; - break; - } - { int const ret = fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR); - if (ret != 0) { - DISPLAY("Error: could not move to end of frame header\n"); - detectError = 1; - break; - } } } - - /* skip the rest of the blocks in the frame */ - { int lastBlock = 0; - do { - BYTE blockHeaderBuffer[3]; - size_t const readBytes = fread(blockHeaderBuffer, 1, 3, srcFile); - if (readBytes != 3) { - DISPLAY("There was a problem reading the block header\n"); - detectError = 1; - break; - } - { U32 const blockHeader = MEM_readLE24(blockHeaderBuffer); - U32 const blockTypeID = (blockHeader >> 1) & 3; - U32 const isRLE = (blockTypeID == 1); - U32 const isWrongBlock = (blockTypeID == 3); - long const blockSize = isRLE ? 1 : (long)(blockHeader >> 3); - if (isWrongBlock) { - DISPLAY("Error: unsupported block type \n"); - detectError = 1; - break; - } - lastBlock = blockHeader & 1; - { int const ret = fseek(srcFile, blockSize, SEEK_CUR); - if (ret != 0) { - DISPLAY("Error: could not skip to end of block\n"); - detectError = 1; - break; - } } } - } while (lastBlock != 1); - - if (detectError) break; - } - - /* check if checksum is used */ - { BYTE const frameHeaderDescriptor = headerBuffer[4]; - int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2; - if (contentChecksumFlag) { - int const ret = fseek(srcFile, 4, SEEK_CUR); - info->usesCheck = 1; - if (ret != 0) { - DISPLAY("Error: could not skip past checksum\n"); - detectError = 1; - break; - } } } - info->numActualFrames++; - } - /* Skippable frame */ - else if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { - U32 const frameSize = MEM_readLE32(headerBuffer + 4); - long const seek = (long)(8 + frameSize - numBytesRead); - int const ret = LONG_SEEK(srcFile, seek, SEEK_CUR); - if (ret != 0) { - DISPLAY("Error: could not find end of skippable frame\n"); - detectError = 1; - break; - } - info->numSkippableFrames++; - } - /* unknown content */ - else { - detectError = 2; - break; - } - } - } /* end analyzing frame */ - fclose(srcFile); - return detectError; -} - -static void displayInfo(const char* inFileName, fileInfo_t* info, int displayLevel){ - unsigned const unit = info->compressedSize < (1 MB) ? (1 KB) : (1 MB); - const char* const unitStr = info->compressedSize < (1 MB) ? "KB" : "MB"; - double const compressedSizeUnit = (double)info->compressedSize / unit; - double const decompressedSizeUnit = (double)info->decompressedSize / unit; - double const ratio = (info->compressedSize == 0) ? 0 : ((double)info->decompressedSize)/info->compressedSize; - const char* const checkString = (info->usesCheck ? "XXH64" : "None"); - if (displayLevel <= 2) { - if (!info->decompUnavailable) { - DISPLAYOUT("Skippable Non-Skippable Compressed Uncompressed Ratio Check Filename\n"); - DISPLAYOUT("%9d %13d %7.2f %2s %9.2f %2s %5.3f %5s %s\n", - info->numSkippableFrames, info->numActualFrames, - compressedSizeUnit, unitStr, decompressedSizeUnit, unitStr, - ratio, checkString, inFileName); - } else { - DISPLAYOUT("Skippable Non-Skippable Compressed Check Filename\n"); - DISPLAYOUT("%9d %13d %7.2f MB %5s %s\n", - info->numSkippableFrames, info->numActualFrames, - compressedSizeUnit, checkString, inFileName); - } - } else { - DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames); - DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames); - DISPLAYOUT("Compressed Size: %.2f %2s (%llu B)\n", - compressedSizeUnit, unitStr, info->compressedSize); - if (!info->decompUnavailable) { - DISPLAYOUT("Decompressed Size: %.2f %2s (%llu B)\n", - decompressedSizeUnit, unitStr, info->decompressedSize); - DISPLAYOUT("Ratio: %.4f\n", ratio); - } - DISPLAYOUT("Check: %s\n", checkString); - DISPLAYOUT("\n"); - } -} - - -static int FIO_listFile(const char* inFileName, int displayLevel, unsigned fileNo, unsigned numFiles){ - /* initialize info to avoid warnings */ - fileInfo_t info; - memset(&info, 0, sizeof(info)); - DISPLAYOUT("%s (%u/%u):\n", inFileName, fileNo, numFiles); - { - int const error = getFileInfo(&info, inFileName); - if (error == 1) { - /* display error, but provide output */ - DISPLAY("An error occurred with getting file info\n"); - } - else if (error == 2) { - DISPLAYOUT("File %s not compressed with zstd\n", inFileName); - if (displayLevel > 2) { - DISPLAYOUT("\n"); - } - return 1; - } - else if (error == 3) { - /* error occurred with opening the file */ - if (displayLevel > 2) { - DISPLAYOUT("\n"); - } - return 1; - } - displayInfo(inFileName, &info, displayLevel); - return error; - } -} - -int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel){ - if (numFiles == 0) { - DISPLAYOUT("No files given\n"); - return 0; - } - DISPLAYOUT("===========================================\n"); - DISPLAYOUT("Printing information about compressed files\n"); - DISPLAYOUT("===========================================\n"); - DISPLAYOUT("Number of files listed: %u\n", numFiles); - { - int error = 0; - unsigned u; - for (u=0; ucompressedSize = (unsigned long long)UTIL_getFileSize(inFileName); + + /* begin analyzing frame */ + for ( ; ; ) { + BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; + size_t const numBytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), srcFile); + if (numBytesRead < ZSTD_frameHeaderSize_min) { + if (feof(srcFile) && numBytesRead == 0 && info->compressedSize > 0) { + break; + } + else if (feof(srcFile)) { + DISPLAY("Error: reached end of file with incomplete frame\n"); + detectError = 2; + break; + } + else { + DISPLAY("Error: did not reach end of file but ran out of frames\n"); + detectError = 1; + break; + } + } + { U32 const magicNumber = MEM_readLE32(headerBuffer); + /* Zstandard frame */ + if (magicNumber == ZSTD_MAGICNUMBER) { + U64 const frameContentSize = ZSTD_getFrameContentSize(headerBuffer, numBytesRead); + if (frameContentSize == ZSTD_CONTENTSIZE_ERROR || frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN) { + info->decompUnavailable = 1; + } else { + info->decompressedSize += frameContentSize; + } + /* move to the end of the frame header */ + { size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead); + if (ZSTD_isError(headerSize)) { + DISPLAY("Error: could not determine frame header size\n"); + detectError = 1; + break; + } + { int const ret = fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR); + if (ret != 0) { + DISPLAY("Error: could not move to end of frame header\n"); + detectError = 1; + break; + } } } + + /* skip the rest of the blocks in the frame */ + { int lastBlock = 0; + do { + BYTE blockHeaderBuffer[3]; + size_t const readBytes = fread(blockHeaderBuffer, 1, 3, srcFile); + if (readBytes != 3) { + DISPLAY("There was a problem reading the block header\n"); + detectError = 1; + break; + } + { U32 const blockHeader = MEM_readLE24(blockHeaderBuffer); + U32 const blockTypeID = (blockHeader >> 1) & 3; + U32 const isRLE = (blockTypeID == 1); + U32 const isWrongBlock = (blockTypeID == 3); + long const blockSize = isRLE ? 1 : (long)(blockHeader >> 3); + if (isWrongBlock) { + DISPLAY("Error: unsupported block type \n"); + detectError = 1; + break; + } + lastBlock = blockHeader & 1; + { int const ret = fseek(srcFile, blockSize, SEEK_CUR); + if (ret != 0) { + DISPLAY("Error: could not skip to end of block\n"); + detectError = 1; + break; + } } } + } while (lastBlock != 1); + + if (detectError) break; + } + + /* check if checksum is used */ + { BYTE const frameHeaderDescriptor = headerBuffer[4]; + int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2; + if (contentChecksumFlag) { + int const ret = fseek(srcFile, 4, SEEK_CUR); + info->usesCheck = 1; + if (ret != 0) { + DISPLAY("Error: could not skip past checksum\n"); + detectError = 1; + break; + } } } + info->numActualFrames++; + } + /* Skippable frame */ + else if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { + U32 const frameSize = MEM_readLE32(headerBuffer + 4); + long const seek = (long)(8 + frameSize - numBytesRead); + int const ret = LONG_SEEK(srcFile, seek, SEEK_CUR); + if (ret != 0) { + DISPLAY("Error: could not find end of skippable frame\n"); + detectError = 1; + break; + } + info->numSkippableFrames++; + } + /* unknown content */ + else { + detectError = 2; + break; + } + } + } /* end analyzing frame */ + fclose(srcFile); + return detectError; +} + +static void displayInfo(const char* inFileName, fileInfo_t* info, int displayLevel){ + unsigned const unit = info->compressedSize < (1 MB) ? (1 KB) : (1 MB); + const char* const unitStr = info->compressedSize < (1 MB) ? "KB" : "MB"; + double const compressedSizeUnit = (double)info->compressedSize / unit; + double const decompressedSizeUnit = (double)info->decompressedSize / unit; + double const ratio = (info->compressedSize == 0) ? 0 : ((double)info->decompressedSize)/info->compressedSize; + const char* const checkString = (info->usesCheck ? "XXH64" : "None"); + if (displayLevel <= 2) { + if (!info->decompUnavailable) { + DISPLAYOUT("Skippable Non-Skippable Compressed Uncompressed Ratio Check Filename\n"); + DISPLAYOUT("%9d %13d %7.2f %2s %9.2f %2s %5.3f %5s %s\n", + info->numSkippableFrames, info->numActualFrames, + compressedSizeUnit, unitStr, decompressedSizeUnit, unitStr, + ratio, checkString, inFileName); + } else { + DISPLAYOUT("Skippable Non-Skippable Compressed Check Filename\n"); + DISPLAYOUT("%9d %13d %7.2f MB %5s %s\n", + info->numSkippableFrames, info->numActualFrames, + compressedSizeUnit, checkString, inFileName); + } + } else { + DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames); + DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames); + DISPLAYOUT("Compressed Size: %.2f %2s (%llu B)\n", + compressedSizeUnit, unitStr, info->compressedSize); + if (!info->decompUnavailable) { + DISPLAYOUT("Decompressed Size: %.2f %2s (%llu B)\n", + decompressedSizeUnit, unitStr, info->decompressedSize); + DISPLAYOUT("Ratio: %.4f\n", ratio); + } + DISPLAYOUT("Check: %s\n", checkString); + DISPLAYOUT("\n"); + } +} + + +static int FIO_listFile(const char* inFileName, int displayLevel, unsigned fileNo, unsigned numFiles){ + /* initialize info to avoid warnings */ + fileInfo_t info; + memset(&info, 0, sizeof(info)); + DISPLAYOUT("%s (%u/%u):\n", inFileName, fileNo, numFiles); + { + int const error = getFileInfo(&info, inFileName); + if (error == 1) { + /* display error, but provide output */ + DISPLAY("An error occurred with getting file info\n"); + } + else if (error == 2) { + DISPLAYOUT("File %s not compressed with zstd\n", inFileName); + if (displayLevel > 2) { + DISPLAYOUT("\n"); + } + return 1; + } + else if (error == 3) { + /* error occurred with opening the file */ + if (displayLevel > 2) { + DISPLAYOUT("\n"); + } + return 1; + } + displayInfo(inFileName, &info, displayLevel); + return error; + } +} + +int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel){ + if (numFiles == 0) { + DISPLAYOUT("No files given\n"); + return 0; + } + DISPLAYOUT("===========================================\n"); + DISPLAYOUT("Printing information about compressed files\n"); + DISPLAYOUT("===========================================\n"); + DISPLAYOUT("Number of files listed: %u\n", numFiles); + { + int error = 0; + unsigned u; + for (u=0; u Date: Fri, 18 Aug 2017 18:39:39 -0700 Subject: [PATCH 307/318] added zstd-decompress to the list of variants tested --- programs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/Makefile b/programs/Makefile index ee623e52f..2b766fb3a 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -139,7 +139,7 @@ default: zstd-release all: zstd .PHONY: allVariants -allVariants: zstd zstd-compress +allVariants: zstd zstd-compress zstd-decompress $(ZSTDDECOMP_O): CFLAGS += $(ALIGN_LOOP) From 4b387729b6d1e977fd2e215b93e2cb96d246e03b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 19 Aug 2017 00:48:29 -0700 Subject: [PATCH 308/318] fixed zstd-small and added it to shortest for CI tests --- programs/.gitignore | 2 ++ programs/Makefile | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/programs/.gitignore b/programs/.gitignore index eeaf051d6..461f70844 100644 --- a/programs/.gitignore +++ b/programs/.gitignore @@ -3,6 +3,8 @@ zstd zstd32 zstd-compress zstd-decompress +zstd-frugal +zstd-small # Object files *.o diff --git a/programs/Makefile b/programs/Makefile index 2b766fb3a..886cb8119 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -139,7 +139,7 @@ default: zstd-release all: zstd .PHONY: allVariants -allVariants: zstd zstd-compress zstd-decompress +allVariants: zstd zstd-compress zstd-decompress zstd-small $(ZSTDDECOMP_O): CFLAGS += $(ALIGN_LOOP) @@ -202,9 +202,9 @@ zstd-pgo : clean zstd $(MAKE) zstd MOREFLAGS=-fprofile-use # minimal target, with only zstd compression and decompression. no bench. no legacy. -zstd-small: CFLAGS = "-Os -s" +zstd-small: CFLAGS = -Os -s zstd-frugal zstd-small: $(ZSTD_FILES) zstdcli.c fileio.c - $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT $^ -o zstd$(EXT) + $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT $^ -o $@$(EXT) zstd-decompress: $(ZSTDCOMMON_FILES) $(ZSTDDECOMP_FILES) zstdcli.c fileio.c $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NOCOMPRESS $^ -o $@$(EXT) @@ -225,6 +225,7 @@ clean: @$(RM) $(ZSTDDIR)/decompress/*.o $(ZSTDDIR)/decompress/zstd_decompress.gcda @$(RM) core *.o tmp* result* *.gcda dictionary *.zst \ zstd$(EXT) zstd32$(EXT) zstd-compress$(EXT) zstd-decompress$(EXT) \ + zstd-small$(EXT) zstd-frugal$(EXT) \ *.gcda default.profraw have_zlib$(EXT) @echo Cleaning completed @@ -301,4 +302,5 @@ uninstall: @$(RM) $(DESTDIR)$(MANDIR)/unzstd.1 @$(RM) $(DESTDIR)$(MANDIR)/zstd.1 @echo zstd programs successfully uninstalled + endif From 9203003d5f20d9797f2298e85633f521c9d3de5d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 19 Aug 2017 01:01:53 -0700 Subject: [PATCH 309/318] fixed zstd-nolegacy and added it to allVariants for CI testings --- programs/.gitignore | 1 + programs/Makefile | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/programs/.gitignore b/programs/.gitignore index 461f70844..a012ce2e2 100644 --- a/programs/.gitignore +++ b/programs/.gitignore @@ -5,6 +5,7 @@ zstd-compress zstd-decompress zstd-frugal zstd-small +zstd-nolegacy # Object files *.o diff --git a/programs/Makefile b/programs/Makefile index 886cb8119..4cfb05e26 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -42,7 +42,7 @@ CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ -DZSTD_NEWAPI \ -DXXH_NAMESPACE=ZSTD_ # because xxhash.o already compiled with this macro from library CFLAGS ?= -O3 -DEBUGFLAGS = -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ +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 \ @@ -139,7 +139,7 @@ default: zstd-release all: zstd .PHONY: allVariants -allVariants: zstd zstd-compress zstd-decompress zstd-small +allVariants: zstd zstd-compress zstd-decompress zstd-small zstd-nolegacy $(ZSTDDECOMP_O): CFLAGS += $(ALIGN_LOOP) @@ -157,7 +157,7 @@ zstd zstd4 : $(ZSTDLIB_FILES) zstdcli.o fileio.o bench.o datagen.o dibio.o ifneq (,$(filter Windows%,$(OS))) windres/generate_res.bat endif - $(CC) $(FLAGS) $^ $(RES_FILE) -o zstd$(EXT) $(LDFLAGS) + $(CC) $(FLAGS) $^ $(RES_FILE) -o $@$(EXT) $(LDFLAGS) .PHONY: zstd-release zstd-release: DEBUGFLAGS := @@ -170,8 +170,8 @@ ifneq (,$(filter Windows%,$(OS))) endif $(CC) -m32 $(FLAGS) $^ $(RES32_FILE) -o $@$(EXT) -zstd-nolegacy : - $(MAKE) zstd ZSTD_LEGACY_SUPPORT=0 +zstd-nolegacy : $(ZSTD_FILES) $(ZDICT_FILES) zstdcli.o fileio.c bench.o datagen.o dibio.o + $(CC) $(FLAGS) $^ -o $@$(EXT) $(LDFLAGS) zstd-nomt : THREAD_CPP := zstd-nomt : THREAD_LD := @@ -212,7 +212,7 @@ zstd-decompress: $(ZSTDCOMMON_FILES) $(ZSTDDECOMP_FILES) zstdcli.c fileio.c zstd-compress: $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) zstdcli.c fileio.c $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NODECOMPRESS $^ -o $@$(EXT) -# zstd is now built with Multi-threading by default +# zstd is now built with multithreading enabled y default zstdmt: zstd .PHONY: generate_res @@ -225,7 +225,7 @@ clean: @$(RM) $(ZSTDDIR)/decompress/*.o $(ZSTDDIR)/decompress/zstd_decompress.gcda @$(RM) core *.o tmp* result* *.gcda dictionary *.zst \ zstd$(EXT) zstd32$(EXT) zstd-compress$(EXT) zstd-decompress$(EXT) \ - zstd-small$(EXT) zstd-frugal$(EXT) \ + zstd-small$(EXT) zstd-frugal$(EXT) zstd-nolegacy$(EXT) \ *.gcda default.profraw have_zlib$(EXT) @echo Cleaning completed From 23706fb743564ec5ab663b8d3105440b5a7783fb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 19 Aug 2017 01:14:36 -0700 Subject: [PATCH 310/318] updated doc on compilation variables --- programs/README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/programs/README.md b/programs/README.md index bd8fba069..3ac2c8f6d 100644 --- a/programs/README.md +++ b/programs/README.md @@ -3,39 +3,38 @@ Command Line Interface for Zstandard library Command Line Interface (CLI) can be created using the `make` command without any additional parameters. There are however other Makefile targets that create different variations of CLI: -- `zstd` : default CLI supporting gzip-like arguments; includes dictionary builder, benchmark, and support for decompression of legacy zstd versions -- `zstd32` : Same as `zstd`, but forced to compile in 32-bits mode -- `zstd_nolegacy` : Same as `zstd` except of support for decompression of legacy zstd versions -- `zstd-small` : CLI optimized for minimal size; without dictionary builder, benchmark, and support for decompression of legacy zstd versions -- `zstd-compress` : compressor-only version of CLI; without dictionary builder, benchmark, and support for decompression of legacy zstd versions -- `zstd-decompress` : decompressor-only version of CLI; without dictionary builder, benchmark, and support for decompression of legacy zstd versions +- `zstd` : default CLI supporting gzip-like arguments; includes dictionary builder, benchmark, and support for decompression of legacy zstd formats +- `zstd_nolegacy` : Same as `zstd` but without support for legacy zstd formats +- `zstd-small` : CLI optimized for minimal size; no dictionary builder, no benchmark, and no support for legacy zstd formats +- `zstd-compress` : version of CLI which can only compress into zstd format +- `zstd-decompress` : version of CLI which can only decompress zstd format #### Compilation variables `zstd` tries to detect and use the following features automatically : - __HAVE_THREAD__ : multithreading is automatically enabled when `pthread` is detected. - It's possible to disable multithread support, by either compiling `zstd-nomt` target or using HAVE_THREAD=0 variable. + 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 done through command `--format=gzip`. + 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 either compiling `zstd-nogz` target or using HAVE_ZLIB=0 variable. + 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 done through commands `--format=xz` and `--format=lzma` respectively. + 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 either compiling `zstd-noxz` target or using HAVE_LZMA=0 variable. + 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. @@ -61,7 +60,7 @@ will rely more and more on previously decoded content to compress the rest of th Usage of the dictionary builder and created dictionaries with CLI: -1. Create the dictionary : `zstd --train FullPathToTrainingSet/* -o dictionaryName` +1. Create the dictionary : `zstd --train PathToTrainingSet/* -o dictionaryName` 2. Compress with the dictionary: `zstd FILE -D dictionaryName` 3. Decompress with the dictionary: `zstd --decompress FILE.zst -D dictionaryName` @@ -70,8 +69,8 @@ Usage of the dictionary builder and created dictionaries with CLI: CLI includes in-memory compression benchmark module for zstd. The benchmark is conducted using given filenames. The files are read into memory and joined together. It makes benchmark more precise as it eliminates I/O overhead. -Many filenames can be supplied as multiple parameters, parameters with wildcards or -names of directories can be used as parameters with the `-r` option. +Multiple filenames can be supplied, as multiple parameters, with wildcards, +or names of directories can be used as parameters with `-r` option. The benchmark measures ratio, compressed size, compression and decompression speed. One can select compression levels starting from `-b` and ending with `-e`. @@ -101,13 +100,14 @@ Advanced arguments : -v : verbose mode; specify multiple times to increase verbosity -q : suppress warnings; specify twice to suppress errors too -c : force write to standard output, even if it is the console + -l : print information about zstd compressed files --ultra : enable levels beyond 19, up to 22 (requires more memory) - -T# : use # threads for compression (default:1) - -B# : select size of each job (default:0==automatic) --no-dictID : don't write dictID into header (dictionary compression) --[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) -M# : Set a memory usage limit for decompression From 2ecd34ee5efaece2b76a872dc4f02b7eaaa053d5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 19 Aug 2017 01:23:49 -0700 Subject: [PATCH 311/318] fixed unused variables warnings --- programs/zstdcli.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 4c551c154..d2bbf5d8a 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -701,7 +701,7 @@ int main(int argCount, const char* argv[]) BMK_setNbSeconds(bench_nbSeconds); BMK_benchFiles(filenameTable, filenameIdx, dictFileName, cLevel, cLevelLast, &compressionParams, setRealTimePrio); #endif - (void)bench_nbSeconds; + (void)bench_nbSeconds; (void)blockSize; (void)setRealTimePrio; goto _end; } @@ -772,6 +772,7 @@ int main(int argCount, const char* argv[]) else operationResult = FIO_compressMultipleFilenames(filenameTable, filenameIdx, outFileName ? outFileName : suffix, dictFileName, cLevel, &compressionParams); #else + (void)suffix; DISPLAY("Compression not supported\n"); #endif } else { /* decompression or test */ From 8b12812147356171ef5f330049c9510eeef0317a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 19 Aug 2017 12:17:57 -0700 Subject: [PATCH 312/318] fix #803 : wrong example in huffman bitstream section, reported by @ulikunitz --- doc/zstd_compression_format.md | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index 1f212fea2..ce703606f 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -16,7 +16,7 @@ Distribution of this document is unlimited. ### Version -0.2.5 (31/03/17) +0.2.6 (19/08/17) Introduction @@ -106,7 +106,7 @@ The structure of a single Zstandard frame is following: | `Magic_Number` | `Frame_Header` |`Data_Block`| [More data blocks] | [`Content_Checksum`] | |:--------------:|:--------------:|:----------:| ------------------ |:--------------------:| -| 4 bytes | 2-14 bytes | n bytes | | 0-4 bytes | +| 4 bytes | 2-14 bytes | n bytes | | 0-4 bytes | __`Magic_Number`__ @@ -1249,23 +1249,25 @@ Consequently, a last byte of `0` is not possible. And the final-bit-flag itself is not part of the useful bitstream. Hence, the last byte contains between 0 and 7 useful bits. -For example, if the literal sequence "0145" was encoded using the prefix codes above, -it would be encoded as: -``` -00000001 01110000 -``` +Starting from the end, +it's possible to read the bitstream in a __little-endian__ fashion, +keeping track of already used bits. Since the bitstream is encoded in reverse +order, starting from the end read symbols in forward order. + +For example, if the literal sequence "0145" was encoded using above prefix code, +it would be encoded (in reverse order) as: |Symbol | 5 | 4 | 1 | 0 | Padding | |--------|------|------|----|---|---------| -|Encoding|`0000`|`0001`|`01`|`1`| `10000` | +|Encoding|`0000`|`0001`|`01`|`1`| `00001` | -Starting from the end, -it's possible to read the bitstream in a __little-endian__ fashion, -keeping track of already used bits. Since the bitstream is encoded in reverse -order, by starting at the end the symbols can be read in forward order. +Resulting in following 2-bytes bitstream : +``` +00010000 00001101 +``` -Reading the last `Max_Number_of_Bits` bits, -it's then possible to compare extracted value to decoding table, +Reading highest `Max_Number_of_Bits` bits, +it's possible to compare extracted value to decoding table, determining the symbol to decode and number of bits to discard. The process continues up to reading the required number of symbols per stream. @@ -1516,12 +1518,13 @@ to crosscheck that an implementation build its decoding tables correctly. Version changes --------------- +- 0.2.6 : fixed an error in huffman example, by Ulrich Kunitz - 0.2.5 : minor typos and clarifications - 0.2.4 : section restructuring, by Sean Purcell - 0.2.3 : clarified several details, by Sean Purcell - 0.2.2 : added predefined codes, by Johannes Rudolph - 0.2.1 : clarify field names, by Przemyslaw Skibinski -- 0.2.0 : numerous format adjustments for zstd v0.8 +- 0.2.0 : numerous format adjustments for zstd v0.8+ - 0.1.2 : limit Huffman tree depth to 11 bits - 0.1.1 : reserved dictID ranges - 0.1.0 : initial release From d0d06e421f5eb8a617d1959ff5573684c293358b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 19 Aug 2017 12:26:09 -0700 Subject: [PATCH 313/318] added alternative representation for huffman bistream --- doc/zstd_compression_format.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index ce703606f..86d5ef263 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -1266,6 +1266,11 @@ Resulting in following 2-bytes bitstream : 00010000 00001101 ``` +alternative representation with clearer separation of fields : +``` +0001_0000 00001_1_01 +``` + Reading highest `Max_Number_of_Bits` bits, it's possible to compare extracted value to decoding table, determining the symbol to decode and number of bits to discard. From 1c108c811ecf25686d78dd5e2144d72de990294d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 19 Aug 2017 13:33:50 -0700 Subject: [PATCH 314/318] cli : Display supported formats on -vV command Requested and inspired by patch from @ib (#771) --- programs/.gitignore | 1 + programs/Makefile | 2 +- programs/README.md | 13 +++++++++++- programs/zstd.1 | 4 ++-- programs/zstd.1.md | 4 +++- programs/zstdcli.c | 51 ++++++++++++++++++++++++++++++++------------- 6 files changed, 55 insertions(+), 20 deletions(-) diff --git a/programs/.gitignore b/programs/.gitignore index a012ce2e2..701830c77 100644 --- a/programs/.gitignore +++ b/programs/.gitignore @@ -1,6 +1,7 @@ # local binary (Makefile) zstd zstd32 +zstd4 zstd-compress zstd-decompress zstd-frugal diff --git a/programs/Makefile b/programs/Makefile index 4cfb05e26..c5469cfc4 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -225,7 +225,7 @@ clean: @$(RM) $(ZSTDDIR)/decompress/*.o $(ZSTDDIR)/decompress/zstd_decompress.gcda @$(RM) core *.o tmp* result* *.gcda dictionary *.zst \ zstd$(EXT) zstd32$(EXT) zstd-compress$(EXT) zstd-decompress$(EXT) \ - zstd-small$(EXT) zstd-frugal$(EXT) zstd-nolegacy$(EXT) \ + zstd-small$(EXT) zstd-frugal$(EXT) zstd-nolegacy$(EXT) zstd4$(EXT) \ *.gcda default.profraw have_zlib$(EXT) @echo Cleaning completed diff --git a/programs/README.md b/programs/README.md index 3ac2c8f6d..8b65dfdb3 100644 --- a/programs/README.md +++ b/programs/README.md @@ -11,7 +11,7 @@ There are however other Makefile targets that create different variations of CLI #### Compilation variables -`zstd` tries to detect and use the following features automatically : +`zstd` scope can be altered by modifying the following compilation variables : - __HAVE_THREAD__ : multithreading is automatically enabled when `pthread` is detected. It's possible to disable multithread support, by setting HAVE_THREAD=0 . @@ -40,6 +40,17 @@ There are however other Makefile targets that create different variations of CLI In which case, linking stage will fail if `lzma` library cannot be found. This might be useful to prevent silent feature disabling. +- __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. + But older versions (< v0.8.0) produced different, incompatible, frames. + By default, `zstd` supports decoding legacy formats >= v0.4.0 (`ZSTD_LEGACY_SUPPORT=4`). + This can be altered by modifying this compilation variable. + `ZSTD_LEGACY_SUPPORT=1` means "support all formats >= v0.1.0". + `ZSTD_LEGACY_SUPPORT=2` means "support all formats >= v0.2.0", and so on. + `ZSTD_LEGACY_SUPPORT=0` means _DO NOT_ support any legacy format. + 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. + #### Aggregation of parameters CLI supports aggregation of parameters i.e. `-b1`, `-e18`, and `-i1` can be joined into `-b1e18i1`. diff --git a/programs/zstd.1 b/programs/zstd.1 index 2b80659cc..5a91eea28 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -1,5 +1,5 @@ . -.TH "ZSTD" "1" "July 2017" "zstd 1.3.1" "User Commands" +.TH "ZSTD" "1" "August 2017" "zstd 1.3.1" "User Commands" . .SH "NAME" \fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files @@ -149,7 +149,7 @@ display help/long help and exit . .TP \fB\-V\fR, \fB\-\-version\fR -display version number and exit +display version number and exit\. Advanced : \fB\-vV\fR also displays supported formats\. \fB\-vvV\fR also displays POSIX support\. . .TP \fB\-v\fR diff --git a/programs/zstd.1.md b/programs/zstd.1.md index ba51c8435..4310afa1a 100644 --- a/programs/zstd.1.md +++ b/programs/zstd.1.md @@ -140,7 +140,9 @@ the last one takes effect. * `-h`/`-H`, `--help`: display help/long help and exit * `-V`, `--version`: - display version number and exit + display version number and exit. + Advanced : `-vV` also displays supported formats. + `-vvV` also displays POSIX support. * `-v`: verbose mode * `-q`, `--quiet`: diff --git a/programs/zstdcli.c b/programs/zstdcli.c index d2bbf5d8a..e7eb71db6 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -16,7 +16,7 @@ #endif #ifndef ZSTDCLI_CLEVEL_MAX -# define ZSTDCLI_CLEVEL_MAX 19 /* when not using --ultra */ +# define ZSTDCLI_CLEVEL_MAX 19 /* without using --ultra */ #endif @@ -26,14 +26,15 @@ **************************************/ #include "platform.h" /* IS_CONSOLE, PLATFORM_POSIX_VERSION */ #include "util.h" /* UTIL_HAS_CREATEFILELIST, UTIL_createFileList */ +#include /* fprintf(), stdin, stdout, stderr */ #include /* strcmp, strlen */ #include /* errno */ -#include "fileio.h" +#include "fileio.h" /* stdinmark, stdoutmark, ZSTD_EXTENSION */ #ifndef ZSTD_NOBENCH # include "bench.h" /* BMK_benchFiles, BMK_SetNbSeconds */ #endif #ifndef ZSTD_NODICT -# include "dibio.h" +# include "dibio.h" /* ZDICT_cover_params_t, DiB_trainFromFiles() */ #endif #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel */ #include "zstd.h" /* ZSTD_VERSION_STRING */ @@ -64,7 +65,7 @@ #define MB *(1 <<20) #define GB *(1U<<30) -#define DEFAULT_DISPLAY_LEVEL 2 +#define DISPLAY_LEVEL_DEFAULT 2 static const char* g_defaultDictName = "dictionary"; static const unsigned g_defaultMaxDictSize = 110 KB; @@ -79,7 +80,7 @@ static U32 g_overlapLog = OVERLAP_LOG_DEFAULT; **************************************/ #define DISPLAY(...) fprintf(g_displayOut, __VA_ARGS__) #define DISPLAYLEVEL(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } -static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; /* 0 : no display, 1: errors, 2 : + result + interaction + warnings, 3 : + progression, 4 : + information */ +static int g_displayLevel = DISPLAY_LEVEL_DEFAULT; /* 0 : no display, 1: errors, 2 : + result + interaction + warnings, 3 : + progression, 4 : + information */ static FILE* g_displayOut; @@ -312,6 +313,35 @@ static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressi return 1; } +static void printVersion(void) +{ + DISPLAY(WELCOME_MESSAGE); + /* format support */ + DISPLAYLEVEL(3, "*** supports: zstd"); +#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>0) && (ZSTD_LEGACY_SUPPORT<8) + DISPLAYLEVEL(3, ", zstd legacy v0.%d+", ZSTD_LEGACY_SUPPORT); +#endif +#ifdef ZSTD_GZCOMPRESS + DISPLAYLEVEL(3, ", gzip"); +#endif +#ifdef ZSTD_LZ4COMPRESS + DISPLAYLEVEL(3, ", lz4"); +#endif +#ifdef ZSTD_LZMACOMPRESS + DISPLAYLEVEL(3, ", lzma, xz "); +#endif + DISPLAYLEVEL(3, "\n"); + /* posix support */ +#ifdef _POSIX_C_SOURCE + DISPLAYLEVEL(4, "_POSIX_C_SOURCE defined: %ldL\n", (long) _POSIX_C_SOURCE); +#endif +#ifdef _POSIX_VERSION + DISPLAYLEVEL(4, "_POSIX_VERSION defined: %ldL \n", (long) _POSIX_VERSION); +#endif +#ifdef PLATFORM_POSIX_VERSION + DISPLAYLEVEL(4, "PLATFORM_POSIX_VERSION defined: %ldL\n", (long) PLATFORM_POSIX_VERSION); +#endif +} typedef enum { zom_compress, zom_decompress, zom_test, zom_bench, zom_train, zom_list } zstd_operation_mode; @@ -491,7 +521,7 @@ int main(int argCount, const char* argv[]) switch(argument[0]) { /* Display help */ - case 'V': g_displayOut=stdout; DISPLAY(WELCOME_MESSAGE); CLEAN_RETURN(0); /* Version Only */ + case 'V': g_displayOut=stdout; printVersion(); CLEAN_RETURN(0); /* Version Only */ case 'H': case 'h': g_displayOut=stdout; CLEAN_RETURN(usage_advanced(programName)); @@ -641,15 +671,6 @@ int main(int argCount, const char* argv[]) /* Welcome message (if verbose) */ DISPLAYLEVEL(3, WELCOME_MESSAGE); -#ifdef _POSIX_C_SOURCE - DISPLAYLEVEL(4, "_POSIX_C_SOURCE defined: %ldL\n", (long) _POSIX_C_SOURCE); -#endif -#ifdef _POSIX_VERSION - DISPLAYLEVEL(4, "_POSIX_VERSION defined: %ldL \n", (long) _POSIX_VERSION); -#endif -#ifdef PLATFORM_POSIX_VERSION - DISPLAYLEVEL(4, "PLATFORM_POSIX_VERSION defined: %ldL\n", (long) PLATFORM_POSIX_VERSION); -#endif if (nbThreads == 0) { /* try to guess */ From 7db552676ebad975e81726d486e6530b35267cae Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 19 Aug 2017 15:07:54 -0700 Subject: [PATCH 315/318] reduced pool queue to 0 to save memory fixed : pool performance when jobs are fires fast and queueSize==0 --- lib/common/pool.c | 13 ++++--------- lib/compress/zstdmt_compress.c | 2 +- lib/legacy/zstd_legacy.h | 4 ++++ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index bcea21ca0..a227044f7 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -76,17 +76,13 @@ static void* POOL_thread(void* opaque) { return opaque; } /* Pop a job off the queue */ - { - POOL_job const job = ctx->queue[ctx->queueHead]; + { POOL_job const job = ctx->queue[ctx->queueHead]; ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize; ctx->numThreadsBusy++; ctx->queueEmpty = ctx->queueHead == ctx->queueTail; /* Unlock the mutex, signal a pusher, and run the job */ pthread_mutex_unlock(&ctx->queueMutex); - - if (ctx->queueSize > 1) { - pthread_cond_signal(&ctx->queuePushCond); - } + pthread_cond_signal(&ctx->queuePushCond); job.function(job.opaque); @@ -96,9 +92,8 @@ static void* POOL_thread(void* opaque) { ctx->numThreadsBusy--; pthread_mutex_unlock(&ctx->queueMutex); pthread_cond_signal(&ctx->queuePushCond); - } - } - } + } } + } /* for (;;) */ /* Unreachable */ } diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 02bb7c593..8564bc439 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -426,7 +426,7 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem) mtctx->allJobsCompleted = 1; mtctx->sectionSize = 0; mtctx->overlapLog = ZSTDMT_OVERLAPLOG_DEFAULT; - mtctx->factory = POOL_create(nbThreads, 1); + mtctx->factory = POOL_create(nbThreads, 0); mtctx->jobs = ZSTDMT_allocJobsTable(&nbJobs, cMem); mtctx->jobIDMask = nbJobs - 1; mtctx->bufPool = ZSTDMT_createBufferPool(nbThreads, cMem); diff --git a/lib/legacy/zstd_legacy.h b/lib/legacy/zstd_legacy.h index 6342192ee..1126e2466 100644 --- a/lib/legacy/zstd_legacy.h +++ b/lib/legacy/zstd_legacy.h @@ -123,6 +123,7 @@ MEM_STATIC size_t ZSTD_decompressLegacy( const void* dict,size_t dictSize) { U32 const version = ZSTD_isLegacy(src, compressedSize); + (void)dst; (void)dstCapacity; (void)dict; (void)dictSize; /* unused when ZSTD_LEGACY_SUPPORT >= 8 */ switch(version) { #if (ZSTD_LEGACY_SUPPORT <= 1) @@ -223,6 +224,7 @@ MEM_STATIC size_t ZSTD_freeLegacyStreamContext(void* legacyContext, U32 version) case 1 : case 2 : case 3 : + (void)legacyContext; return ERROR(version_unsupported); #if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : return ZBUFFv04_freeDCtx((ZBUFFv04_DCtx*)legacyContext); @@ -250,6 +252,7 @@ MEM_STATIC size_t ZSTD_initLegacyStream(void** legacyContext, U32 prevVersion, U case 1 : case 2 : case 3 : + (void)dict; (void)dictSize; return 0; #if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : @@ -306,6 +309,7 @@ MEM_STATIC size_t ZSTD_decompressLegacyStream(void* legacyContext, U32 version, case 1 : case 2 : case 3 : + (void)legacyContext; (void)output; (void)input; return ERROR(version_unsupported); #if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : From d6394cc4c37faccbb5b2191cb5a69a6ca8a8bfa0 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 20 Aug 2017 10:15:44 -0700 Subject: [PATCH 316/318] fixed test-zstd-nolegacy --- tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index 3734f7737..3be79c159 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -280,7 +280,7 @@ test-zstd: zstd zstd-playTests test-zstd32: ZSTD = $(PRGDIR)/zstd32 test-zstd32: zstd32 zstd-playTests -test-zstd-nolegacy: ZSTD = $(PRGDIR)/zstd +test-zstd-nolegacy: ZSTD = $(PRGDIR)/zstd-nolegacy test-zstd-nolegacy: zstd-nolegacy zstd-playTests test-gzstd: gzstd From e8d35cc5e9c6e2055bca5de4bec4591c005c710e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 20 Aug 2017 10:39:20 -0700 Subject: [PATCH 317/318] minor formulation change, recommended by @ulikunitz --- 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 86d5ef263..aa86d1420 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -1266,7 +1266,7 @@ Resulting in following 2-bytes bitstream : 00010000 00001101 ``` -alternative representation with clearer separation of fields : +Here is an alternative representation with the symbol codes separated by underscore: ``` 0001_0000 00001_1_01 ``` From 4912fc2acc3a957723dd5644a6c6075873a8c4f2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 20 Aug 2017 11:45:58 -0700 Subject: [PATCH 318/318] updated NEWS for v1.3.1 --- NEWS | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 641551d9f..59687532f 100644 --- a/NEWS +++ b/NEWS @@ -1,10 +1,17 @@ v1.3.1 -perf: substantially decreased memory usage in Multi-threading mode, thanks to reports by Tino Reichardt +New license : BSD + GPLv2 +perf: substantially decreased memory usage in Multi-threading mode, thanks to reports by Tino Reichardt (@mcmilk) perf: Multi-threading supports up to 256 threads. Cap at 256 when more are requested (#760) cli : improved and fixed --list command, by @ib (#772) -build: fix Visual compilation for non x86/x64 targets, reported by Greg Slazinski (#718) +cli : command -vV to list supported formats, by @ib (#771) +build : fixed binary variants, reported by @svenha (#788) +build : fix Visual compilation for non x86/x64 targets, reported by Greg Slazinski (@GregSlazinski) (#718) API exp : breaking change : ZSTD_getframeHeader() provides more information API exp : breaking change : pinned down values of error codes +doc : fixed huffman example, by Ulrich Kunitz (@ulikunitz) +new : contrib/adaptive-compression, I/O driven compression strength, by Paul Cruz (@paulcruz74) +new : contrib/long_distance_matching, statistics by Stella Lau (@stellamplau) +updated : contrib/linux-kernel, by Nick Terrell (@terrelln) v1.3.0 cli : new : `--list` command, by Paul Cruz

    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≪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 z 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, NULL); ctx->numJobs = numJobs; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); + { + unsigned u; + for (u=0; ujobs[u].jobCompleted_mutex = &ctx->jobCompleted_mutex; + ctx->jobs[u].jobCompleted_cond = &ctx->jobCompleted_cond; + ctx->jobs[u].jobReady_mutex = &ctx->jobReady_mutex; + ctx->jobs[u].jobReady_cond = &ctx->jobReady_cond; + } + } ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; @@ -314,7 +323,7 @@ int main(int argCount, const char* argv[]) } if (feof(srcFile)) break; } - + cleanup: /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; From 0b70152a9b262e6e2f4106c1ca261d7546663865 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 20:05:42 -0700 Subject: [PATCH 009/318] working I believe --- contrib/adaptive-compression/v2.c | 68 ++++++++++++++++++------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index edb2d0f3f..2f58e7503 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -33,6 +33,7 @@ typedef struct { unsigned compressionLevel; unsigned numActiveThreads; unsigned numJobs; + unsigned lastJobID; unsigned nextJobID; unsigned threadError; unsigned allJobsCompleted; @@ -63,7 +64,9 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) pthread_mutex_init(&ctx->allJobsCompleted_mutex, NULL); pthread_cond_init(&ctx->allJobsCompleted_cond, NULL); ctx->numJobs = numJobs; + ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); + DISPLAY("jobs %p\n", ctx->jobs); { unsigned u; for (u=0; ujobCompleted_cond); int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); + int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); + int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); int const fileError = fclose(ctx->dstFile); freeCompressionJobs(ctx); free(ctx->jobs); - return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError; + return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError | allJobsMutexError | allJobsCondError; } } @@ -131,10 +136,10 @@ static void* compressionThread(void* arg) jobDescription* job = &ctx->jobs[currJob]; pthread_mutex_lock(job->jobReady_mutex); while(job->jobReady == 0) { + DISPLAY("waiting\n"); pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); } pthread_mutex_unlock(job->jobReady_mutex); - /* compress the data */ { size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, job->compressionLevel); @@ -145,8 +150,12 @@ static void* compressionThread(void* arg) } job->compressedSize = compressedSize; } + pthread_mutex_lock(job->jobCompleted_mutex); + job->jobCompleted = 1; + pthread_cond_signal(job->jobCompleted_cond); + pthread_mutex_unlock(job->jobCompleted_mutex); currJob++; - if (currJob >= ctx->numJobs || ctx->threadError) { + if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished compressing all jobs */ break; } @@ -158,7 +167,6 @@ static void* outputThread(void* arg) { DISPLAY("started output thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; - DISPLAY("casted ctx\n"); unsigned currJob = 0; for ( ; ; ) { @@ -183,7 +191,7 @@ static void* outputThread(void* arg) } } currJob++; - if (currJob >= ctx->numJobs || ctx->threadError) { + if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished with all jobs */ pthread_mutex_lock(&ctx->allJobsCompleted_mutex); ctx->allJobsCompleted = 1; @@ -220,29 +228,30 @@ static size_t getFileSize(const char* const filename) static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) { unsigned const nextJob = ctx->nextJobID; - jobDescription job = ctx->jobs[nextJob]; - job.compressionLevel = ctx->compressionLevel; - job.src.start = malloc(srcSize); - job.src.size = srcSize; - job.dst.size = ZSTD_compressBound(srcSize); - job.dst.start = malloc(job.dst.size); - job.jobCompleted = 0; - job.jobCompleted_cond = &ctx->jobCompleted_cond; - job.jobCompleted_mutex = &ctx->jobCompleted_mutex; - job.jobReady_cond = &ctx->jobReady_cond; - job.jobReady_mutex = &ctx->jobReady_mutex; - job.jobID = nextJob; - if (!job.src.start || !job.dst.start) { + jobDescription* job = &ctx->jobs[nextJob]; + job->compressionLevel = ctx->compressionLevel; + job->src.start = malloc(srcSize); + job->src.size = srcSize; + job->dst.size = ZSTD_compressBound(srcSize); + job->dst.start = malloc(job->dst.size); + job->jobCompleted = 0; + job->jobCompleted_cond = &ctx->jobCompleted_cond; + job->jobCompleted_mutex = &ctx->jobCompleted_mutex; + job->jobReady_cond = &ctx->jobReady_cond; + job->jobReady_mutex = &ctx->jobReady_mutex; + job->jobID = nextJob; + if (!job->src.start || !job->dst.start) { /* problem occurred, free things then return */ - if (job.src.start) free(job.src.start); - if (job.dst.start) free(job.dst.start); + DISPLAY("Error: problem occurred during job creation\n"); + if (job->src.start) free(job->src.start); + if (job->dst.start) free(job->dst.start); return 1; } - memcpy(job.src.start, data, srcSize); - pthread_mutex_lock(job.jobReady_mutex); - job.jobReady = 1; - pthread_cond_signal(job.jobReady_cond); - pthread_mutex_unlock(job.jobReady_mutex); + memcpy(job->src.start, data, srcSize); + pthread_mutex_lock(job->jobReady_mutex); + job->jobReady = 1; + pthread_cond_signal(job->jobReady_cond); + pthread_mutex_unlock(job->jobReady_mutex); ctx->nextJobID++; return 0; } @@ -305,14 +314,12 @@ int main(int argCount, const char* argv[]) /* creating jobs */ for ( ; ; ) { - DISPLAY("in job creation loop\n"); size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); ret = 1; goto cleanup; } - DISPLAY("reading was fine\n"); /* reading was fine, now create the compression job */ { int const error = createCompressionJob(ctx, src, readSize); @@ -321,9 +328,12 @@ int main(int argCount, const char* argv[]) goto cleanup; } } - if (feof(srcFile)) break; + if (feof(srcFile)) { + ctx->lastJobID = ctx->nextJobID; + break; + } } - + cleanup: /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; From a47ebb16070af45f31407f614b1aee8f031ffc3d Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 09:23:46 -0700 Subject: [PATCH 010/318] removed print statements --- contrib/adaptive-compression/v2.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index 2f58e7503..9a050bf7d 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -66,7 +66,6 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->numJobs = numJobs; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); - DISPLAY("jobs %p\n", ctx->jobs); { unsigned u; for (u=0; unumJobs; u++) { - DISPLAY("freeing compression job %u\n", u); - DISPLAY("%u\n", ctx->numJobs); jobDescription job = ctx->jobs[u]; if (job.dst.start) free(job.dst.start); if (job.src.start) free(job.src.start); @@ -129,14 +126,12 @@ static int freeCCtx(adaptCCtx* ctx) static void* compressionThread(void* arg) { - DISPLAY("started compression thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; unsigned currJob = 0; for ( ; ; ) { jobDescription* job = &ctx->jobs[currJob]; pthread_mutex_lock(job->jobReady_mutex); while(job->jobReady == 0) { - DISPLAY("waiting\n"); pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); } pthread_mutex_unlock(job->jobReady_mutex); @@ -165,7 +160,6 @@ static void* compressionThread(void* arg) static void* outputThread(void* arg) { - DISPLAY("started output thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; unsigned currJob = 0; From 9a147d86715da50610c905d347c88ae10756c9f5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 09:37:52 -0700 Subject: [PATCH 011/318] removed unnecessary checks for null pointer on free --- contrib/adaptive-compression/v2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index 9a050bf7d..df6bf5705 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -98,8 +98,8 @@ static void freeCompressionJobs(adaptCCtx* ctx) unsigned u; for (u=0; unumJobs; u++) { jobDescription job = ctx->jobs[u]; - if (job.dst.start) free(job.dst.start); - if (job.src.start) free(job.src.start); + free(job.dst.start); + free(job.src.start); } } @@ -237,8 +237,8 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) if (!job->src.start || !job->dst.start) { /* problem occurred, free things then return */ DISPLAY("Error: problem occurred during job creation\n"); - if (job->src.start) free(job->src.start); - if (job->dst.start) free(job->dst.start); + free(job->src.start); + free(job->dst.start); return 1; } memcpy(job->src.start, data, srcSize); @@ -332,6 +332,6 @@ cleanup: /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; - if (src != NULL) free(src); + free(src); return ret; } From c9f49198b858a89781562d07a35772712c272b8a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 09:49:27 -0700 Subject: [PATCH 012/318] fixed TODOs --- contrib/adaptive-compression/v2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index df6bf5705..c25f1db82 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -174,13 +174,13 @@ static void* outputThread(void* arg) size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { DISPLAY("Error: an error occurred during compression\n"); - return arg; /* TODO: return something else if error */ + return arg; } { size_t const writeSize = fwrite(ctx->jobs[currJob].dst.start, 1, compressedSize, ctx->dstFile); if (writeSize != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); - return arg; /* TODO: return something else if error */ + return arg; } } } @@ -262,7 +262,7 @@ int main(int argCount, const char* argv[]) BYTE* const src = malloc(FILE_CHUNK_SIZE); FILE* const srcFile = fopen(srcFilename, "rb"); size_t fileSize = getFileSize(srcFilename); - size_t const numJobsPrelim = (fileSize >> 22) + 1; /* TODO: figure out why can't divide here */ + size_t const numJobsPrelim = (fileSize / ((size_t)FILE_CHUNK_SIZE)); size_t const numJobs = (numJobsPrelim * FILE_CHUNK_SIZE) == fileSize ? numJobsPrelim : numJobsPrelim + 1; int ret = 0; adaptCCtx* ctx = NULL; From 5df4cb053029b4b4fcb5c90f8ee831ae423623c2 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 09:57:50 -0700 Subject: [PATCH 013/318] renamed files --- contrib/adaptive-compression/Makefile | 8 ++++---- contrib/adaptive-compression/{v2.c => multi.c} | 4 ++-- contrib/adaptive-compression/run.sh | 8 ++++++-- contrib/adaptive-compression/{v1.c => single.c} | 0 4 files changed, 12 insertions(+), 8 deletions(-) rename contrib/adaptive-compression/{v2.c => multi.c} (99%) rename contrib/adaptive-compression/{v1.c => single.c} (100%) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index fbab219c3..c6862198b 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -18,15 +18,15 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -all: clean v1 v2 -v1: $(ZSTD_FILES) v1.c +all: clean single multi +single: $(ZSTD_FILES) single.c $(CC) $(FLAGS) $^ -o $@ -v2: $(ZSTD_FILES) v2.c +multi: $(ZSTD_FILES) multi.c $(CC) $(FLAGS) $^ -o $@ clean: - @$(RM) -f v1 v2 + @$(RM) -f single multi @$(RM) -rf *.dSYM @$(RM) -f tmp* @echo "finished cleaning" diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/multi.c similarity index 99% rename from contrib/adaptive-compression/v2.c rename to contrib/adaptive-compression/multi.c index c25f1db82..e1fa31774 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/multi.c @@ -174,7 +174,7 @@ static void* outputThread(void* arg) size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { DISPLAY("Error: an error occurred during compression\n"); - return arg; + return arg; } { size_t const writeSize = fwrite(ctx->jobs[currJob].dst.start, 1, compressedSize, ctx->dstFile); @@ -253,7 +253,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { - if (argCount < 2) { + if (argCount < 3) { DISPLAY("Error: not enough arguments\n"); return 1; } diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index f9e276730..b9c4caf7f 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -1,2 +1,6 @@ -make clean v2 -./v2 tests/test2048.pdf tmp.zst +make clean multi +./multi tests/test2048.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test2048.pdf +echo "diff test complete" +make clean diff --git a/contrib/adaptive-compression/v1.c b/contrib/adaptive-compression/single.c similarity index 100% rename from contrib/adaptive-compression/v1.c rename to contrib/adaptive-compression/single.c From 9ccd55f3a8957eca5d580b669b4c69db349fff06 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 10:20:56 -0700 Subject: [PATCH 014/318] free ctx fields when error occurs during creation --- contrib/adaptive-compression/multi.c | 57 +++++++++++++++++----------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index e1fa31774..a461dca72 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -47,6 +47,34 @@ typedef struct { FILE* dstFile; } adaptCCtx; +static void freeCompressionJobs(adaptCCtx* ctx) +{ + unsigned u; + for (u=0; unumJobs; u++) { + jobDescription job = ctx->jobs[u]; + free(job.dst.start); + free(job.src.start); + } +} + +static int freeCCtx(adaptCCtx* ctx) +{ + { + int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); + int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); + int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); + int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); + int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); + int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); + int const fileCloseError = ctx->dstFile != NULL ? fclose(ctx->dstFile) : 0; + if (ctx->jobs){ + freeCompressionJobs(ctx); + free(ctx->jobs); + } + return completedMutexError | completedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError; + } +} + static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) { @@ -80,12 +108,14 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->allJobsCompleted = 0; if (!ctx->jobs) { DISPLAY("Error: could not allocate space for jobs during context creation\n"); + freeCCtx(ctx); return NULL; } { FILE* dstFile = fopen(outFilename, "wb"); if (dstFile == NULL) { DISPLAY("Error: could not open output file\n"); + freeCCtx(ctx); return NULL; } ctx->dstFile = dstFile; @@ -93,35 +123,15 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) return ctx; } -static void freeCompressionJobs(adaptCCtx* ctx) -{ - unsigned u; - for (u=0; unumJobs; u++) { - jobDescription job = ctx->jobs[u]; - free(job.dst.start); - free(job.src.start); - } -} -static int freeCCtx(adaptCCtx* ctx) + +static void waitUntilAllJobsCompleted(adaptCCtx* ctx) { pthread_mutex_lock(&ctx->allJobsCompleted_mutex); while (ctx->allJobsCompleted == 0) { pthread_cond_wait(&ctx->allJobsCompleted_cond, &ctx->allJobsCompleted_mutex); } pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); - { - int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); - int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); - int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); - int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); - int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); - int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); - int const fileError = fclose(ctx->dstFile); - freeCompressionJobs(ctx); - free(ctx->jobs); - return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError | allJobsMutexError | allJobsCondError; - } } static void* compressionThread(void* arg) @@ -140,7 +150,7 @@ static void* compressionThread(void* arg) size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, job->compressionLevel); if (ZSTD_isError(compressedSize)) { ctx->threadError = 1; - DISPLAY("Error: somethign went wrong during compression\n"); + DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(compressedSize)); return arg; } job->compressedSize = compressedSize; @@ -329,6 +339,7 @@ int main(int argCount, const char* argv[]) } cleanup: + waitUntilAllJobsCompleted(ctx); /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; From dd8a591d5d74d50f49ea34088e401ed8eadd78a0 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 10:48:04 -0700 Subject: [PATCH 015/318] moved main logic for job creation into a separate function --- contrib/adaptive-compression/multi.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index a461dca72..b89d0c239 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -260,15 +260,8 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) return 0; } -/* return 0 if successful, else return error */ -int main(int argCount, const char* argv[]) +static int compressFilename(const char* const srcFilename, const char* const dstFilename) { - if (argCount < 3) { - DISPLAY("Error: not enough arguments\n"); - return 1; - } - const char* const srcFilename = argv[1]; - const char* const dstFilename = argv[2]; BYTE* const src = malloc(FILE_CHUNK_SIZE); FILE* const srcFile = fopen(srcFilename, "rb"); size_t fileSize = getFileSize(srcFilename); @@ -346,3 +339,13 @@ cleanup: free(src); return ret; } + +/* return 0 if successful, else return error */ +int main(int argCount, const char* argv[]) +{ + if (argCount < 3) { + DISPLAY("Error: not enough arguments\n"); + return 1; + } + return compressFilename(argv[1], argv[2]); +} From a2680e5b9605941b2b9d250f3a7915fa7f48d5b8 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 11:52:55 -0700 Subject: [PATCH 016/318] removed calculation of file size and replaced with limited number of available jobs --- contrib/adaptive-compression/multi.c | 39 +++++++++++++++++++++++----- contrib/adaptive-compression/run.sh | 33 +++++++++++++++++++++++ 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index b89d0c239..a60f48ece 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,5 +1,6 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define FILE_CHUNK_SIZE 4 << 20 +#define MAX_NUM_JOBS 30; typedef unsigned char BYTE; #include /* fprintf */ @@ -22,10 +23,13 @@ typedef struct { unsigned jobID; unsigned jobCompleted; unsigned jobReady; + unsigned jobWritten; pthread_mutex_t* jobCompleted_mutex; pthread_cond_t* jobCompleted_cond; pthread_mutex_t* jobReady_mutex; pthread_cond_t* jobReady_cond; + pthread_mutex_t* jobWrite_mutex; + pthread_cond_t* jobWrite_cond; size_t compressedSize; } jobDescription; @@ -43,6 +47,8 @@ typedef struct { pthread_cond_t jobReady_cond; pthread_mutex_t allJobsCompleted_mutex; pthread_cond_t allJobsCompleted_cond; + pthread_mutex_t jobWrite_mutex; + pthread_cond_t jobWrite_cond; jobDescription* jobs; FILE* dstFile; } adaptCCtx; @@ -66,12 +72,14 @@ static int freeCCtx(adaptCCtx* ctx) int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); + int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex); + int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); int const fileCloseError = ctx->dstFile != NULL ? fclose(ctx->dstFile) : 0; if (ctx->jobs){ freeCompressionJobs(ctx); free(ctx->jobs); } - return completedMutexError | completedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError; + return completedMutexError | completedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError; } } @@ -91,6 +99,8 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) pthread_cond_init(&ctx->jobReady_cond, NULL); pthread_mutex_init(&ctx->allJobsCompleted_mutex, NULL); pthread_cond_init(&ctx->allJobsCompleted_cond, NULL); + pthread_mutex_init(&ctx->jobWrite_mutex, NULL); + pthread_cond_init(&ctx->jobWrite_cond, NULL); ctx->numJobs = numJobs; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -101,6 +111,9 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobs[u].jobCompleted_cond = &ctx->jobCompleted_cond; ctx->jobs[u].jobReady_mutex = &ctx->jobReady_mutex; ctx->jobs[u].jobReady_cond = &ctx->jobReady_cond; + ctx->jobs[u].jobWrite_mutex = &ctx->jobWrite_mutex; + ctx->jobs[u].jobWrite_cond = &ctx->jobWrite_cond; + ctx->jobs[u].jobWritten = 1; } } ctx->nextJobID = 0; @@ -139,7 +152,8 @@ static void* compressionThread(void* arg) adaptCCtx* ctx = (adaptCCtx*)arg; unsigned currJob = 0; for ( ; ; ) { - jobDescription* job = &ctx->jobs[currJob]; + unsigned const currJobIndex = currJob % ctx->numJobs; + jobDescription* job = &ctx->jobs[currJobIndex]; pthread_mutex_lock(job->jobReady_mutex); while(job->jobReady == 0) { pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); @@ -174,7 +188,8 @@ static void* outputThread(void* arg) unsigned currJob = 0; for ( ; ; ) { - jobDescription* job = &ctx->jobs[currJob]; + unsigned const currJobIndex = currJob % ctx->numJobs; + jobDescription* job = &ctx->jobs[currJobIndex]; pthread_mutex_lock(job->jobCompleted_mutex); while (job->jobCompleted == 0) { pthread_cond_wait(job->jobCompleted_cond, job->jobCompleted_mutex); @@ -187,7 +202,7 @@ static void* outputThread(void* arg) return arg; } { - size_t const writeSize = fwrite(ctx->jobs[currJob].dst.start, 1, compressedSize, ctx->dstFile); + size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, ctx->dstFile); if (writeSize != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); return arg; @@ -195,6 +210,10 @@ static void* outputThread(void* arg) } } currJob++; + pthread_mutex_lock(job->jobWrite_mutex); + job->jobWritten = 1; + pthread_cond_signal(job->jobWrite_cond); + pthread_mutex_unlock(job->jobWrite_mutex); if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished with all jobs */ pthread_mutex_lock(&ctx->allJobsCompleted_mutex); @@ -232,13 +251,20 @@ static size_t getFileSize(const char* const filename) static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) { unsigned const nextJob = ctx->nextJobID; - jobDescription* job = &ctx->jobs[nextJob]; + unsigned const nextJobIndex = nextJob % ctx->numJobs; + jobDescription* job = &ctx->jobs[nextJobIndex]; + pthread_mutex_lock(job->jobWrite_mutex); + while (job->jobWritten == 0) { + pthread_cond_wait(job->jobWrite_cond, job->jobWrite_mutex); + } + pthread_mutex_unlock(job->jobWrite_mutex); job->compressionLevel = ctx->compressionLevel; job->src.start = malloc(srcSize); job->src.size = srcSize; job->dst.size = ZSTD_compressBound(srcSize); job->dst.start = malloc(job->dst.size); job->jobCompleted = 0; + job->jobWritten = 0; job->jobCompleted_cond = &ctx->jobCompleted_cond; job->jobCompleted_mutex = &ctx->jobCompleted_mutex; job->jobReady_cond = &ctx->jobReady_cond; @@ -265,8 +291,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst BYTE* const src = malloc(FILE_CHUNK_SIZE); FILE* const srcFile = fopen(srcFilename, "rb"); size_t fileSize = getFileSize(srcFilename); - size_t const numJobsPrelim = (fileSize / ((size_t)FILE_CHUNK_SIZE)); - size_t const numJobs = (numJobsPrelim * FILE_CHUNK_SIZE) == fileSize ? numJobsPrelim : numJobsPrelim + 1; + size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index b9c4caf7f..9d2e98706 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -1,6 +1,39 @@ make clean multi + ./multi tests/test2048.pdf tmp.zst zstd -d tmp.zst diff tmp tests/test2048.pdf echo "diff test complete" +rm tmp* + +./multi tests/test512.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test512.pdf +echo "diff test complete" +rm tmp* + +./multi tests/test64.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test64.pdf +echo "diff test complete" +rm tmp* + +./multi tests/test16.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test16.pdf +echo "diff test complete" +rm tmp* + +./multi tests/test4.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test4.pdf +echo "diff test complete" +rm tmp* + +./multi tests/test.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test.pdf +echo "diff test complete" +rm tmp* + make clean From 898c1a5b467047637f9ebb8cbe6b525bdee987d5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 11:54:21 -0700 Subject: [PATCH 017/318] removed references to file size computation and file size function --- contrib/adaptive-compression/multi.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index a60f48ece..79904551d 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -226,28 +226,6 @@ static void* outputThread(void* arg) return arg; } - -static size_t getFileSize(const char* const filename) -{ - FILE* fd = fopen(filename, "rb"); - if (fd == NULL) { - DISPLAY("Error: could not open file in order to get file size\n"); - return -1; /* intentional underflow */ - } - if (fseek(fd, 0, SEEK_END) != 0) { - DISPLAY("Error: fseek failed during file size computation\n"); - return -1; - } - { - size_t const fileSize = ftell(fd); - if (fclose(fd) != 0) { - DISPLAY("Error: could not close file during file size computation\n"); - return -1; - } - return fileSize; - } -} - static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) { unsigned const nextJob = ctx->nextJobID; @@ -290,17 +268,12 @@ static int compressFilename(const char* const srcFilename, const char* const dst { BYTE* const src = malloc(FILE_CHUNK_SIZE); FILE* const srcFile = fopen(srcFilename, "rb"); - size_t fileSize = getFileSize(srcFilename); size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; /* checking for errors */ - if (fileSize == (size_t)(-1)) { - ret = 1; - goto cleanup; - } if (!srcFilename || !dstFilename || !src || !srcFile) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; From b42108386a5eaccafbc5b8d15e3ac71e4c548a9a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 12:20:16 -0700 Subject: [PATCH 018/318] added some basic parsing for args --- contrib/adaptive-compression/multi.c | 35 +++++++++++++++++++++++----- contrib/adaptive-compression/run.sh | 12 +++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 79904551d..4eb3d3b16 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,6 +1,8 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define FILE_CHUNK_SIZE 4 << 20 #define MAX_NUM_JOBS 30; +#define stdinmark "/*stdin*\\" +#define stdoutmark "/*stdout*\\" typedef unsigned char BYTE; #include /* fprintf */ @@ -125,7 +127,8 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) return NULL; } { - FILE* dstFile = fopen(outFilename, "wb"); + unsigned const stdoutUsed = !strcmp(outFilename, stdoutmark); + FILE* dstFile = stdoutUsed ? stdout : fopen(outFilename, "wb"); if (dstFile == NULL) { DISPLAY("Error: could not open output file\n"); freeCCtx(ctx); @@ -267,7 +270,8 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) static int compressFilename(const char* const srcFilename, const char* const dstFilename) { BYTE* const src = malloc(FILE_CHUNK_SIZE); - FILE* const srcFile = fopen(srcFilename, "rb"); + unsigned const stdinUsed = !strcmp(srcFilename, stdinmark); + FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb"); size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; @@ -341,9 +345,28 @@ cleanup: /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { - if (argCount < 3) { - DISPLAY("Error: not enough arguments\n"); - return 1; + const char* inFilename = stdinmark; + const char* outFilename = stdoutmark; + unsigned nextArgumentIsOutFilename = 0; + int argNum; + for (argNum=1; argNum Date: Wed, 5 Jul 2017 13:23:34 -0700 Subject: [PATCH 019/318] added tests to run.sh --- contrib/adaptive-compression/run.sh | 52 +++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index ec2df0387..e6cb2066e 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -1,39 +1,79 @@ make clean multi +echo "running file tests" + ./multi tests/test2048.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test2048.pdf -echo "diff test complete" +echo "diff test complete: test2048.pdf" rm tmp* ./multi tests/test512.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test512.pdf -echo "diff test complete" +echo "diff test complete: test512.pdf" rm tmp* ./multi tests/test64.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test64.pdf -echo "diff test complete" +echo "diff test complete: test64.pdf" rm tmp* ./multi tests/test16.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test16.pdf -echo "diff test complete" +echo "diff test complete: test16.pdf" rm tmp* ./multi tests/test4.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test4.pdf -echo "diff test complete" +echo "diff test complete: test4.pdf" rm tmp* ./multi tests/test.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test.pdf -echo "diff test complete" +echo "diff test complete: test.pdf" +rm tmp* + +echo "Running std input/output tests" + +cat tests/test2048.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test2048.pdf +echo "diff test complete: test2048.pdf" +rm tmp* + +cat tests/test512.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test512.pdf +echo "diff test complete: test512.pdf" +rm tmp* + +cat tests/test64.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test64.pdf +echo "diff test complete: test64.pdf" +rm tmp* + +cat tests/test16.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test16.pdf +echo "diff test complete: test16.pdf" +rm tmp* + +cat tests/test4.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test4.pdf +echo "diff test complete: test4.pdf" +rm tmp* + +cat tests/test.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test.pdf +echo "diff test complete: test.pdf" rm tmp* make clean From 88f3d8641e55544fbf22f7226ee1793828c65983 Mon Sep 17 00:00:00 2001 From: Stella Lau Date: Wed, 5 Jul 2017 13:57:07 -0700 Subject: [PATCH 020/318] Initial long distance matcher commit --- contrib/long_distance_matching/Makefile | 27 +++ contrib/long_distance_matching/ldm.c | 43 +++++ contrib/long_distance_matching/ldm.h | 10 ++ contrib/long_distance_matching/main.c | 227 ++++++++++++++++++++++++ contrib/long_distance_matching/main.h | 7 + 5 files changed, 314 insertions(+) create mode 100644 contrib/long_distance_matching/Makefile create mode 100644 contrib/long_distance_matching/ldm.c create mode 100644 contrib/long_distance_matching/ldm.h create mode 100644 contrib/long_distance_matching/main.c create mode 100644 contrib/long_distance_matching/main.h diff --git a/contrib/long_distance_matching/Makefile b/contrib/long_distance_matching/Makefile new file mode 100644 index 000000000..bfe02ea2a --- /dev/null +++ b/contrib/long_distance_matching/Makefile @@ -0,0 +1,27 @@ +# ################################################################ +# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. +# ################################################################ + +# This Makefile presumes libzstd is installed, using `sudo make install` + + +.PHONY: default all clean + +default: all + +all: main + + +main : ldm.c main.c + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +clean: + @rm -f core *.o tmp* result* *.ldm *.ldm.dec \ + main + @echo Cleaning completed + diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c new file mode 100644 index 000000000..34118c81f --- /dev/null +++ b/contrib/long_distance_matching/ldm.c @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "ldm.h" + +typedef uint8_t BYTE; +typedef uint16_t U16; +typedef uint32_t U32; +typedef int32_t S32; +typedef uint64_t U64; + +typedef uint64_t tag; + +struct hash_entry { + U64 offset; + tag t; +}; + +size_t LDM_compress(const char *source, char *dest, size_t source_size, size_t max_dest_size) { + // max_dest_size >= source_size + + + /** + * Loop: + * Find match at position k (hash next n bytes, rolling hash) + * Compute match length + * Output literal length: k (sequences of 4 + (k-4) bytes) + * Output match length + * Output literals + * Output offset + */ + + memcpy(dest, source, source_size); + return source_size; +} + +size_t LDM_decompress(const char *source, char *dest, size_t compressed_size, size_t max_decompressed_size) { + memcpy(dest, source, compressed_size); + return compressed_size; +} + + diff --git a/contrib/long_distance_matching/ldm.h b/contrib/long_distance_matching/ldm.h new file mode 100644 index 000000000..d0151373c --- /dev/null +++ b/contrib/long_distance_matching/ldm.h @@ -0,0 +1,10 @@ +#ifndef LDM_H +#define LDM_H + +#include /* size_t */ + +size_t LDM_compress(const char *source, char *dest, size_t source_size, size_t max_dest_size); + +size_t LDM_decompress(const char *source, char *dest, size_t compressed_size, size_t max_decompressed_size); + +#endif /* LDM_H */ diff --git a/contrib/long_distance_matching/main.c b/contrib/long_distance_matching/main.c new file mode 100644 index 000000000..ddf5145f7 --- /dev/null +++ b/contrib/long_distance_matching/main.c @@ -0,0 +1,227 @@ +#include +#include +#include + +#include "ldm.h" + +#define BUF_SIZE 16*1024 // Block size +#define LDM_HEADER_SIZE 8 + +static size_t compress_file(FILE *in, FILE *out, size_t *size_in, + size_t *size_out) { + char *src, *buf = NULL; + size_t r = 1; + size_t size, n, k, count_in = 0, count_out = 0, offset, frame_size = 0; + + src = malloc(BUF_SIZE); + if (!src) { + printf("Not enough memory\n"); + goto cleanup; + } + + size = BUF_SIZE + LDM_HEADER_SIZE; + buf = malloc(size); + if (!buf) { + printf("Not enough memory\n"); + goto cleanup; + } + + + for (;;) { + k = fread(src, 1, BUF_SIZE, in); + if (k == 0) + break; + count_in += k; + + n = LDM_compress(src, buf, k, BUF_SIZE); + + // n = k; + // offset += n; + offset = k; + count_out += k; + +// k = fwrite(src, 1, offset, out); + + k = fwrite(buf, 1, offset, out); + if (k < offset) { + if (ferror(out)) + printf("Write failed\n"); + else + printf("Short write\n"); + goto cleanup; + } + + } + *size_in = count_in; + *size_out = count_out; + r = 0; + cleanup: + free(src); + free(buf); + return r; +} + +static size_t decompress_file(FILE *in, FILE *out) { + void *src = malloc(BUF_SIZE); + void *dst = NULL; + size_t dst_capacity = BUF_SIZE; + size_t ret = 1; + size_t bytes_written = 0; + + if (!src) { + perror("decompress_file(src)"); + goto cleanup; + } + + while (ret != 0) { + /* Load more input */ + size_t src_size = fread(src, 1, BUF_SIZE, in); + void *src_ptr = src; + void *src_end = src_ptr + src_size; + if (src_size == 0 || ferror(in)) { + printf("(TODO): Decompress: not enough input or error reading file\n"); + //TODO + ret = 0; + goto cleanup; + } + + /* Allocate destination buffer if it hasn't been allocated already */ + if (!dst) { + dst = malloc(dst_capacity); + if (!dst) { + perror("decompress_file(dst)"); + goto cleanup; + } + } + + // TODO + + /* Decompress: + * Continue while there is more input to read. + */ + while (src_ptr != src_end && ret != 0) { + // size_t dst_size = src_size; + size_t dst_size = LDM_decompress(src, dst, src_size, dst_capacity); + size_t written = fwrite(dst, 1, dst_size, out); +// printf("Writing %zu bytes\n", dst_size); + bytes_written += dst_size; + if (written != dst_size) { + printf("Decompress: Failed to write to file\n"); + goto cleanup; + } + src_ptr += src_size; + src_size = src_end - src_ptr; + } + + /* Update input */ + + } + + printf("Wrote %zu bytes\n", bytes_written); + + cleanup: + free(src); + free(dst); + + return ret; +} + +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; +} + +int main(int argc, char *argv[]) { + char inpFilename[256] = { 0 }; + char ldmFilename[256] = { 0 }; + char decFilename[256] = { 0 }; + + if (argc < 2) { + printf("Please specify input filename\n"); + return 0; + } + 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 */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *outFp = fopen(ldmFilename, "wb"); + size_t sizeIn = 0; + size_t sizeOut = 0; + size_t ret; + printf("compress : %s -> %s\n", inpFilename, ldmFilename); + ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut); + if (ret) { + printf("compress : failed with code %zu\n", ret); + return ret; + } + printf("%s: %zu → %zu bytes, %.1f%%\n", + inpFilename, sizeIn, sizeOut, + (double)sizeOut / sizeIn * 100); + printf("compress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* decompress */ + { + FILE *inpFp = fopen(ldmFilename, "rb"); + FILE *outFp = fopen(decFilename, "wb"); + size_t ret; + + printf("decompress : %s -> %s\n", ldmFilename, decFilename); + ret = decompress_file(inpFp, outFp); + if (ret) { + printf("decompress : failed with code %zu\n", ret); + return ret; + } + printf("decompress : done\n"); + + fclose(outFp); + fclose(inpFp); + } + + /* verify */ + { + FILE *inpFp = fopen(inpFilename, "rb"); + FILE *decFp = fopen(decFilename, "rb"); + + 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"); + } + + fclose(decFp); + fclose(inpFp); + } + + + return 0; +} + + diff --git a/contrib/long_distance_matching/main.h b/contrib/long_distance_matching/main.h new file mode 100644 index 000000000..a0b030121 --- /dev/null +++ b/contrib/long_distance_matching/main.h @@ -0,0 +1,7 @@ +#ifndef _MAIN_H +#define _MAIN_H + +void compress_file(FILE *in, FILE *out, int argc, char *argv[]); +void decompress_file(FILE *in, FILE *out, int argc, char *argv[]); + +#endif /* _MAIN_H */ From faeb6e0b1b534c5f73a9282f49cf1209a1417148 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 14:19:56 -0700 Subject: [PATCH 021/318] added filenameTable for multiple files --- contrib/adaptive-compression/multi.c | 75 ++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 4eb3d3b16..a6fd64ce8 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -3,6 +3,7 @@ #define MAX_NUM_JOBS 30; #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" +#define MAX_PATH 256 typedef unsigned char BYTE; #include /* fprintf */ @@ -342,31 +343,73 @@ cleanup: return ret; } +static int compressFilenames(const char** filenameTable, unsigned numFiles) +{ + int ret = 0; + unsigned fileNum; + char outFile[MAX_PATH]; + for (fileNum=0; fileNum MAX_PATH) { + DISPLAY("Error: output filename is too long\n"); + return 1; + } + ret |= compressFilename(filename, outFile); + } + return ret; +} + /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { - const char* inFilename = stdinmark; - const char* outFilename = stdoutmark; - unsigned nextArgumentIsOutFilename = 0; + const char* outFilename = NULL; + const char** filenameTable = (const char**)malloc(argCount*sizeof(const char*)); + unsigned filenameIdx = 0; + filenameTable[0] = stdinmark; + int ret = 0; int argNum; + + if (filenameTable == NULL) { + DISPLAY("Error: could not allocate sapce for filename table.\n"); + return 1; + } + for (argNum=1; argNum 1 && argument[1] == 'o') { + argument += 2; + outFilename = argument; + continue; + } + else { + DISPLAY("Error: invalid argument provided\n"); + ret = 1; + goto _main_exit; } } - if (nextArgumentIsOutFilename) { - outFilename = argument; - } - else { - inFilename = argument; - } + /* regular files to be compressed */ + filenameTable[filenameIdx++] = argument; } - return compressFilename(inFilename, outFilename); + + /* error checking with number of files */ + if (filenameIdx > 1 && outFilename != NULL) { + DISPLAY("Error: multiple input files provided, cannot use specified output file\n"); + ret = 1; + goto _main_exit; + } + + /* compress files */ + if (filenameIdx <= 1) { + ret |= compressFilename(filenameTable[0], outFilename); + } + else { + ret |= compressFilenames(filenameTable, filenameIdx); + } +_main_exit: + free(filenameTable); + return ret; } From 3f52ca94bf8bef4c3378f440e18421245ac8e675 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 14:36:09 -0700 Subject: [PATCH 022/318] added more tests, changed makefile --- contrib/adaptive-compression/Makefile | 2 ++ contrib/adaptive-compression/multi.c | 4 ++-- contrib/adaptive-compression/run.sh | 30 +++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index c6862198b..2ebec9ba9 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -29,4 +29,6 @@ clean: @$(RM) -f single multi @$(RM) -rf *.dSYM @$(RM) -f tmp* + @$(RM) -f tests/*.zst + @$(RM) -f tests/tmp* @echo "finished cleaning" diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index a6fd64ce8..089dfe5a3 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,6 +1,6 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define FILE_CHUNK_SIZE 4 << 20 -#define MAX_NUM_JOBS 30; +#define MAX_NUM_JOBS 50; #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 @@ -96,7 +96,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) } memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = 6; /* default */ - pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); + pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); /* TODO: add checks for errors on each mutex */ pthread_cond_init(&ctx->jobCompleted_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index e6cb2066e..b906ae7d1 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -76,4 +76,34 @@ diff tmp tests/test.pdf echo "diff test complete: test.pdf" rm tmp* +echo "Running multi-file tests" +./multi tests/* +zstd -d tests/test.pdf.zst -o tests/tmp +zstd -d tests/test2.pdf.zst -o tests/tmp2 +zstd -d tests/test4.pdf.zst -o tests/tmp4 +zstd -d tests/test8.pdf.zst -o tests/tmp8 +zstd -d tests/test16.pdf.zst -o tests/tmp16 +zstd -d tests/test32.pdf.zst -o tests/tmp32 +zstd -d tests/test64.pdf.zst -o tests/tmp64 +zstd -d tests/test128.pdf.zst -o tests/tmp128 +zstd -d tests/test256.pdf.zst -o tests/tmp256 +zstd -d tests/test512.pdf.zst -o tests/tmp512 +zstd -d tests/test1024.pdf.zst -o tests/tmp1024 +zstd -d tests/test2048.pdf.zst -o tests/tmp2048 + +diff tests/test.pdf tests/tmp +diff tests/test2.pdf tests/tmp2 +diff tests/test4.pdf tests/tmp4 +diff tests/test8.pdf tests/tmp8 +diff tests/test16.pdf tests/tmp16 +diff tests/test32.pdf tests/tmp32 +diff tests/test64.pdf tests/tmp64 +diff tests/test128.pdf tests/tmp128 +diff tests/test256.pdf tests/tmp256 +diff tests/test512.pdf tests/tmp512 +diff tests/test1024.pdf tests/tmp1024 +diff tests/test2048.pdf tests/tmp2048 + +echo "finished with tests" + make clean From cc714f3bd3346fe62530ef0ae3c8179e4ef6423c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 16:54:34 -0700 Subject: [PATCH 023/318] added print statements and debuglog --- contrib/adaptive-compression/multi.c | 23 ++++++++++++++++++++++- contrib/adaptive-compression/pipetests.sh | 2 ++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100755 contrib/adaptive-compression/pipetests.sh diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 089dfe5a3..40045381f 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,9 +1,11 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define DEBUGLOG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 #define MAX_NUM_JOBS 50; #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 +#define DEFAULT_DISPLAY_LEVEL 1 typedef unsigned char BYTE; #include /* fprintf */ @@ -12,7 +14,7 @@ typedef unsigned char BYTE; #include /* memset */ #include "zstd.h" - +static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; typedef struct { void* start; @@ -158,11 +160,13 @@ static void* compressionThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; + // DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(job->jobReady_mutex); while(job->jobReady == 0) { pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); } pthread_mutex_unlock(job->jobReady_mutex); + // DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); /* compress the data */ { size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, job->compressionLevel); @@ -175,11 +179,13 @@ static void* compressionThread(void* arg) } pthread_mutex_lock(job->jobCompleted_mutex); job->jobCompleted = 1; + DEBUGLOG(2, "signaling for job %u\n", currJob); pthread_cond_signal(job->jobCompleted_cond); pthread_mutex_unlock(job->jobCompleted_mutex); currJob++; if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished compressing all jobs */ + DEBUGLOG(2, "all jobs finished compressing\n"); break; } } @@ -194,11 +200,14 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; + DEBUGLOG(2, "outputThread(): waiting on job completed\n"); pthread_mutex_lock(job->jobCompleted_mutex); while (job->jobCompleted == 0) { + DEBUGLOG(2, "inside job completed wait loop waiting on %u\n", currJob); pthread_cond_wait(job->jobCompleted_cond, job->jobCompleted_mutex); } pthread_mutex_unlock(job->jobCompleted_mutex); + DEBUGLOG(2, "outputThread(): continuing after job completed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { @@ -214,12 +223,17 @@ static void* outputThread(void* arg) } } currJob++; + DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(job->jobWrite_mutex); job->jobWritten = 1; pthread_cond_signal(job->jobWrite_cond); pthread_mutex_unlock(job->jobWrite_mutex); + DEBUGLOG(2, "unlocking job write mutex\n"); + + DEBUGLOG(2, "checking if done: %u/%u\n", currJob, ctx->lastJobID); if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished with all jobs */ + DEBUGLOG(2, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex); ctx->allJobsCompleted = 1; pthread_cond_signal(&ctx->allJobsCompleted_cond); @@ -235,11 +249,13 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; + // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(job->jobWrite_mutex); while (job->jobWritten == 0) { pthread_cond_wait(job->jobWrite_cond, job->jobWrite_mutex); } pthread_mutex_unlock(job->jobWrite_mutex); + // DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); job->compressionLevel = ctx->compressionLevel; job->src.start = malloc(srcSize); job->src.size = srcSize; @@ -329,6 +345,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst } } if (feof(srcFile)) { + DEBUGLOG(2, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); ctx->lastJobID = ctx->nextJobID; break; } @@ -384,6 +401,10 @@ int main(int argCount, const char* argv[]) outFilename = argument; continue; } + else if (strlen(argument) > 1 && argument[1] == 'v') { + g_displayLevel++; + continue; + } else { DISPLAY("Error: invalid argument provided\n"); ret = 1; diff --git a/contrib/adaptive-compression/pipetests.sh b/contrib/adaptive-compression/pipetests.sh new file mode 100755 index 000000000..2924cd5ad --- /dev/null +++ b/contrib/adaptive-compression/pipetests.sh @@ -0,0 +1,2 @@ +make clean multi +pv -q -L 50m tests/test2048.pdf | ./multi -v -otmp.zst From 49af41820d090770f12e338841a8f2358e273e02 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 5 Jul 2017 17:20:52 -0700 Subject: [PATCH 024/318] clarified status of zstdmt_compress.h API --- lib/compress/zstdmt_compress.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index fad63b6d8..a6e1759b9 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -15,10 +15,11 @@ #endif -/* Note : All prototypes defined in this file are labelled experimental. - * No guarantee of API continuity is provided on any of them. - * In fact, the expectation is that these prototypes will be replaced - * by ZSTD_compress_generic() API in the near future */ +/* Note : This is an internal API. + * Some methods are still exposed (ZSTDLIB_API), because for some time, + * it used to be the only way to invoke MT compression. + * Now, it's recommended to use ZSTD_compress_generic() instead. + * These methods will stop being exposed in a future version */ /* === Dependencies === */ #include /* size_t */ From 6f3ad1b22e034cf27f34d63838c6f7cdfe55b015 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 17:24:21 -0700 Subject: [PATCH 025/318] fixed the problem with pipeline tests by changing how jobs move through the threads --- contrib/adaptive-compression/multi.c | 81 +++++++++++----------------- 1 file changed, 30 insertions(+), 51 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 40045381f..fd04a53b8 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -26,15 +26,6 @@ typedef struct { buffer_t dst; unsigned compressionLevel; unsigned jobID; - unsigned jobCompleted; - unsigned jobReady; - unsigned jobWritten; - pthread_mutex_t* jobCompleted_mutex; - pthread_cond_t* jobCompleted_cond; - pthread_mutex_t* jobReady_mutex; - pthread_cond_t* jobReady_cond; - pthread_mutex_t* jobWrite_mutex; - pthread_cond_t* jobWrite_cond; size_t compressedSize; } jobDescription; @@ -45,6 +36,9 @@ typedef struct { unsigned lastJobID; unsigned nextJobID; unsigned threadError; + unsigned jobReadyID; + unsigned jobCompletedID; + unsigned jobWrittenID; unsigned allJobsCompleted; pthread_mutex_t jobCompleted_mutex; pthread_cond_t jobCompleted_cond; @@ -107,20 +101,11 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) pthread_mutex_init(&ctx->jobWrite_mutex, NULL); pthread_cond_init(&ctx->jobWrite_cond, NULL); ctx->numJobs = numJobs; + ctx->jobReadyID = 0; + ctx->jobCompletedID = 0; + ctx->jobWrittenID = 0; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); - { - unsigned u; - for (u=0; ujobs[u].jobCompleted_mutex = &ctx->jobCompleted_mutex; - ctx->jobs[u].jobCompleted_cond = &ctx->jobCompleted_cond; - ctx->jobs[u].jobReady_mutex = &ctx->jobReady_mutex; - ctx->jobs[u].jobReady_cond = &ctx->jobReady_cond; - ctx->jobs[u].jobWrite_mutex = &ctx->jobWrite_mutex; - ctx->jobs[u].jobWrite_cond = &ctx->jobWrite_cond; - ctx->jobs[u].jobWritten = 1; - } - } ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; @@ -161,11 +146,11 @@ static void* compressionThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; // DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); - pthread_mutex_lock(job->jobReady_mutex); - while(job->jobReady == 0) { - pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); + pthread_mutex_lock(&ctx->jobReady_mutex); + while(currJob + 1 > ctx->jobReadyID) { + pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } - pthread_mutex_unlock(job->jobReady_mutex); + pthread_mutex_unlock(&ctx->jobReady_mutex); // DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); /* compress the data */ { @@ -177,11 +162,11 @@ static void* compressionThread(void* arg) } job->compressedSize = compressedSize; } - pthread_mutex_lock(job->jobCompleted_mutex); - job->jobCompleted = 1; + pthread_mutex_lock(&ctx->jobCompleted_mutex); + ctx->jobCompletedID++; DEBUGLOG(2, "signaling for job %u\n", currJob); - pthread_cond_signal(job->jobCompleted_cond); - pthread_mutex_unlock(job->jobCompleted_mutex); + pthread_cond_signal(&ctx->jobCompleted_cond); + pthread_mutex_unlock(&ctx->jobCompleted_mutex); currJob++; if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished compressing all jobs */ @@ -201,12 +186,12 @@ static void* outputThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUGLOG(2, "outputThread(): waiting on job completed\n"); - pthread_mutex_lock(job->jobCompleted_mutex); - while (job->jobCompleted == 0) { + pthread_mutex_lock(&ctx->jobCompleted_mutex); + while (currJob + 1 > ctx->jobCompletedID) { DEBUGLOG(2, "inside job completed wait loop waiting on %u\n", currJob); - pthread_cond_wait(job->jobCompleted_cond, job->jobCompleted_mutex); + pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } - pthread_mutex_unlock(job->jobCompleted_mutex); + pthread_mutex_unlock(&ctx->jobCompleted_mutex); DEBUGLOG(2, "outputThread(): continuing after job completed\n"); { size_t const compressedSize = job->compressedSize; @@ -224,10 +209,10 @@ static void* outputThread(void* arg) } currJob++; DEBUGLOG(2, "locking job write mutex\n"); - pthread_mutex_lock(job->jobWrite_mutex); - job->jobWritten = 1; - pthread_cond_signal(job->jobWrite_cond); - pthread_mutex_unlock(job->jobWrite_mutex); + pthread_mutex_lock(&ctx->jobWrite_mutex); + ctx->jobWrittenID++; + pthread_cond_signal(&ctx->jobWrite_cond); + pthread_mutex_unlock(&ctx->jobWrite_mutex); DEBUGLOG(2, "unlocking job write mutex\n"); DEBUGLOG(2, "checking if done: %u/%u\n", currJob, ctx->lastJobID); @@ -250,23 +235,17 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); - pthread_mutex_lock(job->jobWrite_mutex); - while (job->jobWritten == 0) { - pthread_cond_wait(job->jobWrite_cond, job->jobWrite_mutex); + pthread_mutex_lock(&ctx->jobWrite_mutex); + while (nextJob - ctx->jobWrittenID >= ctx->numJobs) { + pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } - pthread_mutex_unlock(job->jobWrite_mutex); + pthread_mutex_unlock(&ctx->jobWrite_mutex); // DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); job->compressionLevel = ctx->compressionLevel; job->src.start = malloc(srcSize); job->src.size = srcSize; job->dst.size = ZSTD_compressBound(srcSize); job->dst.start = malloc(job->dst.size); - job->jobCompleted = 0; - job->jobWritten = 0; - job->jobCompleted_cond = &ctx->jobCompleted_cond; - job->jobCompleted_mutex = &ctx->jobCompleted_mutex; - job->jobReady_cond = &ctx->jobReady_cond; - job->jobReady_mutex = &ctx->jobReady_mutex; job->jobID = nextJob; if (!job->src.start || !job->dst.start) { /* problem occurred, free things then return */ @@ -276,10 +255,10 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) return 1; } memcpy(job->src.start, data, srcSize); - pthread_mutex_lock(job->jobReady_mutex); - job->jobReady = 1; - pthread_cond_signal(job->jobReady_cond); - pthread_mutex_unlock(job->jobReady_mutex); + pthread_mutex_lock(&ctx->jobReady_mutex); + ctx->jobReadyID++; + pthread_cond_signal(&ctx->jobReady_cond); + pthread_mutex_unlock(&ctx->jobReady_mutex); ctx->nextJobID++; return 0; } From 3345a91964de424080ef95e97833c976edbc5666 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 5 Jul 2017 17:34:15 -0700 Subject: [PATCH 026/318] cli : use new advanced API by default --- programs/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/programs/Makefile b/programs/Makefile index 8b080d446..2460a091f 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -39,6 +39,7 @@ endif CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ -I$(ZSTDDIR)/dictBuilder \ + -DZSTD_NEWAPI \ -DXXH_NAMESPACE=ZSTD_ # because xxhash.o already compiled with this macro from library CFLAGS ?= -O3 DEBUGFLAGS = -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ From 79d4657ce5232cf7900407087962bb4ca978572e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 17:44:36 -0700 Subject: [PATCH 027/318] small changes --- contrib/adaptive-compression/multi.c | 9 ++++++--- contrib/adaptive-compression/pipetests.sh | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index fd04a53b8..208a0a790 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -148,6 +148,7 @@ static void* compressionThread(void* arg) // DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { + DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } pthread_mutex_unlock(&ctx->jobReady_mutex); @@ -185,14 +186,14 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUGLOG(2, "outputThread(): waiting on job completed\n"); + // DEBUGLOG(2, "outputThread(): waiting on job completed\n"); pthread_mutex_lock(&ctx->jobCompleted_mutex); while (currJob + 1 > ctx->jobCompletedID) { - DEBUGLOG(2, "inside job completed wait loop waiting on %u\n", currJob); + DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } pthread_mutex_unlock(&ctx->jobCompleted_mutex); - DEBUGLOG(2, "outputThread(): continuing after job completed\n"); + // DEBUGLOG(2, "outputThread(): continuing after job completed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { @@ -236,7 +237,9 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) jobDescription* job = &ctx->jobs[nextJobIndex]; // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); + DISPLAY("Creating new compression job -- nextJob: %u, jobWrittenID: %u, numJObs: %u\n", nextJob, ctx->jobWrittenID, ctx->numJobs); while (nextJob - ctx->jobWrittenID >= ctx->numJobs) { + DEBUGLOG(2, "waiting on job writtten, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); diff --git a/contrib/adaptive-compression/pipetests.sh b/contrib/adaptive-compression/pipetests.sh index 2924cd5ad..743ce381c 100755 --- a/contrib/adaptive-compression/pipetests.sh +++ b/contrib/adaptive-compression/pipetests.sh @@ -1,2 +1,2 @@ make clean multi -pv -q -L 50m tests/test2048.pdf | ./multi -v -otmp.zst +pv -q -L 500m tests/test2048.pdf | ./multi -v -otmp.zst From d75c0e71c41ca0faf334f6d6f86d4fc30c7809f4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 5 Jul 2017 18:10:07 -0700 Subject: [PATCH 028/318] minor code refactoring --- lib/decompress/zstd_decompress.c | 56 +++++++++++++++----------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 003d703a5..7cb1b3f87 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1440,10 +1440,11 @@ size_t ZSTD_generateNxBytes(void* dst, size_t dstCapacity, BYTE byte, size_t len size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) { #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) - if (ZSTD_isLegacy(src, srcSize)) return ZSTD_findFrameCompressedSizeLegacy(src, srcSize); + if (ZSTD_isLegacy(src, srcSize)) + return ZSTD_findFrameCompressedSizeLegacy(src, srcSize); #endif - if (srcSize >= ZSTD_skippableHeaderSize && - (MEM_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { + if ( (srcSize >= ZSTD_skippableHeaderSize) + && (MEM_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START ) { return ZSTD_skippableHeaderSize + MEM_readLE32((const BYTE*)src + 4); } else { const BYTE* ip = (const BYTE*)src; @@ -1469,7 +1470,8 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); if (ZSTD_isError(cBlockSize)) return cBlockSize; - if (ZSTD_blockHeaderSize + cBlockSize > remainingSize) return ERROR(srcSize_wrong); + if (ZSTD_blockHeaderSize + cBlockSize > remainingSize) + return ERROR(srcSize_wrong); ip += ZSTD_blockHeaderSize + cBlockSize; remainingSize -= ZSTD_blockHeaderSize + cBlockSize; @@ -1490,8 +1492,8 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) /*! ZSTD_decompressFrame() : * @dctx must be properly initialized */ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, - void* dst, size_t dstCapacity, - const void** srcPtr, size_t *srcSizePtr) + void* dst, size_t dstCapacity, + const void** srcPtr, size_t *srcSizePtr) { const BYTE* ip = (const BYTE*)(*srcPtr); BYTE* const ostart = (BYTE* const)dst; @@ -1500,13 +1502,15 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, size_t remainingSize = *srcSizePtr; /* check */ - if (remainingSize < ZSTD_frameHeaderSize_min+ZSTD_blockHeaderSize) return ERROR(srcSize_wrong); + if (remainingSize < ZSTD_frameHeaderSize_min+ZSTD_blockHeaderSize) + return ERROR(srcSize_wrong); /* Frame Header */ { size_t const frameHeaderSize = ZSTD_frameHeaderSize(ip, ZSTD_frameHeaderSize_prefix); if (ZSTD_isError(frameHeaderSize)) return frameHeaderSize; - if (remainingSize < frameHeaderSize+ZSTD_blockHeaderSize) return ERROR(srcSize_wrong); - CHECK_F(ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize)); + if (remainingSize < frameHeaderSize+ZSTD_blockHeaderSize) + return ERROR(srcSize_wrong); + CHECK_F( ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize) ); ip += frameHeaderSize; remainingSize -= frameHeaderSize; } @@ -1538,14 +1542,15 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, } if (ZSTD_isError(decodedSize)) return decodedSize; - if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, op, decodedSize); + if (dctx->fParams.checksumFlag) + XXH64_update(&dctx->xxhState, op, decodedSize); op += decodedSize; ip += cBlockSize; remainingSize -= cBlockSize; if (blockProperties.lastBlock) break; } - if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */ + if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */ U32 const checkCalc = (U32)XXH64_digest(&dctx->xxhState); U32 checkRead; if (remainingSize<4) return ERROR(checksum_wrong); @@ -1567,17 +1572,13 @@ static size_t ZSTD_DDictDictSize(const ZSTD_DDict* ddict); static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, - const void *dict, size_t dictSize, + const void* dict, size_t dictSize, const ZSTD_DDict* ddict) { void* const dststart = dst; + assert(dict==NULL || ddict==NULL); /* either dict or ddict set, not both */ if (ddict) { - if (dict) { - /* programmer error, these two cases should be mutually exclusive */ - return ERROR(GENERIC); - } - dict = ZSTD_DDictDictContent(ddict); dictSize = ZSTD_DDictDictSize(ddict); } @@ -1590,7 +1591,7 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, size_t decodedSize; size_t const frameSize = ZSTD_findFrameCompressedSizeLegacy(src, srcSize); if (ZSTD_isError(frameSize)) return frameSize; - /* legacy support is incompatible with static dctx */ + /* legacy support is not compatible with static dctx */ if (dctx->staticSize) return ERROR(memory_allocation); decodedSize = ZSTD_decompressLegacy(dst, dstCapacity, src, frameSize, dict, dictSize); @@ -1613,16 +1614,13 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, return ERROR(srcSize_wrong); skippableSize = MEM_readLE32((const BYTE *)src + 4) + ZSTD_skippableHeaderSize; - if (srcSize < skippableSize) { - return ERROR(srcSize_wrong); - } + if (srcSize < skippableSize) return ERROR(srcSize_wrong); src = (const BYTE *)src + skippableSize; srcSize -= skippableSize; continue; - } else { - return ERROR(prefix_unknown); } + return ERROR(prefix_unknown); } if (ddict) { @@ -1638,12 +1636,11 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, { const size_t res = ZSTD_decompressFrame(dctx, dst, dstCapacity, &src, &srcSize); if (ZSTD_isError(res)) return res; - /* don't need to bounds check this, ZSTD_decompressFrame will have - * already */ + /* no need to bound check, ZSTD_decompressFrame already has */ dst = (BYTE*)dst + res; dstCapacity -= res; } - } + } /* while (srcSize >= ZSTD_frameHeaderSize_prefix) */ if (srcSize) return ERROR(srcSize_wrong); /* input not entirely consumed */ @@ -1931,8 +1928,9 @@ static size_t ZSTD_decompress_insertDictionary(ZSTD_DCtx* dctx, const void* dict size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) { - CHECK_F(ZSTD_decompressBegin(dctx)); - if (dict && dictSize) CHECK_E(ZSTD_decompress_insertDictionary(dctx, dict, dictSize), dictionary_corrupted); + CHECK_F( ZSTD_decompressBegin(dctx) ); + if (dict && dictSize) + CHECK_E(ZSTD_decompress_insertDictionary(dctx, dict, dictSize), dictionary_corrupted); return 0; } @@ -1961,7 +1959,7 @@ static size_t ZSTD_DDictDictSize(const ZSTD_DDict* ddict) size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dstDCtx, const ZSTD_DDict* ddict) { - CHECK_F(ZSTD_decompressBegin(dstDCtx)); + CHECK_F( ZSTD_decompressBegin(dstDCtx) ); if (ddict) { /* support begin on NULL */ dstDCtx->dictID = ddict->dictID; dstDCtx->base = ddict->dictContent; From f04deff4fc7ffbbcef3a93733c7bb8c51176fc7b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 6 Jul 2017 01:42:46 -0700 Subject: [PATCH 029/318] fixed #718, reported by @GregSlazinski, solution suggested by @mcmilk --- lib/common/zstd_internal.h | 12 ++++++++++++ lib/decompress/zstd_decompress.c | 9 +-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index f2c4e6249..f49f6a13c 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -331,4 +331,16 @@ size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, ZSTD_parameters ZSTD_getParamsFromCDict(const ZSTD_CDict* cdict); +typedef struct { + blockType_e blockType; + U32 lastBlock; + U32 origSize; +} blockProperties_t; + +/*! ZSTD_getcBlockSize() : +* Provides the size of compressed block from block header `src` */ +size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, + blockProperties_t* bpPtr); + + #endif /* ZSTD_CCOMMON_H_MODULE */ diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 7cb1b3f87..6eca1c471 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -53,7 +53,7 @@ # include "zstd_legacy.h" #endif -#if defined(_MSC_VER) && !defined(_M_IA64) /* _mm_prefetch() is not defined for ia64 */ +#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 */ # define ZSTD_PREFETCH(ptr) _mm_prefetch((const char*)ptr, _MM_HINT_T0) #elif defined(__GNUC__) @@ -466,13 +466,6 @@ static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t he } -typedef struct -{ - blockType_e blockType; - U32 lastBlock; - U32 origSize; -} blockProperties_t; - /*! ZSTD_getcBlockSize() : * Provides the size of compressed block from block header `src` */ size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, From 9b2c1acfc0dc37ebe5a5dde7bc48acb2f4c2ac00 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 6 Jul 2017 02:22:57 -0700 Subject: [PATCH 030/318] fixed fullbench --- tests/fullbench.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/fullbench.c b/tests/fullbench.c index 81de5157b..5c105ee75 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -91,12 +91,6 @@ static size_t BMK_findMaxMem(U64 requiredMem) /*_******************************************************* * Benchmark wrappers *********************************************************/ -typedef struct { - blockType_e blockType; - U32 unusedBits; - U32 origSize; -} blockProperties_t; - size_t local_ZSTD_compress(void* dst, size_t dstSize, void* buff2, const void* src, size_t srcSize) { (void)buff2; From 7758ed84581a892939a56f7cdd6237a165835976 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 6 Jul 2017 02:48:00 -0700 Subject: [PATCH 031/318] fixed fullbench, part 2 --- doc/zstd_manual.html | 78 +++++++++++++++++++++++--------------------- tests/fullbench.c | 1 - 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index cd2b06dd8..0c82115b3 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -73,27 +73,41 @@ or an errorCode if it fails (which can be tested using ZSTD_isError()).