From 525f3fab3360f29e4c64b3dfa4ae9699f68eb18d Mon Sep 17 00:00:00 2001
From: Yann Collet
Introduction
@@ -232,33 +234,38 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
since it will play nicer with system's memory, by re-using already allocated memory.
Use one separate ZSTD_CStream per thread for parallel execution.
- Start a new compression by initializing ZSTD_CStream.
+ Start a new compression by initializing ZSTD_CStream context.
Use ZSTD_initCStream() to start a new compression operation.
- Use ZSTD_initCStream_usingDict() or ZSTD_initCStream_usingCDict() for a compression which requires a dictionary (experimental section)
+ Use variants ZSTD_initCStream_usingDict() or ZSTD_initCStream_usingCDict() for streaming with dictionary (experimental section)
- Use ZSTD_compressStream() repetitively to consume input stream.
- The function will automatically update both `pos` fields.
- Note that it may not consume the entire input, in which case `pos < size`,
- and it's up to the caller to present again remaining data.
+ Use ZSTD_compressStream() as many times as necessary to consume input stream.
+ The function will automatically update both `pos` fields within `input` and `output`.
+ Note that the function may not consume the entire input,
+ for example, because the output buffer is already full,
+ in which case `input.pos < input.size`.
+ The caller must check if input has been entirely consumed.
+ If not, the caller must make some room to receive more compressed data,
+ typically by emptying output buffer, or allocating a new output buffer,
+ and then present again remaining input data.
@return : a size hint, preferred nb of bytes to use as input for next function call
or an error code, which can be tested using ZSTD_isError().
Note 1 : it's just a hint, to help latency a little, any other value will work fine.
Note 2 : size hint is guaranteed to be <= ZSTD_CStreamInSize()
- At any moment, it's possible to flush whatever data remains within internal buffer, using ZSTD_flushStream().
- `output->pos` will be updated.
- Note that some content might still be left within internal buffer if `output->size` is too small.
- @return : nb of bytes still present within internal buffer (0 if it's empty)
+ At any moment, it's possible to flush whatever data might remain stuck within internal buffer,
+ using ZSTD_flushStream(). `output->pos` will be updated.
+ Note that, if `output->size` is too small, a single invocation of ZSTD_flushStream() might not be enough (return code > 0).
+ In which case, make some room to receive more compressed data, and call again ZSTD_flushStream().
+ @return : 0 if internal buffers are entirely flushed,
+ >0 if some data still present within internal buffer (the value is minimal estimation of remaining size),
or an error code, which can be tested using ZSTD_isError().
ZSTD_endStream() instructs to finish a frame.
It will perform a flush and write frame epilogue.
The epilogue is required for decoders to consider a frame completed.
- ZSTD_endStream() may not be able to flush full data if `output->size` is too small.
- In which case, call again ZSTD_endStream() to complete the flush.
+ flush() operation is the same, and follows same rules as ZSTD_flushStream().
@return : 0 if frame fully completed and fully flushed,
- or >0 if some data is still present within internal buffer
- (value is minimum size estimation for remaining data to flush, but it could be more)
+ >0 if some data still present within internal buffer (the value is minimal estimation of remaining size),
or an error code, which can be tested using ZSTD_isError().
@@ -388,13 +395,12 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
however it does mean that all frame data must be present and valid.
size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize); -`src` should point to the start of a ZSTD frame - `srcSize` must be >= ZSTD_frameHeaderSize_prefix. - @return : size of the Frame Header -
srcSize must be >= ZSTD_frameHeaderSize_prefix. + @return : size of the Frame Header, + or an error code (if srcSize is too small) +-
size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx);
size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx);
@@ -484,7 +490,7 @@ static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< t
ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);Create a digested dictionary for compression @@ -526,7 +532,7 @@ static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< t
Same as ZSTD_compress_usingCDict(), with fine-tune control over frame parameters
unsigned ZSTD_isFrame(const void* buffer, size_t size);Tells if the content of `buffer` starts with a valid Frame Identifier. @@ -566,7 +572,7 @@ static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< t When identifying the exact failure cause, it's possible to use ZSTD_getFrameHeader(), which will provide a more precise error code.
size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);/**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */ size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/ @@ -598,14 +604,14 @@ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t di size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); /**< note : ddict is referenced, it must outlive decompression session */ size_t ZSTD_resetDStream(ZSTD_DStream* zds); /**< re-use decompression parameters from previous init; saves dictionary loading */
+Buffer-less and synchronous inner streaming functions
This is an advanced API, giving full control over buffer management, for users which need direct control over memory. But it's also a complex one, with several restrictions, documented below. Prefer normal streaming API for an easier experience.-Buffer-less streaming compression (synchronous mode)
+Buffer-less streaming compression (synchronous mode)
A ZSTD_CCtx object is required to track streaming operations. Use ZSTD_createCCtx() / ZSTD_freeCCtx() to manage resource. ZSTD_CCtx object can be re-used multiple times within successive compression operations. @@ -641,7 +647,7 @@ size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */ size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
-Buffer-less streaming decompression (synchronous mode)
+Buffer-less streaming decompression (synchronous mode)
A ZSTD_DCtx object is required to track streaming operations. Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it. A ZSTD_DCtx object can be re-used multiple times. @@ -722,12 +728,17 @@ typedef struct { unsigned dictID; unsigned checksumFlag; } ZSTD_frameHeader; +/** ZSTD_getFrameHeader() : + * decode Frame Header, or requires larger `srcSize`. + * @return : 0, `zfhPtr` is correctly filled, + * >0, `srcSize` is too small, value is wanted `srcSize` amount, + * or an error code, which can be tested using ZSTD_isError() */ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); /**< doesn't consume input */ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize); /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */
typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
-New advanced API (experimental)
+New advanced API (experimental)
typedef enum { /* Opened question : should we have a format ZSTD_f_auto ? @@ -762,16 +773,19 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long * Special: value 0 means "use default windowLog". * Note: Using a window size greater than ZSTD_MAXWINDOWSIZE_DEFAULT (default: 2^27) * requires explicitly allowing such window size during decompression stage. */ - ZSTD_p_hashLog, /* Size of the probe table, as a power of 2. + ZSTD_p_hashLog, /* Size of the initial probe table, as a power of 2. * Resulting table size is (1 << (hashLog+2)). * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX. * Larger tables improve compression ratio of strategies <= dFast, * and improve speed of strategies > dFast. * Special: value 0 means "use default hashLog". */ - ZSTD_p_chainLog, /* Size of the full-search table, as a power of 2. + ZSTD_p_chainLog, /* Size of the multi-probe search table, as a power of 2. * Resulting table size is (1 << (chainLog+2)). + * Must be clamped between ZSTD_CHAINLOG_MIN and ZSTD_CHAINLOG_MAX. * Larger tables result in better and slower compression. * This parameter is useless when using "fast" strategy. + * Note it's still useful when using "dfast" strategy, + * in which case it defines a secondary probe table. * Special: value 0 means "use default chainLog". */ ZSTD_p_searchLog, /* Number of search attempts, as a power of 2. * More attempts result in better and slower compression. @@ -866,13 +880,22 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned value);Set one compression parameter, selected by enum ZSTD_cParameter. - Setting a parameter is generally only possible during frame initialization (before starting compression), - except for a few exceptions which can be updated during compression: compressionLevel, hashLog, chainLog, searchLog, minMatch, targetLength and strategy. - Note : when `value` is an enum, cast it to unsigned for proper type checking. - @result : informational value (typically, value being set clamped correctly), + Setting a parameter is generally only possible during frame initialization (before starting compression). + Exception : when using multi-threading mode (nbThreads >= 1), + following parameters can be updated _during_ compression (within same frame): + => compressionLevel, hashLog, chainLog, searchLog, minMatch, targetLength and strategy. + new parameters will be active on next job, or after a flush(). + Note : when `value` type is not unsigned (int, or enum), cast it to unsigned for proper type checking. + @result : informational value (typically, value being set, correctly clamped), or an error code (which can be tested with ZSTD_isError()).
+size_t ZSTD_CCtx_getParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned* value); +Get the requested value of one compression parameter, selected by enum ZSTD_cParameter. + @result : 0, or an error code (which can be tested with ZSTD_isError()). + +
+size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize);Total input data size to be compressed as a single frame. This value will be controlled at the end, and result in error if not respected. @@ -936,9 +959,16 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t
Return a CCtx to clean state. Useful after an error, or to interrupt an ongoing compression job and start a new one. Any internal data not yet flushed is cancelled. + The parameters and dictionary are kept unchanged, to reset them use ZSTD_CCtx_resetParameters(). + +
+ +size_t ZSTD_CCtx_resetParameters(ZSTD_CCtx* cctx); +All parameters are back to default values (compression level is ZSTD_CLEVEL_DEFAULT). Dictionary (if any) is dropped. - All parameters are back to default values. - It's possible to modify compression parameters after a reset. + Resetting parameters is only possible during frame initialization (before starting compression). + To reset the context use ZSTD_CCtx_reset(). + @return 0 or an error code (which can be checked with ZSTD_isError()).
@@ -1033,6 +1063,13 @@ size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params);
+size_t ZSTD_CCtxParam_getParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, unsigned* value); +Similar to ZSTD_CCtx_getParameter. + Get the requested value of one compression parameter, selected by enum ZSTD_cParameter. + @result : 0, or an error code (which can be tested with ZSTD_isError()). + +
+size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params);Apply a set of ZSTD_CCtx_params to the compression context. @@ -1043,7 +1080,8 @@ size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params);
-Advanced parameters for decompression API
+Advanced decompression API
/* ==================================== */ +
size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType); @@ -1105,6 +1143,10 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t
+ZSTD_getFrameHeader_advanced() :
same as ZSTD_getFrameHeader(), + with added capability to select a format (like ZSTD_f_zstd1_magicless) ++size_t ZSTD_decompress_generic(ZSTD_DCtx* dctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input); @@ -1137,7 +1179,7 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t
-Block level API
+Block level API
Frame metadata cost is typically ~18 bytes, which can be non-negligible for very small blocks (< 100 bytes). User will have to take in charge required information to regenerate data, such as compressed and content sizes. diff --git a/lib/zstd.h b/lib/zstd.h index 387586c1e..aed0c7270 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -272,33 +272,38 @@ typedef struct ZSTD_outBuffer_s { * since it will play nicer with system's memory, by re-using already allocated memory. * Use one separate ZSTD_CStream per thread for parallel execution. * -* Start a new compression by initializing ZSTD_CStream. +* Start a new compression by initializing ZSTD_CStream context. * Use ZSTD_initCStream() to start a new compression operation. -* Use ZSTD_initCStream_usingDict() or ZSTD_initCStream_usingCDict() for a compression which requires a dictionary (experimental section) +* Use variants ZSTD_initCStream_usingDict() or ZSTD_initCStream_usingCDict() for streaming with dictionary (experimental section) * -* Use ZSTD_compressStream() repetitively to consume input stream. -* The function will automatically update both `pos` fields. -* Note that it may not consume the entire input, in which case `pos < size`, -* and it's up to the caller to present again remaining data. +* Use ZSTD_compressStream() as many times as necessary to consume input stream. +* The function will automatically update both `pos` fields within `input` and `output`. +* Note that the function may not consume the entire input, +* for example, because the output buffer is already full, +* in which case `input.pos < input.size`. +* The caller must check if input has been entirely consumed. +* If not, the caller must make some room to receive more compressed data, +* typically by emptying output buffer, or allocating a new output buffer, +* and then present again remaining input data. * @return : a size hint, preferred nb of bytes to use as input for next function call * or an error code, which can be tested using ZSTD_isError(). * Note 1 : it's just a hint, to help latency a little, any other value will work fine. * Note 2 : size hint is guaranteed to be <= ZSTD_CStreamInSize() * -* At any moment, it's possible to flush whatever data remains within internal buffer, using ZSTD_flushStream(). -* `output->pos` will be updated. -* Note that some content might still be left within internal buffer if `output->size` is too small. -* @return : nb of bytes still present within internal buffer (0 if it's empty) +* At any moment, it's possible to flush whatever data might remain stuck within internal buffer, +* using ZSTD_flushStream(). `output->pos` will be updated. +* Note that, if `output->size` is too small, a single invocation of ZSTD_flushStream() might not be enough (return code > 0). +* In which case, make some room to receive more compressed data, and call again ZSTD_flushStream(). +* @return : 0 if internal buffers are entirely flushed, +* >0 if some data still present within internal buffer (the value is minimal estimation of remaining size), * or an error code, which can be tested using ZSTD_isError(). * * ZSTD_endStream() instructs to finish a frame. * It will perform a flush and write frame epilogue. * The epilogue is required for decoders to consider a frame completed. -* ZSTD_endStream() may not be able to flush full data if `output->size` is too small. -* In which case, call again ZSTD_endStream() to complete the flush. +* flush() operation is the same, and follows same rules as ZSTD_flushStream(). * @return : 0 if frame fully completed and fully flushed, - or >0 if some data is still present within internal buffer - (value is minimum size estimation for remaining data to flush, but it could be more) +* >0 if some data still present within internal buffer (the value is minimal estimation of remaining size), * or an error code, which can be tested using ZSTD_isError(). * * *******************************************************************/ From 12f60b8c9896b5439ef530a82d88c0fe2dfda569 Mon Sep 17 00:00:00 2001 From: Yann Collet
Date: Wed, 25 Apr 2018 10:15:43 -0700 Subject: [PATCH 028/288] clarified documentation related to refPrefix() --- doc/zstd_manual.html | 31 ++++++++++++++++++++----------- lib/zstd.h | 31 ++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index ef7c87d9c..aae6d0f37 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -939,18 +939,21 @@ size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size Note 2 : CDict is just referenced, its lifetime must outlive CCtx.
-size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize); -size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); +size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, + const void* prefix, size_t prefixSize); +size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, + const void* prefix, size_t prefixSize, + ZSTD_dictContentType_e dictContentType);Reference a prefix (single-usage dictionary) for next compression job. Decompression need same prefix to properly regenerate data. - Prefix is **only used once**. Tables are discarded at end of compression job. - Subsequent compression jobs will be done without prefix (if none is explicitly referenced). - If there is a need to use same prefix multiple times, consider embedding it into a ZSTD_CDict instead. + Prefix is **only used once**. Tables are discarded at end of compression job (ZSTD_e_end). @result : 0, or an error code (which can be tested with ZSTD_isError()). Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary - Note 1 : Prefix buffer is referenced. It must outlive compression job. + Note 1 : Prefix buffer is referenced. It **must** outlive compression job. + Its contain must remain unmodified up to end of compression (ZSTD_e_end). Note 2 : Referencing a prefix involves building tables, which are dependent on compression parameters. It's a CPU consuming operation, with non-negligible impact on latency. + If there is a need to use same prefix multiple times, consider loadDictionary instead. Note 3 : By default, the prefix is treated as raw content (ZSTD_dm_rawContent). Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode.
@@ -1112,17 +1115,23 @@ size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size
-size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize); -size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); +size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, + const void* prefix, size_t prefixSize); +size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, + const void* prefix, size_t prefixSize, + ZSTD_dictContentType_e dictContentType);Reference a prefix (single-usage dictionary) for next compression job. - Prefix is **only used once**. It must be explicitly referenced before each frame. - If there is a need to use same prefix multiple times, consider embedding it into a ZSTD_DDict instead. + Prefix is **only used once**. Reference is discarded at end of frame. + End of frame is reached when ZSTD_DCtx_decompress_generic() returns 0. @result : 0, or an error code (which can be tested with ZSTD_isError()). Note 1 : Adding any prefix (including NULL) invalidates any previously set prefix or dictionary - Note 2 : Prefix buffer is referenced. It must outlive compression job. + Note 2 : Prefix buffer is referenced. It **must** outlive decompression job. + Prefix buffer must remain unmodified up to the end of frame, + reached when ZSTD_DCtx_decompress_generic() returns 0. Note 3 : By default, the prefix is treated as raw content (ZSTD_dm_rawContent). Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode. Note 4 : Referencing a raw content prefix has almost no cpu nor memory cost. + A fulldict prefix is more costly though.
diff --git a/lib/zstd.h b/lib/zstd.h index aed0c7270..57d3e5f7a 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -1129,18 +1129,21 @@ ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /*! ZSTD_CCtx_refPrefix() : * Reference a prefix (single-usage dictionary) for next compression job. * Decompression need same prefix to properly regenerate data. - * Prefix is **only used once**. Tables are discarded at end of compression job. - * Subsequent compression jobs will be done without prefix (if none is explicitly referenced). - * If there is a need to use same prefix multiple times, consider embedding it into a ZSTD_CDict instead. + * Prefix is **only used once**. Tables are discarded at end of compression job (ZSTD_e_end). * @result : 0, or an error code (which can be tested with ZSTD_isError()). * Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary - * Note 1 : Prefix buffer is referenced. It must outlive compression job. + * Note 1 : Prefix buffer is referenced. It **must** outlive compression job. + * Its contain must remain unmodified up to end of compression (ZSTD_e_end). * Note 2 : Referencing a prefix involves building tables, which are dependent on compression parameters. * It's a CPU consuming operation, with non-negligible impact on latency. + * If there is a need to use same prefix multiple times, consider loadDictionary instead. * Note 3 : By default, the prefix is treated as raw content (ZSTD_dm_rawContent). * Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode. */ -ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize); -ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); +ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, + const void* prefix, size_t prefixSize); +ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, + const void* prefix, size_t prefixSize, + ZSTD_dictContentType_e dictContentType); /*! ZSTD_CCtx_reset() : * Return a CCtx to clean state. @@ -1317,17 +1320,23 @@ ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); /*! ZSTD_DCtx_refPrefix() : * Reference a prefix (single-usage dictionary) for next compression job. - * Prefix is **only used once**. It must be explicitly referenced before each frame. - * If there is a need to use same prefix multiple times, consider embedding it into a ZSTD_DDict instead. + * Prefix is **only used once**. Reference is discarded at end of frame. + * End of frame is reached when ZSTD_DCtx_decompress_generic() returns 0. * @result : 0, or an error code (which can be tested with ZSTD_isError()). * Note 1 : Adding any prefix (including NULL) invalidates any previously set prefix or dictionary - * Note 2 : Prefix buffer is referenced. It must outlive compression job. + * Note 2 : Prefix buffer is referenced. It **must** outlive decompression job. + * Prefix buffer must remain unmodified up to the end of frame, + * reached when ZSTD_DCtx_decompress_generic() returns 0. * Note 3 : By default, the prefix is treated as raw content (ZSTD_dm_rawContent). * Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode. * Note 4 : Referencing a raw content prefix has almost no cpu nor memory cost. + * A fulldict prefix is more costly though. */ -ZSTDLIB_API size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize); -ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); +ZSTDLIB_API size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, + const void* prefix, size_t prefixSize); +ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, + const void* prefix, size_t prefixSize, + ZSTD_dictContentType_e dictContentType); /*! ZSTD_DCtx_setMaxWindowSize() : From ca77822ddfa51c3618ab006a133752cdc13dfd7a Mon Sep 17 00:00:00 2001 From: Nick TerrellDate: Wed, 25 Apr 2018 16:32:29 -0700 Subject: [PATCH 029/288] Fix parameter adjustment with dictionary The new advanced API basically set `requestedParams = appliedParams` when using a dictionary. This halted all parameter adjustment, which can hurt compression ratio if, for example, the window log is small for the first call, but the rest of the files are large. This patch fixes the bug, and checks that the `requestedParams` don't change in the new advanced API when using a dictionary, and generally in the fuzzer. --- lib/compress/zstd_compress.c | 16 ++++------ tests/zstreamtest.c | 58 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 7a5043284..f3a49e672 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1195,18 +1195,16 @@ void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) { static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, - unsigned windowLog, - ZSTD_frameParameters fParams, + ZSTD_CCtx_params params, U64 pledgedSrcSize, ZSTD_buffered_policy_e zbuff) { - { ZSTD_CCtx_params params = cctx->requestedParams; + { unsigned const windowLog = params.cParams.windowLog; + assert(windowLog != 0); /* Copy only compression parameters related to tables. */ params.cParams = cdict->cParams; - if (windowLog) params.cParams.windowLog = windowLog; - params.fParams = fParams; - ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, - ZSTDcrp_noMemset, zbuff); + params.cParams.windowLog = windowLog; + ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, ZSTDcrp_noMemset, zbuff); assert(cctx->appliedParams.cParams.strategy == cdict->cParams.strategy); assert(cctx->appliedParams.cParams.hashLog == cdict->cParams.hashLog); assert(cctx->appliedParams.cParams.chainLog == cdict->cParams.chainLog); @@ -2495,9 +2493,7 @@ size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, assert(!((dict) && (cdict))); /* either dict or cdict, not both */ if (cdict && cdict->dictContentSize>0) { - cctx->requestedParams = params; - return ZSTD_resetCCtx_usingCDict(cctx, cdict, params.cParams.windowLog, - params.fParams, pledgedSrcSize, zbuff); + return ZSTD_resetCCtx_usingCDict(cctx, cdict, params, pledgedSrcSize, zbuff); } CHECK_F( ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 14412f4b9..ffed955a2 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -110,6 +110,20 @@ unsigned int FUZ_rand(unsigned int* seedPtr) #f, ZSTD_getErrorName(err)); \ } +#define CHECK_RET(ret, cond, ...) { \ + if (cond) { \ + DISPLAY("Error %llu => ", (unsigned long long)ret); \ + DISPLAY(__VA_ARGS__); \ + DISPLAY(" (line %u)\n", __LINE__); \ + return ret; \ +} } + +#define CHECK_RET_Z(f) { \ + size_t const err = f; \ + CHECK_RET(err, ZSTD_isError(err), "%s : %s ", \ + #f, ZSTD_getErrorName(err)); \ +} + /*====================================================== * Basic Unit tests @@ -207,6 +221,42 @@ static size_t SEQ_generateRoundTrip(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx, return 0; } +static size_t getCCtxParams(ZSTD_CCtx* zc, ZSTD_parameters* savedParams) +{ + unsigned value; + CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_p_windowLog, &savedParams->cParams.windowLog)); + CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_p_hashLog, &savedParams->cParams.hashLog)); + CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_p_chainLog, &savedParams->cParams.chainLog)); + CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_p_searchLog, &savedParams->cParams.searchLog)); + CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_p_minMatch, &savedParams->cParams.searchLength)); + CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_p_targetLength, &savedParams->cParams.targetLength)); + CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_p_compressionStrategy, &value)); + savedParams->cParams.strategy = value; + + CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_p_checksumFlag, &savedParams->fParams.checksumFlag)); + CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_p_contentSizeFlag, &savedParams->fParams.contentSizeFlag)); + CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_p_dictIDFlag, &value)); + savedParams->fParams.noDictIDFlag = !value; + return 0; +} + +static U32 badParameters(ZSTD_CCtx* zc, ZSTD_parameters const savedParams) +{ + ZSTD_parameters params; + if (ZSTD_isError(getCCtxParams(zc, ¶ms))) return 10; + CHECK_RET(1, params.cParams.windowLog != savedParams.cParams.windowLog, "windowLog"); + CHECK_RET(2, params.cParams.hashLog != savedParams.cParams.hashLog, "hashLog"); + CHECK_RET(3, params.cParams.chainLog != savedParams.cParams.chainLog, "chainLog"); + CHECK_RET(4, params.cParams.searchLog != savedParams.cParams.searchLog, "searchLog"); + CHECK_RET(5, params.cParams.searchLength != savedParams.cParams.searchLength, "searchLength"); + CHECK_RET(6, params.cParams.targetLength != savedParams.cParams.targetLength, "targetLength"); + + CHECK_RET(7, params.fParams.checksumFlag != savedParams.fParams.checksumFlag, "checksumFlag"); + CHECK_RET(8, params.fParams.contentSizeFlag != savedParams.fParams.contentSizeFlag, "contentSizeFlag"); + CHECK_RET(9, params.fParams.noDictIDFlag != savedParams.fParams.noDictIDFlag, "noDictIDFlag"); + return 0; +} + static int basicUnitTests(U32 seed, double compressibility) { size_t const CNBufferSize = COMPRESSIBLE_NOISE_LENGTH; @@ -606,6 +656,8 @@ static int basicUnitTests(U32 seed, double compressibility) for (size = 512; size <= maxSize; size <<= 1) { U64 const crcOrig = XXH64(CNBuffer, size, 0); ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + ZSTD_parameters savedParams; + getCCtxParams(cctx, &savedParams); outBuff.dst = compressedBuffer; outBuff.size = compressedBufferSize; outBuff.pos = 0; @@ -614,6 +666,7 @@ static int basicUnitTests(U32 seed, double compressibility) inBuff.pos = 0; CHECK_Z(ZSTD_CCtx_refCDict(cctx, cdict)); CHECK_Z(ZSTD_compress_generic(cctx, &outBuff, &inBuff, ZSTD_e_end)); + CHECK(badParameters(cctx, savedParams), "Bad CCtx params"); if (inBuff.pos != inBuff.size) goto _output_error; { ZSTD_outBuffer decOut = {decodedBuffer, size, 0}; ZSTD_inBuffer decIn = {outBuff.dst, outBuff.pos, 0}; @@ -1575,6 +1628,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double U64 crcOrig; U32 resetAllowed = 1; size_t maxTestSize; + ZSTD_parameters savedParams; /* init */ if (nbTests >= testNb) { DISPLAYUPDATE(2, "\r%6u/%6u ", testNb, nbTests); } @@ -1737,6 +1791,8 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double } } } + CHECK_Z(getCCtxParams(zc, &savedParams)); + /* multi-segments compression test */ XXH64_reset(&xxhState, 0); { ZSTD_outBuffer outBuff = { cBuffer, cBufferSize, 0 } ; @@ -1779,6 +1835,8 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double DISPLAYLEVEL(5, "Frame completed : %u bytes \n", (U32)cSize); } + CHECK(badParameters(zc, savedParams), "CCtx params are wrong"); + /* multi - fragments decompression test */ if (!dictSize /* don't reset if dictionary : could be different */ && (FUZ_rand(&lseed) & 1)) { DISPLAYLEVEL(5, "resetting DCtx (dict:%08X) \n", (U32)(size_t)dict); From 64bfdca5b9d3391ddf1dc9d6f74adfb13185f8dd Mon Sep 17 00:00:00 2001 From: Peter Seiderer Date: Mon, 16 Apr 2018 20:44:49 +0200 Subject: [PATCH 030/288] Split library install target into pc, static, shared and include only target Signed-off-by: Peter Seiderer --- lib/Makefile | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 72335a5a9..f64f192d4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -159,20 +159,29 @@ libzstd.pc: libzstd.pc.in -e 's|@VERSION@|$(VERSION)|' \ $< >$@ -install: libzstd.a libzstd libzstd.pc +install: install-pc install-static install-shared install-includes + @echo zstd static and shared library installed + +install-pc: libzstd.pc @$(INSTALL) -d -m 755 $(DESTDIR)$(PKGCONFIGDIR)/ $(DESTDIR)$(INCLUDEDIR)/ @$(INSTALL_DATA) libzstd.pc $(DESTDIR)$(PKGCONFIGDIR)/ - @echo Installing libraries + +install-static: libzstd.a + @echo Installing static library @$(INSTALL_DATA) libzstd.a $(DESTDIR)$(LIBDIR) + +install-shared: libzstd + @echo Installing shared library @$(INSTALL_PROGRAM) $(LIBZSTD) $(DESTDIR)$(LIBDIR) @ln -sf $(LIBZSTD) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT_MAJOR) @ln -sf $(LIBZSTD) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT) + +install-includes: @echo Installing includes @$(INSTALL_DATA) zstd.h $(DESTDIR)$(INCLUDEDIR) @$(INSTALL_DATA) common/zstd_errors.h $(DESTDIR)$(INCLUDEDIR) @$(INSTALL_DATA) deprecated/zbuff.h $(DESTDIR)$(INCLUDEDIR) # prototypes generate deprecation warnings @$(INSTALL_DATA) dictBuilder/zdict.h $(DESTDIR)$(INCLUDEDIR) - @echo zstd static and shared library installed uninstall: @$(RM) $(DESTDIR)$(LIBDIR)/libzstd.a From 82ad249645941096c8e1d1171ad6b4cfcfbd5188 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 30 Apr 2018 11:35:49 -0700 Subject: [PATCH 031/288] Clarifications of Zstandard format specification from IETF RFC review --- doc/zstd_compression_format.md | 145 ++++++++++++++++++++------------- 1 file changed, 88 insertions(+), 57 deletions(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index 7bf36c491..66819d136 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.6 (19/08/17) +0.2.7 (30/04/18) Introduction @@ -112,6 +112,11 @@ __`Magic_Number`__ 4 Bytes, __little-endian__ format. Value : 0xFD2FB528 +Note: This value was selected to be less probable to find at the beginning of some random file. +It avoids trivial patterns (0x00, 0xFF, repeated bytes, increasing bytes, etc.), +contains byte values outside of ASCII range, +and doesn't map into UTF8 space. +It reduces the chances that a text file represent this value by accident. __`Frame_Header`__ @@ -171,8 +176,8 @@ according to the following table: |`FCS_Field_Size`| 0 or 1 | 2 | 4 | 8 | When `Flag_Value` is `0`, `FCS_Field_Size` depends on `Single_Segment_flag` : -if `Single_Segment_flag` is set, `Field_Size` is 1. -Otherwise, `Field_Size` is 0 : `Frame_Content_Size` is not provided. +if `Single_Segment_flag` is set, `FCS_Field_Size` is 1. +Otherwise, `FCS_Field_Size` is 0 : `Frame_Content_Size` is not provided. __`Single_Segment_flag`__ @@ -218,11 +223,11 @@ __`Dictionary_ID_flag`__ This is a 2-bits flag (`= FHD & 3`), telling if a dictionary ID is provided within the header. -It also specifies the size of this field as `Field_Size`. +It also specifies the size of this field as `DID_Field_Size`. -|`Flag_Value`| 0 | 1 | 2 | 3 | -| ---------- | --- | --- | --- | --- | -|`Field_Size`| 0 | 1 | 2 | 4 | +|`Flag_Value` | 0 | 1 | 2 | 3 | +| -------------- | --- | --- | --- | --- | +|`DID_Field_Size`| 0 | 1 | 2 | 4 | #### `Window_Descriptor` @@ -270,7 +275,8 @@ the ID of the dictionary required to properly decode the frame. `Dictionary_ID` field is optional. When it's not present, it's up to the decoder to make sure it uses the correct dictionary. -Field size depends on `Dictionary_ID_flag`. +`Dictionary_ID` field size is provided by `DID_Field_Size`. +`DID_Field_Size` is directly derived from value of `Dictionary_ID_flag`. 1 byte can represent an ID 0-255. 2 bytes can represent an ID 0-65535. 4 bytes can represent an ID 0-4294967295. @@ -363,16 +369,14 @@ There are 4 block types : __`Block_Size`__ The upper 21 bits of `Block_Header` represent the `Block_Size`. +`Block_Size` is the size of the block excluding the header. +A block can contain any number of bytes (even zero), up to +`Block_Maximum_Decompressed_Size`, which is the smallest of: +- Window_Size +- 128 KB -Block sizes must respect a few rules : -- For `Compressed_Block`, `Block_Size` is always strictly less than decompressed size. -- Block decompressed size is always <= `Window_Size` -- Block decompressed size is always <= 128 KB. - -A block can contain any number of bytes (even empty), -up to `Block_Maximum_Decompressed_Size`, which is the smallest of : -- `Window_Size` -- 128 KB +A `Compressed_Block` has the extra restriction that `Block_Size` is always +strictly less than the decompressed size. Compressed Blocks @@ -392,8 +396,14 @@ To decode a compressed block, the following elements are necessary : - Previous decoded data, up to a distance of `Window_Size`, or all previously decoded data when `Single_Segment_flag` is set. - List of "recent offsets" from previous `Compressed_Block`. -- Decoding tables of previous `Compressed_Block` for each symbol type - (literals, literals lengths, match lengths, offsets). +- The previous Huffman tree, required by `Treeless_Literals_Block` type +- Previous FSE decoding tables, required by `Repeat_Mode` + for each symbol type (literals lengths, match lengths, offsets) + +Note that decoding tables aren't always from the previous `Compressed_Block`. + +- Every decoding table can come from a dictionary. +- The Huffman tree comes from the previous `Compressed_Literals_Block`. Literals Section ---------------- @@ -460,17 +470,20 @@ For values spanning several bytes, convention is __little-endian__. __`Size_Format` for `Raw_Literals_Block` and `RLE_Literals_Block`__ : -- Value ?0 : `Size_Format` uses 1 bit. +`Size_Format` uses 1 _or_ 2 bits. +Its value is : `Size_Format = (Header[0]>>2) & 3` + +- `Size_Format` == 00 or 10 : `Size_Format` uses 1 bit. `Regenerated_Size` uses 5 bits (0-31). - `Literals_Section_Header` has 1 byte. + `Literals_Section_Header` uses 1 byte. `Regenerated_Size = Header[0]>>3` -- Value 01 : `Size_Format` uses 2 bits. +- `Size_Format` == 01 : `Size_Format` uses 2 bits. `Regenerated_Size` uses 12 bits (0-4095). - `Literals_Section_Header` has 2 bytes. + `Literals_Section_Header` uses 2 bytes. `Regenerated_Size = (Header[0]>>4) + (Header[1]<<4)` -- Value 11 : `Size_Format` uses 2 bits. +- `Size_Format` == 11 : `Size_Format` uses 2 bits. `Regenerated_Size` uses 20 bits (0-1048575). - `Literals_Section_Header` has 3 bytes. + `Literals_Section_Header` uses 3 bytes. `Regenerated_Size = (Header[0]>>4) + (Header[1]<<4) + (Header[2]<<12)` Only Stream1 is present for these cases. @@ -479,18 +492,20 @@ using a long format, even if it's less efficient. __`Size_Format` for `Compressed_Literals_Block` and `Treeless_Literals_Block`__ : -- Value 00 : _A single stream_. +`Size_Format` always uses 2 bits. + +- `Size_Format` == 00 : _A single stream_. Both `Regenerated_Size` and `Compressed_Size` use 10 bits (0-1023). - `Literals_Section_Header` has 3 bytes. -- Value 01 : 4 streams. + `Literals_Section_Header` uses 3 bytes. +- `Size_Format` == 01 : 4 streams. Both `Regenerated_Size` and `Compressed_Size` use 10 bits (0-1023). - `Literals_Section_Header` has 3 bytes. -- Value 10 : 4 streams. + `Literals_Section_Header` uses 3 bytes. +- `Size_Format` == 10 : 4 streams. Both `Regenerated_Size` and `Compressed_Size` use 14 bits (0-16383). - `Literals_Section_Header` has 4 bytes. -- Value 11 : 4 streams. + `Literals_Section_Header` uses 4 bytes. +- `Size_Format` == 11 : 4 streams. Both `Regenerated_Size` and `Compressed_Size` use 18 bits (0-262143). - `Literals_Section_Header` has 5 bytes. + `Literals_Section_Header` uses 5 bytes. Both `Compressed_Size` and `Regenerated_Size` fields follow __little-endian__ convention. Note: `Compressed_Size` __includes__ the size of the Huffman Tree description @@ -516,7 +531,8 @@ it must be used to determine where streams begin. `Total_Streams_Size = Compressed_Size - Huffman_Tree_Description_Size`. For `Treeless_Literals_Block`, -the Huffman table comes from previously compressed literals block. +the Huffman table comes from previously compressed literals block, +or from a dictionary. Huffman compressed data consists of either 1 or 4 Huffman-coded streams. @@ -570,7 +586,8 @@ followed by the bitstream. | -------------------------- | ------------------------- | ---------------- | ---------------------- | --------- | To decode the `Sequences_Section`, it's required to know its size. -This size is deduced from `Block_Size - Literals_Section_Size`. +This size is deduced from the literals section size: +`Sequences_Section_Size = Block_Size - Literals_Section_Size`. #### `Sequences_Section_Header` @@ -614,9 +631,11 @@ They follow the same enumeration : No distribution table will be present. - `RLE_Mode` : The table description consists of a single byte. This code will be repeated for all sequences. -- `Repeat_Mode` : The table used in the previous compressed block will be used again. +- `Repeat_Mode` : The table used in the previous `Compressed_Block` will be used again, + or if this is the first block, table in the dictionary will be used No distribution table will be present. - Note: this includes RLE mode, so if `Repeat_Mode` follows `RLE_Mode`, the same symbol will be repeated. + Note that this includes `RLE_mode`, so if `Repeat_Mode` follows `RLE_Mode`, the same symbol will be repeated. + Note that this also includes `Predefined_Mode`. If this mode is used without any previous sequence table in the frame (or [dictionary](#dictionary-format)) to repeat, this should be treated as corruption. - `FSE_Compressed_Mode` : standard FSE compression. @@ -624,6 +643,8 @@ They follow the same enumeration : The format of this distribution table is described in [FSE Table Description](#fse-table-description). Note that the maximum allowed accuracy log for literals length and match length tables is 9, and the maximum accuracy log for the offsets table is 8. + `FSE_Compressed_Mode` must not be used when only one symbol is present, + `RLE_Mode` should be used instead (although any other mode will work). #### The codes for literals lengths, match lengths, and offsets. @@ -696,7 +717,7 @@ Offset codes are values ranging from `0` to `N`. A decoder is free to limit its maximum `N` supported. Recommendation is to support at least up to `22`. For information, at the time of this writing. -the reference decoder supports a maximum `N` value of `28` in 64-bits mode. +the reference decoder supports a maximum `N` value of `31` in 64-bits mode. An offset code is also the number of additional bits to read in __little-endian__ fashion, and can be translated into an `Offset_Value` using the following formulas : @@ -856,7 +877,8 @@ so an `offset_value` of 1 means `Repeated_Offset2`, an `offset_value` of 2 means `Repeated_Offset3`, and an `offset_value` of 3 means `Repeated_Offset1 - 1_byte`. -For the first block, the starting offset history is populated with the following values : 1, 4 and 8 (in order). +For the first block, the starting offset history is populated with the following values : 1, 4 and 8 (in order), +unless a dictionary is used, in which case they come from the dictionary. Then each block gets its starting offset history from the ending values of the most recent `Compressed_Block`. Note that blocks which are not `Compressed_Block` are skipped, they do not contribute to offset history. @@ -919,6 +941,8 @@ FSE, short for Finite State Entropy, is an entropy codec based on [ANS]. FSE encoding/decoding involves a state that is carried over between symbols, so decoding must be done in the opposite direction as encoding. Therefore, all FSE bitstreams are read from end to beginning. +Note that the order of the bits in the stream is not reversed, +we just read the elements in the reverse order they are written. For additional details on FSE, see [Finite State Entropy]. @@ -943,6 +967,7 @@ The Zstandard format encodes FSE table descriptions as follows: An FSE distribution table describes the probabilities of all symbols from `0` to the last present one (included) on a normalized scale of `1 << Accuracy_Log` . +Note that there must be two or more symbols with nonzero probability. It's a bitstream which is read forward, in __little-endian__ fashion. It's not necessary to know its exact size, @@ -959,24 +984,24 @@ It depends on : __example__ : Presuming an `Accuracy_Log` of 8, and presuming 100 probabilities points have already been distributed, - the decoder may read any value from `0` to `255 - 100 + 1 == 156` (inclusive). - Therefore, it must read `log2sup(156) == 8` bits. + the decoder may read any value from `0` to `256 - 100 + 1 == 157` (inclusive). + Therefore, it must read `log2sup(157) == 8` bits. - Value decoded : small values use 1 less bit : __example__ : - Presuming values from 0 to 156 (inclusive) are possible, - 255-156 = 99 values are remaining in an 8-bits field. + Presuming values from 0 to 157 (inclusive) are possible, + 255-157 = 98 values are remaining in an 8-bits field. They are used this way : - first 99 values (hence from 0 to 98) use only 7 bits, - values from 99 to 156 use 8 bits. + first 98 values (hence from 0 to 97) use only 7 bits, + values from 98 to 157 use 8 bits. This is achieved through this scheme : | Value read | Value decoded | Number of bits used | | ---------- | ------------- | ------------------- | - | 0 - 98 | 0 - 98 | 7 | - | 99 - 127 | 99 - 127 | 8 | - | 128 - 226 | 0 - 98 | 7 | - | 227 - 255 | 128 - 156 | 8 | + | 0 - 97 | 0 - 97 | 7 | + | 98 - 127 | 98 - 127 | 8 | + | 128 - 225 | 0 - 97 | 7 | + | 226 - 255 | 128 - 157 | 8 | Symbols probabilities are read one by one, in order. @@ -1019,12 +1044,12 @@ and instructions to get the next state. Symbols are scanned in their natural order for "less than 1" probabilities. Symbols with this probability are being attributed a single cell, -starting from the end of the table. +starting from the end of the table and retreating. These symbols define a full state reset, reading `Accuracy_Log` bits. -All remaining symbols are sorted in their natural order. +All remaining symbols are allocated in their natural order. Starting from symbol `0` and table position `0`, -each symbol gets attributed as many cells as its probability. +each symbol gets allocated as many cells as its probability. Cell allocation is spreaded, not linear : each successor position follow this rule : @@ -1044,6 +1069,7 @@ Each state will decode the current symbol. To get the `Number_of_Bits` and `Baseline` required for next state, it's first necessary to sort all states in their natural order. The lower states will need 1 more bit than higher ones. +The process is repeated for each symbol. __Example__ : Presuming a symbol has a probability of 5. @@ -1055,10 +1081,12 @@ Presuming the `Accuracy_Log` is 7, it defines 128 states. Divided by 8, each share is 16 large. In order to reach 8, 8-5=3 lowest states will count "double", -taking shares twice larger, +doubling the number of shares (32 in width), requiring one more bit in the process. -Numbering starts from higher states using less bits. +Baseline is assigned starting from the higher states using fewer bits, +and proceeding naturally, then resuming at the first state, +each takes its allocated width from Baseline. | state order | 0 | 1 | 2 | 3 | 4 | | ---------------- | ----- | ----- | ------ | ---- | ----- | @@ -1075,6 +1103,7 @@ See [Appendix A] for the results of this process applied to the default distribu [Appendix A]: #appendix-a---decoding-tables-for-predefined-codes + Huffman Coding -------------- Zstandard Huffman-coded streams are read backwards, @@ -1096,6 +1125,7 @@ The bitstream contains Huffman-coded symbols in __little-endian__ order, with the codes defined by the method below. ### Huffman Tree Description + Prefix coding represents symbols from an a priori known alphabet by bit sequences (codewords), one codeword for each symbol, in a manner such that different symbols may be represented @@ -1112,7 +1142,6 @@ More bits improve accuracy but cost more header size, and require more memory or more complex decoding operations. This specification limits maximum code length to 11 bits. - ##### Representation All literal values from zero (included) to last present one (excluded) @@ -1190,7 +1219,7 @@ and last symbol's weight is not represented. An FSE bitstream starts by a header, describing probabilities distribution. It will create a Decoding Table. -For a list of Huffman weights, the maximum accuracy log is 7 bits. +For a list of Huffman weights, the maximum accuracy log is 6 bits. For more description see the [FSE header description](#fse-table-description) The Huffman header compression uses 2 states, @@ -1330,7 +1359,8 @@ __`Content`__ : The rest of the dictionary is its content. As long as the amount of data decoded from this frame is less than or equal to `Window_Size`, sequence commands may specify offsets longer than the total length of decoded output so far to reference back to the - dictionary. After the total output has surpassed `Window_Size` however, + dictionary, even parts of the dictionary with offsets larger than `Window_Size`. + After the total output has surpassed `Window_Size` however, this is no longer allowed and the dictionary is no longer accessible. [compressed blocks]: #the-format-of-compressed_block @@ -1523,6 +1553,7 @@ to crosscheck that an implementation build its decoding tables correctly. Version changes --------------- +- 0.2.7 : clarifications from IETF RFC review, by Vijay Gurbani and Nick Terrell - 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 From b095bffac6d768b0e8234726bfcf39d5420db5e4 Mon Sep 17 00:00:00 2001 From: cyan4973 Date: Tue, 1 May 2018 05:45:46 -0700 Subject: [PATCH 032/288] ignore Windows build/test artefacts --- .gitignore | 1 + build/.gitignore | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 5fe9afd41..d28a51291 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ zstdmt # Test artefacts tmp* dictionary* +NUL # Build artefacts projects/ diff --git a/build/.gitignore b/build/.gitignore index 85bc9287d..b00e709ba 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -8,13 +8,13 @@ *.user *.opendb -VS2005/ -VS2008/ +VS2008/bin/ VS2010/bin/ VS2010/zwrapbench/ -VS2012/bin/ -VS2013/bin/ -VS2015/bin/ +VS2012/ +VS2013/ +VS2015/ +Studio* # CMake cmake/build/ From 263134ce66342b194472ffd593f3e4d7278b2f12 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 3 May 2018 13:03:08 -0700 Subject: [PATCH 033/288] Fix playTests.sh typo Thanks to @mestia for reporting and finding the bug! Fixes #1116. --- tests/playTests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index 32190daaa..395f23377 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -176,7 +176,7 @@ $ECHO hello > tmp $ZSTD tmp -f -o "$DEVDEVICE" 2>tmplog > "$INTOVOID" grep -v "Refusing to remove non-regular file" tmplog rm -f tmplog -$ZSTD tmp -f -o "$INTONULL" 2>&1 | grep -v "Refusing to remove non-regular file" +$ZSTD tmp -f -o "$INTOVOID" 2>&1 | grep -v "Refusing to remove non-regular file" $ECHO "test : --rm on stdin" $ECHO a | $ZSTD --rm > $INTOVOID # --rm should remain silent rm tmp From 2dbe408a497a0528023c5ea3bc6b20f5b1f5de2b Mon Sep 17 00:00:00 2001 From: Chris Lamb Date: Fri, 4 May 2018 08:39:03 -0700 Subject: [PATCH 034/288] Make the build reproducible Whilst working on the Reproducible Builds effort [0], we noticed that zstd could not be built reproducibly. This is due to the manual page encoding the number of CPUs from the build machine and thus varies across builds. This was originally filed in Debian as #897904 [1]. [0] https://reproducible-builds.org/ [1] https://bugs.debian.org/897904 Signed-off-by: Chris Lamb --- contrib/pzstd/Options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pzstd/Options.cpp b/contrib/pzstd/Options.cpp index d9b216b42..1590d85ee 100644 --- a/contrib/pzstd/Options.cpp +++ b/contrib/pzstd/Options.cpp @@ -85,7 +85,7 @@ void usage() { std::fprintf(stderr, "Usage:\n"); std::fprintf(stderr, " pzstd [args] [FILE(s)]\n"); std::fprintf(stderr, "Parallel ZSTD options:\n"); - std::fprintf(stderr, " -p, --processes # : number of threads to use for (de)compression (default:%d)\n", defaultNumThreads()); + std::fprintf(stderr, " -p, --processes # : number of threads to use for (de)compression (default: )\n"); std::fprintf(stderr, "ZSTD options:\n"); std::fprintf(stderr, " -# : # compression level (1-%d, default:%d)\n", kMaxNonUltraCompressionLevel, kDefaultCompressionLevel); From ad4524d60526932445dd7ed7bcac35c298874e01 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 7 May 2018 12:54:13 -0700 Subject: [PATCH 035/288] fix ZSTD_compressBlock() associated with CDict reported by @let-def. It's actually a bug in ZSTD_compressBegin_usingCDict() which would pass a wrong pledgedSrcSize value (0 instead of ZSTD_CONTENTSIZE_UNKNOWN) resulting in wrong window size, resulting in downsized seqStore, resulting in segfault when writing into the seqStore later in the process. Added a test in fuzzer to cover this use case (fails before the patch). --- lib/compress/zstd_compress.c | 2 +- lib/compress/zstd_double_fast.c | 2 ++ tests/fuzzer.c | 9 +++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index f3a49e672..08dc7db28 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2920,7 +2920,7 @@ size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) { ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; DEBUGLOG(4, "ZSTD_compressBegin_usingCDict : dictIDFlag == %u", !fParams.noDictIDFlag); - return ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, 0); + return ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN); } size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx, diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index a4feafbf2..78aa0cbad 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -204,6 +204,8 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic( const BYTE* const ilimit = iend - 8; U32 offset_1=rep[0], offset_2=rep[1]; + DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_extDict_generic (srcSize=%zu)", srcSize); + /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ const size_t hSmall = ZSTD_hashPtr(ip, hBitsS, mls); diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 9b49ddd08..f9ea209fc 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -1196,6 +1196,15 @@ static int basicUnitTests(U32 seed, double compressibility) if (r != blockSize) goto _output_error; } DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3i : Block compression with CDict : ", testNb++); + { ZSTD_CDict* const cdict = ZSTD_createCDict(CNBuffer, dictSize, 3); + if (cdict==NULL) goto _output_error; + CHECK( ZSTD_compressBegin_usingCDict(cctx, cdict) ); + CHECK( ZSTD_compressBlock(cctx, compressedBuffer, ZSTD_compressBound(blockSize), (char*)CNBuffer+dictSize, blockSize) ); + ZSTD_freeCDict(cdict); + } + DISPLAYLEVEL(3, "OK \n"); + ZSTD_freeCCtx(cctx); } ZSTD_freeDCtx(dctx); From 9a0643b633c00e86db059e3790bdea7155fb6dc9 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Tue, 8 May 2018 20:43:28 +0300 Subject: [PATCH 036/288] lib/Makefile: create include directory before headers installation Make sure that $(INCLUDEDIR) exists before copying the headers there. Otherwise, the contest of header files is copied over $(DESTDIR)$(INCLUDEDIR), making it a regular file. While at it, remove $(DESTDIR)$(INCLUDEDIR) from the list of directories to create in the install-pc target. The install-pc target does not need this directory. --- lib/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Makefile b/lib/Makefile index f64f192d4..d8178c7a5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -163,7 +163,7 @@ install: install-pc install-static install-shared install-includes @echo zstd static and shared library installed install-pc: libzstd.pc - @$(INSTALL) -d -m 755 $(DESTDIR)$(PKGCONFIGDIR)/ $(DESTDIR)$(INCLUDEDIR)/ + @$(INSTALL) -d -m 755 $(DESTDIR)$(PKGCONFIGDIR)/ @$(INSTALL_DATA) libzstd.pc $(DESTDIR)$(PKGCONFIGDIR)/ install-static: libzstd.a @@ -178,6 +178,7 @@ install-shared: libzstd install-includes: @echo Installing includes + @$(INSTALL) -d -m 755 $(DESTDIR)$(INCLUDEDIR)/ @$(INSTALL_DATA) zstd.h $(DESTDIR)$(INCLUDEDIR) @$(INSTALL_DATA) common/zstd_errors.h $(DESTDIR)$(INCLUDEDIR) @$(INSTALL_DATA) deprecated/zbuff.h $(DESTDIR)$(INCLUDEDIR) # prototypes generate deprecation warnings From 2dde9d5abaade164329bf698aa938f1a5b0526d1 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 8 May 2018 11:06:01 -0700 Subject: [PATCH 037/288] Write to /dev/random for test --- tests/playTests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index 395f23377..c8e27f233 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -56,7 +56,7 @@ fi isWindows=false INTOVOID="/dev/null" -DEVDEVICE="/dev/zero" +DEVDEVICE="/dev/random" case "$OS" in Windows*) isWindows=true From a155061328c06587fc3951403b3603cb58e2f297 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 8 May 2018 12:32:16 -0700 Subject: [PATCH 038/288] minor code refactor for readability removed some useless operations from optimal parser (should not change performance, too small a difference) --- lib/compress/zstd_compress.c | 33 ++++++++---- lib/compress/zstd_compress_internal.h | 14 ++--- lib/compress/zstd_opt.c | 76 ++++++++++++++------------- 3 files changed, 68 insertions(+), 55 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index f3a49e672..76b471c5e 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -995,7 +995,11 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pl typedef enum { ZSTDcrp_continue, ZSTDcrp_noMemset } ZSTD_compResetPolicy_e; -static void* ZSTD_reset_matchState(ZSTD_matchState_t* ms, void* ptr, ZSTD_compressionParameters const* cParams, ZSTD_compResetPolicy_e const crp, U32 const forCCtx) +static void* +ZSTD_reset_matchState(ZSTD_matchState_t* ms, + void* ptr, + const ZSTD_compressionParameters* cParams, + ZSTD_compResetPolicy_e const crp, U32 const forCCtx) { size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog); size_t const hSize = ((size_t)1) << cParams->hashLog; @@ -1285,7 +1289,7 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, /* copy dictionary offsets */ { - ZSTD_matchState_t const* srcMatchState = &srcCCtx->blockState.matchState; + const ZSTD_matchState_t* srcMatchState = &srcCCtx->blockState.matchState; ZSTD_matchState_t* dstMatchState = &dstCCtx->blockState.matchState; dstMatchState->window = srcMatchState->window; dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; @@ -1985,8 +1989,9 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, const void* src, size_t srcSize) { ZSTD_matchState_t* const ms = &zc->blockState.matchState; - DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)", - (U32)dstCapacity, ms->window.dictLimit, ms->nextToUpdate); + DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%zu, dictLimit=%u, nextToUpdate=%u)", + dstCapacity, ms->window.dictLimit, ms->nextToUpdate); + if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) { ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize, zc->appliedParams.cParams.searchLength); return 0; /* don't even attempt compression below a certain srcSize */ @@ -1997,6 +2002,8 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, { const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; const U32 current = (U32)(istart-base); + assert(istart >= base); + if (sizeof(ptrdiff_t)==8) assert(istart - base < (ptrdiff_t)(U32)(-1)); /* ensure no overflow */ if (current > ms->nextToUpdate + 384) ms->nextToUpdate = current - MIN(192, (U32)(current - ms->nextToUpdate - 384)); } @@ -2369,6 +2376,8 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, size_t dictID; ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1< 8); + assert(MEM_readLE32(dictPtr) == ZSTD_MAGIC_DICTIONARY); dictPtr += 4; /* skip magic number */ dictID = params->fParams.noDictIDFlag ? 0 : MEM_readLE32(dictPtr); @@ -2447,12 +2456,14 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, /** ZSTD_compress_insertDictionary() : * @return : dictID, or an error code */ -static size_t ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_matchState_t* ms, - ZSTD_CCtx_params const* params, - const void* dict, size_t dictSize, - ZSTD_dictContentType_e dictContentType, - ZSTD_dictTableLoadMethod_e dtlm, - void* workspace) +static size_t +ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, + ZSTD_matchState_t* ms, + const ZSTD_CCtx_params* params, + const void* dict, size_t dictSize, + ZSTD_dictContentType_e dictContentType, + ZSTD_dictTableLoadMethod_e dtlm, + void* workspace) { DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize); if ((dict==NULL) || (dictSize<=8)) return 0; @@ -2726,7 +2737,7 @@ static size_t ZSTD_initCDict_internal( ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams) { - DEBUGLOG(3, "ZSTD_initCDict_internal, dictContentType %u", (U32)dictContentType); + DEBUGLOG(3, "ZSTD_initCDict_internal (dictContentType:%u)", (U32)dictContentType); assert(!ZSTD_checkCParams(cParams)); cdict->cParams = cParams; if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) { diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 0a19b3ecd..bbb66a8ea 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -95,7 +95,7 @@ typedef struct { U32 log2matchLengthSum; /* pow2 to compare log2(mlfreq) to */ U32 log2offCodeSum; /* pow2 to compare log2(offreq) to */ /* end : updated by ZSTD_setLog2Prices */ - U32 staticPrices; /* prices follow a pre-defined cost structure, statistics are irrelevant */ + U32 predefPrices; /* prices follow a pre-defined cost structure, statistics are irrelevant */ } optState_t; typedef struct { @@ -112,11 +112,11 @@ typedef struct { } ZSTD_window_t; typedef struct { - ZSTD_window_t window; /* State for window round buffer management */ - U32 loadedDictEnd; /* index of end of dictionary */ - U32 nextToUpdate; /* index from which to continue table update */ - U32 nextToUpdate3; /* index from which to continue table update */ - U32 hashLog3; /* dispatch table : larger == faster, more memory */ + ZSTD_window_t window; /* State for window round buffer management */ + U32 loadedDictEnd; /* index of end of dictionary */ + U32 nextToUpdate; /* index from which to continue table update */ + U32 nextToUpdate3; /* index from which to continue table update */ + U32 hashLog3; /* dispatch table : larger == faster, more memory */ U32* hashTable; U32* hashTable3; U32* chainTable; @@ -161,7 +161,7 @@ typedef struct { rawSeq* seq; /* The start of the sequences */ size_t pos; /* The position where reading stopped. <= size. */ size_t size; /* The number of sequences. <= capacity. */ - size_t capacity; /* The capacity of the `seq` pointer */ + size_t capacity; /* The capacity starting from `seq` pointer */ } rawSeqStore_t; struct ZSTD_CCtx_params_s { diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index f63f0c585..80b9e5e00 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -32,11 +32,11 @@ static void ZSTD_setLog2Prices(optState_t* optPtr) static void ZSTD_rescaleFreqs(optState_t* const optPtr, const BYTE* const src, size_t const srcSize) { - optPtr->staticPrices = 0; + optPtr->predefPrices = 0; if (optPtr->litLengthSum == 0) { /* first init */ unsigned u; - if (srcSize <= 1024) optPtr->staticPrices = 1; + if (srcSize <= 1024) optPtr->predefPrices = 1; assert(optPtr->litFreq!=NULL); for (u=0; u<=MaxLit; u++) @@ -89,12 +89,12 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, /* ZSTD_rawLiteralsCost() : - * cost of literals (only) in given segment (which length can be null) + * cost of literals (only) in specified segment (which length can be 0). * does not include cost of literalLength symbol */ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, const optState_t* const optPtr) { - if (optPtr->staticPrices) return (litLength*6); /* 6 bit per literal - no statistic used */ + if (optPtr->predefPrices) return (litLength*6); /* 6 bit per literal - no statistic used */ if (litLength == 0) return 0; /* literals */ @@ -110,7 +110,7 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, * cost of literalLength symbol */ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr) { - if (optPtr->staticPrices) return ZSTD_highbit32((U32)litLength+1); + if (optPtr->predefPrices) return ZSTD_highbit32((U32)litLength+1); /* literal Length */ { U32 const llCode = ZSTD_LLcode(litLength); @@ -135,7 +135,7 @@ static U32 ZSTD_fullLiteralsCost(const BYTE* const literals, U32 const litLength * to provide a cost which is directly comparable to a match ending at same position */ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr) { - if (optPtr->staticPrices) return ZSTD_highbit32(litLength+1); + if (optPtr->predefPrices) return ZSTD_highbit32(litLength+1); /* literal Length */ { U32 const llCode = ZSTD_LLcode(litLength); @@ -166,18 +166,18 @@ static int ZSTD_literalsContribution(const BYTE* const literals, U32 const litLe * Provides the cost of the match part (offset + matchLength) of a sequence * Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence. * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */ -FORCE_INLINE_TEMPLATE U32 ZSTD_getMatchPrice( - U32 const offset, U32 const matchLength, - const optState_t* const optPtr, - int const optLevel) +FORCE_INLINE_TEMPLATE U32 +ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, + const optState_t* const optPtr, + int const optLevel) { U32 price; U32 const offCode = ZSTD_highbit32(offset+1); U32 const mlBase = matchLength - MINMATCH; assert(matchLength >= MINMATCH); - if (optPtr->staticPrices) /* fixed scheme, do not use statistics */ - return ZSTD_highbit32((U32)mlBase+1) + 16 + offCode; + if (optPtr->predefPrices) /* fixed scheme, do not use statistics */ + return ZSTD_highbit32(mlBase+1) + 16 + offCode; price = offCode + optPtr->log2offCodeSum - ZSTD_highbit32(optPtr->offCodeFreq[offCode]+1); if ((optLevel<2) /*static*/ && offCode >= 20) price += (offCode-19)*2; /* handicap for long distance offsets, favor decompression speed */ @@ -662,12 +662,13 @@ static int ZSTD_literalsContribution_cached( return contribution; } -FORCE_INLINE_TEMPLATE -size_t ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,seqStore_t* seqStore, - U32 rep[ZSTD_REP_NUM], - ZSTD_compressionParameters const* cParams, - const void* src, size_t srcSize, - const int optLevel, const int extDict) +FORCE_INLINE_TEMPLATE size_t +ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, + seqStore_t* seqStore, + U32 rep[ZSTD_REP_NUM], + const ZSTD_compressionParameters* cParams, + const void* src, size_t srcSize, + const int optLevel, const int extDict) { optState_t* const optStatePtr = &ms->opt; const BYTE* const istart = (const BYTE*)src; @@ -705,17 +706,18 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,seqStore_t* seqStore /* initialize opt[0] */ { U32 i ; for (i=0; i immediate encoding */ { U32 const maxML = matches[nbMatches-1].len; - DEBUGLOG(7, "found %u matches of maxLength=%u and offset=%u at cPos=%u => start new serie", - nbMatches, maxML, matches[nbMatches-1].off, (U32)(ip-prefixStart)); + U32 const maxOffset = matches[nbMatches-1].off; + DEBUGLOG(7, "found %u matches of maxLength=%u and maxOffset=%u at cPos=%u => start new serie", + nbMatches, maxML, maxOffset, (U32)(ip-prefixStart)); if (maxML > sufficient_len) { best_mlen = maxML; - best_off = matches[nbMatches-1].off; + best_off = maxOffset; DEBUGLOG(7, "large match (%u>%u), immediate encoding", best_mlen, sufficient_len); cur = 0; @@ -727,22 +729,23 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,seqStore_t* seqStore { U32 const literalsPrice = ZSTD_fullLiteralsCost_cached(&cachedLitPrice, anchor, litlen, optStatePtr); U32 pos; U32 matchNb; - for (pos = 0; pos < minMatch; pos++) { - opt[pos].mlen = 1; - opt[pos].price = ZSTD_MAX_PRICE; + for (pos = 1; pos < minMatch; pos++) { + opt[pos].price = ZSTD_MAX_PRICE; /* mlen, litlen and price will be fixed during forward scanning */ } for (matchNb = 0; matchNb < nbMatches; matchNb++) { U32 const offset = matches[matchNb].off; U32 const end = matches[matchNb].len; repcodes_t const repHistory = ZSTD_updateRep(rep, offset, ll0); for ( ; pos <= end ; pos++ ) { - U32 const matchPrice = literalsPrice + ZSTD_getMatchPrice(offset, pos, optStatePtr, optLevel); + U32 const matchPrice = ZSTD_getMatchPrice(offset, pos, optStatePtr, optLevel); + U32 const sequencePrice = literalsPrice + matchPrice; DEBUGLOG(7, "rPos:%u => set initial price : %u", - pos, matchPrice); + pos, sequencePrice); opt[pos].mlen = pos; opt[pos].off = offset; opt[pos].litlen = litlen; - opt[pos].price = matchPrice; + opt[pos].price = sequencePrice; + ZSTD_STATIC_ASSERT(sizeof(opt[pos].rep) == sizeof(repHistory)); memcpy(opt[pos].rep, &repHistory, sizeof(repHistory)); } } last_pos = pos-1; @@ -778,7 +781,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,seqStore_t* seqStore if (cur == last_pos) break; - if ( (optLevel==0) /*static*/ + if ( (optLevel==0) /*static_test*/ && (opt[cur+1].price <= opt[cur].price) ) continue; /* skip unpromising positions; about ~+6% speed, -0.01 ratio */ @@ -795,13 +798,12 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,seqStore_t* seqStore cur, nbMatches, maxML); if ( (maxML > sufficient_len) - | (cur + maxML >= ZSTD_OPT_NUM) ) { + || (cur + maxML >= ZSTD_OPT_NUM) ) { best_mlen = maxML; best_off = matches[nbMatches-1].off; last_pos = cur + 1; goto _shortestPath; - } - } + } } /* set prices using matches found at position == cur */ for (matchNb = 0; matchNb < nbMatches; matchNb++) { @@ -814,21 +816,22 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,seqStore_t* seqStore DEBUGLOG(7, "testing match %u => offCode=%u, mlen=%u, llen=%u", matchNb, matches[matchNb].off, lastML, litlen); - for (mlen = lastML; mlen >= startML; mlen--) { + for (mlen = lastML; mlen >= startML; mlen--) { /* scan downward */ U32 const pos = cur + mlen; int const price = basePrice + ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel); if ((pos > last_pos) || (price < opt[pos].price)) { DEBUGLOG(7, "rPos:%u => new better price (%u<%u)", pos, price, opt[pos].price); - while (last_pos < pos) { opt[last_pos+1].price = ZSTD_MAX_PRICE; last_pos++; } + while (last_pos < pos) { opt[last_pos+1].price = ZSTD_MAX_PRICE; last_pos++; } /* fill empty positions */ opt[pos].mlen = mlen; opt[pos].off = offset; opt[pos].litlen = litlen; opt[pos].price = price; + ZSTD_STATIC_ASSERT(sizeof(opt[pos].rep) == sizeof(repHistory)); memcpy(opt[pos].rep, &repHistory, sizeof(repHistory)); } else { - if (optLevel==0) break; /* gets ~+10% speed for about -0.01 ratio loss */ + if (optLevel==0) break; /* early update abort; gets ~+10% speed for about -0.01 ratio loss */ } } } } } /* for (cur = 1; cur <= last_pos; cur++) */ @@ -878,8 +881,7 @@ _shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */ if (repCode >= 2) rep[2] = rep[1]; rep[1] = rep[0]; rep[0] = currentOffset; - } - } + } } ZSTD_updateStats(optStatePtr, llen, anchor, offset, mlen); ZSTD_storeSeq(seqStore, llen, anchor, offset, mlen-MINMATCH); From 338f738c242d857acf3828c27cd475df7657178e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 8 May 2018 15:37:06 -0700 Subject: [PATCH 039/288] pass entropy tables to optimal parser for proper estimation of symbol's weights when using dictionary compression. Note : using only huffman costs is not good enough, presumably because sequence symbol costs are incorrect. --- lib/common/huf.h | 11 +++++++++-- lib/compress/huf_compress.c | 7 +++++++ lib/compress/zstd_compress.c | 1 + lib/compress/zstd_compress_internal.h | 5 ++++- lib/compress/zstd_opt.c | 24 ++++++++++++++++++------ 5 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lib/common/huf.h b/lib/common/huf.h index b4645b4e5..1f46fda2b 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -208,7 +208,7 @@ size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, si typedef enum { HUF_repeat_none, /**< Cannot use the previous table */ HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, 4}X_repeat */ - HUF_repeat_valid /**< Can use the previous table and it is asumed to be valid */ + HUF_repeat_valid /**< Can use the previous table and it is assumed to be valid */ } HUF_repeat; /** HUF_compress4X_repeat() : * Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. @@ -227,7 +227,9 @@ size_t HUF_compress4X_repeat(void* dst, size_t dstSize, */ #define HUF_CTABLE_WORKSPACE_SIZE_U32 (2*HUF_SYMBOLVALUE_MAX +1 +1) #define HUF_CTABLE_WORKSPACE_SIZE (HUF_CTABLE_WORKSPACE_SIZE_U32 * sizeof(unsigned)) -size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize); +size_t HUF_buildCTable_wksp (HUF_CElt* tree, + const U32* count, U32 maxSymbolValue, U32 maxNbBits, + void* workSpace, size_t wkspSize); /*! HUF_readStats() : * Read compact Huffman tree, saved by HUF_writeCTable(). @@ -242,6 +244,11 @@ size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, * Loading a CTable saved with HUF_writeCTable() */ size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize); +/** HUF_getNbBits() : + * Read nbBits from CTable symbolTable, for symbol `symbolValue` presumed <= HUF_SYMBOLVALUE_MAX + * Note 1 : is not inlined, as HUF_CElt definition is private + * Note 2 : const void* used, so that it can provide a statically allocated table as argument (which uses type U32) */ +U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue); /* * HUF_decompress() does the following: diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 83230b415..c01a2381e 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -216,6 +216,13 @@ size_t HUF_readCTable (HUF_CElt* CTable, U32* maxSymbolValuePtr, const void* src return readSize; } +U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue) +{ + const HUF_CElt* table = (const HUF_CElt*)symbolTable; + assert(symbolValue <= HUF_SYMBOLVALUE_MAX); + return table[symbolValue].nbBits; +} + typedef struct nodeElt_s { U32 count; diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 76b471c5e..d3e52c627 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1997,6 +1997,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, return 0; /* don't even attempt compression below a certain srcSize */ } ZSTD_resetSeqStore(&(zc->seqStore)); + ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy; /* required for optimal parser to read stats from dictionary */ /* limited update after a very long match */ { const BYTE* const base = ms->window.base; diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index bbb66a8ea..eeb7b230a 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -76,6 +76,8 @@ typedef struct { U32 rep[ZSTD_REP_NUM]; } ZSTD_optimal_t; +typedef enum { zop_none=0, zop_predef, zop_static } ZSTD_OptPrice_e; + typedef struct { /* All tables are allocated inside cctx->workspace by ZSTD_resetCCtx_internal() */ U32* litFreq; /* table of literals statistics, of size 256 */ @@ -95,7 +97,8 @@ typedef struct { U32 log2matchLengthSum; /* pow2 to compare log2(mlfreq) to */ U32 log2offCodeSum; /* pow2 to compare log2(offreq) to */ /* end : updated by ZSTD_setLog2Prices */ - U32 predefPrices; /* prices follow a pre-defined cost structure, statistics are irrelevant */ + ZSTD_OptPrice_e priceType; /* prices follow a pre-defined cost structure, statistics are irrelevant */ + const ZSTD_entropyCTables_t* symbolCosts; /* pre-calculated symbol costs, from dictionary */ } optState_t; typedef struct { diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 80b9e5e00..ecd676a68 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -32,11 +32,15 @@ static void ZSTD_setLog2Prices(optState_t* optPtr) static void ZSTD_rescaleFreqs(optState_t* const optPtr, const BYTE* const src, size_t const srcSize) { - optPtr->predefPrices = 0; + optPtr->priceType = zop_none; if (optPtr->litLengthSum == 0) { /* first init */ unsigned u; - if (srcSize <= 1024) optPtr->predefPrices = 1; + if (srcSize <= 1024) optPtr->priceType = zop_predef; + assert(optPtr->symbolCosts != NULL); + if (0 && optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid) { /* huffman table presumed generated by dictionary */ + optPtr->priceType = zop_static; + } assert(optPtr->litFreq!=NULL); for (u=0; u<=MaxLit; u++) @@ -94,7 +98,15 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, const optState_t* const optPtr) { - if (optPtr->predefPrices) return (litLength*6); /* 6 bit per literal - no statistic used */ + if (optPtr->priceType == zop_static) { + U32 u, cost; + assert(optPtr->symbolCosts != NULL); + assert(optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid); + for (u=0, cost=0; u < litLength; u++) + cost += HUF_getNbBits(optPtr->symbolCosts->hufCTable, literals[u]); + return cost; + } + if (optPtr->priceType == zop_predef) return (litLength*6); /* 6 bit per literal - no statistic used */ if (litLength == 0) return 0; /* literals */ @@ -110,7 +122,7 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, * cost of literalLength symbol */ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr) { - if (optPtr->predefPrices) return ZSTD_highbit32((U32)litLength+1); + if (optPtr->priceType == zop_predef) return ZSTD_highbit32((U32)litLength+1); /* literal Length */ { U32 const llCode = ZSTD_LLcode(litLength); @@ -135,7 +147,7 @@ static U32 ZSTD_fullLiteralsCost(const BYTE* const literals, U32 const litLength * to provide a cost which is directly comparable to a match ending at same position */ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr) { - if (optPtr->predefPrices) return ZSTD_highbit32(litLength+1); + if (optPtr->priceType == zop_predef) return ZSTD_highbit32(litLength+1); /* literal Length */ { U32 const llCode = ZSTD_LLcode(litLength); @@ -176,7 +188,7 @@ ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, U32 const mlBase = matchLength - MINMATCH; assert(matchLength >= MINMATCH); - if (optPtr->predefPrices) /* fixed scheme, do not use statistics */ + if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */ return ZSTD_highbit32(mlBase+1) + 16 + offCode; price = offCode + optPtr->log2offCodeSum - ZSTD_highbit32(optPtr->offCodeFreq[offCode]+1); From 6a3c34aa58813d55cab5e5c884c95d8ae70b3205 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 8 May 2018 16:11:21 -0700 Subject: [PATCH 040/288] opt: estimate cost of both Hufman and FSE symbols For FSE symbols : provide an upper bound, in nb of bits, since cost function is not able to store fractional bit costs. --- lib/common/fse.h | 6 ++++++ lib/compress/zstd_opt.c | 28 ++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 6a1d272be..556e6c523 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -575,6 +575,12 @@ MEM_STATIC void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* statePt BIT_flushBits(bitC); } +MEM_STATIC U32 FSE_getMaxNbBits(const FSE_symbolCompressionTransform* symbolTT, U32 symbolValue) +{ + assert(symbolValue <= FSE_MAX_SYMBOL_VALUE); + return (symbolTT[symbolValue].deltaNbBits + ((1<<16)-1)) >> 16; +} + /* ====== Decompression ====== */ diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index ecd676a68..a107fccc2 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -38,7 +38,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, unsigned u; if (srcSize <= 1024) optPtr->priceType = zop_predef; assert(optPtr->symbolCosts != NULL); - if (0 && optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid) { /* huffman table presumed generated by dictionary */ + if (optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid) { /* huffman table presumed generated by dictionary */ optPtr->priceType = zop_static; } @@ -122,12 +122,17 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, * cost of literalLength symbol */ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr) { + if (optPtr->priceType == zop_static) { + U32 const llCode = ZSTD_LLcode(litLength); + FSE_CState_t cstate; + FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); + return LL_bits[llCode] + FSE_getMaxNbBits(cstate.symbolTT, llCode); + } if (optPtr->priceType == zop_predef) return ZSTD_highbit32((U32)litLength+1); /* literal Length */ { U32 const llCode = ZSTD_LLcode(litLength); - U32 const price = LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1); - return price; + return LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1); } } @@ -147,7 +152,13 @@ static U32 ZSTD_fullLiteralsCost(const BYTE* const literals, U32 const litLength * to provide a cost which is directly comparable to a match ending at same position */ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr) { - if (optPtr->priceType == zop_predef) return ZSTD_highbit32(litLength+1); + if (optPtr->priceType == zop_static) { + U32 const llCode = ZSTD_LLcode(litLength); + FSE_CState_t cstate; + FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); + return (int)(LL_bits[llCode] + FSE_getMaxNbBits(cstate.symbolTT, llCode)) - FSE_getMaxNbBits(cstate.symbolTT, 0); + } + if (optPtr->priceType >= zop_predef) return ZSTD_highbit32(litLength+1); /* literal Length */ { U32 const llCode = ZSTD_LLcode(litLength); @@ -188,6 +199,15 @@ ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, U32 const mlBase = matchLength - MINMATCH; assert(matchLength >= MINMATCH); + if (optPtr->priceType == zop_static) { + U32 const mlCode = ZSTD_MLcode(mlBase); + FSE_CState_t mlstate, offstate; + FSE_initCState(&mlstate, optPtr->symbolCosts->matchlengthCTable); + FSE_initCState(&offstate, optPtr->symbolCosts->offcodeCTable); + return FSE_getMaxNbBits(offstate.symbolTT, offCode) + offCode + + FSE_getMaxNbBits(mlstate.symbolTT, mlCode) + ML_bits[mlCode]; + } + if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */ return ZSTD_highbit32(mlBase+1) + 16 + offCode; From 1aff63b114d0ca329d6066d9d9d5ce002dbf9f59 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 8 May 2018 16:19:04 -0700 Subject: [PATCH 041/288] opt: shift all costs by 8 bits (* 256) making it possible to represent fractional bit costs. --- lib/compress/zstd_opt.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index a107fccc2..d800254cc 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -104,7 +104,7 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, assert(optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid); for (u=0, cost=0; u < litLength; u++) cost += HUF_getNbBits(optPtr->symbolCosts->hufCTable, literals[u]); - return cost; + return cost << 8; } if (optPtr->priceType == zop_predef) return (litLength*6); /* 6 bit per literal - no statistic used */ if (litLength == 0) return 0; @@ -114,7 +114,7 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, U32 cost = litLength * optPtr->log2litSum; for (u=0; u < litLength; u++) cost -= ZSTD_highbit32(optPtr->litFreq[literals[u]]+1); - return cost; + return cost << 8; } } @@ -126,13 +126,13 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP U32 const llCode = ZSTD_LLcode(litLength); FSE_CState_t cstate; FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); - return LL_bits[llCode] + FSE_getMaxNbBits(cstate.symbolTT, llCode); + return (LL_bits[llCode] + FSE_getMaxNbBits(cstate.symbolTT, llCode)) << 8; } if (optPtr->priceType == zop_predef) return ZSTD_highbit32((U32)litLength+1); /* literal Length */ { U32 const llCode = ZSTD_LLcode(litLength); - return LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1); + return (LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1)) << 8; } } @@ -156,15 +156,16 @@ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* con U32 const llCode = ZSTD_LLcode(litLength); FSE_CState_t cstate; FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); - return (int)(LL_bits[llCode] + FSE_getMaxNbBits(cstate.symbolTT, llCode)) - FSE_getMaxNbBits(cstate.symbolTT, 0); + return ((int)(LL_bits[llCode] + FSE_getMaxNbBits(cstate.symbolTT, llCode)) - FSE_getMaxNbBits(cstate.symbolTT, 0)) * 256; } if (optPtr->priceType >= zop_predef) return ZSTD_highbit32(litLength+1); /* literal Length */ { U32 const llCode = ZSTD_LLcode(litLength); - int const contribution = LL_bits[llCode] + int const contribution = (LL_bits[llCode] + ZSTD_highbit32(optPtr->litLengthFreq[0]+1) - - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1); + - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1)) + * 256; #if 1 return contribution; #else @@ -204,8 +205,9 @@ ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, FSE_CState_t mlstate, offstate; FSE_initCState(&mlstate, optPtr->symbolCosts->matchlengthCTable); FSE_initCState(&offstate, optPtr->symbolCosts->offcodeCTable); - return FSE_getMaxNbBits(offstate.symbolTT, offCode) + offCode - + FSE_getMaxNbBits(mlstate.symbolTT, mlCode) + ML_bits[mlCode]; + return (FSE_getMaxNbBits(offstate.symbolTT, offCode) + offCode + + FSE_getMaxNbBits(mlstate.symbolTT, mlCode) + ML_bits[mlCode]) + * 256; } if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */ @@ -220,7 +222,7 @@ ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, } DEBUGLOG(8, "ZSTD_getMatchPrice(ml:%u) = %u", matchLength, price); - return price; + return price << 8; } static void ZSTD_updateStats(optState_t* const optPtr, From ba2ad9b6b993ace72b153e579be0823a61dc8769 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 8 May 2018 17:43:13 -0700 Subject: [PATCH 042/288] implemented fractional bit cost evaluation for FSE symbols. While it seems to work, the gains are negligible compared to rough maxNbBits evaluation. There are even a few losses sometimes, that still need to be explained. Furthermode, there are still cases where btlazy2 does a better job than btopt, which seems rather strange too. --- lib/common/fse.h | 15 ++++++++++++++- lib/compress/zstd_opt.c | 15 +++++++++------ lib/decompress/huf_decompress.c | 2 +- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 556e6c523..677078558 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -577,10 +577,23 @@ MEM_STATIC void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* statePt MEM_STATIC U32 FSE_getMaxNbBits(const FSE_symbolCompressionTransform* symbolTT, U32 symbolValue) { - assert(symbolValue <= FSE_MAX_SYMBOL_VALUE); return (symbolTT[symbolValue].deltaNbBits + ((1<<16)-1)) >> 16; } +/* FSE_bitCost_b256() : + * Approximate symbol cost, + * provide fractional value, using fixed-point format (8 bit) */ +MEM_STATIC U32 FSE_bitCost_b256(const FSE_symbolCompressionTransform* symbolTT, U32 tableLog, U32 symbolValue) +{ + U32 const minNbBits = symbolTT[symbolValue].deltaNbBits >> 16; + U32 const threshold = (minNbBits+1) << 16; + assert(symbolTT[symbolValue].deltaNbBits + (1< > tableLog; /* linear interpolation (very approximate) */ + assert(normalizedDeltaFromThreshold <= 256); + return (minNbBits+1)*256 - normalizedDeltaFromThreshold; +} + /* ====== Decompression ====== */ diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index d800254cc..80edb1a7b 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -126,11 +126,13 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP U32 const llCode = ZSTD_LLcode(litLength); FSE_CState_t cstate; FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); - return (LL_bits[llCode] + FSE_getMaxNbBits(cstate.symbolTT, llCode)) << 8; + U32 const price = LL_bits[llCode]*256 + FSE_bitCost_b256(cstate.symbolTT, cstate.stateLog, llCode); + DEBUGLOG(8, "ZSTD_litLengthPrice: ll=%u, bitCost=%.2f", litLength, (double)price / 256); + return price; } if (optPtr->priceType == zop_predef) return ZSTD_highbit32((U32)litLength+1); - /* literal Length */ + /* dynamic statistics */ { U32 const llCode = ZSTD_LLcode(litLength); return (LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1)) << 8; } @@ -156,7 +158,9 @@ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* con U32 const llCode = ZSTD_LLcode(litLength); FSE_CState_t cstate; FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); - return ((int)(LL_bits[llCode] + FSE_getMaxNbBits(cstate.symbolTT, llCode)) - FSE_getMaxNbBits(cstate.symbolTT, 0)) * 256; + return (int)(LL_bits[llCode] * 256) + + FSE_bitCost_b256(cstate.symbolTT, cstate.stateLog, llCode) + - FSE_bitCost_b256(cstate.symbolTT, cstate.stateLog, 0); } if (optPtr->priceType >= zop_predef) return ZSTD_highbit32(litLength+1); @@ -205,9 +209,8 @@ ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, FSE_CState_t mlstate, offstate; FSE_initCState(&mlstate, optPtr->symbolCosts->matchlengthCTable); FSE_initCState(&offstate, optPtr->symbolCosts->offcodeCTable); - return (FSE_getMaxNbBits(offstate.symbolTT, offCode) + offCode - + FSE_getMaxNbBits(mlstate.symbolTT, mlCode) + ML_bits[mlCode]) - * 256; + return FSE_bitCost_b256(offstate.symbolTT, offstate.stateLog, offCode) + offCode*256 + + FSE_bitCost_b256(mlstate.symbolTT, mlstate.stateLog, mlCode) + ML_bits[mlCode]*256; } if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */ diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 73f5c46c0..2c2d13803 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -965,7 +965,7 @@ static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, qu U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize) { assert(dstSize > 0); - assert(dstSize <= 128 KB); + assert(dstSize <= 128*1024); /* decoder timing evaluation */ { U32 const Q = (cSrcSize >= dstSize) ? 15 : (U32)(cSrcSize * 16 / dstSize); /* Q < 16 */ U32 const D256 = (U32)(dstSize >> 8); From c0da0f5e9e038d1b362561c73f821d34bb817f70 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 9 May 2018 10:48:09 -0700 Subject: [PATCH 043/288] switchable bit-approximation / fractional-bit accuracy modes also : makes it possible to select nb of fractional bits. --- lib/common/fse.h | 18 +++++++++++------- lib/compress/zstd_opt.c | 42 +++++++++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 677078558..8e44c1a44 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -582,16 +582,20 @@ MEM_STATIC U32 FSE_getMaxNbBits(const FSE_symbolCompressionTransform* symbolTT, /* FSE_bitCost_b256() : * Approximate symbol cost, - * provide fractional value, using fixed-point format (8 bit) */ -MEM_STATIC U32 FSE_bitCost_b256(const FSE_symbolCompressionTransform* symbolTT, U32 tableLog, U32 symbolValue) + * provide fractional value, using fixed-point format (accuracyLog fractional bits) */ +MEM_STATIC U32 FSE_bitCost(const FSE_symbolCompressionTransform* symbolTT, U32 tableLog, U32 symbolValue, U32 accuracyLog) { U32 const minNbBits = symbolTT[symbolValue].deltaNbBits >> 16; U32 const threshold = (minNbBits+1) << 16; - assert(symbolTT[symbolValue].deltaNbBits + (1< > tableLog; /* linear interpolation (very approximate) */ - assert(normalizedDeltaFromThreshold <= 256); - return (minNbBits+1)*256 - normalizedDeltaFromThreshold; + assert(tableLog < 16); + U32 const tableSize = 1 << tableLog; + assert(symbolTT[symbolValue].deltaNbBits + tableSize <= threshold); + U32 const deltaFromThreshold = threshold - (symbolTT[symbolValue].deltaNbBits + tableSize); + assert(accuracyLog < 31-tableLog); /* ensure enough room for renormalization double shift */ + U32 const normalizedDeltaFromThreshold = (deltaFromThreshold << accuracyLog) >> tableLog; /* linear interpolation (very approximate) */ + U32 const bitMultiplier = 1 << accuracyLog; + assert(normalizedDeltaFromThreshold <= bitMultiplier); + return (minNbBits+1)*bitMultiplier - normalizedDeltaFromThreshold; } diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 80edb1a7b..67db85eb9 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -91,6 +91,15 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, ZSTD_setLog2Prices(optPtr); } +#if 1 /* approximation at bit level */ +# define BITCOST_ACCURACY 0 +# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) +# define BITCOST_SYMBOL(t,l,s) ((void)l, FSE_getMaxNbBits(t,s)*BITCOST_MULTIPLIER) +#else /* fractional bit accuracy */ +# define BITCOST_ACCURACY 8 +# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) +# define BITCOST_SYMBOL(t,l,s) FSE_bitCost(t,l,s,BITCOST_ACCURACY) +#endif /* ZSTD_rawLiteralsCost() : * cost of literals (only) in specified segment (which length can be 0). @@ -98,23 +107,23 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, const optState_t* const optPtr) { + if (litLength == 0) return 0; + if (optPtr->priceType == zop_predef) return (litLength*6); /* 6 bit per literal - no statistic used */ if (optPtr->priceType == zop_static) { U32 u, cost; assert(optPtr->symbolCosts != NULL); assert(optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid); for (u=0, cost=0; u < litLength; u++) cost += HUF_getNbBits(optPtr->symbolCosts->hufCTable, literals[u]); - return cost << 8; + return cost * BITCOST_MULTIPLIER; } - if (optPtr->priceType == zop_predef) return (litLength*6); /* 6 bit per literal - no statistic used */ - if (litLength == 0) return 0; - /* literals */ + /* dynamic statistics */ { U32 u; U32 cost = litLength * optPtr->log2litSum; for (u=0; u < litLength; u++) cost -= ZSTD_highbit32(optPtr->litFreq[literals[u]]+1); - return cost << 8; + return cost * BITCOST_MULTIPLIER; } } @@ -126,15 +135,15 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP U32 const llCode = ZSTD_LLcode(litLength); FSE_CState_t cstate; FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); - U32 const price = LL_bits[llCode]*256 + FSE_bitCost_b256(cstate.symbolTT, cstate.stateLog, llCode); - DEBUGLOG(8, "ZSTD_litLengthPrice: ll=%u, bitCost=%.2f", litLength, (double)price / 256); + U32 const price = LL_bits[llCode]*BITCOST_MULTIPLIER + BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, llCode); + DEBUGLOG(8, "ZSTD_litLengthPrice: ll=%u, bitCost=%.2f", litLength, (double)price / BITCOST_MULTIPLIER); return price; } if (optPtr->priceType == zop_predef) return ZSTD_highbit32((U32)litLength+1); /* dynamic statistics */ { U32 const llCode = ZSTD_LLcode(litLength); - return (LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1)) << 8; + return (LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1)) * BITCOST_MULTIPLIER; } } @@ -158,18 +167,18 @@ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* con U32 const llCode = ZSTD_LLcode(litLength); FSE_CState_t cstate; FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); - return (int)(LL_bits[llCode] * 256) - + FSE_bitCost_b256(cstate.symbolTT, cstate.stateLog, llCode) - - FSE_bitCost_b256(cstate.symbolTT, cstate.stateLog, 0); + return (int)(LL_bits[llCode] * BITCOST_MULTIPLIER) + + BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, llCode) + - BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, 0); } if (optPtr->priceType >= zop_predef) return ZSTD_highbit32(litLength+1); - /* literal Length */ + /* dynamic statistics */ { U32 const llCode = ZSTD_LLcode(litLength); int const contribution = (LL_bits[llCode] + ZSTD_highbit32(optPtr->litLengthFreq[0]+1) - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1)) - * 256; + * BITCOST_MULTIPLIER; #if 1 return contribution; #else @@ -209,13 +218,14 @@ ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, FSE_CState_t mlstate, offstate; FSE_initCState(&mlstate, optPtr->symbolCosts->matchlengthCTable); FSE_initCState(&offstate, optPtr->symbolCosts->offcodeCTable); - return FSE_bitCost_b256(offstate.symbolTT, offstate.stateLog, offCode) + offCode*256 - + FSE_bitCost_b256(mlstate.symbolTT, mlstate.stateLog, mlCode) + ML_bits[mlCode]*256; + return BITCOST_SYMBOL(offstate.symbolTT, offstate.stateLog, offCode) + offCode*BITCOST_MULTIPLIER + + BITCOST_SYMBOL(mlstate.symbolTT, mlstate.stateLog, mlCode) + ML_bits[mlCode]*BITCOST_MULTIPLIER; } if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */ return ZSTD_highbit32(mlBase+1) + 16 + offCode; + /* dynamic statistics */ price = offCode + optPtr->log2offCodeSum - ZSTD_highbit32(optPtr->offCodeFreq[offCode]+1); if ((optLevel<2) /*static*/ && offCode >= 20) price += (offCode-19)*2; /* handicap for long distance offsets, favor decompression speed */ @@ -225,7 +235,7 @@ ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, } DEBUGLOG(8, "ZSTD_getMatchPrice(ml:%u) = %u", matchLength, price); - return price << 8; + return price * BITCOST_MULTIPLIER; } static void ZSTD_updateStats(optState_t* const optPtr, From 4d5bd32a001a7093fe7091e8605736369ad94aa9 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 9 May 2018 12:00:12 -0700 Subject: [PATCH 044/288] added traces to look at symbol costs evaluation looks correct. --- lib/common/bitstream.h | 24 ++++++++++++++++++++++++ lib/common/zstd_internal.h | 2 ++ lib/compress/fse_compress.c | 12 ++++++++++++ lib/compress/zstd_compress.c | 8 ++++---- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/lib/common/bitstream.h b/lib/common/bitstream.h index f7f389fe0..04440fde1 100644 --- a/lib/common/bitstream.h +++ b/lib/common/bitstream.h @@ -63,6 +63,30 @@ extern "C" { # endif #endif +#if defined(BIT_DEBUG) && (BIT_DEBUG>=2) +# include +extern int g_debuglog_enable; +/* recommended values for BIT_DEBUG display levels : + * 1 : no display, enables assert() only + * 2 : reserved for currently active debug path + * 3 : events once per object lifetime (CCtx, CDict, etc.) + * 4 : events once per frame + * 5 : events once per block + * 6 : events once per sequence (*very* verbose) */ +# define RAWLOG(l, ...) { \ + if ((g_debuglog_enable) & (l<=BIT_DEBUG)) { \ + fprintf(stderr, __VA_ARGS__); \ + } } +# define DEBUGLOG(l, ...) { \ + if ((g_debuglog_enable) & (l<=BIT_DEBUG)) { \ + fprintf(stderr, __FILE__ ": " __VA_ARGS__); \ + fprintf(stderr, " \n"); \ + } } +#else +# define RAWLOG(l, ...) {} /* disabled */ +# define DEBUGLOG(l, ...) {} /* disabled */ +#endif + /*========================================= * Target specific diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 65c08a825..981ce7f54 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -52,6 +52,8 @@ extern "C" { #define ZSTD_STATIC_ASSERT(c) { enum { ZSTD_static_assert = 1/(int)(!!(c)) }; } +#undef RAWLOG +#undef DEBUGLOG #if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=2) # include extern int g_debuglog_enable; diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index cb8f1fa32..80044b9c2 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -160,6 +160,18 @@ size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsi total += normalizedCounter[s]; } } } } +#if 0 /* debug : symbol costs */ + DEBUGLOG(2, "\n --- table statistics : "); + { U32 symbol; + for (symbol=0; symbol<=maxSymbolValue; symbol++) { + DEBUGLOG(2, "%3u: w=%3i, maxBits=%u, fracBits=%.2f", + symbol, normalizedCounter[symbol], + FSE_getMaxNbBits(symbolTT, symbol), + (double)FSE_bitCost(symbolTT, tableLog, symbol, 8) / 256); + } + } +#endif + return 0; } diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d3e52c627..d69b01773 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3450,7 +3450,7 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 22, 21, 22, 4, 5, 48, ZSTD_btopt }, /* level 16 */ { 23, 22, 22, 4, 4, 48, ZSTD_btopt }, /* level 17 */ { 23, 22, 22, 5, 3, 64, ZSTD_btopt }, /* level 18 */ - { 23, 23, 22, 7, 3,128, ZSTD_btopt }, /* level 19 */ + { 23, 23, 22, 7, 3,128, ZSTD_btultra }, /* level 19 */ { 25, 25, 23, 7, 3,128, ZSTD_btultra }, /* level 20 */ { 26, 26, 24, 7, 3,256, ZSTD_btultra }, /* level 21 */ { 27, 27, 25, 9, 3,512, ZSTD_btultra }, /* level 22 */ @@ -3476,7 +3476,7 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 18, 19, 18, 6, 3, 32, ZSTD_btopt }, /* level 16.*/ { 18, 19, 18, 8, 3, 64, ZSTD_btopt }, /* level 17.*/ { 18, 19, 18, 9, 3,128, ZSTD_btopt }, /* level 18.*/ - { 18, 19, 18, 10, 3,256, ZSTD_btopt }, /* level 19.*/ + { 18, 19, 18, 9, 3,256, ZSTD_btultra }, /* level 19.*/ { 18, 19, 18, 11, 3,512, ZSTD_btultra }, /* level 20.*/ { 18, 19, 18, 12, 3,512, ZSTD_btultra }, /* level 21.*/ { 18, 19, 18, 13, 3,512, ZSTD_btultra }, /* level 22.*/ @@ -3502,7 +3502,7 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 17, 18, 17, 7, 3, 32, ZSTD_btopt }, /* level 16.*/ { 17, 18, 17, 7, 3, 64, ZSTD_btopt }, /* level 17.*/ { 17, 18, 17, 7, 3,256, ZSTD_btopt }, /* level 18.*/ - { 17, 18, 17, 8, 3,256, ZSTD_btopt }, /* level 19.*/ + { 17, 18, 17, 7, 3,256, ZSTD_btultra }, /* level 19.*/ { 17, 18, 17, 9, 3,256, ZSTD_btultra }, /* level 20.*/ { 17, 18, 17, 10, 3,256, ZSTD_btultra }, /* level 21.*/ { 17, 18, 17, 11, 3,512, ZSTD_btultra }, /* level 22.*/ @@ -3528,7 +3528,7 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 14, 15, 15, 6, 3, 96, ZSTD_btopt }, /* level 16.*/ { 14, 15, 15, 6, 3,128, ZSTD_btopt }, /* level 17.*/ { 14, 15, 15, 6, 3,256, ZSTD_btopt }, /* level 18.*/ - { 14, 15, 15, 7, 3,256, ZSTD_btopt }, /* level 19.*/ + { 14, 15, 15, 6, 3,256, ZSTD_btultra }, /* level 19.*/ { 14, 15, 15, 8, 3,256, ZSTD_btultra }, /* level 20.*/ { 14, 15, 15, 9, 3,256, ZSTD_btultra }, /* level 21.*/ { 14, 15, 15, 10, 3,256, ZSTD_btultra }, /* level 22.*/ From c39061cb7bef5074f0026e71afdbb89ea0f368c4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 9 May 2018 12:07:25 -0700 Subject: [PATCH 045/288] fixed declaration-after-statement warning --- lib/common/fse.h | 18 ++++++++++-------- lib/compress/zstd_opt.c | 8 ++++---- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 8e44c1a44..864212743 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -582,20 +582,22 @@ MEM_STATIC U32 FSE_getMaxNbBits(const FSE_symbolCompressionTransform* symbolTT, /* FSE_bitCost_b256() : * Approximate symbol cost, - * provide fractional value, using fixed-point format (accuracyLog fractional bits) */ + * provide fractional value, using fixed-point format (accuracyLog fractional bits) + * note: assume symbolValue is valid */ MEM_STATIC U32 FSE_bitCost(const FSE_symbolCompressionTransform* symbolTT, U32 tableLog, U32 symbolValue, U32 accuracyLog) { U32 const minNbBits = symbolTT[symbolValue].deltaNbBits >> 16; U32 const threshold = (minNbBits+1) << 16; assert(tableLog < 16); - U32 const tableSize = 1 << tableLog; - assert(symbolTT[symbolValue].deltaNbBits + tableSize <= threshold); - U32 const deltaFromThreshold = threshold - (symbolTT[symbolValue].deltaNbBits + tableSize); assert(accuracyLog < 31-tableLog); /* ensure enough room for renormalization double shift */ - U32 const normalizedDeltaFromThreshold = (deltaFromThreshold << accuracyLog) >> tableLog; /* linear interpolation (very approximate) */ - U32 const bitMultiplier = 1 << accuracyLog; - assert(normalizedDeltaFromThreshold <= bitMultiplier); - return (minNbBits+1)*bitMultiplier - normalizedDeltaFromThreshold; + { U32 const tableSize = 1 << tableLog; + assert(symbolTT[symbolValue].deltaNbBits + tableSize <= threshold); + { U32 const deltaFromThreshold = threshold - (symbolTT[symbolValue].deltaNbBits + tableSize); + U32 const normalizedDeltaFromThreshold = (deltaFromThreshold << accuracyLog) >> tableLog; /* linear interpolation (very approximate) */ + U32 const bitMultiplier = 1 << accuracyLog; + assert(normalizedDeltaFromThreshold <= bitMultiplier); + return (minNbBits+1)*bitMultiplier - normalizedDeltaFromThreshold; + } } } diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 67db85eb9..73ecda721 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -135,10 +135,10 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP U32 const llCode = ZSTD_LLcode(litLength); FSE_CState_t cstate; FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); - U32 const price = LL_bits[llCode]*BITCOST_MULTIPLIER + BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, llCode); - DEBUGLOG(8, "ZSTD_litLengthPrice: ll=%u, bitCost=%.2f", litLength, (double)price / BITCOST_MULTIPLIER); - return price; - } + { U32 const price = LL_bits[llCode]*BITCOST_MULTIPLIER + BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, llCode); + DEBUGLOG(8, "ZSTD_litLengthPrice: ll=%u, bitCost=%.2f", litLength, (double)price / BITCOST_MULTIPLIER); + return price; + } } if (optPtr->priceType == zop_predef) return ZSTD_highbit32((U32)litLength+1); /* dynamic statistics */ From ac6105463a69d48652e28e1919ea326d4151dd53 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 9 May 2018 15:46:11 -0700 Subject: [PATCH 046/288] opt: minor improvements to log traces slight improvement when using fractional-bit evaluation (opt:dictionay) --- lib/compress/fse_compress.c | 4 ++-- lib/compress/zstd_compress.c | 10 ++++----- lib/compress/zstd_opt.c | 39 +++++++++++++++++++++++++----------- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index 80044b9c2..8e170150f 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -161,10 +161,10 @@ size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsi } } } } #if 0 /* debug : symbol costs */ - DEBUGLOG(2, "\n --- table statistics : "); + DEBUGLOG(5, "\n --- table statistics : "); { U32 symbol; for (symbol=0; symbol<=maxSymbolValue; symbol++) { - DEBUGLOG(2, "%3u: w=%3i, maxBits=%u, fracBits=%.2f", + DEBUGLOG(5, "%3u: w=%3i, maxBits=%u, fracBits=%.2f", symbol, normalizedCounter[symbol], FSE_getMaxNbBits(symbolTT, symbol), (double)FSE_bitCost(symbolTT, tableLog, symbol, 8) / 256); diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d69b01773..d6e3e6b07 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3450,7 +3450,7 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 22, 21, 22, 4, 5, 48, ZSTD_btopt }, /* level 16 */ { 23, 22, 22, 4, 4, 48, ZSTD_btopt }, /* level 17 */ { 23, 22, 22, 5, 3, 64, ZSTD_btopt }, /* level 18 */ - { 23, 23, 22, 7, 3,128, ZSTD_btultra }, /* level 19 */ + { 23, 23, 22, 7, 3,128, ZSTD_btopt }, /* level 19 */ { 25, 25, 23, 7, 3,128, ZSTD_btultra }, /* level 20 */ { 26, 26, 24, 7, 3,256, ZSTD_btultra }, /* level 21 */ { 27, 27, 25, 9, 3,512, ZSTD_btultra }, /* level 22 */ @@ -3476,7 +3476,7 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 18, 19, 18, 6, 3, 32, ZSTD_btopt }, /* level 16.*/ { 18, 19, 18, 8, 3, 64, ZSTD_btopt }, /* level 17.*/ { 18, 19, 18, 9, 3,128, ZSTD_btopt }, /* level 18.*/ - { 18, 19, 18, 9, 3,256, ZSTD_btultra }, /* level 19.*/ + { 18, 19, 18, 10, 3,256, ZSTD_btopt }, /* level 19.*/ { 18, 19, 18, 11, 3,512, ZSTD_btultra }, /* level 20.*/ { 18, 19, 18, 12, 3,512, ZSTD_btultra }, /* level 21.*/ { 18, 19, 18, 13, 3,512, ZSTD_btultra }, /* level 22.*/ @@ -3502,7 +3502,7 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 17, 18, 17, 7, 3, 32, ZSTD_btopt }, /* level 16.*/ { 17, 18, 17, 7, 3, 64, ZSTD_btopt }, /* level 17.*/ { 17, 18, 17, 7, 3,256, ZSTD_btopt }, /* level 18.*/ - { 17, 18, 17, 7, 3,256, ZSTD_btultra }, /* level 19.*/ + { 17, 18, 17, 8, 3,256, ZSTD_btopt }, /* level 19.*/ { 17, 18, 17, 9, 3,256, ZSTD_btultra }, /* level 20.*/ { 17, 18, 17, 10, 3,256, ZSTD_btultra }, /* level 21.*/ { 17, 18, 17, 11, 3,512, ZSTD_btultra }, /* level 22.*/ @@ -3528,10 +3528,10 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 14, 15, 15, 6, 3, 96, ZSTD_btopt }, /* level 16.*/ { 14, 15, 15, 6, 3,128, ZSTD_btopt }, /* level 17.*/ { 14, 15, 15, 6, 3,256, ZSTD_btopt }, /* level 18.*/ - { 14, 15, 15, 6, 3,256, ZSTD_btultra }, /* level 19.*/ + { 14, 15, 15, 7, 3,256, ZSTD_btopt }, /* level 19.*/ { 14, 15, 15, 8, 3,256, ZSTD_btultra }, /* level 20.*/ { 14, 15, 15, 9, 3,256, ZSTD_btultra }, /* level 21.*/ - { 14, 15, 15, 10, 3,256, ZSTD_btultra }, /* level 22.*/ + { 14, 15, 15, 10, 3,512, ZSTD_btultra }, /* level 22.*/ }, }; diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 73ecda721..486c86931 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -101,6 +101,12 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, # define BITCOST_SYMBOL(t,l,s) FSE_bitCost(t,l,s,BITCOST_ACCURACY) #endif +MEM_STATIC double +ZSTD_fCost(U32 price) +{ + return (double)price / (BITCOST_MULTIPLIER*8); +} + /* ZSTD_rawLiteralsCost() : * cost of literals (only) in specified segment (which length can be 0). * does not include cost of literalLength symbol */ @@ -433,7 +439,7 @@ void ZSTD_updateTree_internal( const BYTE* const base = ms->window.base; U32 const target = (U32)(ip - base); U32 idx = ms->nextToUpdate; - DEBUGLOG(7, "ZSTD_updateTree_internal, from %u to %u (extDict:%u)", + DEBUGLOG(8, "ZSTD_updateTree_internal, from %u to %u (extDict:%u)", idx, target, extDict); while(idx < target) @@ -481,7 +487,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( U32 nbCompares = 1U << cParams->searchLog; size_t bestLength = lengthToBeat-1; - DEBUGLOG(7, "ZSTD_insertBtAndGetAllMatches"); + DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches"); /* check repCode */ { U32 const lastR = ZSTD_REP_NUM + ll0; @@ -612,7 +618,7 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches ( ZSTD_match_t* matches, U32 const lengthToBeat) { U32 const matchLengthSearch = cParams->searchLength; - DEBUGLOG(7, "ZSTD_BtGetAllMatches"); + DEBUGLOG(8, "ZSTD_BtGetAllMatches"); if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */ ZSTD_updateTree_internal(ms, cParams, ip, iHighLimit, matchLengthSearch, extDict); switch(matchLengthSearch) @@ -786,8 +792,8 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, for ( ; pos <= end ; pos++ ) { U32 const matchPrice = ZSTD_getMatchPrice(offset, pos, optStatePtr, optLevel); U32 const sequencePrice = literalsPrice + matchPrice; - DEBUGLOG(7, "rPos:%u => set initial price : %u", - pos, sequencePrice); + DEBUGLOG(7, "rPos:%u => set initial price : %.2f", + pos, ZSTD_fCost(sequencePrice)); opt[pos].mlen = pos; opt[pos].off = offset; opt[pos].litlen = litlen; @@ -814,14 +820,18 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, } assert(price < 1000000000); /* overflow check */ if (price <= opt[cur].price) { - DEBUGLOG(7, "rPos:%u : better price (%u<%u) using literal", - cur, price, opt[cur].price); + DEBUGLOG(7, "rPos:%u : better price (%.2f<=%.2f) using literal", + cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price)); opt[cur].mlen = 1; opt[cur].off = 0; opt[cur].litlen = litlen; opt[cur].price = price; memcpy(opt[cur].rep, opt[cur-1].rep, sizeof(opt[cur].rep)); - } } + } else { + DEBUGLOG(7, "rPos:%u : literal would cost more (%.2f>%.2f)", + cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price)); + } + } /* last match must start at a minimum distance of 8 from oend */ if (inr > ilimit) continue; @@ -829,8 +839,10 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, if (cur == last_pos) break; if ( (optLevel==0) /*static_test*/ - && (opt[cur+1].price <= opt[cur].price) ) + && (opt[cur+1].price <= opt[cur].price + (BITCOST_MULTIPLIER/2)) ) { + DEBUGLOG(7, "move to next rPos:%u : price is <=", cur+1); continue; /* skip unpromising positions; about ~+6% speed, -0.01 ratio */ + } { U32 const ll0 = (opt[cur].mlen != 1); U32 const litlen = (opt[cur].mlen == 1) ? opt[cur].litlen : 0; @@ -838,7 +850,10 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, U32 const basePrice = previousPrice + ZSTD_fullLiteralsCost(inr-litlen, litlen, optStatePtr); U32 const nbMatches = ZSTD_BtGetAllMatches(ms, cParams, inr, iend, extDict, opt[cur].rep, ll0, matches, minMatch); U32 matchNb; - if (!nbMatches) continue; + if (!nbMatches) { + DEBUGLOG(7, "rPos:%u : no match found", cur); + continue; + } { U32 const maxML = matches[nbMatches-1].len; DEBUGLOG(7, "rPos:%u, found %u matches, of maxLength=%u", @@ -868,8 +883,8 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, int const price = basePrice + ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel); if ((pos > last_pos) || (price < opt[pos].price)) { - DEBUGLOG(7, "rPos:%u => new better price (%u<%u)", - pos, price, opt[pos].price); + DEBUGLOG(7, "rPos:%u => new better price (%.2f<%.2f)", + pos, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price)); while (last_pos < pos) { opt[last_pos+1].price = ZSTD_MAX_PRICE; last_pos++; } /* fill empty positions */ opt[pos].mlen = mlen; opt[pos].off = offset; From 74b1c75d64cb531526fd7c60f897f9bc0323986c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 10 May 2018 16:32:36 -0700 Subject: [PATCH 047/288] btopt : minor adjustment of update frequencies --- lib/compress/zstd_compress_internal.h | 2 +- lib/compress/zstd_opt.c | 37 ++++++++++++++++----------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index eeb7b230a..25d3137a2 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -76,7 +76,7 @@ typedef struct { U32 rep[ZSTD_REP_NUM]; } ZSTD_optimal_t; -typedef enum { zop_none=0, zop_predef, zop_static } ZSTD_OptPrice_e; +typedef enum { zop_dynamic=0, zop_predef, zop_static } ZSTD_OptPrice_e; typedef struct { /* All tables are allocated inside cctx->workspace by ZSTD_resetCCtx_internal() */ diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 486c86931..7e7a6935f 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -12,7 +12,7 @@ #include "zstd_opt.h" -#define ZSTD_LITFREQ_ADD 2 /* scaling factor for litFreq, so that frequencies adapt faster to new stats. Also used for matchSum (?) */ +#define ZSTD_LITFREQ_ADD 2 /* scaling factor for litFreq, so that frequencies adapt faster to new stats */ #define ZSTD_FREQ_DIV 4 /* log factor when using previous stats to init next stats */ #define ZSTD_MAX_PRICE (1<<30) @@ -32,24 +32,31 @@ static void ZSTD_setLog2Prices(optState_t* optPtr) static void ZSTD_rescaleFreqs(optState_t* const optPtr, const BYTE* const src, size_t const srcSize) { - optPtr->priceType = zop_none; + optPtr->priceType = zop_dynamic; - if (optPtr->litLengthSum == 0) { /* first init */ + if (optPtr->litLengthSum == 0) { /* first block : init */ unsigned u; - if (srcSize <= 1024) optPtr->priceType = zop_predef; + if (srcSize <= 1024) /* heuristic */ + optPtr->priceType = zop_predef; + assert(optPtr->symbolCosts != NULL); if (optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid) { /* huffman table presumed generated by dictionary */ - optPtr->priceType = zop_static; + if (srcSize <= 8192) /* heuristic */ + optPtr->priceType = zop_static; + else { + assert(optPtr->priceType == zop_dynamic); + } + + } - assert(optPtr->litFreq!=NULL); - for (u=0; u<=MaxLit; u++) - optPtr->litFreq[u] = 0; - for (u=0; u litFreq[src[u]]++; + assert(optPtr->litFreq != NULL); + { unsigned max = MaxLit; + FSE_count(optPtr->litFreq, &max, src, srcSize); /* use raw first block to init statistics */ + } optPtr->litSum = 0; for (u=0; u<=MaxLit; u++) { - optPtr->litFreq[u] = 1 + (optPtr->litFreq[u] >> ZSTD_FREQ_DIV); + optPtr->litFreq[u] = 1 + (optPtr->litFreq[u] >> (ZSTD_FREQ_DIV+1)); optPtr->litSum += optPtr->litFreq[u]; } @@ -63,7 +70,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, optPtr->offCodeFreq[u] = 1; optPtr->offCodeSum = (MaxOff+1); - } else { + } else { /* new block : re-use previous statistics, scaled down */ unsigned u; optPtr->litSum = 0; @@ -73,17 +80,17 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, } optPtr->litLengthSum = 0; for (u=0; u<=MaxLL; u++) { - optPtr->litLengthFreq[u] = 1 + (optPtr->litLengthFreq[u]>>(ZSTD_FREQ_DIV+1)); + optPtr->litLengthFreq[u] = 1 + (optPtr->litLengthFreq[u] >> ZSTD_FREQ_DIV); optPtr->litLengthSum += optPtr->litLengthFreq[u]; } optPtr->matchLengthSum = 0; for (u=0; u<=MaxML; u++) { - optPtr->matchLengthFreq[u] = 1 + (optPtr->matchLengthFreq[u]>>ZSTD_FREQ_DIV); + optPtr->matchLengthFreq[u] = 1 + (optPtr->matchLengthFreq[u] >> ZSTD_FREQ_DIV); optPtr->matchLengthSum += optPtr->matchLengthFreq[u]; } optPtr->offCodeSum = 0; for (u=0; u<=MaxOff; u++) { - optPtr->offCodeFreq[u] = 1 + (optPtr->offCodeFreq[u]>>ZSTD_FREQ_DIV); + optPtr->offCodeFreq[u] = 1 + (optPtr->offCodeFreq[u] >> ZSTD_FREQ_DIV); optPtr->offCodeSum += optPtr->offCodeFreq[u]; } } From 1a26ec6e8d6575e671e2304f1fd43eeb880e015d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 10 May 2018 17:59:12 -0700 Subject: [PATCH 048/288] opt: init statistics from dictionary instead of starting from fake "default" statistics. --- lib/common/entropy_common.c | 5 ++ lib/compress/fse_compress.c | 5 +- lib/compress/zstd_compress.c | 3 +- lib/compress/zstd_opt.c | 97 ++++++++++++++++++++++++++++-------- 4 files changed, 87 insertions(+), 23 deletions(-) diff --git a/lib/common/entropy_common.c b/lib/common/entropy_common.c index b37a082fe..344c32361 100644 --- a/lib/common/entropy_common.c +++ b/lib/common/entropy_common.c @@ -143,6 +143,11 @@ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* t } } /* while ((remaining>1) & (charnum<=*maxSVPtr)) */ if (remaining != 1) return ERROR(corruption_detected); if (bitCount > 32) return ERROR(corruption_detected); + /* zeroise the rest */ + { unsigned symbNb = charnum; + for (symbNb=charnum; symbNb <= *maxSVPtr; symbNb++) + normalizedCounter[symbNb] = 0; + } *maxSVPtr = charnum-1; ip += (bitCount+7)>>3; diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index 8e170150f..5df92db45 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -143,7 +143,10 @@ size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsi for (s=0; s<=maxSymbolValue; s++) { switch (normalizedCounter[s]) { - case 0: break; + case 0: + /* filling nonetheless, for compatibility with FSE_getMaxNbBits() */ + symbolTT[s].deltaNbBits = (tableLog+1) << 16; + break; case -1: case 1: diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d6e3e6b07..58daf5d0c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2396,7 +2396,8 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, 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(bs->entropy.offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog, workspace, HUF_WORKSPACE_SIZE), + /* fill all offset symbols to avoid garbage at end of table */ + CHECK_E( FSE_buildCTable_wksp(bs->entropy.offcodeCTable, offcodeNCount, MaxOff, offcodeLog, workspace, HUF_WORKSPACE_SIZE), dictionary_corrupted); dictPtr += offcodeHeaderSize; } diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 7e7a6935f..9233d5d6f 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -35,7 +35,6 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, optPtr->priceType = zop_dynamic; if (optPtr->litLengthSum == 0) { /* first block : init */ - unsigned u; if (srcSize <= 1024) /* heuristic */ optPtr->priceType = zop_predef; @@ -47,29 +46,85 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, assert(optPtr->priceType == zop_dynamic); } + assert(optPtr->litFreq != NULL); + assert(optPtr->symbolCosts != NULL); + optPtr->litSum = 0; + { unsigned lit; + for (lit=0; lit<=MaxLit; lit++) { + U32 const scaleLog = 12; /* scale to 4K */ + U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->hufCTable, lit); + assert(bitCost < scaleLog); + optPtr->litFreq[lit] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; + optPtr->litSum += optPtr->litFreq[lit]; + } } + + { unsigned ll; + FSE_CState_t llstate; + FSE_initCState(&llstate, optPtr->symbolCosts->litlengthCTable); + optPtr->litLengthSum = 0; + for (ll=0; ll<=MaxLL; ll++) { + U32 const scaleLog = 11; /* scale to 2K */ + U32 const bitCost = FSE_getMaxNbBits(llstate.symbolTT, ll); + assert(bitCost < scaleLog); + optPtr->litLengthFreq[ll] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; + optPtr->litLengthSum += optPtr->litLengthFreq[ll]; + } } + + { unsigned ml; + FSE_CState_t mlstate; + FSE_initCState(&mlstate, optPtr->symbolCosts->matchlengthCTable); + optPtr->matchLengthSum = 0; + for (ml=0; ml<=MaxML; ml++) { + U32 const scaleLog = 11; /* scale to 2K */ + U32 const bitCost = FSE_getMaxNbBits(mlstate.symbolTT, ml); + assert(bitCost < scaleLog); + optPtr->matchLengthFreq[ml] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; + optPtr->matchLengthSum += optPtr->matchLengthFreq[ml]; + } } + + { unsigned of; + FSE_CState_t ofstate; + FSE_initCState(&ofstate, optPtr->symbolCosts->offcodeCTable); + optPtr->offCodeSum = 0; + for (of=0; of<=MaxOff; of++) { + U32 const scaleLog = 11; /* scale to 2K */ + U32 const bitCost = FSE_getMaxNbBits(ofstate.symbolTT, of); + assert(bitCost < scaleLog); + optPtr->offCodeFreq[of] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; + optPtr->offCodeSum += optPtr->offCodeFreq[of]; + } } + + } else { /* not a dictionary */ + + assert(optPtr->litFreq != NULL); + optPtr->litSum = 0; + { unsigned lit = MaxLit; + FSE_count(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */ + for (lit=0; lit<=MaxLit; lit++) { + optPtr->litFreq[lit] = 1 + (optPtr->litFreq[lit] >> (ZSTD_FREQ_DIV+1)); + optPtr->litSum += optPtr->litFreq[lit]; + } } + + { unsigned ll; + for (ll=0; ll<=MaxLL; ll++) + optPtr->litLengthFreq[ll] = 1; + optPtr->litLengthSum = MaxLL+1; + } + + { unsigned ml; + for (ml=0; ml<=MaxML; ml++) + optPtr->matchLengthFreq[ml] = 1; + optPtr->matchLengthSum = MaxML+1; + } + + { unsigned of; + for (of=0; of<=MaxOff; of++) + optPtr->offCodeFreq[of] = 1; + optPtr->offCodeSum = MaxOff+1; + } } - assert(optPtr->litFreq != NULL); - { unsigned max = MaxLit; - FSE_count(optPtr->litFreq, &max, src, srcSize); /* use raw first block to init statistics */ - } - optPtr->litSum = 0; - for (u=0; u<=MaxLit; u++) { - optPtr->litFreq[u] = 1 + (optPtr->litFreq[u] >> (ZSTD_FREQ_DIV+1)); - optPtr->litSum += optPtr->litFreq[u]; - } - - for (u=0; u<=MaxLL; u++) - optPtr->litLengthFreq[u] = 1; - optPtr->litLengthSum = MaxLL+1; - for (u=0; u<=MaxML; u++) - optPtr->matchLengthFreq[u] = 1; - optPtr->matchLengthSum = MaxML+1; - for (u=0; u<=MaxOff; u++) - optPtr->offCodeFreq[u] = 1; - optPtr->offCodeSum = (MaxOff+1); - } else { /* new block : re-use previous statistics, scaled down */ unsigned u; From 09d0fa29eefb40d665faa420847deeb24ae99e6a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 10 May 2018 18:13:48 -0700 Subject: [PATCH 049/288] minor adjusting of weights --- lib/compress/zstd_opt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 9233d5d6f..29c4c9136 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -51,7 +51,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, optPtr->litSum = 0; { unsigned lit; for (lit=0; lit<=MaxLit; lit++) { - U32 const scaleLog = 12; /* scale to 4K */ + U32 const scaleLog = 11; /* scale to 2K */ U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->hufCTable, lit); assert(bitCost < scaleLog); optPtr->litFreq[lit] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; @@ -63,7 +63,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, FSE_initCState(&llstate, optPtr->symbolCosts->litlengthCTable); optPtr->litLengthSum = 0; for (ll=0; ll<=MaxLL; ll++) { - U32 const scaleLog = 11; /* scale to 2K */ + U32 const scaleLog = 10; /* scale to 1K */ U32 const bitCost = FSE_getMaxNbBits(llstate.symbolTT, ll); assert(bitCost < scaleLog); optPtr->litLengthFreq[ll] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; @@ -75,7 +75,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, FSE_initCState(&mlstate, optPtr->symbolCosts->matchlengthCTable); optPtr->matchLengthSum = 0; for (ml=0; ml<=MaxML; ml++) { - U32 const scaleLog = 11; /* scale to 2K */ + U32 const scaleLog = 10; U32 const bitCost = FSE_getMaxNbBits(mlstate.symbolTT, ml); assert(bitCost < scaleLog); optPtr->matchLengthFreq[ml] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; @@ -87,7 +87,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, FSE_initCState(&ofstate, optPtr->symbolCosts->offcodeCTable); optPtr->offCodeSum = 0; for (of=0; of<=MaxOff; of++) { - U32 const scaleLog = 11; /* scale to 2K */ + U32 const scaleLog = 10; U32 const bitCost = FSE_getMaxNbBits(ofstate.symbolTT, of); assert(bitCost < scaleLog); optPtr->offCodeFreq[of] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; From 0d7626672d19744330c4a2e8fcb022b47a40b618 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 10 May 2018 18:17:21 -0700 Subject: [PATCH 050/288] fixed c++ conversion warning --- lib/common/fse.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 864212743..5a2344441 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -575,8 +575,9 @@ MEM_STATIC void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* statePt BIT_flushBits(bitC); } -MEM_STATIC U32 FSE_getMaxNbBits(const FSE_symbolCompressionTransform* symbolTT, U32 symbolValue) +MEM_STATIC U32 FSE_getMaxNbBits(const void* symbolTTPtr, U32 symbolValue) { + const FSE_symbolCompressionTransform* symbolTT = (const FSE_symbolCompressionTransform*) symbolTTPtr; return (symbolTT[symbolValue].deltaNbBits + ((1<<16)-1)) >> 16; } From 99ddca43a6252369c3ad062e906c5d4f50b74f3c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 10 May 2018 19:48:09 -0700 Subject: [PATCH 051/288] fixed wrong assertion base can actually overflow --- lib/compress/zstd_compress.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 58daf5d0c..3a45d58dc 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2003,7 +2003,6 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, { const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; const U32 current = (U32)(istart-base); - assert(istart >= base); if (sizeof(ptrdiff_t)==8) assert(istart - base < (ptrdiff_t)(U32)(-1)); /* ensure no overflow */ if (current > ms->nextToUpdate + 384) ms->nextToUpdate = current - MIN(192, (U32)(current - ms->nextToUpdate - 384)); From 3193d692c2f64108f55b49c3a0ff0c84187658ea Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 11 May 2018 11:31:48 -0700 Subject: [PATCH 052/288] minor patch, ensuring LIBDIR is created before installation follow-up from #1123 --- lib/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Makefile b/lib/Makefile index d8178c7a5..3a160f88c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -168,10 +168,12 @@ install-pc: libzstd.pc install-static: libzstd.a @echo Installing static library + @$(INSTALL) -d -m 755 $(DESTDIR)$(LIBDIR)/ @$(INSTALL_DATA) libzstd.a $(DESTDIR)$(LIBDIR) install-shared: libzstd @echo Installing shared library + @$(INSTALL) -d -m 755 $(DESTDIR)$(LIBDIR)/ @$(INSTALL_PROGRAM) $(LIBZSTD) $(DESTDIR)$(LIBDIR) @ln -sf $(LIBZSTD) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT_MAJOR) @ln -sf $(LIBZSTD) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT) From 761758982e8546f1a44230d2a47b3974c6162311 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 11 May 2018 15:54:06 -0700 Subject: [PATCH 053/288] replaced FSE_count by FSE_count_simple to reduce usage of stack memory. Also : tweaked a few comments, as suggested by @terrelln --- lib/compress/zstd_compress_internal.h | 4 ++-- lib/compress/zstd_opt.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 25d3137a2..0f1830a5e 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -97,8 +97,8 @@ typedef struct { U32 log2matchLengthSum; /* pow2 to compare log2(mlfreq) to */ U32 log2offCodeSum; /* pow2 to compare log2(offreq) to */ /* end : updated by ZSTD_setLog2Prices */ - ZSTD_OptPrice_e priceType; /* prices follow a pre-defined cost structure, statistics are irrelevant */ - const ZSTD_entropyCTables_t* symbolCosts; /* pre-calculated symbol costs, from dictionary */ + ZSTD_OptPrice_e priceType; /* prices can be determined dynamically, or follow dictionary statistics, or a pre-defined cost structure */ + const ZSTD_entropyCTables_t* symbolCosts; /* pre-calculated dictionary statistics */ } optState_t; typedef struct { diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 29c4c9136..76144f730 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -97,9 +97,9 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, } else { /* not a dictionary */ assert(optPtr->litFreq != NULL); - optPtr->litSum = 0; { unsigned lit = MaxLit; - FSE_count(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */ + FSE_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */ + optPtr->litSum = 0; for (lit=0; lit<=MaxLit; lit++) { optPtr->litFreq[lit] = 1 + (optPtr->litFreq[lit] >> (ZSTD_FREQ_DIV+1)); optPtr->litSum += optPtr->litFreq[lit]; @@ -244,7 +244,7 @@ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* con /* dynamic statistics */ { U32 const llCode = ZSTD_LLcode(litLength); int const contribution = (LL_bits[llCode] - + ZSTD_highbit32(optPtr->litLengthFreq[0]+1) + + ZSTD_highbit32(optPtr->litLengthFreq[0]+1) /* note: log2litLengthSum cancels out with following one */ - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1)) * BITCOST_MULTIPLIER; #if 1 From 17c19fbbb5930beb470b3db3afdafeb822d750db Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 11 May 2018 17:32:26 -0700 Subject: [PATCH 054/288] generalized use of readU32FromChar() and check input overflow --- tests/paramgrill.c | 143 ++++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 67 deletions(-) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 13b102b2d..13ab7a285 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -18,6 +18,7 @@ #include /* strcmp */ #include /* log */ #include +#include #include "mem.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters, ZSTD_estimateCCtxSize */ @@ -32,7 +33,7 @@ **************************************/ #define PROGRAM_DESCRIPTION "ZSTD parameters tester" #define AUTHOR "Yann Collet" -#define WELCOME_MESSAGE "*** %s %s %i-bits, by %s (%s) ***\n", PROGRAM_DESCRIPTION, ZSTD_VERSION_STRING, (int)(sizeof(void*)*8), AUTHOR, __DATE__ +#define WELCOME_MESSAGE "*** %s %s %i-bits, by %s ***\n", PROGRAM_DESCRIPTION, ZSTD_VERSION_STRING, (int)(sizeof(void*)*8), AUTHOR #define KB *(1<<10) @@ -47,7 +48,6 @@ static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31)); #define COMPRESSIBILITY_DEFAULT 0.50 -static const size_t sampleSize = 10000000; static const double g_grillDuration_s = 90000; /* about 24 hours */ static const U64 g_maxParamTime = 15 * SEC_TO_MICRO; @@ -566,18 +566,17 @@ static void BMK_selectRandomStart( { U32 const id = (FUZ_rand(&g_rand) % (ZSTD_maxCLevel()+1)); if ((id==0) || (winners[id].params.windowLog==0)) { - /* totally random entry */ + /* use some random entry */ ZSTD_compressionParameters const p = ZSTD_adjustCParams(randomParams(), srcSize, 0); playAround(f, winners, p, srcBuffer, srcSize, ctx); - } - else + } else { playAround(f, winners, winners[id].params, srcBuffer, srcSize, ctx); + } } -static void BMK_benchMem(void* srcBuffer, size_t srcSize) +static void BMK_benchMem_usingCCtx(ZSTD_CCtx* cctx, const void* srcBuffer, size_t srcSize) { - ZSTD_CCtx* const ctx = ZSTD_createCCtx(); ZSTD_compressionParameters params; winnerInfo_t winners[NB_LEVELS_TRACKED]; const char* const rfName = "grillResults.txt"; @@ -585,25 +584,24 @@ static void BMK_benchMem(void* srcBuffer, size_t srcSize) const size_t blockSize = g_blockSize ? g_blockSize : srcSize; /* init */ - if (ctx==NULL) { DISPLAY("ZSTD_createCCtx() failed \n"); exit(1); } memset(winners, 0, sizeof(winners)); if (f==NULL) { DISPLAY("error opening %s \n", rfName); exit(1); } if (g_singleRun) { BMK_result_t testResult; g_params = ZSTD_adjustCParams(g_params, srcSize, 0); - BMK_benchParam(&testResult, srcBuffer, srcSize, ctx, g_params); + BMK_benchParam(&testResult, srcBuffer, srcSize, cctx, g_params); DISPLAY("\n"); return; } - if (g_target) + if (g_target) { g_cSpeedTarget[1] = g_target * 1000000; - else { + } else { /* baseline config for level 1 */ BMK_result_t testResult; params = ZSTD_getCParams(1, blockSize, 0); - BMK_benchParam(&testResult, srcBuffer, srcSize, ctx, params); + BMK_benchParam(&testResult, srcBuffer, srcSize, cctx, params); g_cSpeedTarget[1] = (testResult.cSpeed * 31) / 32; } @@ -618,14 +616,14 @@ static void BMK_benchMem(void* srcBuffer, size_t srcSize) int i; for (i=0; i<=maxSeeds; i++) { params = ZSTD_getCParams(i, blockSize, 0); - BMK_seed(winners, params, srcBuffer, srcSize, ctx); + BMK_seed(winners, params, srcBuffer, srcSize, cctx); } } BMK_printWinners(f, winners, srcSize); /* start tests */ { const time_t grillStart = time(NULL); do { - BMK_selectRandomStart(f, winners, srcBuffer, srcSize, ctx); + BMK_selectRandomStart(f, winners, srcBuffer, srcSize, cctx); } while (BMK_timeSpan(grillStart) < g_grillDuration_s); } @@ -635,19 +633,24 @@ static void BMK_benchMem(void* srcBuffer, size_t srcSize) /* clean up*/ fclose(f); - ZSTD_freeCCtx(ctx); +} + +static void BMK_benchMem(const void* srcBuffer, size_t srcSize) +{ + ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + if (cctx==NULL) { DISPLAY("ZSTD_createCCtx() failed \n"); exit(1); } + BMK_benchMem_usingCCtx(cctx, srcBuffer, srcSize); + ZSTD_freeCCtx(cctx); } static int benchSample(void) { - void* origBuff; - size_t const benchedSize = sampleSize; - const char* const name = "Sample 10MiB"; + const char* const name = "Sample 10MB"; + size_t const benchedSize = 10000000; - /* Allocation */ - origBuff = malloc(benchedSize); - if (!origBuff) { DISPLAY("\nError: not enough memory!\n"); return 12; } + void* origBuff = malloc(benchedSize); + if (!origBuff) { perror("not enough memory"); return 12; } /* Fill buffer */ RDG_genBuffer(origBuff, benchedSize, g_compressibility, 0.0, 0); @@ -662,6 +665,9 @@ static int benchSample(void) } +/* benchFiles() : + * note: while this function takes a table of filenames, + * in practice, only the first filename will be used */ int benchFiles(const char** fileNamesTable, int nbFiles) { int fileIdx=0; @@ -680,7 +686,7 @@ int benchFiles(const char** fileNamesTable, int nbFiles) return 11; } if (inFileSize == UTIL_FILESIZE_UNKNOWN) { - DISPLAY("Pb evaluatin size of %s \n", inFileName); + DISPLAY("Pb evaluating size of %s \n", inFileName); fclose(inFile); return 11; } @@ -843,6 +849,38 @@ int optimizeForSize(const char* inFileName, U32 targetSpeed) return 0; } +/*! 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 will exit() program if digit sequence overflows */ +static unsigned readU32FromChar(const char** stringPtr) +{ + unsigned result = 0; + while ((**stringPtr >='0') && (**stringPtr <='9')) { + unsigned const max = (((unsigned)(-1)) / 10) - 1; + if (result > max) { + DISPLAY("error: numeric value too large \n"); + exit(1); + } + result *= 10, result += **stringPtr - '0', (*stringPtr)++ ; + } + if ((**stringPtr=='K') || (**stringPtr=='M')) { + unsigned const maxK = ((unsigned)(-1)) >> 10; + result <<= 10; + if (**stringPtr=='M') { + if (result > maxK) { + DISPLAY("error: numeric value too large \n"); + exit(1); + } + result <<= 10; + } + (*stringPtr)++; /* skip `K` or `M` */ + if (**stringPtr=='i') (*stringPtr)++; + if (**stringPtr=='B') (*stringPtr)++; + } + return result; +} static int usage(const char* exename) { @@ -893,12 +931,11 @@ int main(int argc, const char** argv) /* Welcome message */ DISPLAY(WELCOME_MESSAGE); - if (argc<1) { badusage(exename); return 1; } + if (argc<2) { assert(argc==1); badusage(exename); return 1; } for(i=1; i ='0') & (argument[0] <='9')) - g_nbIterations = *argument++ - '0'; + g_nbIterations = readU32FromChar(&argument); break; /* Sample compressibility (when no file provided) */ case 'P': argument++; - { U32 proba32 = 0; - while ((argument[0]>= '0') & (argument[0]<= '9')) - proba32 = (proba32*10) + (*argument++ - '0'); + { U32 const proba32 = readU32FromChar(&argument); g_compressibility = (double)proba32 / 100.; } break; case 'O': argument++; - optimizer=1; - targetSpeed = 0; - while ((*argument >= '0') & (*argument <= '9')) - targetSpeed = (targetSpeed*10) + (*argument++ - '0'); + optimizer = 1; + targetSpeed = readU32FromChar(&argument); break; /* Run Single conf */ @@ -951,51 +983,35 @@ int main(int argc, const char** argv) switch(*argument) { case 'w': - g_params.windowLog = 0; argument++; - while ((*argument>= '0') && (*argument<='9')) - g_params.windowLog *= 10, g_params.windowLog += *argument++ - '0'; + g_params.windowLog = readU32FromChar(&argument); continue; case 'c': - g_params.chainLog = 0; argument++; - while ((*argument>= '0') && (*argument<='9')) - g_params.chainLog *= 10, g_params.chainLog += *argument++ - '0'; + g_params.chainLog = readU32FromChar(&argument); continue; case 'h': - g_params.hashLog = 0; argument++; - while ((*argument>= '0') && (*argument<='9')) - g_params.hashLog *= 10, g_params.hashLog += *argument++ - '0'; + g_params.hashLog = readU32FromChar(&argument); continue; case 's': - g_params.searchLog = 0; argument++; - while ((*argument>= '0') && (*argument<='9')) - g_params.searchLog *= 10, g_params.searchLog += *argument++ - '0'; + g_params.searchLog = readU32FromChar(&argument); continue; case 'l': /* search length */ - g_params.searchLength = 0; argument++; - while ((*argument>= '0') && (*argument<='9')) - g_params.searchLength *= 10, g_params.searchLength += *argument++ - '0'; + g_params.searchLength = readU32FromChar(&argument); continue; case 't': /* target length */ - g_params.targetLength = 0; argument++; - while ((*argument>= '0') && (*argument<='9')) - g_params.targetLength *= 10, g_params.targetLength += *argument++ - '0'; + g_params.targetLength = readU32FromChar(&argument); continue; case 'S': /* strategy */ argument++; - while ((*argument>= '0') && (*argument<='9')) - g_params.strategy = (ZSTD_strategy)(*argument++ - '0'); + g_params.strategy = readU32FromChar(&argument); continue; case 'L': - { int cLevel = 0; - argument++; - while ((*argument>= '0') && (*argument<='9')) - cLevel *= 10, cLevel += *argument++ - '0'; + { int const cLevel = readU32FromChar(&argument); g_params = ZSTD_getCParams(cLevel, g_blockSize, 0); continue; } @@ -1008,20 +1024,13 @@ int main(int argc, const char** argv) /* target level1 speed objective, in MB/s */ case 'T': argument++; - g_target = 0; - while ((*argument >= '0') && (*argument <= '9')) - g_target = (g_target*10) + (*argument++ - '0'); + g_target = readU32FromChar(&argument); break; /* cut input into blocks */ case 'B': - g_blockSize = 0; argument++; - while ((*argument >='0') & (*argument <='9')) - g_blockSize = (g_blockSize*10) + (*argument++ - '0'); - if (*argument=='K') g_blockSize<<=10, argument++; /* allows using KB notation */ - if (*argument=='M') g_blockSize<<=20, argument++; - if (*argument=='B') argument++; + g_blockSize = readU32FromChar(&argument); DISPLAY("using %u KB block size \n", g_blockSize>>10); break; From a3f2e84a37a1b8c228aae0a4cb1de629f12e070a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 11 May 2018 19:43:08 -0700 Subject: [PATCH 055/288] added programmable constraints --- tests/paramgrill.c | 119 ++++++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 44 deletions(-) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 13ab7a285..d318696ec 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -43,8 +43,6 @@ #define NBLOOPS 2 #define TIMELOOP (2 * SEC_TO_MICRO) -#define NB_LEVELS_TRACKED 30 - static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31)); #define COMPRESSIBILITY_DEFAULT 0.50 @@ -150,10 +148,11 @@ typedef struct } blockParam_t; -static size_t BMK_benchParam(BMK_result_t* resultPtr, - const void* srcBuffer, size_t srcSize, - ZSTD_CCtx* ctx, - const ZSTD_compressionParameters cParams) +static size_t +BMK_benchParam(BMK_result_t* resultPtr, + const void* srcBuffer, size_t srcSize, + ZSTD_CCtx* ctx, + const ZSTD_compressionParameters cParams) { const size_t blockSize = g_blockSize ? g_blockSize : srcSize; const U32 nbBlocks = (U32) ((srcSize + (blockSize-1)) / blockSize); @@ -191,8 +190,7 @@ static size_t BMK_benchParam(BMK_result_t* resultPtr, crcOrig = XXH64(srcBuffer, srcSize, 0); /* Init blockTable data */ - { - U32 i; + { U32 i; size_t remaining = srcSize; const char* srcPtr = (const char*)srcBuffer; char* cPtr = (char*)compressedBuffer; @@ -323,8 +321,6 @@ static void BMK_printWinner(FILE* f, U32 cLevel, BMK_result_t result, ZSTD_compr } -static double g_cSpeedTarget[NB_LEVELS_TRACKED] = { 0. }; /* NB_LEVELS_TRACKED : checked at main() */ - typedef struct { BMK_result_t result; ZSTD_compressionParameters params; @@ -350,6 +346,36 @@ static void BMK_printWinners(FILE* f, const winnerInfo_t* winners, size_t srcSiz BMK_printWinners2(stdout, winners, srcSize); } + +typedef struct { + double cSpeed_min; + double dSpeed_min; + U32 windowLog_max; + ZSTD_strategy strategy_max; +} level_constraints_t; + +#define NB_LEVELS_TRACKED 23 +static level_constraints_t g_level_constraint[NB_LEVELS_TRACKED]; + +static void BMK_init_level_constraints(int bytePerSec_level1) +{ + assert(NB_LEVELS_TRACKED == ZSTD_maxCLevel()+1); + memset(g_level_constraint, 0, sizeof(g_level_constraint)); + g_level_constraint[1].cSpeed_min = bytePerSec_level1; + g_level_constraint[1].dSpeed_min = 0.; + g_level_constraint[1].windowLog_max = 19; + g_level_constraint[1].strategy_max = ZSTD_fast; + + /* establish speed objectives (relative to level 1) */ + { int l; + for (l=2; l = 20 may use windowlog > 23 */ + g_level_constraint[l].strategy_max = (l<19) ? ZSTD_btopt : ZSTD_btultra; /* level 19 is allowed to use btultra */ + } } +} + static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters params, const void* srcBuffer, size_t srcSize, ZSTD_CCtx* ctx) @@ -360,9 +386,16 @@ static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters para BMK_benchParam(&testResult, srcBuffer, srcSize, ctx, params); + for (cLevel = 1; cLevel <= ZSTD_maxCLevel(); cLevel++) { - if (testResult.cSpeed < g_cSpeedTarget[cLevel]) + if (testResult.cSpeed < g_level_constraint[cLevel].cSpeed_min) continue; /* not fast enough for this level */ + if (testResult.dSpeed < g_level_constraint[cLevel].dSpeed_min) + continue; /* not fast enough for this level */ + if (params.windowLog > g_level_constraint[cLevel].windowLog_max) + continue; /* too much memory for this level */ + if (params.strategy > g_level_constraint[cLevel].strategy_max) + continue; /* forbidden strategy for this level */ if (winners[cLevel].result.cSize==0) { /* first solution for this cLevel */ winners[cLevel].result = testResult; @@ -575,40 +608,36 @@ static void BMK_selectRandomStart( } -static void BMK_benchMem_usingCCtx(ZSTD_CCtx* cctx, const void* srcBuffer, size_t srcSize) +static void BMK_benchOnce(ZSTD_CCtx* cctx, const void* srcBuffer, size_t srcSize) +{ + BMK_result_t testResult; + g_params = ZSTD_adjustCParams(g_params, srcSize, 0); + BMK_benchParam(&testResult, srcBuffer, srcSize, cctx, g_params); + DISPLAY("\n"); + return; +} + +static void BMK_benchFullTable(ZSTD_CCtx* cctx, const void* srcBuffer, size_t srcSize) { ZSTD_compressionParameters params; winnerInfo_t winners[NB_LEVELS_TRACKED]; const char* const rfName = "grillResults.txt"; FILE* const f = fopen(rfName, "w"); - const size_t blockSize = g_blockSize ? g_blockSize : srcSize; + const size_t blockSize = g_blockSize ? g_blockSize : srcSize; /* cut by block or not ? */ /* init */ + assert(g_singleRun==0); memset(winners, 0, sizeof(winners)); if (f==NULL) { DISPLAY("error opening %s \n", rfName); exit(1); } - if (g_singleRun) { - BMK_result_t testResult; - g_params = ZSTD_adjustCParams(g_params, srcSize, 0); - BMK_benchParam(&testResult, srcBuffer, srcSize, cctx, g_params); - DISPLAY("\n"); - return; - } - if (g_target) { - g_cSpeedTarget[1] = g_target * 1000000; + BMK_init_level_constraints(g_target*1000000); } else { /* baseline config for level 1 */ + ZSTD_compressionParameters const l1params = ZSTD_getCParams(1, blockSize, 0); BMK_result_t testResult; - params = ZSTD_getCParams(1, blockSize, 0); - BMK_benchParam(&testResult, srcBuffer, srcSize, cctx, params); - g_cSpeedTarget[1] = (testResult.cSpeed * 31) / 32; - } - - /* establish speed objectives (relative to level 1) */ - { int i; - for (i=2; i<=ZSTD_maxCLevel(); i++) - g_cSpeedTarget[i] = (g_cSpeedTarget[i-1] * 25) / 32; + BMK_benchParam(&testResult, srcBuffer, srcSize, cctx, l1params); + BMK_init_level_constraints((int)((testResult.cSpeed * 31) / 32)); } /* populate initial solution */ @@ -635,6 +664,14 @@ static void BMK_benchMem_usingCCtx(ZSTD_CCtx* cctx, const void* srcBuffer, size_ fclose(f); } +static void BMK_benchMem_usingCCtx(ZSTD_CCtx* cctx, const void* srcBuffer, size_t srcSize) +{ + if (g_singleRun) + return BMK_benchOnce(cctx, srcBuffer, srcSize); + else + return BMK_benchFullTable(cctx, srcBuffer, srcSize); +} + static void BMK_benchMem(const void* srcBuffer, size_t srcSize) { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); @@ -922,17 +959,11 @@ int main(int argc, const char** argv) U32 main_pause = 0; U32 targetSpeed = 0; - /* checks */ - if (NB_LEVELS_TRACKED <= ZSTD_maxCLevel()) { - DISPLAY("Error : NB_LEVELS_TRACKED <= ZSTD_maxCLevel() \n"); - exit(1); - } + assert(argc>=1); /* for exename */ /* Welcome message */ DISPLAY(WELCOME_MESSAGE); - if (argc<2) { assert(argc==1); badusage(exename); return 1; } - for(i=1; i Date: Sat, 12 May 2018 09:40:04 -0700 Subject: [PATCH 056/288] paramgrill: subtle change in level spacing distance between levels is slightly increased to compensate for level 1 speed improvements and the will to have stronger level 19 extending the range of speed to cover. --- tests/paramgrill.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index d318696ec..984fb308f 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -47,7 +47,7 @@ static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t #define COMPRESSIBILITY_DEFAULT 0.50 -static const double g_grillDuration_s = 90000; /* about 24 hours */ +static const double g_grillDuration_s = 99999; /* about 27 hours */ static const U64 g_maxParamTime = 15 * SEC_TO_MICRO; static const U64 g_maxVariationTime = 60 * SEC_TO_MICRO; static const int g_maxNbVariations = 64; @@ -369,9 +369,9 @@ static void BMK_init_level_constraints(int bytePerSec_level1) /* establish speed objectives (relative to level 1) */ { int l; for (l=2; l = 20 may use windowlog > 23 */ + g_level_constraint[l].windowLog_max = (l<20) ? 23 : l+5; /* only --ultra levels >= 20 can use windowlog > 23 */ g_level_constraint[l].strategy_max = (l<19) ? ZSTD_btopt : ZSTD_btultra; /* level 19 is allowed to use btultra */ } } } From b824d213cbdeeed846e4085da33ab0e47ab7d227 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 12 May 2018 10:21:30 -0700 Subject: [PATCH 057/288] fix #1115 --- programs/zstd.1 | 36 +++++++++++++++++++++++++----------- programs/zstd.1.md | 14 +++++++------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/programs/zstd.1 b/programs/zstd.1 index ccc6cacb6..507933c97 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -1,5 +1,5 @@ . -.TH "ZSTD" "1" "2018-01-27" "zstd 1.3.4" "User Commands" +.TH "ZSTD" "1" "2018-05-12" "zstd 1.3.4" "User Commands" . .SH "NAME" \fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files @@ -111,8 +111,16 @@ enables long distance matching with \fB#\fR \fBwindowLog\fR, if not \fB#\fR is n Note: If \fBwindowLog\fR is set to larger than 27, \fB\-\-long=windowLog\fR or \fB\-\-memory=windowSize\fR needs to be passed to the decompressor\. . .TP +\fB\-\-fast[=#]\fR +switch to ultra\-fast compression levels\. If \fB=#\fR is not present, it defaults to \fB1\fR\. The higher the value, the faster the compression speed, at the cost of some compression ratio\. This setting overwrites compression level if one was set previously\. Similarly, if a compression level is set after \fB\-\-fast\fR, it overrides it\. +. +.TP \fB\-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\. 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\. +Compress using \fB#\fR working 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==200\. This modifier does nothing if \fBzstd\fR is compiled without multithread support\. +. +.TP +\fB\-\-single\-thread\fR +Does not spawn a thread for compression, use caller thread instead\. This is the only available mode when multithread support is disabled\. In this mode, compression is serialized with I/O\. (This is different from \fB\-T1\fR, which spawns 1 compression thread in parallel of I/O)\. Single\-thread mode also features lower memory usage\. . .TP \fB\-D file\fR @@ -335,13 +343,19 @@ The minimum \fIslen\fR is 3 and the maximum is 7\. . .TP \fBtargetLen\fR=\fItlen\fR, \fBtlen\fR=\fItlen\fR -Specify the minimum match length that causes a match finder to stop searching for better matches\. +The impact of this field vary depending on selected strategy\. . .IP -A larger minimum match length usually improves compression ratio but decreases compression speed\. This option is only used with strategies ZSTD_btopt and ZSTD_btultra\. +For ZSTD_btopt and ZSTD_btultra, it specifies the minimum match length that causes match finder to stop searching for better matches\. A larger \fBtargetLen\fR usually improves compression ratio but decreases compression speed\. . .IP -The minimum \fItlen\fR is 4 and the maximum is 999\. +For ZSTD_fast, it specifies the amount of data skipped between match sampling\. Impact is reversed : a larger \fBtargetLen\fR increases compression speed but decreases compression ratio\. +. +.IP +For all other strategies, this field has no impact\. +. +.IP +The minimum \fItlen\fR is 1 and the maximum is 999\. . .TP \fBoverlapLog\fR=\fIovlog\fR, \fBovlog\fR=\fIovlog\fR @@ -374,7 +388,7 @@ This option is ignored unless long distance matching is enabled\. Larger/very small values usually decrease compression ratio\. . .IP -The minumum \fIldmslen\fR is 4 and the maximum is 4096 (default: 64)\. +The minimum \fIldmslen\fR is 4 and the maximum is 4096 (default: 64)\. . .TP \fBldmBucketSizeLog\fR=\fIldmblog\fR, \fBldmblog\fR=\fIldmblog\fR @@ -402,14 +416,14 @@ Larger values will improve compression speed\. Deviating far from the default va .IP The default value is \fBwlog \- ldmhlog\fR\. . -.SS "\-B#:" -Select the size of each compression job\. This parameter is available only when multi\-threading is enabled\. Default value is \fB4 * windowSize\fR, which means it varies depending on compression level\. \fB\-B#\fR makes it possible to select a custom value\. Note that job size must respect a minimum value which is enforced transparently\. This minimum is either 1 MB, or \fBoverlapSize\fR, whichever is largest\. -. .SS "Example" -The following parameters sets advanced compression options to those of predefined level 19 for files bigger than 256 KB: +The following parameters sets advanced compression options to something similar to predefined level 19 for files bigger than 256 KB: . .P -\fB\-\-zstd\fR=windowLog=23,chainLog=23,hashLog=22,searchLog=6,searchLength=3,targetLength=48,strategy=6 +\fB\-\-zstd\fR=wlog=23,clog=23,hlog=22,slog=6,slen=3,tlen=48,strat=6 +. +.SS "\-B#:" +Select the size of each compression job\. This parameter is available only when multi\-threading is enabled\. Default value is \fB4 * windowSize\fR, which means it varies depending on compression level\. \fB\-B#\fR makes it possible to select a custom value\. Note that job size must respect a minimum value which is enforced transparently\. This minimum is either 1 MB, or \fBoverlapSize\fR, whichever is largest\. . .SH "BUGS" Report bugs at: https://github\.com/facebook/zstd/issues diff --git a/programs/zstd.1.md b/programs/zstd.1.md index 8a9d18da8..22f7d0425 100644 --- a/programs/zstd.1.md +++ b/programs/zstd.1.md @@ -392,7 +392,7 @@ The list of available _options_: Larger/very small values usually decrease compression ratio. - The minumum _ldmslen_ is 4 and the maximum is 4096 (default: 64). + The minimum _ldmslen_ is 4 and the maximum is 4096 (default: 64). - `ldmBucketSizeLog`=_ldmblog_, `ldmblog`=_ldmblog_: Specify the size of each bucket for the hash table used for long distance @@ -416,6 +416,12 @@ The list of available _options_: The default value is `wlog - ldmhlog`. +### Example +The following parameters sets advanced compression options to something +similar to predefined level 19 for files bigger than 256 KB: + +`--zstd`=wlog=23,clog=23,hlog=22,slog=6,slen=3,tlen=48,strat=6 + ### -B#: Select the size of each compression job. This parameter is available only when multi-threading is enabled. @@ -424,12 +430,6 @@ Default value is `4 * windowSize`, which means it varies depending on compressio Note that job size must respect a minimum value which is enforced transparently. This minimum is either 1 MB, or `overlapSize`, whichever is largest. -### Example -The following parameters sets advanced compression options to those of -predefined level 19 for files bigger than 256 KB: - -`--zstd`=windowLog=23,chainLog=23,hashLog=22,searchLog=6,searchLength=3,targetLength=48,strategy=6 - BUGS ---- Report bugs at: https://github.com/facebook/zstd/issues From 3f89cd108109d413fc576dca7b81fcbf5ff6d28e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 12 May 2018 12:34:34 -0700 Subject: [PATCH 058/288] minor : factor out errorOut() --- tests/paramgrill.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 984fb308f..7e4c6b75f 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -886,6 +886,11 @@ int optimizeForSize(const char* inFileName, U32 targetSpeed) return 0; } +static void errorOut(const char* msg) +{ + DISPLAY("%s \n", msg); exit(1); +} + /*! readU32FromChar() : * @return : unsigned integer value read from input in `char` format. * allows and interprets K, KB, KiB, M, MB and MiB suffix. @@ -893,23 +898,19 @@ int optimizeForSize(const char* inFileName, U32 targetSpeed) * Note : function will exit() program if digit sequence overflows */ static unsigned readU32FromChar(const char** stringPtr) { + const char errorMsg[] = "error: numeric value too large"; unsigned result = 0; while ((**stringPtr >='0') && (**stringPtr <='9')) { unsigned const max = (((unsigned)(-1)) / 10) - 1; - if (result > max) { - DISPLAY("error: numeric value too large \n"); - exit(1); - } + if (result > max) errorOut(errorMsg); result *= 10, result += **stringPtr - '0', (*stringPtr)++ ; } if ((**stringPtr=='K') || (**stringPtr=='M')) { unsigned const maxK = ((unsigned)(-1)) >> 10; + if (result > maxK) errorOut(errorMsg); result <<= 10; if (**stringPtr=='M') { - if (result > maxK) { - DISPLAY("error: numeric value too large \n"); - exit(1); - } + if (result > maxK) errorOut(errorMsg); result <<= 10; } (*stringPtr)++; /* skip `K` or `M` */ From 9cd5c63771a21c5769366e058d1d8bf1cea89970 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 12 May 2018 14:29:33 -0700 Subject: [PATCH 059/288] cli: control numeric argument overflow exit on overflow backported from paramgrill added associated test case --- programs/zstdcli.c | 22 ++++++++++++++++++---- tests/playTests.sh | 2 ++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 24488191f..28bfdc539 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -219,20 +219,34 @@ static int exeNameMatch(const char* exeName, const char* test) (exeName[strlen(test)] == '\0' || exeName[strlen(test)] == '.'); } +static void errorOut(const char* msg) +{ + DISPLAY("%s \n", msg); exit(1); +} + /*! 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 */ + * Note : function will exit() program if digit sequence overflows */ static unsigned readU32FromChar(const char** stringPtr) { + const char errorMsg[] = "error: numeric value too large"; unsigned result = 0; - while ((**stringPtr >='0') && (**stringPtr <='9')) + while ((**stringPtr >='0') && (**stringPtr <='9')) { + unsigned const max = (((unsigned)(-1)) / 10) - 1; + if (result > max) errorOut(errorMsg); result *= 10, result += **stringPtr - '0', (*stringPtr)++ ; + } if ((**stringPtr=='K') || (**stringPtr=='M')) { + unsigned const maxK = ((unsigned)(-1)) >> 10; + if (result > maxK) errorOut(errorMsg); result <<= 10; - if (**stringPtr=='M') result <<= 10; - (*stringPtr)++ ; + if (**stringPtr=='M') { + if (result > maxK) errorOut(errorMsg); + result <<= 10; + } + (*stringPtr)++; /* skip `K` or `M` */ if (**stringPtr=='i') (*stringPtr)++; if (**stringPtr=='B') (*stringPtr)++; } diff --git a/tests/playTests.sh b/tests/playTests.sh index c8e27f233..200de4bd9 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -108,6 +108,8 @@ $ECHO "test : --fast aka negative compression levels" $ZSTD --fast -f tmp # == -1 $ZSTD --fast=3 -f tmp # == -3 $ZSTD --fast=200000 -f tmp # == no compression +$ECHO "test : too large numeric argument" +$ZSTD --fast=9999999999 -f tmp && die "should have refused numeric value" $ECHO "test : compress to stdout" $ZSTD tmp -c > tmpCompressed $ZSTD tmp --stdout > tmpCompressed # long command format From b4250489cf558100c863716a7b3c4cf7ef343a89 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 13 May 2018 01:53:38 -0700 Subject: [PATCH 060/288] update compression levels for large inputs --- lib/compress/zstd_compress.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 08dc7db28..a979335a1 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3423,25 +3423,25 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 19, 13, 14, 1, 7, 1, ZSTD_fast }, /* level 1 */ { 19, 15, 16, 1, 6, 1, ZSTD_fast }, /* level 2 */ { 20, 16, 17, 1, 5, 8, ZSTD_dfast }, /* level 3 */ - { 20, 17, 18, 1, 5, 8, ZSTD_dfast }, /* level 4 */ - { 20, 17, 18, 2, 5, 16, ZSTD_greedy }, /* level 5 */ - { 21, 17, 19, 2, 5, 16, ZSTD_lazy }, /* level 6 */ - { 21, 18, 19, 3, 5, 16, ZSTD_lazy }, /* level 7 */ - { 21, 18, 20, 3, 5, 16, ZSTD_lazy2 }, /* level 8 */ - { 21, 19, 20, 3, 5, 16, ZSTD_lazy2 }, /* level 9 */ - { 21, 19, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */ - { 22, 20, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */ + { 20, 18, 18, 1, 5, 8, ZSTD_dfast }, /* level 4 */ + { 20, 18, 18, 2, 5, 16, ZSTD_greedy }, /* level 5 */ + { 21, 18, 19, 2, 5, 16, ZSTD_lazy }, /* level 6 */ + { 21, 18, 19, 3, 5, 16, ZSTD_lazy2 }, /* level 7 */ + { 21, 19, 19, 3, 5, 16, ZSTD_lazy2 }, /* level 8 */ + { 21, 19, 20, 4, 5, 16, ZSTD_lazy2 }, /* level 9 */ + { 21, 20, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */ + { 21, 21, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */ { 22, 20, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */ { 22, 21, 22, 4, 5, 32, ZSTD_btlazy2 }, /* level 13 */ { 22, 21, 22, 5, 5, 32, ZSTD_btlazy2 }, /* level 14 */ { 22, 22, 22, 6, 5, 32, ZSTD_btlazy2 }, /* level 15 */ { 22, 21, 22, 4, 5, 48, ZSTD_btopt }, /* level 16 */ - { 23, 22, 22, 4, 4, 48, ZSTD_btopt }, /* level 17 */ - { 23, 22, 22, 5, 3, 64, ZSTD_btopt }, /* level 18 */ - { 23, 23, 22, 7, 3,128, ZSTD_btopt }, /* level 19 */ - { 25, 25, 23, 7, 3,128, ZSTD_btultra }, /* level 20 */ - { 26, 26, 24, 7, 3,256, ZSTD_btultra }, /* level 21 */ - { 27, 27, 25, 9, 3,512, ZSTD_btultra }, /* level 22 */ + { 23, 22, 22, 4, 4, 64, ZSTD_btopt }, /* level 17 */ + { 23, 23, 22, 6, 3,256, ZSTD_btopt }, /* level 18 */ + { 23, 24, 22, 7, 3,256, ZSTD_btultra }, /* level 19 */ + { 25, 25, 23, 7, 3,256, ZSTD_btultra }, /* level 20 */ + { 26, 26, 24, 7, 3,512, ZSTD_btultra }, /* level 21 */ + { 27, 27, 25, 9, 3,999, ZSTD_btultra }, /* level 22 */ }, { /* for srcSize <= 256 KB */ /* W, C, H, S, L, T, strat */ From c9227ee16bfc95ec99aeb4ca8ddf1217f9129482 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 13 May 2018 17:15:07 -0700 Subject: [PATCH 061/288] update table for 128 KB blocks --- lib/compress/zstd_compress.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index a979335a1..622b069be 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3471,26 +3471,26 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV }, { /* for srcSize <= 128 KB */ /* W, C, H, S, L, T, strat */ - { 17, 12, 12, 1, 5, 1, ZSTD_fast }, /* level 0 - not used */ + { 17, 12, 12, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ { 17, 12, 13, 1, 6, 1, ZSTD_fast }, /* level 1 */ - { 17, 13, 16, 1, 5, 1, ZSTD_fast }, /* level 2 */ - { 17, 16, 16, 2, 5, 8, ZSTD_dfast }, /* level 3 */ - { 17, 13, 15, 3, 4, 8, ZSTD_greedy }, /* level 4 */ - { 17, 15, 17, 4, 4, 8, ZSTD_greedy }, /* level 5 */ - { 17, 16, 17, 3, 4, 8, ZSTD_lazy }, /* level 6 */ - { 17, 15, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 7 */ + { 17, 13, 15, 1, 5, 1, ZSTD_fast }, /* level 2 */ + { 17, 15, 16, 2, 5, 8, ZSTD_dfast }, /* level 3 */ + { 17, 17, 17, 2, 4, 8, ZSTD_dfast }, /* level 4 */ + { 17, 16, 17, 3, 4, 8, ZSTD_greedy }, /* level 5 */ + { 17, 17, 17, 3, 4, 8, ZSTD_lazy }, /* level 6 */ + { 17, 17, 17, 3, 4, 8, ZSTD_lazy2 }, /* level 7 */ { 17, 17, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ { 17, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ { 17, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ { 17, 17, 17, 7, 4, 8, ZSTD_lazy2 }, /* level 11 */ - { 17, 17, 17, 8, 4, 8, ZSTD_lazy2 }, /* level 12 */ - { 17, 18, 17, 6, 4, 8, ZSTD_btlazy2 }, /* level 13.*/ - { 17, 17, 17, 7, 3, 8, ZSTD_btopt }, /* level 14.*/ - { 17, 17, 17, 7, 3, 16, ZSTD_btopt }, /* level 15.*/ - { 17, 18, 17, 7, 3, 32, ZSTD_btopt }, /* level 16.*/ - { 17, 18, 17, 7, 3, 64, ZSTD_btopt }, /* level 17.*/ - { 17, 18, 17, 7, 3,256, ZSTD_btopt }, /* level 18.*/ - { 17, 18, 17, 8, 3,256, ZSTD_btopt }, /* level 19.*/ + { 17, 18, 17, 6, 4, 16, ZSTD_btlazy2 }, /* level 12 */ + { 17, 18, 17, 8, 4, 16, ZSTD_btlazy2 }, /* level 13.*/ + { 17, 18, 17, 4, 4, 32, ZSTD_btopt }, /* level 14.*/ + { 17, 18, 17, 6, 3, 64, ZSTD_btopt }, /* level 15.*/ + { 17, 18, 17, 7, 3,128, ZSTD_btopt }, /* level 16.*/ + { 17, 18, 17, 7, 3,256, ZSTD_btopt }, /* level 17.*/ + { 17, 18, 17, 8, 3,256, ZSTD_btopt }, /* level 18.*/ + { 17, 18, 17, 8, 3,256, ZSTD_btultra }, /* level 19.*/ { 17, 18, 17, 9, 3,256, ZSTD_btultra }, /* level 20.*/ { 17, 18, 17, 10, 3,256, ZSTD_btultra }, /* level 21.*/ { 17, 18, 17, 11, 3,512, ZSTD_btultra }, /* level 22.*/ From 2c392952f994bd05d130a62cc8fbe667e3fc2815 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 13 May 2018 17:25:53 -0700 Subject: [PATCH 062/288] paramgrill: use NB_LEVELS_TRACKED in loop make it easier to generate/track more levels than ZSTD_maxClevel() --- tests/paramgrill.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 7e4c6b75f..3172ab067 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -42,6 +42,7 @@ #define NBLOOPS 2 #define TIMELOOP (2 * SEC_TO_MICRO) +#define NB_LEVELS_TRACKED 22 /* ensured being >= ZSTD_maxCLevel() in BMK_init_level_constraints() */ static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31)); @@ -333,7 +334,7 @@ static void BMK_printWinners2(FILE* f, const winnerInfo_t* winners, size_t srcSi fprintf(f, "\n /* Proposed configurations : */ \n"); fprintf(f, " /* W, C, H, S, L, T, strat */ \n"); - for (cLevel=0; cLevel <= ZSTD_maxCLevel(); cLevel++) + for (cLevel=0; cLevel <= NB_LEVELS_TRACKED; cLevel++) BMK_printWinner(f, cLevel, winners[cLevel].result, winners[cLevel].params, srcSize); } @@ -354,12 +355,11 @@ typedef struct { ZSTD_strategy strategy_max; } level_constraints_t; -#define NB_LEVELS_TRACKED 23 -static level_constraints_t g_level_constraint[NB_LEVELS_TRACKED]; +static level_constraints_t g_level_constraint[NB_LEVELS_TRACKED+1]; static void BMK_init_level_constraints(int bytePerSec_level1) { - assert(NB_LEVELS_TRACKED == ZSTD_maxCLevel()+1); + assert(NB_LEVELS_TRACKED >= ZSTD_maxCLevel()); memset(g_level_constraint, 0, sizeof(g_level_constraint)); g_level_constraint[1].cSpeed_min = bytePerSec_level1; g_level_constraint[1].dSpeed_min = 0.; @@ -368,7 +368,7 @@ static void BMK_init_level_constraints(int bytePerSec_level1) /* establish speed objectives (relative to level 1) */ { int l; - for (l=2; l = 20 can use windowlog > 23 */ @@ -387,7 +387,7 @@ static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters para BMK_benchParam(&testResult, srcBuffer, srcSize, ctx, params); - for (cLevel = 1; cLevel <= ZSTD_maxCLevel(); cLevel++) { + for (cLevel = 1; cLevel <= NB_LEVELS_TRACKED; cLevel++) { if (testResult.cSpeed < g_level_constraint[cLevel].cSpeed_min) continue; /* not fast enough for this level */ if (testResult.dSpeed < g_level_constraint[cLevel].dSpeed_min) @@ -597,7 +597,7 @@ static void BMK_selectRandomStart( const void* srcBuffer, size_t srcSize, ZSTD_CCtx* ctx) { - U32 const id = (FUZ_rand(&g_rand) % (ZSTD_maxCLevel()+1)); + U32 const id = FUZ_rand(&g_rand) % (NB_LEVELS_TRACKED+1); if ((id==0) || (winners[id].params.windowLog==0)) { /* use some random entry */ ZSTD_compressionParameters const p = ZSTD_adjustCParams(randomParams(), srcSize, 0); @@ -620,7 +620,7 @@ static void BMK_benchOnce(ZSTD_CCtx* cctx, const void* srcBuffer, size_t srcSize static void BMK_benchFullTable(ZSTD_CCtx* cctx, const void* srcBuffer, size_t srcSize) { ZSTD_compressionParameters params; - winnerInfo_t winners[NB_LEVELS_TRACKED]; + winnerInfo_t winners[NB_LEVELS_TRACKED+1]; const char* const rfName = "grillResults.txt"; FILE* const f = fopen(rfName, "w"); const size_t blockSize = g_blockSize ? g_blockSize : srcSize; /* cut by block or not ? */ From e26be5a7b395d4e72fe715fad8aae28cf94b163c Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Mon, 14 May 2018 11:55:21 -0400 Subject: [PATCH 063/288] Travis CI Runs apt-get Update --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3ca725526..a9c1db525 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,12 @@ # Medium Tests: Run on all commits/PRs to dev branch language: c -sudo: required dist: trusty +sudo: required +addons: + apt: + update: true + matrix: include: # Ubuntu 14.04 From d59cf02df0938c3147f4290f48cf3e533129406e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 14 May 2018 15:32:28 -0700 Subject: [PATCH 064/288] decompress: changed error code when input is too large ZSTD_decompress() can decompress multiple frames sent as a single input. But the input size must be the exact sum of all compressed frames, no more. In the case of a mistake on srcSize, being larger than required, ZSTD_decompress() will try to decompress a new frame after current one, and fail. As a consequence, it will issue an error code, ERROR(prefix_unknown). While the error is technically correct (the decoder could not recognise the header of _next_ frame), it's confusing, as users will believe that the first header of the first frame is wrong, which is not the case (it's correct). It makes it more difficult to understand that the error is in the source size, which is too large. This patch changes the error code provided in such a scenario. If (at least) a first frame was successfully decoded, and then following bytes are garbage values, the decoder assumes the provided input size is wrong (too large), and issue the error code ERROR(srcSize_wrong). --- lib/decompress/zstd_decompress.c | 27 +++++++++++++++++++-------- tests/fuzzer.c | 6 ++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 103dcc648..32a213089 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1882,6 +1882,7 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) { void* const dststart = dst; + int moreThan1Frame = 0; assert(dict==NULL || ddict==NULL); /* either dict or ddict set, not both */ if (ddict) { @@ -1890,7 +1891,6 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, } while (srcSize >= ZSTD_frameHeaderSize_prefix) { - U32 magicNumber; #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) if (ZSTD_isLegacy(src, srcSize)) { @@ -1912,10 +1912,9 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, } #endif - magicNumber = MEM_readLE32(src); - DEBUGLOG(4, "reading magic number %08X (expecting %08X)", - (U32)magicNumber, (U32)ZSTD_MAGICNUMBER); - if (magicNumber != ZSTD_MAGICNUMBER) { + { U32 const magicNumber = MEM_readLE32(src); + DEBUGLOG(4, "reading magic number %08X (expecting %08X)", + (U32)magicNumber, (U32)ZSTD_MAGICNUMBER); if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { size_t skippableSize; if (srcSize < ZSTD_skippableHeaderSize) @@ -1927,9 +1926,7 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, src = (const BYTE *)src + skippableSize; srcSize -= skippableSize; continue; - } - return ERROR(prefix_unknown); - } + } } if (ddict) { /* we were called from ZSTD_decompress_usingDDict */ @@ -1943,11 +1940,25 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, { const size_t res = ZSTD_decompressFrame(dctx, dst, dstCapacity, &src, &srcSize); + if ( (ZSTD_getErrorCode(res) == ZSTD_error_prefix_unknown) + && (moreThan1Frame==1) ) { + /* at least one frame successfully completed, + * but following bytes are garbage : + * it's more likely to be a srcSize error, + * specifying more bytes than compressed size of frame(s). + * This error message replaces ERROR(prefix_unknown), + * which would be confusing, as the first header is actually correct. + * Note that one could be unlucky, it might be a corruption error instead, + * happening right at the place where we expect zstd magic bytes. + * But this is _much_ less likely than a srcSize field error. */ + return ERROR(srcSize_wrong); + } if (ZSTD_isError(res)) return res; /* no need to bound check, ZSTD_decompressFrame already has */ dst = (BYTE*)dst + res; dstCapacity -= res; } + moreThan1Frame = 1; } /* while (srcSize >= ZSTD_frameHeaderSize_prefix) */ if (srcSize) return ERROR(srcSize_wrong); /* input not entirely consumed */ diff --git a/tests/fuzzer.c b/tests/fuzzer.c index f9ea209fc..b0c425e1e 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -375,6 +375,12 @@ static int basicUnitTests(U32 seed, double compressibility) if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; } DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3i : decompress too large input : ", testNb++); + { size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, compressedBufferSize); + if (!ZSTD_isError(r)) goto _output_error; + if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; } + DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3d : check CCtx size after compressing empty input : ", testNb++); { ZSTD_CCtx* cctx = ZSTD_createCCtx(); size_t const r = ZSTD_compressCCtx(cctx, compressedBuffer, compressedBufferSize, NULL, 0, 19); From 2c26df0e137e69d5a8834c0b0cc178c84a8d4ac6 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 14 May 2018 18:04:08 -0700 Subject: [PATCH 065/288] opt: removed static prices after testing, it's actually always better to use dynamic prices albeit initialised from dictionary. --- lib/compress/zstd_compress_internal.h | 2 +- lib/compress/zstd_opt.c | 42 ++------------------------- 2 files changed, 3 insertions(+), 41 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 0f1830a5e..d9edfc31c 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -76,7 +76,7 @@ typedef struct { U32 rep[ZSTD_REP_NUM]; } ZSTD_optimal_t; -typedef enum { zop_dynamic=0, zop_predef, zop_static } ZSTD_OptPrice_e; +typedef enum { zop_dynamic=0, zop_predef } ZSTD_OptPrice_e; typedef struct { /* All tables are allocated inside cctx->workspace by ZSTD_resetCCtx_internal() */ diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 76144f730..fa4681611 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -40,14 +40,9 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, assert(optPtr->symbolCosts != NULL); if (optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid) { /* huffman table presumed generated by dictionary */ - if (srcSize <= 8192) /* heuristic */ - optPtr->priceType = zop_static; - else { - assert(optPtr->priceType == zop_dynamic); - } + optPtr->priceType = zop_dynamic; assert(optPtr->litFreq != NULL); - assert(optPtr->symbolCosts != NULL); optPtr->litSum = 0; { unsigned lit; for (lit=0; lit<=MaxLit; lit++) { @@ -156,7 +151,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, #if 1 /* approximation at bit level */ # define BITCOST_ACCURACY 0 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define BITCOST_SYMBOL(t,l,s) ((void)l, FSE_getMaxNbBits(t,s)*BITCOST_MULTIPLIER) +# define BITCOST_SYMBOL(t,l,s) ((void)(l), FSE_getMaxNbBits(t,s) * BITCOST_MULTIPLIER) #else /* fractional bit accuracy */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) @@ -177,14 +172,6 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, { if (litLength == 0) return 0; if (optPtr->priceType == zop_predef) return (litLength*6); /* 6 bit per literal - no statistic used */ - if (optPtr->priceType == zop_static) { - U32 u, cost; - assert(optPtr->symbolCosts != NULL); - assert(optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid); - for (u=0, cost=0; u < litLength; u++) - cost += HUF_getNbBits(optPtr->symbolCosts->hufCTable, literals[u]); - return cost * BITCOST_MULTIPLIER; - } /* dynamic statistics */ { U32 u; @@ -199,14 +186,6 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, * cost of literalLength symbol */ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr) { - if (optPtr->priceType == zop_static) { - U32 const llCode = ZSTD_LLcode(litLength); - FSE_CState_t cstate; - FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); - { U32 const price = LL_bits[llCode]*BITCOST_MULTIPLIER + BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, llCode); - DEBUGLOG(8, "ZSTD_litLengthPrice: ll=%u, bitCost=%.2f", litLength, (double)price / BITCOST_MULTIPLIER); - return price; - } } if (optPtr->priceType == zop_predef) return ZSTD_highbit32((U32)litLength+1); /* dynamic statistics */ @@ -231,14 +210,6 @@ static U32 ZSTD_fullLiteralsCost(const BYTE* const literals, U32 const litLength * to provide a cost which is directly comparable to a match ending at same position */ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr) { - if (optPtr->priceType == zop_static) { - U32 const llCode = ZSTD_LLcode(litLength); - FSE_CState_t cstate; - FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); - return (int)(LL_bits[llCode] * BITCOST_MULTIPLIER) - + BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, llCode) - - BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, 0); - } if (optPtr->priceType >= zop_predef) return ZSTD_highbit32(litLength+1); /* dynamic statistics */ @@ -281,15 +252,6 @@ ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, U32 const mlBase = matchLength - MINMATCH; assert(matchLength >= MINMATCH); - if (optPtr->priceType == zop_static) { - U32 const mlCode = ZSTD_MLcode(mlBase); - FSE_CState_t mlstate, offstate; - FSE_initCState(&mlstate, optPtr->symbolCosts->matchlengthCTable); - FSE_initCState(&offstate, optPtr->symbolCosts->offcodeCTable); - return BITCOST_SYMBOL(offstate.symbolTT, offstate.stateLog, offCode) + offCode*BITCOST_MULTIPLIER - + BITCOST_SYMBOL(mlstate.symbolTT, mlstate.stateLog, mlCode) + ML_bits[mlCode]*BITCOST_MULTIPLIER; - } - if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */ return ZSTD_highbit32(mlBase+1) + 16 + offCode; From 30d9c84b1ab9a2b100cfe667558e975d70070736 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 15 May 2018 09:46:20 -0700 Subject: [PATCH 066/288] Fix failing Travis tests --- lib/compress/zstd_opt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 76144f730..3a48187c5 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -53,7 +53,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, for (lit=0; lit<=MaxLit; lit++) { U32 const scaleLog = 11; /* scale to 2K */ U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->hufCTable, lit); - assert(bitCost < scaleLog); + assert(bitCost <= scaleLog); optPtr->litFreq[lit] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; optPtr->litSum += optPtr->litFreq[lit]; } } From 18fc3d3cd5d48121e0a4ea4c2f5c46ac12c328af Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 16 May 2018 14:53:35 -0700 Subject: [PATCH 067/288] introduced bit-fractional cost evaluation this improves compression ratio by a *tiny* amount. It also reduces speed by a small amount. Consequently, bit-fractional evaluation is only turned on for btultra. --- lib/compress/zstd_compress_internal.h | 12 +- lib/compress/zstd_opt.c | 168 +++++++++++++++----------- 2 files changed, 104 insertions(+), 76 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index d9edfc31c..8e13aadd8 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -91,13 +91,11 @@ typedef struct { U32 litLengthSum; /* nb of litLength codes */ U32 matchLengthSum; /* nb of matchLength codes */ U32 offCodeSum; /* nb of offset codes */ - /* begin updated by ZSTD_setLog2Prices */ - U32 log2litSum; /* pow2 to compare log2(litfreq) to */ - U32 log2litLengthSum; /* pow2 to compare log2(llfreq) to */ - U32 log2matchLengthSum; /* pow2 to compare log2(mlfreq) to */ - U32 log2offCodeSum; /* pow2 to compare log2(offreq) to */ - /* end : updated by ZSTD_setLog2Prices */ - ZSTD_OptPrice_e priceType; /* prices can be determined dynamically, or follow dictionary statistics, or a pre-defined cost structure */ + U32 litSumBasePrice; /* to compare to log2(litfreq) */ + U32 litLengthSumBasePrice; /* to compare to log2(llfreq) */ + U32 matchLengthSumBasePrice;/* to compare to log2(mlfreq) */ + U32 offCodeSumBasePrice; /* to compare to log2(offreq) */ + ZSTD_OptPrice_e priceType; /* prices can be determined dynamically, or follow a pre-defined cost structure */ const ZSTD_entropyCTables_t* symbolCosts; /* pre-calculated dictionary statistics */ } optState_t; diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index fa4681611..274bce148 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -20,17 +20,55 @@ /*-************************************* * Price functions for optimal parser ***************************************/ -static void ZSTD_setLog2Prices(optState_t* optPtr) + +#if 0 /* approximation at bit level */ +# define BITCOST_ACCURACY 0 +# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) +# define WEIGHT(stat) ((void)opt, ZSTD_bitWeight(stat)) +#elif 0 /* fractional bit accuracy */ +# define BITCOST_ACCURACY 8 +# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) +# define WEIGHT(stat,opt) ((void)opt, ZSTD_fracWeight(stat)) +#else /* opt==approx, ultra==accurate */ +# define BITCOST_ACCURACY 8 +# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) +# define WEIGHT(stat,opt) (opt ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat) ) +#endif + +MEM_STATIC U32 ZSTD_bitWeight(U32 stat) { - optPtr->log2litSum = ZSTD_highbit32(optPtr->litSum+1); - optPtr->log2litLengthSum = ZSTD_highbit32(optPtr->litLengthSum+1); - optPtr->log2matchLengthSum = ZSTD_highbit32(optPtr->matchLengthSum+1); - optPtr->log2offCodeSum = ZSTD_highbit32(optPtr->offCodeSum+1); + return (ZSTD_highbit32((stat)+1) * BITCOST_MULTIPLIER); +} + +MEM_STATIC U32 ZSTD_fracWeight(U32 stat) +{ + U32 const hb = stat ? ZSTD_highbit32(stat) : 0; + U32 const BWeight = hb * BITCOST_MULTIPLIER; + U32 const FWeight = (stat << BITCOST_ACCURACY) >> hb; + U32 const weight = BWeight + FWeight; + assert(hb + BITCOST_ACCURACY < 31); + DEBUGLOG(2, "stat=%u, hb=%u, weight=%u", stat, hb, weight) + return weight; +} + +/* debugging function, @return price in bytes */ +MEM_STATIC double ZSTD_fCost(U32 price) +{ + return (double)price / (BITCOST_MULTIPLIER*8); +} + +static void ZSTD_setBasePrices(optState_t* optPtr, int optLevel) +{ + optPtr->litSumBasePrice = WEIGHT(optPtr->litSum, optLevel); + optPtr->litLengthSumBasePrice = WEIGHT(optPtr->litLengthSum, optLevel); + optPtr->matchLengthSumBasePrice = WEIGHT(optPtr->matchLengthSum, optLevel); + optPtr->offCodeSumBasePrice = WEIGHT(optPtr->offCodeSum, optLevel); } static void ZSTD_rescaleFreqs(optState_t* const optPtr, - const BYTE* const src, size_t const srcSize) + const BYTE* const src, size_t const srcSize, + int optLevel) { optPtr->priceType = zop_dynamic; @@ -103,20 +141,20 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, { unsigned ll; for (ll=0; ll<=MaxLL; ll++) optPtr->litLengthFreq[ll] = 1; - optPtr->litLengthSum = MaxLL+1; } + optPtr->litLengthSum = MaxLL+1; { unsigned ml; for (ml=0; ml<=MaxML; ml++) optPtr->matchLengthFreq[ml] = 1; - optPtr->matchLengthSum = MaxML+1; } + optPtr->matchLengthSum = MaxML+1; { unsigned of; for (of=0; of<=MaxOff; of++) optPtr->offCodeFreq[of] = 1; - optPtr->offCodeSum = MaxOff+1; } + optPtr->offCodeSum = MaxOff+1; } @@ -145,52 +183,37 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, } } - ZSTD_setLog2Prices(optPtr); -} - -#if 1 /* approximation at bit level */ -# define BITCOST_ACCURACY 0 -# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define BITCOST_SYMBOL(t,l,s) ((void)(l), FSE_getMaxNbBits(t,s) * BITCOST_MULTIPLIER) -#else /* fractional bit accuracy */ -# define BITCOST_ACCURACY 8 -# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define BITCOST_SYMBOL(t,l,s) FSE_bitCost(t,l,s,BITCOST_ACCURACY) -#endif - -MEM_STATIC double -ZSTD_fCost(U32 price) -{ - return (double)price / (BITCOST_MULTIPLIER*8); + ZSTD_setBasePrices(optPtr, optLevel); } /* ZSTD_rawLiteralsCost() : - * cost of literals (only) in specified segment (which length can be 0). - * does not include cost of literalLength symbol */ + * price of literals (only) in specified segment (which length can be 0). + * does not include price of literalLength symbol */ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, - const optState_t* const optPtr) + const optState_t* const optPtr, + int optLevel) { if (litLength == 0) return 0; - if (optPtr->priceType == zop_predef) return (litLength*6); /* 6 bit per literal - no statistic used */ + if (optPtr->priceType == zop_predef) return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */ /* dynamic statistics */ - { U32 u; - U32 cost = litLength * optPtr->log2litSum; + { U32 price = litLength * optPtr->litSumBasePrice; + U32 u; for (u=0; u < litLength; u++) - cost -= ZSTD_highbit32(optPtr->litFreq[literals[u]]+1); - return cost * BITCOST_MULTIPLIER; + price -= WEIGHT(optPtr->litFreq[literals[u]], optLevel); + return price; } } /* ZSTD_litLengthPrice() : * cost of literalLength symbol */ -static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr) +static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr, int optLevel) { - if (optPtr->priceType == zop_predef) return ZSTD_highbit32((U32)litLength+1); + if (optPtr->priceType == zop_predef) return WEIGHT(litLength, optLevel); /* dynamic statistics */ { U32 const llCode = ZSTD_LLcode(litLength); - return (LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1)) * BITCOST_MULTIPLIER; + return (LL_bits[llCode] * BITCOST_MULTIPLIER) + (optPtr->litLengthSumBasePrice - WEIGHT(optPtr->litLengthFreq[llCode], optLevel)); } } @@ -198,26 +221,26 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP * cost of the literal part of a sequence, * including literals themselves, and literalLength symbol */ static U32 ZSTD_fullLiteralsCost(const BYTE* const literals, U32 const litLength, - const optState_t* const optPtr) + const optState_t* const optPtr, + int optLevel) { - return ZSTD_rawLiteralsCost(literals, litLength, optPtr) - + ZSTD_litLengthPrice(litLength, optPtr); + return ZSTD_rawLiteralsCost(literals, litLength, optPtr, optLevel) + + ZSTD_litLengthPrice(litLength, optPtr, optLevel); } /* ZSTD_litLengthContribution() : * @return ( cost(litlength) - cost(0) ) * this value can then be added to rawLiteralsCost() * to provide a cost which is directly comparable to a match ending at same position */ -static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr) +static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr, int optLevel) { - if (optPtr->priceType >= zop_predef) return ZSTD_highbit32(litLength+1); + if (optPtr->priceType >= zop_predef) return WEIGHT(litLength, optLevel); /* dynamic statistics */ { U32 const llCode = ZSTD_LLcode(litLength); - int const contribution = (LL_bits[llCode] - + ZSTD_highbit32(optPtr->litLengthFreq[0]+1) /* note: log2litLengthSum cancels out with following one */ - - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1)) - * BITCOST_MULTIPLIER; + int const contribution = (LL_bits[llCode] * BITCOST_MULTIPLIER) + + WEIGHT(optPtr->litLengthFreq[0], optLevel) /* note: log2litLengthSum cancel out */ + - WEIGHT(optPtr->litLengthFreq[llCode], optLevel); #if 1 return contribution; #else @@ -231,10 +254,11 @@ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* con * which can be compared to the ending cost of a match * should a new match start at this position */ static int ZSTD_literalsContribution(const BYTE* const literals, U32 const litLength, - const optState_t* const optPtr) + const optState_t* const optPtr, + int optLevel) { - int const contribution = ZSTD_rawLiteralsCost(literals, litLength, optPtr) - + ZSTD_litLengthContribution(litLength, optPtr); + int const contribution = ZSTD_rawLiteralsCost(literals, litLength, optPtr, optLevel) + + ZSTD_litLengthContribution(litLength, optPtr, optLevel); return contribution; } @@ -243,7 +267,8 @@ static int ZSTD_literalsContribution(const BYTE* const literals, U32 const litLe * Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence. * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */ FORCE_INLINE_TEMPLATE U32 -ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, +ZSTD_getMatchPrice(U32 const offset, + U32 const matchLength, const optState_t* const optPtr, int const optLevel) { @@ -253,19 +278,20 @@ ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, assert(matchLength >= MINMATCH); if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */ - return ZSTD_highbit32(mlBase+1) + 16 + offCode; + return WEIGHT(mlBase, optLevel) + ((16 + offCode) * BITCOST_MULTIPLIER); /* dynamic statistics */ - price = offCode + optPtr->log2offCodeSum - ZSTD_highbit32(optPtr->offCodeFreq[offCode]+1); - if ((optLevel<2) /*static*/ && offCode >= 20) price += (offCode-19)*2; /* handicap for long distance offsets, favor decompression speed */ + price = (offCode * BITCOST_MULTIPLIER) + (optPtr->offCodeSumBasePrice - WEIGHT(optPtr->offCodeFreq[offCode], optLevel)); + if ((optLevel<2) /*static*/ && offCode >= 20) + price += (offCode-19)*2 * BITCOST_MULTIPLIER; /* handicap for long distance offsets, favor decompression speed */ /* match Length */ { U32 const mlCode = ZSTD_MLcode(mlBase); - price += ML_bits[mlCode] + optPtr->log2matchLengthSum - ZSTD_highbit32(optPtr->matchLengthFreq[mlCode]+1); + price += (ML_bits[mlCode] * BITCOST_MULTIPLIER) + (optPtr->matchLengthSumBasePrice - WEIGHT(optPtr->matchLengthFreq[mlCode], optLevel)); } DEBUGLOG(8, "ZSTD_getMatchPrice(ml:%u) = %u", matchLength, price); - return price * BITCOST_MULTIPLIER; + return price; } static void ZSTD_updateStats(optState_t* const optPtr, @@ -695,7 +721,8 @@ typedef struct { static U32 ZSTD_rawLiteralsCost_cached( cachedLiteralPrice_t* const cachedLitPrice, const BYTE* const anchor, U32 const litlen, - const optState_t* const optStatePtr) + const optState_t* const optStatePtr, + int optLevel) { U32 startCost; U32 remainingLength; @@ -712,7 +739,7 @@ static U32 ZSTD_rawLiteralsCost_cached( remainingLength = litlen; } - { U32 const rawLitCost = startCost + ZSTD_rawLiteralsCost(startPosition, remainingLength, optStatePtr); + { U32 const rawLitCost = startCost + ZSTD_rawLiteralsCost(startPosition, remainingLength, optStatePtr, optLevel); cachedLitPrice->anchor = anchor; cachedLitPrice->litlen = litlen; cachedLitPrice->rawLitCost = rawLitCost; @@ -723,19 +750,21 @@ static U32 ZSTD_rawLiteralsCost_cached( static U32 ZSTD_fullLiteralsCost_cached( cachedLiteralPrice_t* const cachedLitPrice, const BYTE* const anchor, U32 const litlen, - const optState_t* const optStatePtr) + const optState_t* const optStatePtr, + int optLevel) { - return ZSTD_rawLiteralsCost_cached(cachedLitPrice, anchor, litlen, optStatePtr) - + ZSTD_litLengthPrice(litlen, optStatePtr); + return ZSTD_rawLiteralsCost_cached(cachedLitPrice, anchor, litlen, optStatePtr, optLevel) + + ZSTD_litLengthPrice(litlen, optStatePtr, optLevel); } static int ZSTD_literalsContribution_cached( cachedLiteralPrice_t* const cachedLitPrice, const BYTE* const anchor, U32 const litlen, - const optState_t* const optStatePtr) + const optState_t* const optStatePtr, + int optLevel) { - int const contribution = ZSTD_rawLiteralsCost_cached(cachedLitPrice, anchor, litlen, optStatePtr) - + ZSTD_litLengthContribution(litlen, optStatePtr); + int const contribution = ZSTD_rawLiteralsCost_cached(cachedLitPrice, anchor, litlen, optStatePtr, optLevel) + + ZSTD_litLengthContribution(litlen, optStatePtr, optLevel); return contribution; } @@ -765,8 +794,9 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, /* init */ DEBUGLOG(5, "ZSTD_compressBlock_opt_generic"); + assert(optLevel <= 2); ms->nextToUpdate3 = ms->nextToUpdate; - ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize); + ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize, optLevel); ip += (ip==prefixStart); memset(&cachedLitPrice, 0, sizeof(cachedLitPrice)); @@ -803,7 +833,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, } } /* set prices for first matches starting position == 0 */ - { U32 const literalsPrice = ZSTD_fullLiteralsCost_cached(&cachedLitPrice, anchor, litlen, optStatePtr); + { U32 const literalsPrice = ZSTD_fullLiteralsCost_cached(&cachedLitPrice, anchor, litlen, optStatePtr, optLevel); U32 pos; U32 matchNb; for (pos = 1; pos < minMatch; pos++) { @@ -838,9 +868,9 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, { U32 const litlen = (opt[cur-1].mlen == 1) ? opt[cur-1].litlen + 1 : 1; int price; /* note : contribution can be negative */ if (cur > litlen) { - price = opt[cur - litlen].price + ZSTD_literalsContribution(inr-litlen, litlen, optStatePtr); + price = opt[cur - litlen].price + ZSTD_literalsContribution(inr-litlen, litlen, optStatePtr, optLevel); } else { - price = ZSTD_literalsContribution_cached(&cachedLitPrice, anchor, litlen, optStatePtr); + price = ZSTD_literalsContribution_cached(&cachedLitPrice, anchor, litlen, optStatePtr, optLevel); } assert(price < 1000000000); /* overflow check */ if (price <= opt[cur].price) { @@ -871,7 +901,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, { U32 const ll0 = (opt[cur].mlen != 1); U32 const litlen = (opt[cur].mlen == 1) ? opt[cur].litlen : 0; U32 const previousPrice = (cur > litlen) ? opt[cur-litlen].price : 0; - U32 const basePrice = previousPrice + ZSTD_fullLiteralsCost(inr-litlen, litlen, optStatePtr); + U32 const basePrice = previousPrice + ZSTD_fullLiteralsCost(inr-litlen, litlen, optStatePtr, optLevel); U32 const nbMatches = ZSTD_BtGetAllMatches(ms, cParams, inr, iend, extDict, opt[cur].rep, ll0, matches, minMatch); U32 matchNb; if (!nbMatches) { @@ -973,7 +1003,7 @@ _shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */ ZSTD_storeSeq(seqStore, llen, anchor, offset, mlen-MINMATCH); anchor = ip; } } - ZSTD_setLog2Prices(optStatePtr); + ZSTD_setBasePrices(optStatePtr, optLevel); } /* while (ip < ilimit) */ /* Return the last literals size */ From 63eeeaa1dd60022e657c95317bdd15aeae4436d9 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 16 May 2018 16:13:37 -0700 Subject: [PATCH 068/288] update table levels for blocks <= 16K also : allow hlog to be slighly larger than windowlog, as it's apparently good for both speed and compression ratio. --- lib/compress/zstd_compress.c | 58 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 622b069be..d4e3462d2 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -743,7 +743,7 @@ ZSTD_compressionParameters ZSTD_adjustCParams_internal(ZSTD_compressionParameter ZSTD_highbit32(tSize-1) + 1; if (cPar.windowLog > srcLog) cPar.windowLog = srcLog; } - if (cPar.hashLog > cPar.windowLog) cPar.hashLog = cPar.windowLog; + if (cPar.hashLog > cPar.windowLog+1) cPar.hashLog = cPar.windowLog+1; { U32 const cycleLog = ZSTD_cycleLog(cPar.chainLog, cPar.strategy); if (cycleLog > cPar.windowLog) cPar.chainLog -= (cycleLog - cPar.windowLog); @@ -3422,11 +3422,11 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 19, 12, 13, 1, 6, 1, ZSTD_fast }, /* base for negative levels */ { 19, 13, 14, 1, 7, 1, ZSTD_fast }, /* level 1 */ { 19, 15, 16, 1, 6, 1, ZSTD_fast }, /* level 2 */ - { 20, 16, 17, 1, 5, 8, ZSTD_dfast }, /* level 3 */ - { 20, 18, 18, 1, 5, 8, ZSTD_dfast }, /* level 4 */ - { 20, 18, 18, 2, 5, 16, ZSTD_greedy }, /* level 5 */ - { 21, 18, 19, 2, 5, 16, ZSTD_lazy }, /* level 6 */ - { 21, 18, 19, 3, 5, 16, ZSTD_lazy2 }, /* level 7 */ + { 20, 16, 17, 1, 5, 1, ZSTD_dfast }, /* level 3 */ + { 20, 18, 18, 1, 5, 1, ZSTD_dfast }, /* level 4 */ + { 20, 18, 18, 2, 5, 2, ZSTD_greedy }, /* level 5 */ + { 21, 18, 19, 2, 5, 4, ZSTD_lazy }, /* level 6 */ + { 21, 18, 19, 3, 5, 8, ZSTD_lazy2 }, /* level 7 */ { 21, 19, 19, 3, 5, 16, ZSTD_lazy2 }, /* level 8 */ { 21, 19, 20, 4, 5, 16, ZSTD_lazy2 }, /* level 9 */ { 21, 20, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */ @@ -3447,12 +3447,12 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV /* W, C, H, S, L, T, strat */ { 18, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ { 18, 13, 14, 1, 6, 1, ZSTD_fast }, /* level 1 */ - { 18, 14, 13, 1, 5, 8, ZSTD_dfast }, /* level 2 */ - { 18, 16, 15, 1, 5, 8, ZSTD_dfast }, /* level 3 */ - { 18, 15, 17, 1, 5, 8, ZSTD_greedy }, /* level 4.*/ - { 18, 16, 17, 4, 5, 8, ZSTD_greedy }, /* level 5.*/ - { 18, 16, 17, 3, 5, 8, ZSTD_lazy }, /* level 6.*/ - { 18, 17, 17, 4, 4, 8, ZSTD_lazy }, /* level 7 */ + { 18, 14, 13, 1, 5, 1, ZSTD_dfast }, /* level 2 */ + { 18, 16, 15, 1, 5, 1, ZSTD_dfast }, /* level 3 */ + { 18, 15, 17, 1, 5, 2, ZSTD_greedy }, /* level 4.*/ + { 18, 16, 17, 4, 5, 2, ZSTD_greedy }, /* level 5.*/ + { 18, 16, 17, 3, 5, 4, ZSTD_lazy }, /* level 6.*/ + { 18, 17, 17, 4, 4, 4, ZSTD_lazy }, /* level 7 */ { 18, 17, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ { 18, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ { 18, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ @@ -3474,10 +3474,10 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 17, 12, 12, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ { 17, 12, 13, 1, 6, 1, ZSTD_fast }, /* level 1 */ { 17, 13, 15, 1, 5, 1, ZSTD_fast }, /* level 2 */ - { 17, 15, 16, 2, 5, 8, ZSTD_dfast }, /* level 3 */ - { 17, 17, 17, 2, 4, 8, ZSTD_dfast }, /* level 4 */ - { 17, 16, 17, 3, 4, 8, ZSTD_greedy }, /* level 5 */ - { 17, 17, 17, 3, 4, 8, ZSTD_lazy }, /* level 6 */ + { 17, 15, 16, 2, 5, 1, ZSTD_dfast }, /* level 3 */ + { 17, 17, 17, 2, 4, 1, ZSTD_dfast }, /* level 4 */ + { 17, 16, 17, 3, 4, 2, ZSTD_greedy }, /* level 5 */ + { 17, 17, 17, 3, 4, 4, ZSTD_lazy }, /* level 6 */ { 17, 17, 17, 3, 4, 8, ZSTD_lazy2 }, /* level 7 */ { 17, 17, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ { 17, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ @@ -3498,25 +3498,25 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { /* for srcSize <= 16 KB */ /* W, C, H, S, L, T, strat */ { 14, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ - { 14, 14, 14, 1, 6, 1, ZSTD_fast }, /* level 1 */ - { 14, 14, 14, 1, 4, 1, ZSTD_fast }, /* level 2 */ - { 14, 14, 14, 1, 4, 6, ZSTD_dfast }, /* level 3.*/ - { 14, 14, 14, 4, 4, 6, ZSTD_greedy }, /* level 4.*/ - { 14, 14, 14, 3, 4, 6, ZSTD_lazy }, /* level 5.*/ - { 14, 14, 14, 4, 4, 6, ZSTD_lazy2 }, /* level 6 */ - { 14, 14, 14, 5, 4, 6, ZSTD_lazy2 }, /* level 7 */ - { 14, 14, 14, 6, 4, 6, ZSTD_lazy2 }, /* level 8.*/ - { 14, 15, 14, 6, 4, 6, ZSTD_btlazy2 }, /* level 9.*/ - { 14, 15, 14, 3, 3, 6, ZSTD_btopt }, /* level 10.*/ - { 14, 15, 14, 6, 3, 8, ZSTD_btopt }, /* level 11.*/ + { 14, 14, 15, 1, 5, 1, ZSTD_fast }, /* level 1 */ + { 14, 14, 15, 1, 4, 1, ZSTD_fast }, /* level 2 */ + { 14, 14, 14, 2, 4, 1, ZSTD_dfast }, /* level 3.*/ + { 14, 14, 14, 4, 4, 2, ZSTD_greedy }, /* level 4.*/ + { 14, 14, 14, 3, 4, 4, ZSTD_lazy }, /* level 5.*/ + { 14, 14, 14, 4, 4, 8, ZSTD_lazy2 }, /* level 6 */ + { 14, 14, 14, 6, 4, 8, ZSTD_lazy2 }, /* level 7 */ + { 14, 14, 14, 8, 4, 8, ZSTD_lazy2 }, /* level 8.*/ + { 14, 15, 14, 5, 4, 8, ZSTD_btlazy2 }, /* level 9.*/ + { 14, 15, 14, 9, 4, 8, ZSTD_btlazy2 }, /* level 10.*/ + { 14, 15, 14, 3, 4, 12, ZSTD_btopt }, /* level 11.*/ { 14, 15, 14, 6, 3, 16, ZSTD_btopt }, /* level 12.*/ { 14, 15, 14, 6, 3, 24, ZSTD_btopt }, /* level 13.*/ { 14, 15, 15, 6, 3, 48, ZSTD_btopt }, /* level 14.*/ { 14, 15, 15, 6, 3, 64, ZSTD_btopt }, /* level 15.*/ { 14, 15, 15, 6, 3, 96, ZSTD_btopt }, /* level 16.*/ { 14, 15, 15, 6, 3,128, ZSTD_btopt }, /* level 17.*/ - { 14, 15, 15, 6, 3,256, ZSTD_btopt }, /* level 18.*/ - { 14, 15, 15, 7, 3,256, ZSTD_btopt }, /* level 19.*/ + { 14, 15, 15, 7, 3,256, ZSTD_btopt }, /* level 18.*/ + { 14, 15, 15, 6, 3,256, ZSTD_btultra }, /* level 19.*/ { 14, 15, 15, 8, 3,256, ZSTD_btultra }, /* level 20.*/ { 14, 15, 15, 9, 3,256, ZSTD_btultra }, /* level 21.*/ { 14, 15, 15, 10, 3,256, ZSTD_btultra }, /* level 22.*/ From a243020d3785432b514beff05f2096d0ff72f0ca Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 17 May 2018 11:19:05 -0700 Subject: [PATCH 069/288] slightly improved weight calculation translating into a tiny compression ratio improvement --- lib/compress/zstd_compress.c | 2 +- lib/compress/zstd_compress_internal.h | 14 ++++++++------ lib/compress/zstd_opt.c | 14 +++++++------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d4191afa1..1b4882841 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1977,7 +1977,7 @@ static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr, seqStorePtr->lit += lastLLSize; } -static void ZSTD_resetSeqStore(seqStore_t* ssPtr) +void ZSTD_resetSeqStore(seqStore_t* ssPtr) { ssPtr->lit = ssPtr->litStart; ssPtr->sequences = ssPtr->sequencesStart; diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 8e13aadd8..18fa87fe5 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -80,12 +80,12 @@ typedef enum { zop_dynamic=0, zop_predef } ZSTD_OptPrice_e; typedef struct { /* All tables are allocated inside cctx->workspace by ZSTD_resetCCtx_internal() */ - U32* litFreq; /* table of literals statistics, of size 256 */ - U32* litLengthFreq; /* table of litLength statistics, of size (MaxLL+1) */ - U32* matchLengthFreq; /* table of matchLength statistics, of size (MaxML+1) */ - U32* offCodeFreq; /* table of offCode statistics, of size (MaxOff+1) */ - ZSTD_match_t* matchTable; /* list of found matches, of size ZSTD_OPT_NUM+1 */ - ZSTD_optimal_t* priceTable; /* All positions tracked by optimal parser, of size ZSTD_OPT_NUM+1 */ + U32* litFreq; /* table of literals statistics, of size 256 */ + U32* litLengthFreq; /* table of litLength statistics, of size (MaxLL+1) */ + U32* matchLengthFreq; /* table of matchLength statistics, of size (MaxML+1) */ + U32* offCodeFreq; /* table of offCode statistics, of size (MaxOff+1) */ + ZSTD_match_t* matchTable; /* list of found matches, of size ZSTD_OPT_NUM+1 */ + ZSTD_optimal_t* priceTable; /* All positions tracked by optimal parser, of size ZSTD_OPT_NUM+1 */ U32 litSum; /* nb of literals */ U32 litLengthSum; /* nb of litLength codes */ @@ -658,6 +658,8 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize); +void ZSTD_resetSeqStore(seqStore_t* ssPtr); + /*! ZSTD_compressStream_generic() : * Private use only. To be called from zstdmt_compress.c in single-thread mode. */ size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 274bce148..827fb49a4 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -25,29 +25,29 @@ # define BITCOST_ACCURACY 0 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) # define WEIGHT(stat) ((void)opt, ZSTD_bitWeight(stat)) -#elif 0 /* fractional bit accuracy */ +#elif 1 /* fractional bit accuracy */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) # define WEIGHT(stat,opt) ((void)opt, ZSTD_fracWeight(stat)) -#else /* opt==approx, ultra==accurate */ +#else /* opt==approx, ultra==accurate */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define WEIGHT(stat,opt) (opt ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat) ) +# define WEIGHT(stat,opt) (opt ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat)) #endif MEM_STATIC U32 ZSTD_bitWeight(U32 stat) { - return (ZSTD_highbit32((stat)+1) * BITCOST_MULTIPLIER); + return (ZSTD_highbit32(stat+1) * BITCOST_MULTIPLIER); } -MEM_STATIC U32 ZSTD_fracWeight(U32 stat) +MEM_STATIC U32 ZSTD_fracWeight(U32 rawStat) { - U32 const hb = stat ? ZSTD_highbit32(stat) : 0; + U32 const stat = rawStat + 1; + U32 const hb = ZSTD_highbit32(stat); U32 const BWeight = hb * BITCOST_MULTIPLIER; U32 const FWeight = (stat << BITCOST_ACCURACY) >> hb; U32 const weight = BWeight + FWeight; assert(hb + BITCOST_ACCURACY < 31); - DEBUGLOG(2, "stat=%u, hb=%u, weight=%u", stat, hb, weight) return weight; } From 134388ba6bea15bc085e1a260245bff507e41530 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 17 May 2018 12:19:37 -0700 Subject: [PATCH 070/288] collect statistics for first block in ultra mode this patch makes btultra do 2 passes on the first block, the first one being dedicated to collecting statistics so that the 2nd pass is more accurate. It translates into a very small compression ratio gain : enwik7, level 20: blocks 4K : 2.142 -> 2.153 blocks 16K : 2.447 -> 2.457 blocks 64K : 2.716 -> 2.726 On the other hand, the cpu cost is doubled. The trade off looks bad. Though, that's ultimately a price to pay to reach better compression ratio. So it's only enabled when setting btultra. --- lib/compress/zstd_opt.c | 88 ++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 827fb49a4..df4124b62 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -25,7 +25,7 @@ # define BITCOST_ACCURACY 0 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) # define WEIGHT(stat) ((void)opt, ZSTD_bitWeight(stat)) -#elif 1 /* fractional bit accuracy */ +#elif 0 /* fractional bit accuracy */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) # define WEIGHT(stat,opt) ((void)opt, ZSTD_fracWeight(stat)) @@ -66,6 +66,17 @@ static void ZSTD_setBasePrices(optState_t* optPtr, int optLevel) } +static U32 ZSTD_downscaleStat(U32* table, U32 lastEltIndex, int malus) +{ + U32 s, sum=0; + assert(ZSTD_FREQ_DIV+malus > 0 && ZSTD_FREQ_DIV+malus < 31); + for (s=0; s<=lastEltIndex; s++) { + table[s] = 1 + (table[s] >> (ZSTD_FREQ_DIV+malus)); + sum += table[s]; + } + return sum; +} + static void ZSTD_rescaleFreqs(optState_t* const optPtr, const BYTE* const src, size_t const srcSize, int optLevel) @@ -132,11 +143,8 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, assert(optPtr->litFreq != NULL); { unsigned lit = MaxLit; FSE_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */ - optPtr->litSum = 0; - for (lit=0; lit<=MaxLit; lit++) { - optPtr->litFreq[lit] = 1 + (optPtr->litFreq[lit] >> (ZSTD_FREQ_DIV+1)); - optPtr->litSum += optPtr->litFreq[lit]; - } } + } + optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1); { unsigned ll; for (ll=0; ll<=MaxLL; ll++) @@ -159,28 +167,11 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, } } else { /* new block : re-use previous statistics, scaled down */ - unsigned u; - optPtr->litSum = 0; - for (u=0; u<=MaxLit; u++) { - optPtr->litFreq[u] = 1 + (optPtr->litFreq[u] >> (ZSTD_FREQ_DIV+1)); - optPtr->litSum += optPtr->litFreq[u]; - } - optPtr->litLengthSum = 0; - for (u=0; u<=MaxLL; u++) { - optPtr->litLengthFreq[u] = 1 + (optPtr->litLengthFreq[u] >> ZSTD_FREQ_DIV); - optPtr->litLengthSum += optPtr->litLengthFreq[u]; - } - optPtr->matchLengthSum = 0; - for (u=0; u<=MaxML; u++) { - optPtr->matchLengthFreq[u] = 1 + (optPtr->matchLengthFreq[u] >> ZSTD_FREQ_DIV); - optPtr->matchLengthSum += optPtr->matchLengthFreq[u]; - } - optPtr->offCodeSum = 0; - for (u=0; u<=MaxOff; u++) { - optPtr->offCodeFreq[u] = 1 + (optPtr->offCodeFreq[u] >> ZSTD_FREQ_DIV); - optPtr->offCodeSum += optPtr->offCodeFreq[u]; - } + optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1); + optPtr->litLengthSum = ZSTD_downscaleStat(optPtr->litLengthFreq, MaxLL, 0); + optPtr->matchLengthSum = ZSTD_downscaleStat(optPtr->matchLengthFreq, MaxML, 0); + optPtr->offCodeSum = ZSTD_downscaleStat(optPtr->offCodeFreq, MaxOff, 0); } ZSTD_setBasePrices(optPtr, optLevel); @@ -194,7 +185,8 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, int optLevel) { if (litLength == 0) return 0; - if (optPtr->priceType == zop_predef) return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */ + if (optPtr->priceType == zop_predef) + return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */ /* dynamic statistics */ { U32 price = litLength * optPtr->litSumBasePrice; @@ -1013,29 +1005,61 @@ _shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */ size_t ZSTD_compressBlock_btopt( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) + const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock_btopt"); return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, 0 /*extDict*/); } + +static U32 ZSTD_upscaleStat(U32* table, U32 lastEltIndex, int bonus) +{ + U32 s, sum=0; + assert(ZSTD_FREQ_DIV+bonus > 0); + for (s=0; s<=lastEltIndex; s++) { + table[s] <<= ZSTD_FREQ_DIV+bonus; + table[s]--; + sum += table[s]; + } + return sum; +} + +static void ZSTD_upscaleStats(optState_t* optPtr) +{ + optPtr->litSum = ZSTD_upscaleStat(optPtr->litFreq, MaxLit, 0); + optPtr->litLengthSum = ZSTD_upscaleStat(optPtr->litLengthFreq, MaxLL, 1); + optPtr->matchLengthSum = ZSTD_upscaleStat(optPtr->matchLengthFreq, MaxML, 1); + optPtr->offCodeSum = ZSTD_upscaleStat(optPtr->offCodeFreq, MaxOff, 1); +} + size_t ZSTD_compressBlock_btultra( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) + const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) { + if (ms->opt.litLengthSum==0) { /* first block */ + U32 tmpRep[ZSTD_REP_NUM]; + assert(ms->nextToUpdate >= ms->window.dictLimit + && ms->nextToUpdate <= ms->window.dictLimit + 1); + memcpy(tmpRep, rep, sizeof(tmpRep)); + ZSTD_compressBlock_opt_generic(ms, seqStore, tmpRep, cParams, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/); /* generate stats into ms->opt*/ + ZSTD_resetSeqStore(seqStore); + ZSTD_window_update(&ms->window, src, srcSize); /* invalidate first scan from history, since it overlaps perfectly */ + ms->nextToUpdate = ms->window.dictLimit; + ZSTD_upscaleStats(&ms->opt); + } return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/); } size_t ZSTD_compressBlock_btopt_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) + const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) { return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, 1 /*extDict*/); } size_t ZSTD_compressBlock_btultra_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) + const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) { return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, 1 /*extDict*/); } From 8572b4d09fcce71b5f8eb69e9986f124a414d8c8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 17 May 2018 16:13:53 -0700 Subject: [PATCH 071/288] fixed a pretty complex bug when combining ldm + btultra --- lib/compress/zstd_compress_internal.h | 10 ++++++---- lib/compress/zstd_ldm.c | 10 +++------- lib/compress/zstd_opt.c | 19 +++++++++++++++---- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 18fa87fe5..92dd18240 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -580,12 +580,13 @@ MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window, { U32 const current = (U32)((BYTE const*)srcEnd - window->base); U32 loadedDictEnd = loadedDictEndPtr != NULL ? *loadedDictEndPtr : 0; + DEBUGLOG(5, "ZSTD_window_enforceMaxDist: current=%u, maxDist=%u", current, maxDist); if (current > maxDist + loadedDictEnd) { U32 const newLowLimit = current - maxDist; if (window->lowLimit < newLowLimit) window->lowLimit = newLowLimit; if (window->dictLimit < window->lowLimit) { - DEBUGLOG(5, "Update dictLimit from %u to %u", window->dictLimit, - window->lowLimit); + DEBUGLOG(5, "Update dictLimit to match lowLimit, from %u to %u", + window->dictLimit, window->lowLimit); window->dictLimit = window->lowLimit; } if (loadedDictEndPtr) @@ -605,12 +606,12 @@ MEM_STATIC U32 ZSTD_window_update(ZSTD_window_t* window, { BYTE const* const ip = (BYTE const*)src; U32 contiguous = 1; + DEBUGLOG(5, "ZSTD_window_update"); /* Check if blocks follow each other */ if (src != window->nextSrc) { /* not contiguous */ size_t const distanceFromBase = (size_t)(window->nextSrc - window->base); - DEBUGLOG(5, "Non contiguous blocks, new segment starts at %u", - window->dictLimit); + DEBUGLOG(5, "Non contiguous blocks, new segment starts at %u", window->dictLimit); window->lowLimit = window->dictLimit; assert(distanceFromBase == (size_t)(U32)distanceFromBase); /* should never overflow */ window->dictLimit = (U32)distanceFromBase; @@ -627,6 +628,7 @@ MEM_STATIC U32 ZSTD_window_update(ZSTD_window_t* window, ptrdiff_t const highInputIdx = (ip + srcSize) - window->dictBase; U32 const lowLimitMax = (highInputIdx > (ptrdiff_t)window->dictLimit) ? window->dictLimit : (U32)highInputIdx; window->lowLimit = lowLimitMax; + DEBUGLOG(5, "Overlapping extDict and input : new lowLimit = %u", window->lowLimit); } return contiguous; } diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index 9d825e695..03348f177 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -225,12 +225,10 @@ static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms, { case ZSTD_fast: ZSTD_fillHashTable(ms, cParams, iend, ZSTD_dtlm_fast); - ms->nextToUpdate = (U32)(iend - ms->window.base); break; case ZSTD_dfast: ZSTD_fillDoubleHashTable(ms, cParams, iend, ZSTD_dtlm_fast); - ms->nextToUpdate = (U32)(iend - ms->window.base); break; case ZSTD_greedy: @@ -597,13 +595,13 @@ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, unsigned const minMatch = cParams->searchLength; ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(cParams->strategy, extDict); - BYTE const* const base = ms->window.base; /* Input bounds */ BYTE const* const istart = (BYTE const*)src; BYTE const* const iend = istart + srcSize; /* Input positions */ BYTE const* ip = istart; + DEBUGLOG(5, "ZSTD_ldm_blockCompress: srcSize=%zu", srcSize); assert(rawSeqStore->pos <= rawSeqStore->size); assert(rawSeqStore->size <= rawSeqStore->capacity); /* Loop through each sequence and apply the block compressor to the lits */ @@ -623,12 +621,12 @@ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_ldm_limitTableUpdate(ms, ip); ZSTD_ldm_fillFastTables(ms, cParams, ip); /* Run the block compressor */ + DEBUGLOG(5, "calling block compressor on segment of size %u", sequence.litLength); { size_t const newLitLength = blockCompressor(ms, seqStore, rep, cParams, ip, sequence.litLength); ip += sequence.litLength; - ms->nextToUpdate = (U32)(ip - base); /* Update the repcodes */ for (i = ZSTD_REP_NUM - 1; i > 0; i--) rep[i] = rep[i-1]; @@ -644,10 +642,8 @@ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_ldm_limitTableUpdate(ms, ip); ZSTD_ldm_fillFastTables(ms, cParams, ip); /* Compress the last literals */ - { - size_t const lastLiterals = blockCompressor(ms, seqStore, rep, cParams, + { size_t const lastLiterals = blockCompressor(ms, seqStore, rep, cParams, ip, iend - ip); - ms->nextToUpdate = (U32)(iend - base); return lastLiterals; } } diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index df4124b62..0a1c4c718 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -481,7 +481,7 @@ void ZSTD_updateTree_internal( const BYTE* const base = ms->window.base; U32 const target = (U32)(ip - base); U32 idx = ms->nextToUpdate; - DEBUGLOG(8, "ZSTD_updateTree_internal, from %u to %u (extDict:%u)", + DEBUGLOG(5, "ZSTD_updateTree_internal, from %u to %u (extDict:%u)", idx, target, extDict); while(idx < target) @@ -529,7 +529,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( U32 nbCompares = 1U << cParams->searchLog; size_t bestLength = lengthToBeat-1; - DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches"); + DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches: current=%u", current); /* check repCode */ { U32 const lastR = ZSTD_REP_NUM + ll0; @@ -1036,17 +1036,28 @@ size_t ZSTD_compressBlock_btultra( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) { - if (ms->opt.litLengthSum==0) { /* first block */ + DEBUGLOG(5, "ZSTD_compressBlock_btultra (srcSize=%zu)", srcSize); + assert(srcSize <= ZSTD_BLOCKSIZE_MAX); + if ( (ms->opt.litLengthSum==0) /* first block */ + && (seqStore->sequences == seqStore->sequencesStart) /* no ldm */ + && (ms->window.dictLimit == ms->window.lowLimit) ) { /* no dictionary */ U32 tmpRep[ZSTD_REP_NUM]; + DEBUGLOG(5, "ZSTD_compressBlock_btultra: first block: collecting statistics"); assert(ms->nextToUpdate >= ms->window.dictLimit && ms->nextToUpdate <= ms->window.dictLimit + 1); memcpy(tmpRep, rep, sizeof(tmpRep)); ZSTD_compressBlock_opt_generic(ms, seqStore, tmpRep, cParams, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/); /* generate stats into ms->opt*/ ZSTD_resetSeqStore(seqStore); - ZSTD_window_update(&ms->window, src, srcSize); /* invalidate first scan from history, since it overlaps perfectly */ + /* invalidate first scan from history */ + ms->window.base -= srcSize; + ms->window.dictLimit += srcSize; + ms->window.lowLimit = ms->window.dictLimit; ms->nextToUpdate = ms->window.dictLimit; + ms->nextToUpdate3 = ms->window.dictLimit; + /* re-inforce weight of collected statistics */ ZSTD_upscaleStats(&ms->opt); } + DEBUGLOG(5, "base=%p, src=%p, src-base=%zi", ms->window.base, src, (const BYTE*)src - (const BYTE*)ms->window.base); return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/); } From af3da079d19a337e22fed9bb7b9dbeb4ca689e3a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 17 May 2018 17:27:27 -0700 Subject: [PATCH 072/288] fixed minor conversion warning --- lib/compress/zstd_opt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 0a1c4c718..9288e0ac8 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -1050,7 +1050,7 @@ size_t ZSTD_compressBlock_btultra( ZSTD_resetSeqStore(seqStore); /* invalidate first scan from history */ ms->window.base -= srcSize; - ms->window.dictLimit += srcSize; + ms->window.dictLimit += (U32)srcSize; ms->window.lowLimit = ms->window.dictLimit; ms->nextToUpdate = ms->window.dictLimit; ms->nextToUpdate3 = ms->window.dictLimit; From 16bb8f1f9e8ec952458be9681a125d29627cdde9 Mon Sep 17 00:00:00 2001 From: fbrosson Date: Fri, 18 May 2018 17:05:36 +0000 Subject: [PATCH 073/288] Drop colon in asm snippet to make old versions of gcc happy. --- lib/common/cpu.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/common/cpu.h b/lib/common/cpu.h index 4eb48e39e..88e0ebf44 100644 --- a/lib/common/cpu.h +++ b/lib/common/cpu.h @@ -72,8 +72,7 @@ MEM_STATIC ZSTD_cpuid_t ZSTD_cpuid(void) { "cpuid\n\t" "popl %%ebx\n\t" : "=a"(f1a), "=c"(f1c), "=d"(f1d) - : "a"(1) - :); + : "a"(1)); } if (n >= 7) { __asm__( From 291824f49dbe50a4812c204994ae288c4c43979a Mon Sep 17 00:00:00 2001 From: fbrosson Date: Fri, 18 May 2018 18:40:11 +0000 Subject: [PATCH 074/288] __builtin_prefetch did probably not exist before gcc 3.1. --- lib/common/compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/compiler.h b/lib/common/compiler.h index b588e1104..366ed2b4b 100644 --- a/lib/common/compiler.h +++ b/lib/common/compiler.h @@ -92,7 +92,7 @@ #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__) +#elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) ) # define PREFETCH(ptr) __builtin_prefetch(ptr, 0, 0) #else # define PREFETCH(ptr) /* disabled */ From a95e9e80d17b43e50be5cad40d760613ed42514c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 18 May 2018 14:09:42 -0700 Subject: [PATCH 075/288] adding some debug functions to observe statistics --- lib/compress/zstd_compress.c | 51 ++++++++++++++------------- lib/compress/zstd_compress_internal.h | 27 ++++++++++++++ lib/compress/zstd_opt.c | 1 - 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 1b4882841..88c81533c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1561,15 +1561,16 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) mlCodeTable[seqStorePtr->longLengthPos] = MaxML; } + typedef enum { ZSTD_defaultDisallowed = 0, ZSTD_defaultAllowed = 1 } ZSTD_defaultPolicy_e; -MEM_STATIC -symbolEncodingType_e ZSTD_selectEncodingType( - FSE_repeat* repeatMode, size_t const mostFrequent, size_t nbSeq, - U32 defaultNormLog, ZSTD_defaultPolicy_e const isDefaultAllowed) +MEM_STATIC symbolEncodingType_e +ZSTD_selectEncodingType( + FSE_repeat* repeatMode, size_t const mostFrequent, size_t nbSeq, + U32 defaultNormLog, ZSTD_defaultPolicy_e const isDefaultAllowed) { #define MIN_SEQ_FOR_DYNAMIC_FSE 64 #define MAX_SEQ_FOR_STATIC_FSE 1000 @@ -1605,17 +1606,17 @@ symbolEncodingType_e ZSTD_selectEncodingType( return set_compressed; } -MEM_STATIC -size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, - FSE_CTable* nextCTable, U32 FSELog, symbolEncodingType_e type, - U32* count, U32 max, - BYTE const* codeTable, size_t nbSeq, - S16 const* defaultNorm, U32 defaultNormLog, U32 defaultMax, - FSE_CTable const* prevCTable, size_t prevCTableSize, - void* workspace, size_t workspaceSize) +MEM_STATIC size_t +ZSTD_buildCTable(void* dst, size_t dstCapacity, + FSE_CTable* nextCTable, U32 FSELog, symbolEncodingType_e type, + U32* count, U32 max, + const BYTE* codeTable, size_t nbSeq, + const S16* defaultNorm, U32 defaultNormLog, U32 defaultMax, + const FSE_CTable* prevCTable, size_t prevCTableSize, + void* workspace, size_t workspaceSize) { BYTE* op = (BYTE*)dst; - BYTE const* const oend = op + dstCapacity; + const BYTE* const oend = op + dstCapacity; switch (type) { case set_rle: @@ -1865,9 +1866,9 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode; LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog, ZSTD_defaultAllowed); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype, - count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, - prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable), - workspace, HUF_WORKSPACE_SIZE); + count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, + prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable), + workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; } } @@ -1880,9 +1881,9 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode; Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog, defaultPolicy); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, - count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, - prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable), - workspace, HUF_WORKSPACE_SIZE); + count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, + prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable), + workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; } } @@ -1893,9 +1894,9 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode; MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog, ZSTD_defaultAllowed); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype, - count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, - prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable), - workspace, HUF_WORKSPACE_SIZE); + count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, + prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable), + workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; } } @@ -1917,9 +1918,9 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, } MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, - ZSTD_entropyCTables_t const* prevEntropy, + const ZSTD_entropyCTables_t* prevEntropy, ZSTD_entropyCTables_t* nextEntropy, - ZSTD_CCtx_params const* cctxParams, + const ZSTD_CCtx_params* cctxParams, void* dst, size_t dstCapacity, size_t srcSize, U32* workspace, int bmi2) { @@ -1934,7 +1935,7 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, if (ZSTD_isError(cSize)) return cSize; /* Check compressibility */ - { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize); /* note : fixed formula, maybe should depend on compression level, or strategy */ + { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize); /* note : fixed formula; to be refined, depending on compression level, or strategy */ if (cSize >= maxCSize) return 0; /* block not compressed */ } diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 92dd18240..5d6a1e940 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -633,6 +633,33 @@ MEM_STATIC U32 ZSTD_window_update(ZSTD_window_t* window, return contiguous; } + +/* debug functions */ + +MEM_STATIC double ZSTD_fWeight(U32 rawStat) +{ + U32 const fp_accuracy = 8; + U32 const fp_multiplier = (1 << fp_accuracy); + U32 const stat = rawStat + 1; + U32 const hb = ZSTD_highbit32(stat); + U32 const BWeight = hb * fp_multiplier; + U32 const FWeight = (stat << fp_accuracy) >> hb; + U32 const weight = BWeight + FWeight; + assert(hb + fp_accuracy < 31); + return (double)weight / fp_multiplier; +} + +MEM_STATIC void ZSTD_debugTable(const U32* table, U32 max) +{ + unsigned u, sum; + for (u=0, sum=0; u<=max; u++) sum += table[u]; + DEBUGLOG(2, "total nb elts: %u", sum); + for (u=0; u<=max; u++) { + DEBUGLOG(2, "%2u: %5u (%.2f)", + u, table[u], ZSTD_fWeight(sum) - ZSTD_fWeight(table[u]) ); + } +} + #if defined (__cplusplus) } #endif diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 9288e0ac8..03c5b5b61 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -1057,7 +1057,6 @@ size_t ZSTD_compressBlock_btultra( /* re-inforce weight of collected statistics */ ZSTD_upscaleStats(&ms->opt); } - DEBUGLOG(5, "base=%p, src=%p, src-base=%zi", ms->window.base, src, (const BYTE*)src - (const BYTE*)ms->window.base); return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/); } From 7cbb8bbbbf4d04b49b23267e461fcb4ac42a0c8f Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 18 May 2018 15:25:10 -0700 Subject: [PATCH 076/288] [cover] Small compression ratio improvement The cover algorithm selects one segment per epoch, and it selects the epoch size such that `epochs * segmentSize ~= dictSize`. Selecting less epochs gives the algorithm more candidates to choose from for each segment it selects, and then it will loop back to the first epoch when it hits the last one. The trade off is that now it takes longer to select each segment, since it has to look at more data before making a choice. I benchmarked on the following data sets using this command: ```sh $ZSTD -T0 -3 --train-cover=d=8,steps=256 $DIR -r -o dict && $ZSTD -3 -D dict -rc $DIR | wc -c ``` | Data set | k (approx) | Before | After | % difference | |--------------|------------|----------|----------|--------------| | GitHub | ~1000 | 738138 | 746610 | +1.14% | | hg-changelog | ~90 | 4295156 | 4285336 | -0.23% | | hg-commands | ~500 | 1095580 | 1079814 | -1.44% | | hg-manifest | ~400 | 16559892 | 16504346 | -0.34% | There is some noise in the measurements, since small changes to `k` can have large differences, which is why I'm using `steps=256`, to try to minimize the noise. However, the GitHub data set still has some noise. If I run the GitHub data set on my Mac, which presumably lists directory entries in a different order, so the dictionary builder sees the files in a different order, or I use `steps=1024` I see these results. | Run | Before | After | % difference | |------------|--------|--------|--------------| | steps=1024 | 738138 | 734470 | -0.50% | | MacBook | 738451 | 737132 | -0.18% | Question: Should we expose this as a parameter? I don't think it is necessary. Someone might want to turn it up to exchange a much longer dictionary building time in exchange for a slightly better dictionary. I tested `2`, `4`, and `16`, and `4` got most of the benefit of `16` with a faster running time. --- lib/dictBuilder/cover.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c index 6d473624d..448f71372 100644 --- a/lib/dictBuilder/cover.c +++ b/lib/dictBuilder/cover.c @@ -620,7 +620,7 @@ static size_t COVER_buildDictionary(const COVER_ctx_t *ctx, U32 *freqs, /* Divide the data up into epochs of equal size. * We will select at least one segment from each epoch. */ - const U32 epochs = (U32)(dictBufferCapacity / parameters.k); + const U32 epochs = MAX(1, (U32)(dictBufferCapacity / parameters.k / 4)); const U32 epochSize = (U32)(ctx->suffixSize / epochs); size_t epoch; DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n", epochs, From b0b3fb517dba3ff3ed4f4d45652f2ff9cc94c3c6 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 18 May 2018 17:17:12 -0700 Subject: [PATCH 077/288] updated compression levels for blocks of 256KB --- lib/compress/zstd_compress.c | 42 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d4e3462d2..faecad40f 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3447,27 +3447,27 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV /* W, C, H, S, L, T, strat */ { 18, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ { 18, 13, 14, 1, 6, 1, ZSTD_fast }, /* level 1 */ - { 18, 14, 13, 1, 5, 1, ZSTD_dfast }, /* level 2 */ - { 18, 16, 15, 1, 5, 1, ZSTD_dfast }, /* level 3 */ - { 18, 15, 17, 1, 5, 2, ZSTD_greedy }, /* level 4.*/ - { 18, 16, 17, 4, 5, 2, ZSTD_greedy }, /* level 5.*/ - { 18, 16, 17, 3, 5, 4, ZSTD_lazy }, /* level 6.*/ - { 18, 17, 17, 4, 4, 4, ZSTD_lazy }, /* level 7 */ - { 18, 17, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ - { 18, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ - { 18, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ - { 18, 18, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 11.*/ - { 18, 18, 17, 5, 4, 8, ZSTD_btlazy2 }, /* level 12.*/ - { 18, 19, 17, 7, 4, 8, ZSTD_btlazy2 }, /* level 13 */ - { 18, 18, 18, 4, 4, 16, ZSTD_btopt }, /* level 14.*/ - { 18, 18, 18, 4, 3, 16, ZSTD_btopt }, /* level 15.*/ - { 18, 19, 18, 6, 3, 32, ZSTD_btopt }, /* level 16.*/ - { 18, 19, 18, 8, 3, 64, ZSTD_btopt }, /* level 17.*/ - { 18, 19, 18, 9, 3,128, ZSTD_btopt }, /* level 18.*/ - { 18, 19, 18, 10, 3,256, ZSTD_btopt }, /* level 19.*/ - { 18, 19, 18, 11, 3,512, ZSTD_btultra }, /* level 20.*/ - { 18, 19, 18, 12, 3,512, ZSTD_btultra }, /* level 21.*/ - { 18, 19, 18, 13, 3,512, ZSTD_btultra }, /* level 22.*/ + { 18, 14, 14, 1, 5, 1, ZSTD_dfast }, /* level 2 */ + { 18, 16, 16, 1, 4, 1, ZSTD_dfast }, /* level 3 */ + { 18, 16, 17, 2, 5, 2, ZSTD_greedy }, /* level 4.*/ + { 18, 18, 18, 3, 5, 2, ZSTD_greedy }, /* level 5.*/ + { 18, 18, 19, 3, 5, 4, ZSTD_lazy }, /* level 6.*/ + { 18, 18, 19, 4, 4, 4, ZSTD_lazy }, /* level 7 */ + { 18, 18, 19, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ + { 18, 18, 19, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ + { 18, 18, 19, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ + { 18, 18, 19, 5, 4, 16, ZSTD_btlazy2 }, /* level 11.*/ + { 18, 19, 19, 6, 4, 16, ZSTD_btlazy2 }, /* level 12.*/ + { 18, 19, 19, 8, 4, 16, ZSTD_btlazy2 }, /* level 13 */ + { 18, 18, 19, 4, 4, 24, ZSTD_btopt }, /* level 14.*/ + { 18, 18, 19, 4, 3, 24, ZSTD_btopt }, /* level 15.*/ + { 18, 19, 19, 6, 3, 64, ZSTD_btopt }, /* level 16.*/ + { 18, 19, 19, 8, 3,128, ZSTD_btopt }, /* level 17.*/ + { 18, 19, 19, 10, 3,256, ZSTD_btopt }, /* level 18.*/ + { 18, 19, 19, 10, 3,256, ZSTD_btultra }, /* level 19.*/ + { 18, 19, 19, 11, 3,512, ZSTD_btultra }, /* level 20.*/ + { 18, 19, 19, 12, 3,512, ZSTD_btultra }, /* level 21.*/ + { 18, 19, 19, 13, 3,999, ZSTD_btultra }, /* level 22.*/ }, { /* for srcSize <= 128 KB */ /* W, C, H, S, L, T, strat */ From 49cf880513c124e1116f80f4ca0c860ffe937151 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 16 Apr 2018 15:37:27 -0700 Subject: [PATCH 078/288] Approximate FSE encoding costs for selection Estimate the cost for using FSE modes `set_basic`, `set_compressed`, and `set_repeat`, and select the one with the lowest cost. * The cost of `set_basic` is computed using the cross-entropy cost function `ZSTD_crossEntropyCost()`, using the normalized default count and the count. * The cost of `set_repeat` is computed using `FSE_bitCost()`. We check the previous table to see if it is able to represent the distribution. * The cost of `set_compressed` is computed with the entropy cost function `ZSTD_entropyCost()`, together with the cost of writing the normalized count `ZSTD_NCountCost()`. --- lib/common/fse.h | 10 +- lib/compress/zstd_compress.c | 239 ++++++++++++++++++++++---- lib/compress/zstd_compress_internal.h | 6 +- 3 files changed, 217 insertions(+), 38 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 5a2344441..3d11a75e8 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -402,6 +402,7 @@ typedef struct { const void* stateTable; const void* symbolTT; unsigned stateLog; + unsigned maxSymbolValue; } FSE_CState_t; static void FSE_initCState(FSE_CState_t* CStatePtr, const FSE_CTable* ct); @@ -538,11 +539,13 @@ MEM_STATIC void FSE_initCState(FSE_CState_t* statePtr, const FSE_CTable* ct) { const void* ptr = ct; const U16* u16ptr = (const U16*) ptr; - const U32 tableLog = MEM_read16(ptr); + const U32 tableLog = MEM_read16(u16ptr); + const U32 maxSymbolValue = MEM_read16(u16ptr + 1); statePtr->value = (ptrdiff_t)1< stateTable = u16ptr+2; statePtr->symbolTT = ((const U32*)ct + 1 + (tableLog ? (1<<(tableLog-1)) : 1)); statePtr->stateLog = tableLog; + statePtr->maxSymbolValue = maxSymbolValue; } @@ -581,12 +584,13 @@ MEM_STATIC U32 FSE_getMaxNbBits(const void* symbolTTPtr, U32 symbolValue) return (symbolTT[symbolValue].deltaNbBits + ((1<<16)-1)) >> 16; } -/* FSE_bitCost_b256() : +/* FSE_bitCost() : * Approximate symbol cost, * provide fractional value, using fixed-point format (accuracyLog fractional bits) * note: assume symbolValue is valid */ -MEM_STATIC U32 FSE_bitCost(const FSE_symbolCompressionTransform* symbolTT, U32 tableLog, U32 symbolValue, U32 accuracyLog) +MEM_STATIC U32 FSE_bitCost(const void* symbolTTPtr, U32 tableLog, U32 symbolValue, U32 accuracyLog) { + const FSE_symbolCompressionTransform* symbolTT = (const FSE_symbolCompressionTransform*) symbolTTPtr; U32 const minNbBits = symbolTT[symbolValue].deltaNbBits >> 16; U32 const threshold = (minNbBits+1) << 16; assert(tableLog < 16); diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d8420a8a6..114845b15 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1561,6 +1561,129 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) mlCodeTable[seqStorePtr->longLengthPos] = MaxML; } + +/** + * -log2(x / 256) lookup table for x in [0, 256). + * If x == 0: Return 0 + * Else: Return floor(-log2(x / 256) * 256) + */ +static unsigned const kInverseProbabiltyLog256[256] = { + 0, 2048, 1792, 1642, 1536, 1453, 1386, 1329, 1280, 1236, 1197, 1162, + 1130, 1100, 1073, 1047, 1024, 1001, 980, 960, 941, 923, 906, 889, + 874, 859, 844, 830, 817, 804, 791, 779, 768, 756, 745, 734, + 724, 714, 704, 694, 685, 676, 667, 658, 650, 642, 633, 626, + 618, 610, 603, 595, 588, 581, 574, 567, 561, 554, 548, 542, + 535, 529, 523, 517, 512, 506, 500, 495, 489, 484, 478, 473, + 468, 463, 458, 453, 448, 443, 438, 434, 429, 424, 420, 415, + 411, 407, 402, 398, 394, 390, 386, 382, 377, 373, 370, 366, + 362, 358, 354, 350, 347, 343, 339, 336, 332, 329, 325, 322, + 318, 315, 311, 308, 305, 302, 298, 295, 292, 289, 286, 282, + 279, 276, 273, 270, 267, 264, 261, 258, 256, 253, 250, 247, + 244, 241, 239, 236, 233, 230, 228, 225, 222, 220, 217, 215, + 212, 209, 207, 204, 202, 199, 197, 194, 192, 190, 187, 185, + 182, 180, 178, 175, 173, 171, 168, 166, 164, 162, 159, 157, + 155, 153, 151, 149, 146, 144, 142, 140, 138, 136, 134, 132, + 130, 128, 126, 123, 121, 119, 117, 115, 114, 112, 110, 108, + 106, 104, 102, 100, 98, 96, 94, 93, 91, 89, 87, 85, + 83, 82, 80, 78, 76, 74, 73, 71, 69, 67, 66, 64, + 62, 61, 59, 57, 55, 54, 52, 50, 49, 47, 46, 44, + 42, 41, 39, 37, 36, 34, 33, 31, 30, 28, 26, 25, + 23, 22, 20, 19, 17, 16, 14, 13, 11, 10, 8, 7, + 5, 4, 2, 1, +}; + + +/** + * Returns the cost in bits of encoding the distribution described by count + * using the entropy bound. + */ +static size_t ZSTD_entropyCost(unsigned const* count, unsigned const max, size_t const total) +{ + unsigned cost = 0; + unsigned s; + for (s = 0; s <= max; ++s) { + unsigned norm = (unsigned)((256 * count[s]) / total); + if (count[s] != 0 && norm == 0) + norm = 1; + assert(count[s] < total); + cost += count[s] * kInverseProbabiltyLog256[norm]; + } + return cost >> 8; +} + + +/** + * Returns the cost in bits of encoding the distribution in count using the + * table described by norm. The max symbol support by norm is assumed >= max. + * norm must be valid for every symbol with non-zero probability in count. + */ +static size_t ZSTD_crossEntropyCost(short const* norm, unsigned accuracyLog, + unsigned const* count, unsigned const max) +{ + unsigned const shift = 8 - accuracyLog; + size_t cost = 0; + unsigned s; + assert(accuracyLog <= 8); + for (s = 0; s <= max; ++s) { + unsigned const normAcc = norm[s] != -1 ? norm[s] : 1; + unsigned const norm256 = normAcc << shift; + assert(norm256 > 0); + assert(norm256 < 256); + cost += count[s] * kInverseProbabiltyLog256[norm256]; + } + return cost >> 8; +} + + +/** + * Returns the cost in bits of encoding the distribution in count using ctable. + * Returns an error if ctable cannot represent all the symbols in count. + */ +static size_t ZSTD_fseBitCost( + FSE_CTable const* ctable, + unsigned const* count, + unsigned const max) +{ + unsigned const kAccuracyLog = 8; + size_t cost = 0; + unsigned s; + FSE_CState_t cstate; + FSE_initCState(&cstate, ctable); + if (cstate.maxSymbolValue < max) { + DEBUGLOG(5, "Repeat FSE_CTable has maxSymbolValue %u < %u", + cstate.maxSymbolValue, max); + return ERROR(GENERIC); + } + for (s = 0; s <= max; ++s) { + unsigned const tableLog = cstate.stateLog; + unsigned const badCost = (tableLog + 1) << kAccuracyLog; + unsigned const bitCost = FSE_bitCost(cstate.symbolTT, tableLog, s, kAccuracyLog); + if (count[s] == 0) + continue; + if (bitCost >= badCost) { + DEBUGLOG(5, "Repeat FSE_CTable has Prob[%u] == 0", s); + return ERROR(GENERIC); + } + cost += count[s] * bitCost; + } + return cost >> kAccuracyLog; +} + +/** + * Returns the cost in bytes of encoding the normalized count header. + * Returns an error if any of the helper functions return an error. + */ +static size_t ZSTD_NCountCost(unsigned const* count, unsigned const max, + size_t const nbSeq, unsigned const FSELog) +{ + BYTE wksp[FSE_NCOUNTBOUND]; + S16 norm[MaxSeq + 1]; + const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max); + CHECK_F(FSE_normalizeCount(norm, tableLog, count, nbSeq, max)); + return FSE_writeNCount(wksp, sizeof(wksp), norm, max, tableLog); +} + + typedef enum { ZSTD_defaultDisallowed = 0, ZSTD_defaultAllowed = 1 @@ -1568,37 +1691,73 @@ typedef enum { MEM_STATIC symbolEncodingType_e ZSTD_selectEncodingType( - FSE_repeat* repeatMode, size_t const mostFrequent, size_t nbSeq, - U32 defaultNormLog, ZSTD_defaultPolicy_e const isDefaultAllowed) + FSE_repeat* repeatMode, unsigned const* count, unsigned const max, + size_t const mostFrequent, size_t nbSeq, unsigned const FSELog, + FSE_CTable const* prevCTable, + short const* defaultNorm, U32 defaultNormLog, + ZSTD_defaultPolicy_e const isDefaultAllowed, + ZSTD_strategy const strategy) { #define MIN_SEQ_FOR_DYNAMIC_FSE 64 #define MAX_SEQ_FOR_STATIC_FSE 1000 ZSTD_STATIC_ASSERT(ZSTD_defaultDisallowed == 0 && ZSTD_defaultAllowed != 0); - if ((mostFrequent == nbSeq) && (!isDefaultAllowed || nbSeq > 2)) { + if (mostFrequent == nbSeq) { + *repeatMode = FSE_repeat_none; + if (isDefaultAllowed && nbSeq <= 2) { + /* Prefer set_basic over set_rle when there are 2 or less symbols, + * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol. + * If basic encoding isn't possible, always choose RLE. + */ + DEBUGLOG(5, "Selected set_basic"); + return set_basic; + } DEBUGLOG(5, "Selected set_rle"); - /* Prefer set_basic over set_rle when there are 2 or less symbols, - * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol. - * If basic encoding isn't possible, always choose RLE. - */ - *repeatMode = FSE_repeat_check; return set_rle; } - if ( isDefaultAllowed - && (*repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { - DEBUGLOG(5, "Selected set_repeat"); - return set_repeat; - } - if ( isDefaultAllowed - && ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) ) { - DEBUGLOG(5, "Selected set_basic"); - /* The format allows default tables to be repeated, but it isn't useful. - * When using simple heuristics to select encoding type, we don't want - * to confuse these tables with dictionaries. When running more careful - * analysis, we don't need to waste time checking both repeating tables - * and default tables. - */ - *repeatMode = FSE_repeat_none; - return set_basic; + if (strategy < ZSTD_lazy) { + if (isDefaultAllowed) { + if ((*repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + DEBUGLOG(5, "Selected set_repeat"); + return set_repeat; + } + if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) { + DEBUGLOG(5, "Selected set_basic"); + /* The format allows default tables to be repeated, but it isn't useful. + * When using simple heuristics to select encoding type, we don't want + * to confuse these tables with dictionaries. When running more careful + * analysis, we don't need to waste time checking both repeating tables + * and default tables. + */ + *repeatMode = FSE_repeat_none; + return set_basic; + } + } + } else { + size_t const basicCost = isDefaultAllowed ? ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, count, max) : ERROR(GENERIC); + size_t const repeatCost = *repeatMode != FSE_repeat_none ? ZSTD_fseBitCost(prevCTable, count, max) : ERROR(GENERIC); + size_t const NCountCost = ZSTD_NCountCost(count, max, nbSeq, FSELog); + size_t const compressedCost = (NCountCost << 3) + ZSTD_entropyCost(count, max, nbSeq); + + if (isDefaultAllowed) { + assert(!ZSTD_isError(basicCost)); + assert(!(*repeatMode == FSE_repeat_valid && ZSTD_isError(repeatCost))); + } + assert(!ZSTD_isError(NCountCost)); + assert(compressedCost < ERROR(maxCode)); + DEBUGLOG(5, "Estimated bit costs: basic=%u\trepeat=%u\tcompressed=%u", + (U32)basicCost, (U32)repeatCost, (U32)compressedCost); + if (basicCost <= repeatCost && basicCost <= compressedCost) { + DEBUGLOG(5, "Selected set_basic"); + assert(isDefaultAllowed); + *repeatMode = FSE_repeat_none; + return set_basic; + } + if (repeatCost <= compressedCost) { + DEBUGLOG(5, "Selected set_repeat"); + assert(!ZSTD_isError(repeatCost)); + return set_repeat; + } + assert(compressedCost < basicCost && compressedCost < repeatCost); } DEBUGLOG(5, "Selected set_compressed"); *repeatMode = FSE_repeat_check; @@ -1803,6 +1962,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, const int bmi2) { const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; + ZSTD_strategy const strategy = cctxParams->cParams.strategy; U32 count[MaxSeq+1]; FSE_CTable* CTable_LitLength = nextEntropy->litlengthCTable; FSE_CTable* CTable_OffsetBits = nextEntropy->offcodeCTable; @@ -1844,13 +2004,20 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, else op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3; if (nbSeq==0) { - memcpy(nextEntropy->litlengthCTable, prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable)); - nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode; - memcpy(nextEntropy->offcodeCTable, prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable)); - nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode; - memcpy(nextEntropy->matchlengthCTable, prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable)); - nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode; - return op - ostart; + /* Check that all the Huffman data is first */ + ZSTD_STATIC_ASSERT(offsetof(ZSTD_entropyCTables_t, hufCTable) == 0); + ZSTD_STATIC_ASSERT( + offsetof(ZSTD_entropyCTables_t, hufCTable_repeatMode) == + sizeof(prevEntropy->hufCTable)); + ZSTD_STATIC_ASSERT( + offsetof(ZSTD_entropyCTables_t, offcodeCTable) == + sizeof(prevEntropy->hufCTable) + sizeof(prevEntropy->hufCTable_repeatMode)); + /* Copy starting at the first FSE element */ + memcpy( + nextEntropy->offcodeCTable, + prevEntropy->offcodeCTable, + sizeof(*prevEntropy) - offsetof(ZSTD_entropyCTables_t, offcodeCTable)); + return op - ostart; } /* seqHead : flags for FSE encoding type */ @@ -1863,7 +2030,9 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, workspace); DEBUGLOG(5, "Building LL table"); nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode; - LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog, ZSTD_defaultAllowed); + LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode, count, max, mostFrequent, nbSeq, LLFSELog, prevEntropy->litlengthCTable, LL_defaultNorm, LL_defaultNormLog, ZSTD_defaultAllowed, strategy); + assert(set_basic < set_compressed && set_rle < set_compressed); + assert(!(LLtype < set_compressed && nextEntropy->litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype, count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable), @@ -1878,7 +2047,8 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed; DEBUGLOG(5, "Building OF table"); nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode; - Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog, defaultPolicy); + Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode, count, max, mostFrequent, nbSeq, OffFSELog, prevEntropy->offcodeCTable, OF_defaultNorm, OF_defaultNormLog, defaultPolicy, strategy); + assert(!(Offtype < set_compressed && nextEntropy->offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable), @@ -1891,7 +2061,8 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, workspace); DEBUGLOG(5, "Building ML table"); nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode; - MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog, ZSTD_defaultAllowed); + MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode, count, max, mostFrequent, nbSeq, MLFSELog, prevEntropy->matchlengthCTable, ML_defaultNorm, ML_defaultNormLog, ZSTD_defaultAllowed, strategy); + assert(!(MLtype < set_compressed && nextEntropy->matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype, count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable), diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 0f1830a5e..6c4e8bdc3 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -53,11 +53,15 @@ typedef struct ZSTD_prefixDict_s { } ZSTD_prefixDict; typedef struct { + /* Huffman data + * Must be before the FSE data. + */ U32 hufCTable[HUF_CTABLE_SIZE_U32(255)]; + HUF_repeat hufCTable_repeatMode; + /* FSE data */ 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)]; - HUF_repeat hufCTable_repeatMode; FSE_repeat offcode_repeatMode; FSE_repeat matchlength_repeatMode; FSE_repeat litlength_repeatMode; From a8ddf1d370fc1ff8e61f75225f756c81ec719c7e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 22 May 2018 15:06:36 -0700 Subject: [PATCH 079/288] disable 2-passes strategy --- lib/compress/zstd_ldm.c | 6 ++---- lib/compress/zstd_opt.c | 11 ++++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index 03348f177..423aedafd 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -642,8 +642,6 @@ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_ldm_limitTableUpdate(ms, ip); ZSTD_ldm_fillFastTables(ms, cParams, ip); /* Compress the last literals */ - { size_t const lastLiterals = blockCompressor(ms, seqStore, rep, cParams, - ip, iend - ip); - return lastLiterals; - } + return blockCompressor(ms, seqStore, rep, cParams, + ip, iend - ip); } diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index b72eea69e..4295ec1bc 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -1012,6 +1012,7 @@ size_t ZSTD_compressBlock_btopt( } +/* used in 2-pass strategy */ static U32 ZSTD_upscaleStat(U32* table, U32 lastEltIndex, int bonus) { U32 s, sum=0; @@ -1024,7 +1025,8 @@ static U32 ZSTD_upscaleStat(U32* table, U32 lastEltIndex, int bonus) return sum; } -static void ZSTD_upscaleStats(optState_t* optPtr) +/* used in 2-pass strategy */ +MEM_STATIC void ZSTD_upscaleStats(optState_t* optPtr) { optPtr->litSum = ZSTD_upscaleStat(optPtr->litFreq, MaxLit, 0); optPtr->litLengthSum = ZSTD_upscaleStat(optPtr->litLengthFreq, MaxLL, 1); @@ -1037,6 +1039,12 @@ size_t ZSTD_compressBlock_btultra( const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock_btultra (srcSize=%zu)", srcSize); +#if 0 + /* 2-pass strategy (disabled) + * this strategy makes a first pass over first block to collect statistics + * and seed next round's statistics with it. + * The compression ratio gain is generally small (~0.5% on first block), + * the cost is 2x cpu time on first block. */ assert(srcSize <= ZSTD_BLOCKSIZE_MAX); if ( (ms->opt.litLengthSum==0) /* first block */ && (seqStore->sequences == seqStore->sequencesStart) /* no ldm */ @@ -1057,6 +1065,7 @@ size_t ZSTD_compressBlock_btultra( /* re-inforce weight of collected statistics */ ZSTD_upscaleStats(&ms->opt); } +#endif return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/); } From e3959d5eba0cdccc7b97aa27e1f9ddc088b99cf9 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 22 May 2018 16:06:33 -0700 Subject: [PATCH 080/288] Fixes --- lib/common/fse.h | 5 +- lib/compress/zstd_compress.c | 109 ++++++++++++-------------- lib/compress/zstd_compress_internal.h | 16 ++-- lib/compress/zstd_opt.c | 22 +++--- 4 files changed, 74 insertions(+), 78 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 3d11a75e8..e88a5ef5a 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -402,7 +402,6 @@ typedef struct { const void* stateTable; const void* symbolTT; unsigned stateLog; - unsigned maxSymbolValue; } FSE_CState_t; static void FSE_initCState(FSE_CState_t* CStatePtr, const FSE_CTable* ct); @@ -539,13 +538,11 @@ MEM_STATIC void FSE_initCState(FSE_CState_t* statePtr, const FSE_CTable* ct) { const void* ptr = ct; const U16* u16ptr = (const U16*) ptr; - const U32 tableLog = MEM_read16(u16ptr); - const U32 maxSymbolValue = MEM_read16(u16ptr + 1); + const U32 tableLog = MEM_read16(ptr); statePtr->value = (ptrdiff_t)1< stateTable = u16ptr+2; statePtr->symbolTT = ((const U32*)ct + 1 + (tableLog ? (1<<(tableLog-1)) : 1)); statePtr->stateLog = tableLog; - statePtr->maxSymbolValue = maxSymbolValue; } diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 114845b15..22c704f11 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -946,10 +946,10 @@ static void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs) int i; for (i = 0; i < ZSTD_REP_NUM; ++i) bs->rep[i] = repStartValue[i]; - bs->entropy.hufCTable_repeatMode = HUF_repeat_none; - bs->entropy.offcode_repeatMode = FSE_repeat_none; - bs->entropy.matchlength_repeatMode = FSE_repeat_none; - bs->entropy.litlength_repeatMode = FSE_repeat_none; + bs->entropy.huf.repeatMode = HUF_repeat_none; + bs->entropy.fse.offcode_repeatMode = FSE_repeat_none; + bs->entropy.fse.matchlength_repeatMode = FSE_repeat_none; + bs->entropy.fse.litlength_repeatMode = FSE_repeat_none; } /*! ZSTD_invalidateMatchState() @@ -1455,8 +1455,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_entropyCTables_t const* prevEntropy, - ZSTD_entropyCTables_t* nextEntropy, +static size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, + ZSTD_hufCTables_t* nextHuf, ZSTD_strategy strategy, int disableLiteralCompression, void* dst, size_t dstCapacity, const void* src, size_t srcSize, @@ -1473,27 +1473,25 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy, disableLiteralCompression); /* Prepare nextEntropy assuming reusing the existing table */ - nextEntropy->hufCTable_repeatMode = prevEntropy->hufCTable_repeatMode; - memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, - sizeof(prevEntropy->hufCTable)); + memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); if (disableLiteralCompression) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); /* small ? don't even attempt compression (speed opt) */ # define COMPRESS_LITERALS_SIZE_MIN 63 - { size_t const minLitSize = (prevEntropy->hufCTable_repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN; + { size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN; 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 = prevEntropy->hufCTable_repeatMode; + { HUF_repeat repeat = prevHuf->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, - workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat, bmi2) + workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2) : HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, - workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat, bmi2); + workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2); if (repeat != HUF_repeat_none) { /* reused the existing table */ hType = set_repeat; @@ -1501,17 +1499,17 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy, } if ((cLitSize==0) | (cLitSize >= srcSize - minGain) | ERR_isError(cLitSize)) { - memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, sizeof(prevEntropy->hufCTable)); + memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } if (cLitSize==1) { - memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, sizeof(prevEntropy->hufCTable)); + memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); } if (hType == set_compressed) { /* using a newly constructed table */ - nextEntropy->hufCTable_repeatMode = HUF_repeat_check; + nextHuf->repeatMode = HUF_repeat_check; } /* Build header */ @@ -1635,6 +1633,14 @@ static size_t ZSTD_crossEntropyCost(short const* norm, unsigned accuracyLog, } +static unsigned ZSTD_getFSEMaxSymbolValue(FSE_CTable const* ctable) { + void const* ptr = ctable; + U16 const* u16ptr = (U16 const*)ptr; + U32 const maxSymbolValue = MEM_read16(u16ptr + 1); + return maxSymbolValue; +} + + /** * Returns the cost in bits of encoding the distribution in count using ctable. * Returns an error if ctable cannot represent all the symbols in count. @@ -1649,9 +1655,9 @@ static size_t ZSTD_fseBitCost( unsigned s; FSE_CState_t cstate; FSE_initCState(&cstate, ctable); - if (cstate.maxSymbolValue < max) { + if (ZSTD_getFSEMaxSymbolValue(ctable) < max) { DEBUGLOG(5, "Repeat FSE_CTable has maxSymbolValue %u < %u", - cstate.maxSymbolValue, max); + ZSTD_getFSEMaxSymbolValue(ctable), max); return ERROR(GENERIC); } for (s = 0; s <= max; ++s) { @@ -1964,9 +1970,9 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; ZSTD_strategy const strategy = cctxParams->cParams.strategy; U32 count[MaxSeq+1]; - FSE_CTable* CTable_LitLength = nextEntropy->litlengthCTable; - FSE_CTable* CTable_OffsetBits = nextEntropy->offcodeCTable; - FSE_CTable* CTable_MatchLength = nextEntropy->matchlengthCTable; + FSE_CTable* CTable_LitLength = nextEntropy->fse.litlengthCTable; + FSE_CTable* CTable_OffsetBits = nextEntropy->fse.offcodeCTable; + FSE_CTable* CTable_MatchLength = nextEntropy->fse.matchlengthCTable; U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */ const seqDef* const sequences = seqStorePtr->sequencesStart; const BYTE* const ofCodeTable = seqStorePtr->ofCode; @@ -1984,7 +1990,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, { const BYTE* const literals = seqStorePtr->litStart; size_t const litSize = seqStorePtr->lit - literals; size_t const cSize = ZSTD_compressLiterals( - prevEntropy, nextEntropy, + &prevEntropy->huf, &nextEntropy->huf, cctxParams->cParams.strategy, cctxParams->disableLiteralCompression, op, dstCapacity, literals, litSize, @@ -2004,19 +2010,8 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, else op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3; if (nbSeq==0) { - /* Check that all the Huffman data is first */ - ZSTD_STATIC_ASSERT(offsetof(ZSTD_entropyCTables_t, hufCTable) == 0); - ZSTD_STATIC_ASSERT( - offsetof(ZSTD_entropyCTables_t, hufCTable_repeatMode) == - sizeof(prevEntropy->hufCTable)); - ZSTD_STATIC_ASSERT( - offsetof(ZSTD_entropyCTables_t, offcodeCTable) == - sizeof(prevEntropy->hufCTable) + sizeof(prevEntropy->hufCTable_repeatMode)); - /* Copy starting at the first FSE element */ - memcpy( - nextEntropy->offcodeCTable, - prevEntropy->offcodeCTable, - sizeof(*prevEntropy) - offsetof(ZSTD_entropyCTables_t, offcodeCTable)); + /* Copy the old tables over as if we repeated them */ + memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse)); return op - ostart; } @@ -2029,13 +2024,13 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, { U32 max = MaxLL; size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, workspace); DEBUGLOG(5, "Building LL table"); - nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode; - LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode, count, max, mostFrequent, nbSeq, LLFSELog, prevEntropy->litlengthCTable, LL_defaultNorm, LL_defaultNormLog, ZSTD_defaultAllowed, strategy); + nextEntropy->fse.litlength_repeatMode = prevEntropy->fse.litlength_repeatMode; + LLtype = ZSTD_selectEncodingType(&nextEntropy->fse.litlength_repeatMode, count, max, mostFrequent, nbSeq, LLFSELog, prevEntropy->fse.litlengthCTable, LL_defaultNorm, LL_defaultNormLog, ZSTD_defaultAllowed, strategy); assert(set_basic < set_compressed && set_rle < set_compressed); - assert(!(LLtype < set_compressed && nextEntropy->litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ + assert(!(LLtype < set_compressed && nextEntropy->fse.litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype, count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, - prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable), + prevEntropy->fse.litlengthCTable, sizeof(prevEntropy->fse.litlengthCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; @@ -2046,12 +2041,12 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */ ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed; DEBUGLOG(5, "Building OF table"); - nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode; - Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode, count, max, mostFrequent, nbSeq, OffFSELog, prevEntropy->offcodeCTable, OF_defaultNorm, OF_defaultNormLog, defaultPolicy, strategy); - assert(!(Offtype < set_compressed && nextEntropy->offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */ + nextEntropy->fse.offcode_repeatMode = prevEntropy->fse.offcode_repeatMode; + Offtype = ZSTD_selectEncodingType(&nextEntropy->fse.offcode_repeatMode, count, max, mostFrequent, nbSeq, OffFSELog, prevEntropy->fse.offcodeCTable, OF_defaultNorm, OF_defaultNormLog, defaultPolicy, strategy); + assert(!(Offtype < set_compressed && nextEntropy->fse.offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, - prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable), + prevEntropy->fse.offcodeCTable, sizeof(prevEntropy->fse.offcodeCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; @@ -2060,12 +2055,12 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, { U32 max = MaxML; size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, workspace); DEBUGLOG(5, "Building ML table"); - nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode; - MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode, count, max, mostFrequent, nbSeq, MLFSELog, prevEntropy->matchlengthCTable, ML_defaultNorm, ML_defaultNormLog, ZSTD_defaultAllowed, strategy); - assert(!(MLtype < set_compressed && nextEntropy->matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ + nextEntropy->fse.matchlength_repeatMode = prevEntropy->fse.matchlength_repeatMode; + MLtype = ZSTD_selectEncodingType(&nextEntropy->fse.matchlength_repeatMode, count, max, mostFrequent, nbSeq, MLFSELog, prevEntropy->fse.matchlengthCTable, ML_defaultNorm, ML_defaultNormLog, ZSTD_defaultAllowed, strategy); + assert(!(MLtype < set_compressed && nextEntropy->fse.matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype, count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, - prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable), + prevEntropy->fse.matchlengthCTable, sizeof(prevEntropy->fse.matchlengthCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; @@ -2113,8 +2108,8 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, * block. After the first block, the offcode table might not have large * enough codes to represent the offsets in the data. */ - if (nextEntropy->offcode_repeatMode == FSE_repeat_valid) - nextEntropy->offcode_repeatMode = FSE_repeat_check; + if (nextEntropy->fse.offcode_repeatMode == FSE_repeat_valid) + nextEntropy->fse.offcode_repeatMode = FSE_repeat_check; return cSize; } @@ -2555,7 +2550,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, dictPtr += 4; { unsigned maxSymbolValue = 255; - size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.hufCTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr); + size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.huf.CTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr); if (HUF_isError(hufHeaderSize)) return ERROR(dictionary_corrupted); if (maxSymbolValue < 255) return ERROR(dictionary_corrupted); dictPtr += hufHeaderSize; @@ -2567,7 +2562,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted); /* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */ /* fill all offset symbols to avoid garbage at end of table */ - CHECK_E( FSE_buildCTable_wksp(bs->entropy.offcodeCTable, offcodeNCount, MaxOff, offcodeLog, workspace, HUF_WORKSPACE_SIZE), + CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.offcodeCTable, offcodeNCount, MaxOff, offcodeLog, workspace, HUF_WORKSPACE_SIZE), dictionary_corrupted); dictPtr += offcodeHeaderSize; } @@ -2579,7 +2574,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, 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(bs->entropy.matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, workspace, HUF_WORKSPACE_SIZE), + CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, workspace, HUF_WORKSPACE_SIZE), dictionary_corrupted); dictPtr += matchlengthHeaderSize; } @@ -2591,7 +2586,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, 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(bs->entropy.litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, workspace, HUF_WORKSPACE_SIZE), + CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, workspace, HUF_WORKSPACE_SIZE), dictionary_corrupted); dictPtr += litlengthHeaderSize; } @@ -2617,10 +2612,10 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, if (bs->rep[u] > dictContentSize) return ERROR(dictionary_corrupted); } } - bs->entropy.hufCTable_repeatMode = HUF_repeat_valid; - bs->entropy.offcode_repeatMode = FSE_repeat_valid; - bs->entropy.matchlength_repeatMode = FSE_repeat_valid; - bs->entropy.litlength_repeatMode = FSE_repeat_valid; + bs->entropy.huf.repeatMode = HUF_repeat_valid; + bs->entropy.fse.offcode_repeatMode = FSE_repeat_valid; + bs->entropy.fse.matchlength_repeatMode = FSE_repeat_valid; + bs->entropy.fse.litlength_repeatMode = FSE_repeat_valid; CHECK_F(ZSTD_loadDictionaryContent(ms, params, dictPtr, dictContentSize, dtlm)); return dictID; } diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 6c4e8bdc3..937234c34 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -53,18 +53,22 @@ typedef struct ZSTD_prefixDict_s { } ZSTD_prefixDict; typedef struct { - /* Huffman data - * Must be before the FSE data. - */ - U32 hufCTable[HUF_CTABLE_SIZE_U32(255)]; - HUF_repeat hufCTable_repeatMode; - /* FSE data */ + U32 CTable[HUF_CTABLE_SIZE_U32(255)]; + HUF_repeat repeatMode; +} ZSTD_hufCTables_t; + +typedef struct { FSE_CTable offcodeCTable[FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)]; FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)]; FSE_CTable litlengthCTable[FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)]; FSE_repeat offcode_repeatMode; FSE_repeat matchlength_repeatMode; FSE_repeat litlength_repeatMode; +} ZSTD_fseCTables_t; + +typedef struct { + ZSTD_hufCTables_t huf; + ZSTD_fseCTables_t fse; } ZSTD_entropyCTables_t; typedef struct { diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 3a48187c5..521fbbf39 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -39,7 +39,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, optPtr->priceType = zop_predef; assert(optPtr->symbolCosts != NULL); - if (optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid) { /* huffman table presumed generated by dictionary */ + if (optPtr->symbolCosts->huf.repeatMode == HUF_repeat_valid) { /* huffman table presumed generated by dictionary */ if (srcSize <= 8192) /* heuristic */ optPtr->priceType = zop_static; else { @@ -52,7 +52,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, { unsigned lit; for (lit=0; lit<=MaxLit; lit++) { U32 const scaleLog = 11; /* scale to 2K */ - U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->hufCTable, lit); + U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->huf.CTable, lit); assert(bitCost <= scaleLog); optPtr->litFreq[lit] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; optPtr->litSum += optPtr->litFreq[lit]; @@ -60,7 +60,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, { unsigned ll; FSE_CState_t llstate; - FSE_initCState(&llstate, optPtr->symbolCosts->litlengthCTable); + FSE_initCState(&llstate, optPtr->symbolCosts->fse.litlengthCTable); optPtr->litLengthSum = 0; for (ll=0; ll<=MaxLL; ll++) { U32 const scaleLog = 10; /* scale to 1K */ @@ -72,7 +72,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, { unsigned ml; FSE_CState_t mlstate; - FSE_initCState(&mlstate, optPtr->symbolCosts->matchlengthCTable); + FSE_initCState(&mlstate, optPtr->symbolCosts->fse.matchlengthCTable); optPtr->matchLengthSum = 0; for (ml=0; ml<=MaxML; ml++) { U32 const scaleLog = 10; @@ -84,7 +84,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, { unsigned of; FSE_CState_t ofstate; - FSE_initCState(&ofstate, optPtr->symbolCosts->offcodeCTable); + FSE_initCState(&ofstate, optPtr->symbolCosts->fse.offcodeCTable); optPtr->offCodeSum = 0; for (of=0; of<=MaxOff; of++) { U32 const scaleLog = 10; @@ -180,9 +180,9 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, if (optPtr->priceType == zop_static) { U32 u, cost; assert(optPtr->symbolCosts != NULL); - assert(optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid); + assert(optPtr->symbolCosts->huf.repeatMode == HUF_repeat_valid); for (u=0, cost=0; u < litLength; u++) - cost += HUF_getNbBits(optPtr->symbolCosts->hufCTable, literals[u]); + cost += HUF_getNbBits(optPtr->symbolCosts->huf.CTable, literals[u]); return cost * BITCOST_MULTIPLIER; } @@ -202,7 +202,7 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP if (optPtr->priceType == zop_static) { U32 const llCode = ZSTD_LLcode(litLength); FSE_CState_t cstate; - FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); + FSE_initCState(&cstate, optPtr->symbolCosts->fse.litlengthCTable); { U32 const price = LL_bits[llCode]*BITCOST_MULTIPLIER + BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, llCode); DEBUGLOG(8, "ZSTD_litLengthPrice: ll=%u, bitCost=%.2f", litLength, (double)price / BITCOST_MULTIPLIER); return price; @@ -234,7 +234,7 @@ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* con if (optPtr->priceType == zop_static) { U32 const llCode = ZSTD_LLcode(litLength); FSE_CState_t cstate; - FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); + FSE_initCState(&cstate, optPtr->symbolCosts->fse.litlengthCTable); return (int)(LL_bits[llCode] * BITCOST_MULTIPLIER) + BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, llCode) - BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, 0); @@ -284,8 +284,8 @@ ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, if (optPtr->priceType == zop_static) { U32 const mlCode = ZSTD_MLcode(mlBase); FSE_CState_t mlstate, offstate; - FSE_initCState(&mlstate, optPtr->symbolCosts->matchlengthCTable); - FSE_initCState(&offstate, optPtr->symbolCosts->offcodeCTable); + FSE_initCState(&mlstate, optPtr->symbolCosts->fse.matchlengthCTable); + FSE_initCState(&offstate, optPtr->symbolCosts->fse.offcodeCTable); return BITCOST_SYMBOL(offstate.symbolTT, offstate.stateLog, offCode) + offCode*BITCOST_MULTIPLIER + BITCOST_SYMBOL(mlstate.symbolTT, mlstate.stateLog, mlCode) + ML_bits[mlCode]*BITCOST_MULTIPLIER; } From 73f4c890cd07dbc6e0f24f7399456a060a5a7105 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 22 May 2018 16:12:33 -0700 Subject: [PATCH 081/288] Clarify what happens when Number_of_Sequences == 0 --- doc/zstd_compression_format.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index 66819d136..d95475443 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -603,6 +603,7 @@ Let's call its first byte `byte0`. - `if (byte0 == 0)` : there are no sequences. The sequence section stops there. Decompressed content is defined entirely as Literals Section content. + The FSE tables used in `Repeat_Mode` aren't updated. - `if (byte0 < 128)` : `Number_of_Sequences = byte0` . Uses 1 byte. - `if (byte0 < 255)` : `Number_of_Sequences = ((byte0-128) << 8) + byte1` . Uses 2 bytes. - `if (byte0 == 255)`: `Number_of_Sequences = byte1 + (byte2<<8) + 0x7F00` . Uses 3 bytes. @@ -631,7 +632,7 @@ They follow the same enumeration : No distribution table will be present. - `RLE_Mode` : The table description consists of a single byte. This code will be repeated for all sequences. -- `Repeat_Mode` : The table used in the previous `Compressed_Block` will be used again, +- `Repeat_Mode` : The table used in the previous `Compressed_Block` with `Number_of_Sequences > 0` will be used again, or if this is the first block, table in the dictionary will be used No distribution table will be present. Note that this includes `RLE_mode`, so if `Repeat_Mode` follows `RLE_Mode`, the same symbol will be repeated. From a97e9a627adf18b0efc9f2c4aeeac25b2a2af730 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 23 May 2018 12:16:00 -0700 Subject: [PATCH 082/288] [zstd] Fix decompression edge case This edge case is only possible with the new optimal encoding selector, since before zstd would always choose `set_basic` for small numbers of sequences. Fix `FSE_readNCount()` to support buffers < 4 bytes. Credit to OSS-Fuzz --- lib/common/entropy_common.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/common/entropy_common.c b/lib/common/entropy_common.c index 344c32361..a8d0b146b 100644 --- a/lib/common/entropy_common.c +++ b/lib/common/entropy_common.c @@ -72,7 +72,14 @@ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* t unsigned charnum = 0; int previous0 = 0; - if (hbSize < 4) return ERROR(srcSize_wrong); + if (hbSize < 4) { + /* This function only works when hbSize >= 4 */ + char buffer[4]; + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, headerBuffer, hbSize); + return FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr, buffer, sizeof(buffer)); + } + bitStream = MEM_readLE32(ip); nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */ if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) return ERROR(tableLog_tooLarge); @@ -105,6 +112,7 @@ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* t if (n0 > *maxSVPtr) return ERROR(maxSymbolValue_tooSmall); while (charnum < n0) normalizedCounter[charnum++] = 0; if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) { + assert((bitCount >> 3) <= 3); /* For first condition to work */ ip += bitCount>>3; bitCount &= 7; bitStream = MEM_readLE32(ip) >> bitCount; From c92dd11940f68c71d3b627de2612537b7e2ae92a Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 23 May 2018 14:47:20 -0700 Subject: [PATCH 083/288] Error if reported size is too large in edge case --- lib/common/entropy_common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/common/entropy_common.c b/lib/common/entropy_common.c index a8d0b146b..2edb6e9be 100644 --- a/lib/common/entropy_common.c +++ b/lib/common/entropy_common.c @@ -77,8 +77,13 @@ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* t char buffer[4]; memset(buffer, 0, sizeof(buffer)); memcpy(buffer, headerBuffer, hbSize); - return FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr, buffer, sizeof(buffer)); + size_t const countSize = FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr, + buffer, sizeof(buffer)); + if (FSE_isError(countSize)) return countSize; + if (countSize > hbSize) return ERROR(corruption_detected); + return countSize; } + assert(hbSize >= 4); bitStream = MEM_readLE32(ip); nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */ From d18a4057796a43ce4f3d1c928d612e91e297b061 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Fri, 27 Apr 2018 18:46:59 -0400 Subject: [PATCH 084/288] Refer to the Dictionary Match State In-Place (Sometimes) --- lib/compress/zstd_compress.c | 68 +++++++++++++++++---------- lib/compress/zstd_compress_internal.h | 6 ++- 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 22c704f11..18091b09c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -963,6 +963,7 @@ static void ZSTD_invalidateMatchState(ZSTD_matchState_t* ms) ms->nextToUpdate = ms->window.dictLimit + 1; ms->loadedDictEnd = 0; ms->opt.litLengthSum = 0; /* force reset of btopt stats */ + ms->dictMatchState = NULL; } /*! ZSTD_continueCCtx() : @@ -1203,42 +1204,61 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, U64 pledgedSrcSize, ZSTD_buffered_policy_e zbuff) { + /* We have a choice between copying the dictionary context into the working + * context, or referencing the dictionary context from the working context + * in-place. We decide here which strategy to use. */ + /* TODO: pick reasonable cut-off size, handle ZSTD_CONTENTSIZE_UNKNOWN */ + int attachDict = pledgedSrcSize < 64 KB + && cdict->cParams.strategy == ZSTD_fast + && ZSTD_equivalentCParams(cctx->appliedParams.cParams, + cdict->cParams); + { unsigned const windowLog = params.cParams.windowLog; assert(windowLog != 0); /* Copy only compression parameters related to tables. */ params.cParams = cdict->cParams; params.cParams.windowLog = windowLog; - ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, ZSTDcrp_noMemset, zbuff); + ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, + attachDict ? ZSTDcrp_continue : ZSTDcrp_noMemset, + zbuff); assert(cctx->appliedParams.cParams.strategy == cdict->cParams.strategy); assert(cctx->appliedParams.cParams.hashLog == cdict->cParams.hashLog); assert(cctx->appliedParams.cParams.chainLog == cdict->cParams.chainLog); } - /* copy tables */ - { size_t const chainSize = (cdict->cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << cdict->cParams.chainLog); - size_t const hSize = (size_t)1 << cdict->cParams.hashLog; - size_t const tableSpace = (chainSize + hSize) * sizeof(U32); - assert((U32*)cctx->blockState.matchState.chainTable == (U32*)cctx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */ - assert((U32*)cctx->blockState.matchState.hashTable3 == (U32*)cctx->blockState.matchState.chainTable + chainSize); - assert((U32*)cdict->matchState.chainTable == (U32*)cdict->matchState.hashTable + hSize); /* chainTable must follow hashTable */ - assert((U32*)cdict->matchState.hashTable3 == (U32*)cdict->matchState.chainTable + chainSize); - memcpy(cctx->blockState.matchState.hashTable, cdict->matchState.hashTable, tableSpace); /* presumes all tables follow each other */ - } - /* Zero the hashTable3, since the cdict never fills it */ - { size_t const h3Size = (size_t)1 << cctx->blockState.matchState.hashLog3; - assert(cdict->matchState.hashLog3 == 0); - memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32)); + if (attachDict) { + DEBUGLOG(4, "attaching dictionary into context"); + cctx->blockState.matchState.dictMatchState = &cdict->matchState; + } else { + DEBUGLOG(4, "copying dictionary into context"); + /* copy tables */ + { size_t const chainSize = (cdict->cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << cdict->cParams.chainLog); + size_t const hSize = (size_t)1 << cdict->cParams.hashLog; + size_t const tableSpace = (chainSize + hSize) * sizeof(U32); + assert((U32*)cctx->blockState.matchState.chainTable == (U32*)cctx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */ + assert((U32*)cctx->blockState.matchState.hashTable3 == (U32*)cctx->blockState.matchState.chainTable + chainSize); + assert((U32*)cdict->matchState.chainTable == (U32*)cdict->matchState.hashTable + hSize); /* chainTable must follow hashTable */ + assert((U32*)cdict->matchState.hashTable3 == (U32*)cdict->matchState.chainTable + chainSize); + memcpy(cctx->blockState.matchState.hashTable, cdict->matchState.hashTable, tableSpace); /* presumes all tables follow each other */ + } + + /* Zero the hashTable3, since the cdict never fills it */ + { size_t const h3Size = (size_t)1 << cctx->blockState.matchState.hashLog3; + assert(cdict->matchState.hashLog3 == 0); + memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32)); + } + + /* copy dictionary offsets */ + { + ZSTD_matchState_t const* srcMatchState = &cdict->matchState; + ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState; + dstMatchState->window = srcMatchState->window; + dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; + dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3; + dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd; + } } - /* copy dictionary offsets */ - { - ZSTD_matchState_t const* srcMatchState = &cdict->matchState; - ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState; - dstMatchState->window = srcMatchState->window; - dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; - dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3; - dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd; - } cctx->dictID = cdict->dictID; /* copy block state */ diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 937234c34..9209fb020 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -122,7 +122,8 @@ typedef struct { U32 lowLimit; /* below that point, no more data */ } ZSTD_window_t; -typedef struct { +typedef struct ZSTD_matchState_t ZSTD_matchState_t; +struct ZSTD_matchState_t { ZSTD_window_t window; /* State for window round buffer management */ U32 loadedDictEnd; /* index of end of dictionary */ U32 nextToUpdate; /* index from which to continue table update */ @@ -132,7 +133,8 @@ typedef struct { U32* hashTable3; U32* chainTable; optState_t opt; /* optimal parser state */ -} ZSTD_matchState_t; + const ZSTD_matchState_t *dictMatchState; +}; typedef struct { ZSTD_compressedBlockState_t* prevCBlock; From 8d24ff03534daba8b8fd2169c1dc6873548dda04 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Sat, 28 Apr 2018 00:42:37 -0400 Subject: [PATCH 085/288] Preliminary Support in ZSTD_compressBlock_fast_generic() for Ext Dict Ctx --- lib/compress/zstd_compress_internal.h | 2 + lib/compress/zstd_fast.c | 92 +++++++++++++++++++++------ 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 9209fb020..05685e559 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -250,6 +250,8 @@ struct ZSTD_CCtx_s { typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e; +typedef enum { ZSTD_noDictMatchState, ZSTD_hasDictMatchState } ZSTD_hasDictMatchState_e; + typedef size_t (*ZSTD_blockCompressor) ( ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 22b84d1c6..df4423fc1 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -45,7 +45,8 @@ FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_fast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, - U32 const hlog, U32 const stepSize, U32 const mls) + U32 const hlog, U32 const stepSize, U32 const mls, + ZSTD_hasDictMatchState_e const hasDict) { U32* const hashTable = ms->hashTable; const BYTE* const base = ms->window.base; @@ -59,6 +60,19 @@ size_t ZSTD_compressBlock_fast_generic( U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; + const ZSTD_matchState_t* const dms = ms->dictMatchState; + const U32* const dictHashTable = hasDict == ZSTD_hasDictMatchState ? + dms->hashTable : NULL; + const U32 dictLowestIndex = hasDict == ZSTD_hasDictMatchState ? + dms->window.dictLimit : 0; + const BYTE* const dictBase = hasDict == ZSTD_hasDictMatchState ? + dms->window.base : NULL; + const BYTE* const dictLowest = hasDict == ZSTD_hasDictMatchState ? + dictBase + dictLowestIndex : NULL; + const BYTE* const dictEnd = hasDict == ZSTD_hasDictMatchState ? + dms->window.nextSrc : NULL; + const U32 dictIndexDelta = lowestIndex - (dictEnd - dictBase); + /* init */ ip += (ip==lowest); { U32 const maxRep = (U32)(ip-lowest); @@ -75,19 +89,41 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* match = base + matchIndex; hashTable[h] = current; /* update hash table */ - if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { + if ((hasDict != ZSTD_hasDictMatchState || current >= lowestIndex + offset_1) + && (offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else { if ( (matchIndex <= lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { - assert(stepSize >= 1); - ip += ((ip-anchor) >> kSearchStrength) + stepSize; - continue; - } - mLength = ZSTD_count(ip+4, match+4, iend) + 4; - { U32 const offset = (U32)(ip-match); + if (hasDict == ZSTD_hasDictMatchState) { + U32 const dictMatchIndex = dictHashTable[h]; + const BYTE* dictMatch = dictBase + dictMatchIndex; + if (dictMatchIndex <= dictLowestIndex || + MEM_read32(dictMatch) != MEM_read32(ip)) { + assert(stepSize >= 1); + ip += ((ip-anchor) >> kSearchStrength) + stepSize; + continue; + } + + mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, istart) + 4; + { U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); + DEBUGLOG(6, "ip %p (%u) dictMatch %p (%u) idxDelta %u", ip, current, dictMatch, dictMatchIndex, dictIndexDelta); + while (((ip>anchor) & (dictMatch>dictLowest)) && (ip[-1] == dictMatch[-1])) { ip--; dictMatch--; mLength++; } /* catch up */ + offset_2 = offset_1; + offset_1 = offset; + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + } + + } else { + assert(stepSize >= 1); + ip += ((ip-anchor) >> kSearchStrength) + stepSize; + continue; + } + } else { + U32 const offset = (U32)(ip-match); + mLength = ZSTD_count(ip+4, match+4, iend) + 4; while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; @@ -104,6 +140,7 @@ size_t ZSTD_compressBlock_fast_generic( hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ while ( (ip <= ilimit) + && (hasDict != ZSTD_hasDictMatchState || ip - offset_2 >= istart) && ( (offset_2>0) & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { /* store sequence */ @@ -132,17 +169,34 @@ size_t ZSTD_compressBlock_fast( U32 const hlog = cParams->hashLog; U32 const mls = cParams->searchLength; U32 const stepSize = cParams->targetLength; - switch(mls) - { - default: /* includes case 3 */ - case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4); - case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5); - case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6); - case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7); + if (ms->dictMatchState != NULL) { + ZSTD_hasDictMatchState_e const hdms = ZSTD_hasDictMatchState; + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, hdms); + case 5 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, hdms); + case 6 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, hdms); + case 7 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, hdms); + } + } else { + ZSTD_hasDictMatchState_e const hdms = ZSTD_noDictMatchState; + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, hdms); + case 5 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, hdms); + case 6 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, hdms); + case 7 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, hdms); + } } } From 70a537d1d7113a0604c0c22fe44fa1ae07824723 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 1 May 2018 16:21:18 -0400 Subject: [PATCH 086/288] Initial Repcode Check Support for Ext Dict Ctx --- lib/compress/zstd_fast.c | 42 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index df4423fc1..7ea865112 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -87,10 +87,20 @@ size_t ZSTD_compressBlock_fast_generic( U32 const current = (U32)(ip-base); U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; + const int repIndex = current + 1 - offset_1; + const BYTE* repBase = hasDict == ZSTD_hasDictMatchState && repIndex < lowestIndex ? dictBase - dictIndexDelta : base; + const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ - if ((hasDict != ZSTD_hasDictMatchState || current >= lowestIndex + offset_1) - && (offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { + if (hasDict == ZSTD_hasDictMatchState + && (((U32)((lowestIndex-1) - (U32)repIndex) >= 3) /* intentional underflow */) + && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { + const BYTE* repMatchEnd = repIndex < lowestIndex ? dictEnd : iend; + mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; + ip++; + ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); + } else if (hasDict == ZSTD_noDictMatchState + && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); @@ -109,7 +119,6 @@ size_t ZSTD_compressBlock_fast_generic( mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, istart) + 4; { U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); - DEBUGLOG(6, "ip %p (%u) dictMatch %p (%u) idxDelta %u", ip, current, dictMatch, dictMatchIndex, dictIndexDelta); while (((ip>anchor) & (dictMatch>dictLowest)) && (ip[-1] == dictMatch[-1])) { ip--; dictMatch--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; @@ -139,6 +148,31 @@ size_t ZSTD_compressBlock_fast_generic( hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2; /* here because current+2 could be > iend-8 */ hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ + + if (hasDict == ZSTD_hasDictMatchState) { + while (ip <= ilimit) { + U32 const current2 = (U32)(ip-base); + int const repIndex2 = current2 - offset_2; + const BYTE* repMatch2 = hasDict == ZSTD_hasDictMatchState + && repIndex2 < lowestIndex ? + dictBase - dictIndexDelta + repIndex2 : + base + repIndex2; + if ( (((U32)((lowestIndex-1) - (U32)repIndex2) >= 3)) /* intentional overflow */ + && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { + const BYTE* const repEnd2 = repIndex2 < lowestIndex ? dictEnd : iend; + size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; + U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ + ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); + hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; + ip += repLength2; + anchor = ip; + continue; + } + break; + } + } + + if (hasDict == ZSTD_noDictMatchState) { while ( (ip <= ilimit) && (hasDict != ZSTD_hasDictMatchState || ip - offset_2 >= istart) && ( (offset_2>0) @@ -151,7 +185,7 @@ size_t ZSTD_compressBlock_fast_generic( ip += rLength; anchor = ip; continue; /* faster when present ... (?) */ - } } } + } } } } /* save reps for next block */ rep[0] = offset_1 ? offset_1 : offsetSaved; From 6929964d65778ce6f0b851f955b3bb68f1a3490d Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 2 May 2018 15:12:18 -0400 Subject: [PATCH 087/288] Add bounds check in repcode tests --- lib/compress/zstd_fast.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 7ea865112..fb86199e2 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -60,18 +60,33 @@ size_t ZSTD_compressBlock_fast_generic( U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; + /* This is all complicated by the fact that we need to handle positions + * specified in 3 different ways: by direct pointers, by indices relative + * to the working context base, and by indices relative to the dict context + * base. + * + * Hence the unfortunate collision of "lowestDictIndex", which is the lowest + * index in the dict's index space, and "dictLowestIndex", which is the same + * position in the working context's index space. + */ + const ZSTD_matchState_t* const dms = ms->dictMatchState; const U32* const dictHashTable = hasDict == ZSTD_hasDictMatchState ? dms->hashTable : NULL; - const U32 dictLowestIndex = hasDict == ZSTD_hasDictMatchState ? + const U32 lowestDictIndex = hasDict == ZSTD_hasDictMatchState ? dms->window.dictLimit : 0; const BYTE* const dictBase = hasDict == ZSTD_hasDictMatchState ? dms->window.base : NULL; const BYTE* const dictLowest = hasDict == ZSTD_hasDictMatchState ? - dictBase + dictLowestIndex : NULL; + dictBase + lowestDictIndex : NULL; const BYTE* const dictEnd = hasDict == ZSTD_hasDictMatchState ? dms->window.nextSrc : NULL; - const U32 dictIndexDelta = lowestIndex - (dictEnd - dictBase); + const U32 dictIndexDelta = hasDict == ZSTD_hasDictMatchState ? + lowestIndex - (dictEnd - dictBase) : + 0; + ptrdiff_t dictLowestIndex = hasDict == ZSTD_hasDictMatchState ? + lowestDictIndex + dictIndexDelta : + lowestIndex; /* init */ ip += (ip==lowest); @@ -87,15 +102,15 @@ size_t ZSTD_compressBlock_fast_generic( U32 const current = (U32)(ip-base); U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; - const int repIndex = current + 1 - offset_1; - const BYTE* repBase = hasDict == ZSTD_hasDictMatchState && repIndex < lowestIndex ? dictBase - dictIndexDelta : base; + const ptrdiff_t repIndex = current + 1 - offset_1; + const BYTE* repBase = hasDict == ZSTD_hasDictMatchState && repIndex < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ if (hasDict == ZSTD_hasDictMatchState - && (((U32)((lowestIndex-1) - (U32)repIndex) >= 3) /* intentional underflow */) + && (((U32)((lowestIndex-1) - repIndex) >= 3) & (repIndex > dictLowestIndex) /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < lowestIndex ? dictEnd : iend; + const BYTE* repMatchEnd = repIndex < (ptrdiff_t)lowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); @@ -110,7 +125,7 @@ size_t ZSTD_compressBlock_fast_generic( if (hasDict == ZSTD_hasDictMatchState) { U32 const dictMatchIndex = dictHashTable[h]; const BYTE* dictMatch = dictBase + dictMatchIndex; - if (dictMatchIndex <= dictLowestIndex || + if (dictMatchIndex <= lowestDictIndex || MEM_read32(dictMatch) != MEM_read32(ip)) { assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; @@ -152,14 +167,14 @@ size_t ZSTD_compressBlock_fast_generic( if (hasDict == ZSTD_hasDictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); - int const repIndex2 = current2 - offset_2; + ptrdiff_t const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = hasDict == ZSTD_hasDictMatchState - && repIndex2 < lowestIndex ? + && repIndex2 < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; - if ( (((U32)((lowestIndex-1) - (U32)repIndex2) >= 3)) /* intentional overflow */ + if ( (((U32)((lowestIndex-1) - (U32)repIndex2) >= 3) & (repIndex2 > dictLowestIndex)) /* intentional overflow */ && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < lowestIndex ? dictEnd : iend; + const BYTE* const repEnd2 = repIndex2 < (ptrdiff_t)lowestIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); From 265c2869d1e955ae7a026f9e4130e4b981cb2835 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 2 May 2018 17:10:51 -0400 Subject: [PATCH 088/288] Split Wrapper Functions to Cause Inlining --- lib/compress/zstd_compress.c | 15 ++++--- lib/compress/zstd_compress_internal.h | 14 +++++- lib/compress/zstd_fast.c | 62 +++++++++++++++------------ lib/compress/zstd_fast.h | 3 ++ lib/compress/zstd_ldm.c | 3 +- 5 files changed, 62 insertions(+), 35 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 18091b09c..c58909fbe 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2137,9 +2137,9 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, /* ZSTD_selectBlockCompressor() : * Not static, but internal use only (used by long distance matcher) * assumption : strat is a valid strategy */ -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict) +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict, ZSTD_hasDictMatchState_e hdms) { - static const ZSTD_blockCompressor blockCompressor[2][(unsigned)ZSTD_btultra+1] = { + static const ZSTD_blockCompressor blockCompressor[3][(unsigned)ZSTD_btultra+1] = { { ZSTD_compressBlock_fast /* default for 0 */, ZSTD_compressBlock_fast, ZSTD_compressBlock_doubleFast, ZSTD_compressBlock_greedy, ZSTD_compressBlock_lazy, ZSTD_compressBlock_lazy2, ZSTD_compressBlock_btlazy2, @@ -2147,13 +2147,16 @@ ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict { ZSTD_compressBlock_fast_extDict /* default for 0 */, ZSTD_compressBlock_fast_extDict, ZSTD_compressBlock_doubleFast_extDict, ZSTD_compressBlock_greedy_extDict, ZSTD_compressBlock_lazy_extDict,ZSTD_compressBlock_lazy2_extDict, ZSTD_compressBlock_btlazy2_extDict, - ZSTD_compressBlock_btopt_extDict, ZSTD_compressBlock_btultra_extDict } + ZSTD_compressBlock_btopt_extDict, ZSTD_compressBlock_btultra_extDict }, + { ZSTD_compressBlock_fast_extDictMatchState /* default for 0 */, + ZSTD_compressBlock_fast_extDictMatchState, + NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); assert((U32)strat >= (U32)ZSTD_fast); assert((U32)strat <= (U32)ZSTD_btultra); - return blockCompressor[extDict!=0][(U32)strat]; + return blockCompressor[hdms == ZSTD_hasDictMatchState ? 2 : (extDict!=0)][(U32)strat]; } static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr, @@ -2196,6 +2199,8 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, /* select and store sequences */ { U32 const extDict = ZSTD_window_hasExtDict(ms->window); + ZSTD_hasDictMatchState_e const hdms = + ZSTD_matchState_hasDictMatchState(ms); size_t lastLLSize; { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) @@ -2229,7 +2234,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, src, srcSize, extDict); assert(ldmSeqStore.pos == ldmSeqStore.size); } else { /* not long range mode */ - ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict); + ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict, hdms); lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, src, srcSize); } { const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize; diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 05685e559..f3a4347b7 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -252,10 +252,20 @@ typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e; typedef enum { ZSTD_noDictMatchState, ZSTD_hasDictMatchState } ZSTD_hasDictMatchState_e; +/** + * ZSTD_matchState_hasDictMatchState(): + * Does what the label says. + */ +MEM_STATIC ZSTD_hasDictMatchState_e ZSTD_matchState_hasDictMatchState(const ZSTD_matchState_t *ms) +{ + return ms->dictMatchState != NULL ? ZSTD_hasDictMatchState : ZSTD_noDictMatchState; +} + + typedef size_t (*ZSTD_blockCompressor) ( ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict); +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict, ZSTD_hasDictMatchState_e hdms); MEM_STATIC U32 ZSTD_LLcode(U32 litLength) @@ -512,6 +522,8 @@ MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window) return window.lowLimit < window.dictLimit; } + + /** * ZSTD_window_needOverflowCorrection(): * Returns non-zero if the indices are getting too large and need overflow diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index fb86199e2..5f152488c 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -218,34 +218,40 @@ size_t ZSTD_compressBlock_fast( U32 const hlog = cParams->hashLog; U32 const mls = cParams->searchLength; U32 const stepSize = cParams->targetLength; - if (ms->dictMatchState != NULL) { - ZSTD_hasDictMatchState_e const hdms = ZSTD_hasDictMatchState; - switch(mls) - { - default: /* includes case 3 */ - case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, hdms); - case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, hdms); - case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, hdms); - case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, hdms); - } - } else { - ZSTD_hasDictMatchState_e const hdms = ZSTD_noDictMatchState; - switch(mls) - { - default: /* includes case 3 */ - case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, hdms); - case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, hdms); - case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, hdms); - case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, hdms); - } + assert(ms->dictMatchState == NULL); + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_noDictMatchState); + case 5 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_noDictMatchState); + case 6 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_noDictMatchState); + case 7 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_noDictMatchState); + } +} + +size_t ZSTD_compressBlock_fast_extDictMatchState( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) +{ + U32 const hlog = cParams->hashLog; + U32 const mls = cParams->searchLength; + U32 const stepSize = cParams->targetLength; + assert(ms->dictMatchState != NULL); + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_hasDictMatchState); + case 5 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_hasDictMatchState); + case 6 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_hasDictMatchState); + case 7 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_hasDictMatchState); } } diff --git a/lib/compress/zstd_fast.h b/lib/compress/zstd_fast.h index 746849fce..804d36f2a 100644 --- a/lib/compress/zstd_fast.h +++ b/lib/compress/zstd_fast.h @@ -24,6 +24,9 @@ void ZSTD_fillHashTable(ZSTD_matchState_t* ms, size_t ZSTD_compressBlock_fast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_fast_extDictMatchState( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); size_t ZSTD_compressBlock_fast_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index 9d825e695..b58c2f1ca 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -596,7 +596,8 @@ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, { unsigned const minMatch = cParams->searchLength; ZSTD_blockCompressor const blockCompressor = - ZSTD_selectBlockCompressor(cParams->strategy, extDict); + ZSTD_selectBlockCompressor(cParams->strategy, extDict, + ZSTD_matchState_hasDictMatchState(ms)); BYTE const* const base = ms->window.base; /* Input bounds */ BYTE const* const istart = (BYTE const*)src; From b67196f30d093c0be0fab4e090c9c748779ab27a Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 2 May 2018 17:34:34 -0400 Subject: [PATCH 089/288] Coalesce hasDictMatchState and extDict Checks into One Enum and Rename Stuff --- lib/compress/zstd_compress.c | 20 +++++------ lib/compress/zstd_compress_internal.h | 23 ++++++------ lib/compress/zstd_fast.c | 52 ++++++++++++++------------- lib/compress/zstd_fast.h | 2 +- lib/compress/zstd_ldm.c | 7 ++-- lib/compress/zstd_ldm.h | 3 +- 6 files changed, 52 insertions(+), 55 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c58909fbe..6bbd09c0a 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2137,7 +2137,7 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, /* ZSTD_selectBlockCompressor() : * Not static, but internal use only (used by long distance matcher) * assumption : strat is a valid strategy */ -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict, ZSTD_hasDictMatchState_e hdms) +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e dictMode) { static const ZSTD_blockCompressor blockCompressor[3][(unsigned)ZSTD_btultra+1] = { { ZSTD_compressBlock_fast /* default for 0 */, @@ -2148,15 +2148,15 @@ ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict ZSTD_compressBlock_fast_extDict, ZSTD_compressBlock_doubleFast_extDict, ZSTD_compressBlock_greedy_extDict, ZSTD_compressBlock_lazy_extDict,ZSTD_compressBlock_lazy2_extDict, ZSTD_compressBlock_btlazy2_extDict, ZSTD_compressBlock_btopt_extDict, ZSTD_compressBlock_btultra_extDict }, - { ZSTD_compressBlock_fast_extDictMatchState /* default for 0 */, - ZSTD_compressBlock_fast_extDictMatchState, - NULL, NULL, NULL, NULL, NULL, NULL, NULL } + { ZSTD_compressBlock_fast_dictMatchState /* default for 0 */, + ZSTD_compressBlock_fast_dictMatchState, + NULL, NULL, NULL, NULL, NULL, NULL, NULL /* unimplemented as of yet */ } }; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); assert((U32)strat >= (U32)ZSTD_fast); assert((U32)strat <= (U32)ZSTD_btultra); - return blockCompressor[hdms == ZSTD_hasDictMatchState ? 2 : (extDict!=0)][(U32)strat]; + return blockCompressor[(int)dictMode][(U32)strat]; } static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr, @@ -2198,9 +2198,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, } /* select and store sequences */ - { U32 const extDict = ZSTD_window_hasExtDict(ms->window); - ZSTD_hasDictMatchState_e const hdms = - ZSTD_matchState_hasDictMatchState(ms); + { ZSTD_dictMode_e const dictMode = ZSTD_matchState_dictMode(ms); size_t lastLLSize; { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) @@ -2214,7 +2212,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, ms, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, - src, srcSize, extDict); + src, srcSize); assert(zc->externSeqStore.pos <= zc->externSeqStore.size); } else if (zc->appliedParams.ldmParams.enableLdm) { rawSeqStore_t ldmSeqStore = {NULL, 0, 0, 0}; @@ -2231,10 +2229,10 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, ms, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, - src, srcSize, extDict); + src, srcSize); assert(ldmSeqStore.pos == ldmSeqStore.size); } else { /* not long range mode */ - ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict, hdms); + ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, dictMode); lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, src, srcSize); } { const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize; diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index f3a4347b7..32bbe08b8 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -250,22 +250,13 @@ struct ZSTD_CCtx_s { typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e; -typedef enum { ZSTD_noDictMatchState, ZSTD_hasDictMatchState } ZSTD_hasDictMatchState_e; - -/** - * ZSTD_matchState_hasDictMatchState(): - * Does what the label says. - */ -MEM_STATIC ZSTD_hasDictMatchState_e ZSTD_matchState_hasDictMatchState(const ZSTD_matchState_t *ms) -{ - return ms->dictMatchState != NULL ? ZSTD_hasDictMatchState : ZSTD_noDictMatchState; -} +typedef enum { ZSTD_noDict = 0, ZSTD_extDict = 1, ZSTD_dictMatchState = 2 } ZSTD_dictMode_e; typedef size_t (*ZSTD_blockCompressor) ( ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict, ZSTD_hasDictMatchState_e hdms); +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e hdms); MEM_STATIC U32 ZSTD_LLcode(U32 litLength) @@ -522,7 +513,15 @@ MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window) return window.lowLimit < window.dictLimit; } - +/** + * ZSTD_matchState_dictMode(): + * Does what the label says. + */ +MEM_STATIC ZSTD_dictMode_e ZSTD_matchState_dictMode(const ZSTD_matchState_t *ms) +{ + return ms->dictMatchState != NULL ? ZSTD_dictMatchState : + ZSTD_window_hasExtDict(ms->window) ? ZSTD_extDict : ZSTD_noDict; +} /** * ZSTD_window_needOverflowCorrection(): diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 5f152488c..8f3d33e66 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -46,7 +46,7 @@ size_t ZSTD_compressBlock_fast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, U32 const hlog, U32 const stepSize, U32 const mls, - ZSTD_hasDictMatchState_e const hasDict) + ZSTD_dictMode_e const hasDict) { U32* const hashTable = ms->hashTable; const BYTE* const base = ms->window.base; @@ -71,23 +71,25 @@ size_t ZSTD_compressBlock_fast_generic( */ const ZSTD_matchState_t* const dms = ms->dictMatchState; - const U32* const dictHashTable = hasDict == ZSTD_hasDictMatchState ? + const U32* const dictHashTable = hasDict == ZSTD_dictMatchState ? dms->hashTable : NULL; - const U32 lowestDictIndex = hasDict == ZSTD_hasDictMatchState ? + const U32 lowestDictIndex = hasDict == ZSTD_dictMatchState ? dms->window.dictLimit : 0; - const BYTE* const dictBase = hasDict == ZSTD_hasDictMatchState ? + const BYTE* const dictBase = hasDict == ZSTD_dictMatchState ? dms->window.base : NULL; - const BYTE* const dictLowest = hasDict == ZSTD_hasDictMatchState ? + const BYTE* const dictLowest = hasDict == ZSTD_dictMatchState ? dictBase + lowestDictIndex : NULL; - const BYTE* const dictEnd = hasDict == ZSTD_hasDictMatchState ? + const BYTE* const dictEnd = hasDict == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; - const U32 dictIndexDelta = hasDict == ZSTD_hasDictMatchState ? + const U32 dictIndexDelta = hasDict == ZSTD_dictMatchState ? lowestIndex - (dictEnd - dictBase) : 0; - ptrdiff_t dictLowestIndex = hasDict == ZSTD_hasDictMatchState ? + ptrdiff_t dictLowestIndex = hasDict == ZSTD_dictMatchState ? lowestDictIndex + dictIndexDelta : lowestIndex; + assert(hasDict == ZSTD_noDict || hasDict == ZSTD_dictMatchState); + /* init */ ip += (ip==lowest); { U32 const maxRep = (U32)(ip-lowest); @@ -103,18 +105,18 @@ size_t ZSTD_compressBlock_fast_generic( U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; const ptrdiff_t repIndex = current + 1 - offset_1; - const BYTE* repBase = hasDict == ZSTD_hasDictMatchState && repIndex < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta : base; + const BYTE* repBase = hasDict == ZSTD_dictMatchState && repIndex < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ - if (hasDict == ZSTD_hasDictMatchState + if (hasDict == ZSTD_dictMatchState && (((U32)((lowestIndex-1) - repIndex) >= 3) & (repIndex > dictLowestIndex) /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { const BYTE* repMatchEnd = repIndex < (ptrdiff_t)lowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else if (hasDict == ZSTD_noDictMatchState + } else if (hasDict == ZSTD_noDict && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; @@ -122,7 +124,7 @@ size_t ZSTD_compressBlock_fast_generic( } else { if ( (matchIndex <= lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { - if (hasDict == ZSTD_hasDictMatchState) { + if (hasDict == ZSTD_dictMatchState) { U32 const dictMatchIndex = dictHashTable[h]; const BYTE* dictMatch = dictBase + dictMatchIndex; if (dictMatchIndex <= lowestDictIndex || @@ -164,11 +166,11 @@ size_t ZSTD_compressBlock_fast_generic( hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ - if (hasDict == ZSTD_hasDictMatchState) { + if (hasDict == ZSTD_dictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); ptrdiff_t const repIndex2 = current2 - offset_2; - const BYTE* repMatch2 = hasDict == ZSTD_hasDictMatchState + const BYTE* repMatch2 = hasDict == ZSTD_dictMatchState && repIndex2 < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; @@ -187,9 +189,9 @@ size_t ZSTD_compressBlock_fast_generic( } } - if (hasDict == ZSTD_noDictMatchState) { + if (hasDict == ZSTD_noDict) { while ( (ip <= ilimit) - && (hasDict != ZSTD_hasDictMatchState || ip - offset_2 >= istart) + && (hasDict != ZSTD_dictMatchState || ip - offset_2 >= istart) && ( (offset_2>0) & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { /* store sequence */ @@ -223,17 +225,17 @@ size_t ZSTD_compressBlock_fast( { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_noDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_noDict); case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_noDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_noDict); case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_noDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_noDict); case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_noDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_noDict); } } -size_t ZSTD_compressBlock_fast_extDictMatchState( +size_t ZSTD_compressBlock_fast_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { @@ -245,13 +247,13 @@ size_t ZSTD_compressBlock_fast_extDictMatchState( { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_hasDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_dictMatchState); case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_hasDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_dictMatchState); case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_hasDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_dictMatchState); case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_hasDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_dictMatchState); } } diff --git a/lib/compress/zstd_fast.h b/lib/compress/zstd_fast.h index 804d36f2a..7e7435f8c 100644 --- a/lib/compress/zstd_fast.h +++ b/lib/compress/zstd_fast.h @@ -24,7 +24,7 @@ void ZSTD_fillHashTable(ZSTD_matchState_t* ms, size_t ZSTD_compressBlock_fast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); -size_t ZSTD_compressBlock_fast_extDictMatchState( +size_t ZSTD_compressBlock_fast_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); size_t ZSTD_compressBlock_fast_extDict( diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index b58c2f1ca..b0c5d0654 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -591,13 +591,12 @@ static rawSeq maybeSplitSequence(rawSeqStore_t* rawSeqStore, size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize, - int const extDict) + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { unsigned const minMatch = cParams->searchLength; ZSTD_blockCompressor const blockCompressor = - ZSTD_selectBlockCompressor(cParams->strategy, extDict, - ZSTD_matchState_hasDictMatchState(ms)); + ZSTD_selectBlockCompressor(cParams->strategy, + ZSTD_matchState_dictMode(ms)); BYTE const* const base = ms->window.base; /* Input bounds */ BYTE const* const istart = (BYTE const*)src; diff --git a/lib/compress/zstd_ldm.h b/lib/compress/zstd_ldm.h index 0c3789ff1..96588adb0 100644 --- a/lib/compress/zstd_ldm.h +++ b/lib/compress/zstd_ldm.h @@ -62,8 +62,7 @@ size_t ZSTD_ldm_generateSequences( size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, - void const* src, size_t srcSize, - int const extDict); + void const* src, size_t srcSize); /** * ZSTD_ldm_skipSequences(): From c31ee3c7f827ebb1f3141ca4162e4721d3617752 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 2 May 2018 20:30:03 -0400 Subject: [PATCH 090/288] Fix Rep Code Initialization --- lib/compress/zstd_fast.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 8f3d33e66..067efba5e 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -91,8 +91,10 @@ size_t ZSTD_compressBlock_fast_generic( assert(hasDict == ZSTD_noDict || hasDict == ZSTD_dictMatchState); /* init */ - ip += (ip==lowest); - { U32 const maxRep = (U32)(ip-lowest); + ip += (hasDict == ZSTD_noDict && ip == lowest); + { U32 const maxRep = hasDict == ZSTD_dictMatchState ? + (U32)(ip - dictLowest) : + (U32)(ip - lowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } From 66bc1ca64142a63f846035c4f5539d400113e56b Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 2 May 2018 22:28:29 -0400 Subject: [PATCH 091/288] Change Cut-Off to 8 KB --- lib/compress/zstd_compress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 6bbd09c0a..b877a7fb2 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1208,7 +1208,7 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, * context, or referencing the dictionary context from the working context * in-place. We decide here which strategy to use. */ /* TODO: pick reasonable cut-off size, handle ZSTD_CONTENTSIZE_UNKNOWN */ - int attachDict = pledgedSrcSize < 64 KB + int attachDict = pledgedSrcSize <= 8 KB && cdict->cParams.strategy == ZSTD_fast && ZSTD_equivalentCParams(cctx->appliedParams.cParams, cdict->cParams); From ca26cecc7a48e317bee986c0e34dd8a9887eb2f5 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Fri, 4 May 2018 13:08:07 -0400 Subject: [PATCH 092/288] Rename and Reformat --- lib/compress/zstd_fast.c | 163 +++++++++++++++++++-------------------- 1 file changed, 80 insertions(+), 83 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 067efba5e..d4eaeb6d4 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -53,23 +53,13 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ms->window.dictLimit; - const BYTE* const lowest = base + lowestIndex; + const U32 localLowestIndex = ms->window.dictLimit; + const BYTE* const localLowest = base + localLowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; - /* This is all complicated by the fact that we need to handle positions - * specified in 3 different ways: by direct pointers, by indices relative - * to the working context base, and by indices relative to the dict context - * base. - * - * Hence the unfortunate collision of "lowestDictIndex", which is the lowest - * index in the dict's index space, and "dictLowestIndex", which is the same - * position in the working context's index space. - */ - const ZSTD_matchState_t* const dms = ms->dictMatchState; const U32* const dictHashTable = hasDict == ZSTD_dictMatchState ? dms->hashTable : NULL; @@ -82,19 +72,19 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const dictEnd = hasDict == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = hasDict == ZSTD_dictMatchState ? - lowestIndex - (dictEnd - dictBase) : + localLowestIndex - (dictEnd - dictBase) : 0; - ptrdiff_t dictLowestIndex = hasDict == ZSTD_dictMatchState ? + ptrdiff_t dictLowestLocalIndex = hasDict == ZSTD_dictMatchState ? lowestDictIndex + dictIndexDelta : - lowestIndex; + localLowestIndex; assert(hasDict == ZSTD_noDict || hasDict == ZSTD_dictMatchState); /* init */ - ip += (hasDict == ZSTD_noDict && ip == lowest); + ip += (hasDict == ZSTD_noDict && ip == localLowest); { U32 const maxRep = hasDict == ZSTD_dictMatchState ? (U32)(ip - dictLowest) : - (U32)(ip - lowest); + (U32)(ip - localLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } @@ -106,57 +96,63 @@ size_t ZSTD_compressBlock_fast_generic( U32 const current = (U32)(ip-base); U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; - const ptrdiff_t repIndex = current + 1 - offset_1; - const BYTE* repBase = hasDict == ZSTD_dictMatchState && repIndex < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta : base; + const ptrdiff_t repIndex = (ptrdiff_t)current + 1 - offset_1; + const BYTE* repBase = (hasDict == ZSTD_dictMatchState + && repIndex < (ptrdiff_t)localLowestIndex) ? + dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ if (hasDict == ZSTD_dictMatchState - && (((U32)((lowestIndex-1) - repIndex) >= 3) & (repIndex > dictLowestIndex) /* intentional underflow */) + && (((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) + & (repIndex > dictLowestLocalIndex)) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < (ptrdiff_t)lowestIndex ? dictEnd : iend; + const BYTE* repMatchEnd = repIndex < (ptrdiff_t)localLowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else if (hasDict == ZSTD_noDict - && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { + } else if ( hasDict == ZSTD_noDict + && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else { - if ( (matchIndex <= lowestIndex) - || (MEM_read32(match) != MEM_read32(ip)) ) { - if (hasDict == ZSTD_dictMatchState) { - U32 const dictMatchIndex = dictHashTable[h]; - const BYTE* dictMatch = dictBase + dictMatchIndex; - if (dictMatchIndex <= lowestDictIndex || - MEM_read32(dictMatch) != MEM_read32(ip)) { - assert(stepSize >= 1); - ip += ((ip-anchor) >> kSearchStrength) + stepSize; - continue; - } - - mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, istart) + 4; - { U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); - while (((ip>anchor) & (dictMatch>dictLowest)) && (ip[-1] == dictMatch[-1])) { ip--; dictMatch--; mLength++; } /* catch up */ - offset_2 = offset_1; - offset_1 = offset; - ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); - } - - } else { + } else if ( (matchIndex <= localLowestIndex) + || (MEM_read32(match) != MEM_read32(ip)) ) { + if (hasDict == ZSTD_dictMatchState) { + U32 const dictMatchIndex = dictHashTable[h]; + const BYTE* dictMatch = dictBase + dictMatchIndex; + if (dictMatchIndex <= lowestDictIndex || + MEM_read32(dictMatch) != MEM_read32(ip)) { assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; continue; + } else { + /* found a dict match */ + U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); + mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, istart) + 4; + while (((ip>anchor) & (dictMatch>dictLowest)) + && (ip[-1] == dictMatch[-1])) { + ip--; dictMatch--; mLength++; + } /* catch up */ + offset_2 = offset_1; + offset_1 = offset; + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } } else { - U32 const offset = (U32)(ip-match); - mLength = ZSTD_count(ip+4, match+4, iend) + 4; - while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - offset_2 = offset_1; - offset_1 = offset; - ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); - } } + assert(stepSize >= 1); + ip += ((ip-anchor) >> kSearchStrength) + stepSize; + continue; + } + } else { + /* found a regular match */ + U32 const offset = (U32)(ip-match); + mLength = ZSTD_count(ip+4, match+4, iend) + 4; + while (((ip>anchor) & (match>localLowest)) + && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + offset_2 = offset_1; + offset_1 = offset; + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + } /* match found */ ip += mLength; @@ -169,41 +165,42 @@ size_t ZSTD_compressBlock_fast_generic( /* check immediate repcode */ if (hasDict == ZSTD_dictMatchState) { - while (ip <= ilimit) { - U32 const current2 = (U32)(ip-base); - ptrdiff_t const repIndex2 = current2 - offset_2; - const BYTE* repMatch2 = hasDict == ZSTD_dictMatchState - && repIndex2 < (ptrdiff_t)lowestIndex ? - dictBase - dictIndexDelta + repIndex2 : - base + repIndex2; - if ( (((U32)((lowestIndex-1) - (U32)repIndex2) >= 3) & (repIndex2 > dictLowestIndex)) /* intentional overflow */ - && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < (ptrdiff_t)lowestIndex ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; - U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); - hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; - ip += repLength2; - anchor = ip; - continue; + while (ip <= ilimit) { + U32 const current2 = (U32)(ip-base); + ptrdiff_t const repIndex2 = (ptrdiff_t)current2 - offset_2; + const BYTE* repMatch2 = hasDict == ZSTD_dictMatchState + && repIndex2 < (ptrdiff_t)localLowestIndex ? + dictBase - dictIndexDelta + repIndex2 : + base + repIndex2; + if ( (((U32)((localLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) + & (repIndex2 > dictLowestLocalIndex)) + && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { + const BYTE* const repEnd2 = repIndex2 < (ptrdiff_t)localLowestIndex ? dictEnd : iend; + size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; + U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ + ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); + hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; + ip += repLength2; + anchor = ip; + continue; + } + break; } - break; - } } if (hasDict == ZSTD_noDict) { - while ( (ip <= ilimit) - && (hasDict != ZSTD_dictMatchState || ip - offset_2 >= istart) - && ( (offset_2>0) - & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { - /* store sequence */ - size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; - { U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */ - hashTable[ZSTD_hashPtr(ip, hlog, mls)] = (U32)(ip-base); - ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); - ip += rLength; - anchor = ip; - continue; /* faster when present ... (?) */ + while ( (ip <= ilimit) + && (ip - offset_2 >= istart) + && ( (offset_2>0) + & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { + /* store sequence */ + size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; + U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */ + hashTable[ZSTD_hashPtr(ip, hlog, mls)] = (U32)(ip-base); + ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); + ip += rLength; + anchor = ip; + continue; /* faster when present ... (?) */ } } } } /* save reps for next block */ From ae4fcf781613255b76dbf3da55f452bbc5537065 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 13:14:20 -0400 Subject: [PATCH 093/288] Respond to PR Comments; Formatting/Style/Lint Fixes --- lib/compress/zstd_compress.c | 5 ++++- lib/compress/zstd_compress_internal.h | 10 +++++++--- lib/compress/zstd_fast.c | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b877a7fb2..f0576f081 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2152,11 +2152,14 @@ ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMo ZSTD_compressBlock_fast_dictMatchState, NULL, NULL, NULL, NULL, NULL, NULL, NULL /* unimplemented as of yet */ } }; + ZSTD_blockCompressor selectedCompressor; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); assert((U32)strat >= (U32)ZSTD_fast); assert((U32)strat <= (U32)ZSTD_btultra); - return blockCompressor[(int)dictMode][(U32)strat]; + selectedCompressor = blockCompressor[(int)dictMode][(U32)strat]; + assert(selectedCompressor != NULL); + return selectedCompressor; } static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr, diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 32bbe08b8..913497e7f 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -515,12 +515,16 @@ MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window) /** * ZSTD_matchState_dictMode(): - * Does what the label says. + * Inspects the provided matchState and figures out what dictMode should be + * passed to the compressor. */ MEM_STATIC ZSTD_dictMode_e ZSTD_matchState_dictMode(const ZSTD_matchState_t *ms) { - return ms->dictMatchState != NULL ? ZSTD_dictMatchState : - ZSTD_window_hasExtDict(ms->window) ? ZSTD_extDict : ZSTD_noDict; + return ms->dictMatchState != NULL ? + ZSTD_dictMatchState : + ZSTD_window_hasExtDict(ms->window) ? + ZSTD_extDict : + ZSTD_noDict; } /** diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index d4eaeb6d4..60c88e571 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -72,7 +72,7 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const dictEnd = hasDict == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = hasDict == ZSTD_dictMatchState ? - localLowestIndex - (dictEnd - dictBase) : + localLowestIndex - (U32)(dictEnd - dictBase) : 0; ptrdiff_t dictLowestLocalIndex = hasDict == ZSTD_dictMatchState ? lowestDictIndex + dictIndexDelta : From 191fc74a51aa20d46b1ec996a7e20dc3f1dbaf5e Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 15:14:12 -0400 Subject: [PATCH 094/288] Rename 'hasDict' to 'dictMode' --- lib/compress/zstd_compress_internal.h | 2 +- lib/compress/zstd_fast.c | 36 +++++++++++++-------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 913497e7f..80c03433f 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -256,7 +256,7 @@ typedef enum { ZSTD_noDict = 0, ZSTD_extDict = 1, ZSTD_dictMatchState = 2 } ZSTD typedef size_t (*ZSTD_blockCompressor) ( ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e hdms); +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e dictMode); MEM_STATIC U32 ZSTD_LLcode(U32 litLength) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 60c88e571..f211f1425 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -46,7 +46,7 @@ size_t ZSTD_compressBlock_fast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, U32 const hlog, U32 const stepSize, U32 const mls, - ZSTD_dictMode_e const hasDict) + ZSTD_dictMode_e const dictMode) { U32* const hashTable = ms->hashTable; const BYTE* const base = ms->window.base; @@ -61,28 +61,28 @@ size_t ZSTD_compressBlock_fast_generic( U32 offsetSaved = 0; const ZSTD_matchState_t* const dms = ms->dictMatchState; - const U32* const dictHashTable = hasDict == ZSTD_dictMatchState ? + const U32* const dictHashTable = dictMode == ZSTD_dictMatchState ? dms->hashTable : NULL; - const U32 lowestDictIndex = hasDict == ZSTD_dictMatchState ? + const U32 lowestDictIndex = dictMode == ZSTD_dictMatchState ? dms->window.dictLimit : 0; - const BYTE* const dictBase = hasDict == ZSTD_dictMatchState ? + const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; - const BYTE* const dictLowest = hasDict == ZSTD_dictMatchState ? + const BYTE* const dictLowest = dictMode == ZSTD_dictMatchState ? dictBase + lowestDictIndex : NULL; - const BYTE* const dictEnd = hasDict == ZSTD_dictMatchState ? + const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; - const U32 dictIndexDelta = hasDict == ZSTD_dictMatchState ? + const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? localLowestIndex - (U32)(dictEnd - dictBase) : 0; - ptrdiff_t dictLowestLocalIndex = hasDict == ZSTD_dictMatchState ? + ptrdiff_t dictLowestLocalIndex = dictMode == ZSTD_dictMatchState ? lowestDictIndex + dictIndexDelta : localLowestIndex; - assert(hasDict == ZSTD_noDict || hasDict == ZSTD_dictMatchState); + assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); /* init */ - ip += (hasDict == ZSTD_noDict && ip == localLowest); - { U32 const maxRep = hasDict == ZSTD_dictMatchState ? + ip += (dictMode == ZSTD_noDict && ip == localLowest); + { U32 const maxRep = dictMode == ZSTD_dictMatchState ? (U32)(ip - dictLowest) : (U32)(ip - localLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; @@ -97,13 +97,13 @@ size_t ZSTD_compressBlock_fast_generic( U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; const ptrdiff_t repIndex = (ptrdiff_t)current + 1 - offset_1; - const BYTE* repBase = (hasDict == ZSTD_dictMatchState + const BYTE* repBase = (dictMode == ZSTD_dictMatchState && repIndex < (ptrdiff_t)localLowestIndex) ? dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ - if (hasDict == ZSTD_dictMatchState + if (dictMode == ZSTD_dictMatchState && (((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) & (repIndex > dictLowestLocalIndex)) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { @@ -111,14 +111,14 @@ size_t ZSTD_compressBlock_fast_generic( mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else if ( hasDict == ZSTD_noDict + } else if ( dictMode == ZSTD_noDict && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else if ( (matchIndex <= localLowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { - if (hasDict == ZSTD_dictMatchState) { + if (dictMode == ZSTD_dictMatchState) { U32 const dictMatchIndex = dictHashTable[h]; const BYTE* dictMatch = dictBase + dictMatchIndex; if (dictMatchIndex <= lowestDictIndex || @@ -164,11 +164,11 @@ size_t ZSTD_compressBlock_fast_generic( hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ - if (hasDict == ZSTD_dictMatchState) { + if (dictMode == ZSTD_dictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); ptrdiff_t const repIndex2 = (ptrdiff_t)current2 - offset_2; - const BYTE* repMatch2 = hasDict == ZSTD_dictMatchState + const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState && repIndex2 < (ptrdiff_t)localLowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; @@ -188,7 +188,7 @@ size_t ZSTD_compressBlock_fast_generic( } } - if (hasDict == ZSTD_noDict) { + if (dictMode == ZSTD_noDict) { while ( (ip <= ilimit) && (ip - offset_2 >= istart) && ( (offset_2>0) From 154eb0941990b97543081b03bf38c469d3a3c172 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 18:40:23 -0400 Subject: [PATCH 095/288] Switch to Original Match Calc for noDict Repcode Check --- lib/compress/zstd_fast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index f211f1425..48a165d3f 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -112,7 +112,7 @@ size_t ZSTD_compressBlock_fast_generic( ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else if ( dictMode == ZSTD_noDict - && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { + && (offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); From d005e5daf4323b0c67eb34391ca51445a60372a5 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 10 May 2018 13:46:19 -0400 Subject: [PATCH 096/288] Whitespace Fix --- lib/compress/zstd_fast.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 48a165d3f..df2a0a055 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -130,7 +130,7 @@ size_t ZSTD_compressBlock_fast_generic( /* found a dict match */ U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, istart) + 4; - while (((ip>anchor) & (dictMatch>dictLowest)) + while (((ip>anchor) & (dictMatch>dictLowest)) && (ip[-1] == dictMatch[-1])) { ip--; dictMatch--; mLength++; } /* catch up */ @@ -162,8 +162,8 @@ size_t ZSTD_compressBlock_fast_generic( /* Fill Table */ hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2; /* here because current+2 could be > iend-8 */ hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); - /* check immediate repcode */ + /* check immediate repcode */ if (dictMode == ZSTD_dictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); From 2d598e6fedd798086894f953fbc44b189bca746a Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 10 May 2018 17:17:10 -0400 Subject: [PATCH 097/288] Force Working Context Indices Greater than Dict Indices --- lib/compress/zstd_compress.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index f0576f081..e05882684 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1229,6 +1229,17 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, if (attachDict) { DEBUGLOG(4, "attaching dictionary into context"); cctx->blockState.matchState.dictMatchState = &cdict->matchState; + + /* prep working match state so dict matches never have negative indices + * when they are translated to the working context's index space. */ + if (cctx->blockState.matchState.window.dictLimit < + (U32)(cdict->matchState.window.nextSrc - cdict->matchState.window.base)) { + cctx->blockState.matchState.window.nextSrc = + cctx->blockState.matchState.window.base + + ( cdict->matchState.window.nextSrc + - cdict->matchState.window.base); + ZSTD_window_clear(&cctx->blockState.matchState.window); + } } else { DEBUGLOG(4, "copying dictionary into context"); /* copy tables */ From 1a7b34ef28d309f58ce4988071eb2b6bf4830599 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 10 May 2018 17:18:08 -0400 Subject: [PATCH 098/288] Use New Index Invariant to Simplify Conditionals --- lib/compress/zstd_fast.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index df2a0a055..dacb26370 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -74,16 +74,18 @@ size_t ZSTD_compressBlock_fast_generic( const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? localLowestIndex - (U32)(dictEnd - dictBase) : 0; - ptrdiff_t dictLowestLocalIndex = dictMode == ZSTD_dictMatchState ? - lowestDictIndex + dictIndexDelta : - localLowestIndex; assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); + /* otherwise, we would get index underflow when translating a dict index + * into a local index */ + assert(dictMode != ZSTD_dictMatchState + || localLowestIndex >= (U32)(dictEnd - dictBase)); + /* init */ ip += (dictMode == ZSTD_noDict && ip == localLowest); { U32 const maxRep = dictMode == ZSTD_dictMatchState ? - (U32)(ip - dictLowest) : + (U32)(ip - localLowest + dictEnd - dictLowest) : (U32)(ip - localLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; @@ -96,23 +98,22 @@ size_t ZSTD_compressBlock_fast_generic( U32 const current = (U32)(ip-base); U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; - const ptrdiff_t repIndex = (ptrdiff_t)current + 1 - offset_1; + const U32 repIndex = current + 1 - offset_1; const BYTE* repBase = (dictMode == ZSTD_dictMatchState - && repIndex < (ptrdiff_t)localLowestIndex) ? + && repIndex < localLowestIndex) ? dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ if (dictMode == ZSTD_dictMatchState - && (((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) - & (repIndex > dictLowestLocalIndex)) + && ((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < (ptrdiff_t)localLowestIndex ? dictEnd : iend; + const BYTE* repMatchEnd = repIndex < localLowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else if ( dictMode == ZSTD_noDict - && (offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { + && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); @@ -167,15 +168,14 @@ size_t ZSTD_compressBlock_fast_generic( if (dictMode == ZSTD_dictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); - ptrdiff_t const repIndex2 = (ptrdiff_t)current2 - offset_2; + U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState - && repIndex2 < (ptrdiff_t)localLowestIndex ? + && repIndex2 < localLowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; - if ( (((U32)((localLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) - & (repIndex2 > dictLowestLocalIndex)) + if ( ((U32)((localLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < (ptrdiff_t)localLowestIndex ? dictEnd : iend; + const BYTE* const repEnd2 = repIndex2 < localLowestIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); @@ -190,7 +190,6 @@ size_t ZSTD_compressBlock_fast_generic( if (dictMode == ZSTD_noDict) { while ( (ip <= ilimit) - && (ip - offset_2 >= istart) && ( (offset_2>0) & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { /* store sequence */ From b05ae9b6086fea37b7c7edee1fc8296e01a1b521 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 01:15:33 -0400 Subject: [PATCH 099/288] Refine ip Initialization to Avoid ARM Weirdness --- lib/compress/zstd_fast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index dacb26370..5c6f0dc8b 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -83,7 +83,7 @@ size_t ZSTD_compressBlock_fast_generic( || localLowestIndex >= (U32)(dictEnd - dictBase)); /* init */ - ip += (dictMode == ZSTD_noDict && ip == localLowest); + ip += (ip - localLowest + dictEnd - dictLowest == 0); { U32 const maxRep = dictMode == ZSTD_dictMatchState ? (U32)(ip - localLowest + dictEnd - dictLowest) : (U32)(ip - localLowest); From 3ba70cc759550c7ac4f3c02d0dfc3de113d594e9 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 13:08:03 -0400 Subject: [PATCH 100/288] Clear the Dictionary When Sliding the Window --- lib/compress/zstd_compress.c | 4 +++- lib/compress/zstd_compress_internal.h | 13 ++++++++----- lib/compress/zstd_ldm.c | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e05882684..9f488b9ac 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1240,6 +1240,7 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, - cdict->matchState.window.base); ZSTD_window_clear(&cctx->blockState.matchState.window); } + cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; } else { DEBUGLOG(4, "copying dictionary into context"); /* copy tables */ @@ -2313,8 +2314,9 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, if (ms->nextToUpdate < correction) ms->nextToUpdate = 0; else ms->nextToUpdate -= correction; ms->loadedDictEnd = 0; + ms->dictMatchState = NULL; } - ZSTD_window_enforceMaxDist(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd); + ZSTD_window_enforceMaxDist(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd, &ms->dictMatchState); if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit; { size_t cSize = ZSTD_compressBlock_internal(cctx, diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 80c03433f..a61fc3742 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -520,10 +520,10 @@ MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window) */ MEM_STATIC ZSTD_dictMode_e ZSTD_matchState_dictMode(const ZSTD_matchState_t *ms) { - return ms->dictMatchState != NULL ? - ZSTD_dictMatchState : - ZSTD_window_hasExtDict(ms->window) ? - ZSTD_extDict : + return ZSTD_window_hasExtDict(ms->window) ? + ZSTD_extDict : + ms->dictMatchState != NULL ? + ZSTD_dictMatchState : ZSTD_noDict; } @@ -605,7 +605,8 @@ MEM_STATIC U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog, */ MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window, void const* srcEnd, U32 maxDist, - U32* loadedDictEndPtr) + U32* loadedDictEndPtr, + const ZSTD_matchState_t** dictMatchStatePtr) { U32 const current = (U32)((BYTE const*)srcEnd - window->base); U32 loadedDictEnd = loadedDictEndPtr != NULL ? *loadedDictEndPtr : 0; @@ -619,6 +620,8 @@ MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window, } if (loadedDictEndPtr) *loadedDictEndPtr = 0; + if (dictMatchStatePtr) + *dictMatchStatePtr = NULL; } } diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index b0c5d0654..03d1f54c4 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -508,7 +508,7 @@ size_t ZSTD_ldm_generateSequences( * * Try invalidation after the sequence generation and test the * the offset against maxDist directly. */ - ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, NULL); + ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, NULL, NULL); /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */ newLeftoverSize = ZSTD_ldm_generateSequences_internal( ldmState, sequences, params, chunkStart, chunkSize); From 7e0402e738f2e3ff6d74e63283bc7c514b67b3a4 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 13:13:19 -0400 Subject: [PATCH 101/288] Also Attach Dict When Source Size is Unknown --- lib/compress/zstd_compress.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 9f488b9ac..4d4e171bc 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1208,7 +1208,8 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, * context, or referencing the dictionary context from the working context * in-place. We decide here which strategy to use. */ /* TODO: pick reasonable cut-off size, handle ZSTD_CONTENTSIZE_UNKNOWN */ - int attachDict = pledgedSrcSize <= 8 KB + int attachDict = ( pledgedSrcSize <= 8 KB + || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) && cdict->cParams.strategy == ZSTD_fast && ZSTD_equivalentCParams(cctx->appliedParams.cParams, cdict->cParams); From 95bdf20a872ab7eef689799e19f83fa1462a44ba Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 13:16:50 -0400 Subject: [PATCH 102/288] Moar Renames --- lib/compress/zstd_fast.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 5c6f0dc8b..09b1a8ec3 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -53,8 +53,8 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 localLowestIndex = ms->window.dictLimit; - const BYTE* const localLowest = base + localLowestIndex; + const U32 prefixLowestIndex = ms->window.dictLimit; + const BYTE* const prefixLowest = base + prefixLowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; @@ -63,16 +63,16 @@ size_t ZSTD_compressBlock_fast_generic( const ZSTD_matchState_t* const dms = ms->dictMatchState; const U32* const dictHashTable = dictMode == ZSTD_dictMatchState ? dms->hashTable : NULL; - const U32 lowestDictIndex = dictMode == ZSTD_dictMatchState ? + const U32 dictLowestIndex = dictMode == ZSTD_dictMatchState ? dms->window.dictLimit : 0; const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; const BYTE* const dictLowest = dictMode == ZSTD_dictMatchState ? - dictBase + lowestDictIndex : NULL; + dictBase + dictLowestIndex : NULL; const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? - localLowestIndex - (U32)(dictEnd - dictBase) : + prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); @@ -80,13 +80,13 @@ size_t ZSTD_compressBlock_fast_generic( /* otherwise, we would get index underflow when translating a dict index * into a local index */ assert(dictMode != ZSTD_dictMatchState - || localLowestIndex >= (U32)(dictEnd - dictBase)); + || prefixLowestIndex >= (U32)(dictEnd - dictBase)); /* init */ - ip += (ip - localLowest + dictEnd - dictLowest == 0); + ip += (ip - prefixLowest + dictEnd - dictLowest == 0); { U32 const maxRep = dictMode == ZSTD_dictMatchState ? - (U32)(ip - localLowest + dictEnd - dictLowest) : - (U32)(ip - localLowest); + (U32)(ip - prefixLowest + dictEnd - dictLowest) : + (U32)(ip - prefixLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } @@ -100,15 +100,15 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* match = base + matchIndex; const U32 repIndex = current + 1 - offset_1; const BYTE* repBase = (dictMode == ZSTD_dictMatchState - && repIndex < localLowestIndex) ? + && repIndex < prefixLowestIndex) ? dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ if (dictMode == ZSTD_dictMatchState - && ((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) + && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < localLowestIndex ? dictEnd : iend; + const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); @@ -117,12 +117,12 @@ size_t ZSTD_compressBlock_fast_generic( mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else if ( (matchIndex <= localLowestIndex) + } else if ( (matchIndex <= prefixLowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { if (dictMode == ZSTD_dictMatchState) { U32 const dictMatchIndex = dictHashTable[h]; const BYTE* dictMatch = dictBase + dictMatchIndex; - if (dictMatchIndex <= lowestDictIndex || + if (dictMatchIndex <= dictLowestIndex || MEM_read32(dictMatch) != MEM_read32(ip)) { assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; @@ -148,7 +148,7 @@ size_t ZSTD_compressBlock_fast_generic( /* found a regular match */ U32 const offset = (U32)(ip-match); mLength = ZSTD_count(ip+4, match+4, iend) + 4; - while (((ip>anchor) & (match>localLowest)) + while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; @@ -170,12 +170,12 @@ size_t ZSTD_compressBlock_fast_generic( U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState - && repIndex2 < localLowestIndex ? + && repIndex2 < prefixLowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; - if ( ((U32)((localLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) + if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < localLowestIndex ? dictEnd : iend; + const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); From a44ab3b475882fc5447a949e8b21e68d9ed9be5e Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 15:41:37 -0400 Subject: [PATCH 103/288] Remove Out-of-Date Comment --- lib/compress/zstd_compress.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 4d4e171bc..aa7fc1e8d 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1207,7 +1207,6 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, /* We have a choice between copying the dictionary context into the working * context, or referencing the dictionary context from the working context * in-place. We decide here which strategy to use. */ - /* TODO: pick reasonable cut-off size, handle ZSTD_CONTENTSIZE_UNKNOWN */ int attachDict = ( pledgedSrcSize <= 8 KB || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) && cdict->cParams.strategy == ZSTD_fast From 9c92223468acca6e5a7082e4e09b1f6870df7aa4 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 15:45:37 -0400 Subject: [PATCH 104/288] Avoid Undefined Behavior in Match Ptr Calculation --- lib/compress/zstd_fast.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 09b1a8ec3..b21bc7683 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -99,10 +99,10 @@ size_t ZSTD_compressBlock_fast_generic( U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; const U32 repIndex = current + 1 - offset_1; - const BYTE* repBase = (dictMode == ZSTD_dictMatchState + const BYTE* repMatch = (dictMode == ZSTD_dictMatchState && repIndex < prefixLowestIndex) ? - dictBase - dictIndexDelta : base; - const BYTE* repMatch = repBase + repIndex; + dictBase + (repIndex - dictIndexDelta) : + base + repIndex; hashTable[h] = current; /* update hash table */ if (dictMode == ZSTD_dictMatchState From 582b7f85ed25bf828854f9507d1c75c4d74962bf Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 17:23:16 -0400 Subject: [PATCH 105/288] Don't Attach Empty Dict Contents In weird corner cases, they produce unexpected results... --- lib/compress/zstd_compress.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index aa7fc1e8d..b1d52b9aa 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1227,20 +1227,25 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, } if (attachDict) { - DEBUGLOG(4, "attaching dictionary into context"); - cctx->blockState.matchState.dictMatchState = &cdict->matchState; + if (cdict->matchState.window.nextSrc - cdict->matchState.window.base == 0) { + /* don't even attach dictionaries with no contents */ + DEBUGLOG(4, "skipping attaching empty dictionary"); + } else { + DEBUGLOG(4, "attaching dictionary into context"); + cctx->blockState.matchState.dictMatchState = &cdict->matchState; - /* prep working match state so dict matches never have negative indices - * when they are translated to the working context's index space. */ - if (cctx->blockState.matchState.window.dictLimit < - (U32)(cdict->matchState.window.nextSrc - cdict->matchState.window.base)) { - cctx->blockState.matchState.window.nextSrc = - cctx->blockState.matchState.window.base + - ( cdict->matchState.window.nextSrc - - cdict->matchState.window.base); - ZSTD_window_clear(&cctx->blockState.matchState.window); + /* prep working match state so dict matches never have negative indices + * when they are translated to the working context's index space. */ + if (cctx->blockState.matchState.window.dictLimit < + (U32)(cdict->matchState.window.nextSrc - cdict->matchState.window.base)) { + cctx->blockState.matchState.window.nextSrc = + cctx->blockState.matchState.window.base + + ( cdict->matchState.window.nextSrc + - cdict->matchState.window.base); + ZSTD_window_clear(&cctx->blockState.matchState.window); + } + cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; } - cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; } else { DEBUGLOG(4, "copying dictionary into context"); /* copy tables */ From 7ef85e061877d96991a4fc419ad96146afc8f88b Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Mon, 21 May 2018 18:27:08 -0400 Subject: [PATCH 106/288] Fixes in re Comments --- lib/compress/zstd_compress.c | 24 ++++++++++++------------ lib/compress/zstd_fast.c | 17 +++++++++++------ 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b1d52b9aa..e49046fcb 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1207,11 +1207,12 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, /* We have a choice between copying the dictionary context into the working * context, or referencing the dictionary context from the working context * in-place. We decide here which strategy to use. */ - int attachDict = ( pledgedSrcSize <= 8 KB - || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) - && cdict->cParams.strategy == ZSTD_fast - && ZSTD_equivalentCParams(cctx->appliedParams.cParams, - cdict->cParams); + const int attachDict = ( pledgedSrcSize <= 8 KB + || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) + && cdict->cParams.strategy == ZSTD_fast + && ZSTD_equivalentCParams(cctx->appliedParams.cParams, + cdict->cParams); + { unsigned const windowLog = params.cParams.windowLog; assert(windowLog != 0); @@ -1227,7 +1228,9 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, } if (attachDict) { - if (cdict->matchState.window.nextSrc - cdict->matchState.window.base == 0) { + const U32 cdictLen = (U32)( cdict->matchState.window.nextSrc + - cdict->matchState.window.base); + if (cdictLen == 0) { /* don't even attach dictionaries with no contents */ DEBUGLOG(4, "skipping attaching empty dictionary"); } else { @@ -1236,15 +1239,12 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, /* prep working match state so dict matches never have negative indices * when they are translated to the working context's index space. */ - if (cctx->blockState.matchState.window.dictLimit < - (U32)(cdict->matchState.window.nextSrc - cdict->matchState.window.base)) { + if (cctx->blockState.matchState.window.dictLimit < cdictLen) { cctx->blockState.matchState.window.nextSrc = - cctx->blockState.matchState.window.base + - ( cdict->matchState.window.nextSrc - - cdict->matchState.window.base); + cctx->blockState.matchState.window.base + cdictLen; ZSTD_window_clear(&cctx->blockState.matchState.window); } - cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; + cctx->blockState.matchState.loadedDictEnd = params.forceWindow ? 0 : cdictLen; } } else { DEBUGLOG(4, "copying dictionary into context"); diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index b21bc7683..3bac2bddd 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -74,6 +74,7 @@ size_t ZSTD_compressBlock_fast_generic( const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; + const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictLowest); assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); @@ -83,13 +84,18 @@ size_t ZSTD_compressBlock_fast_generic( || prefixLowestIndex >= (U32)(dictEnd - dictBase)); /* init */ - ip += (ip - prefixLowest + dictEnd - dictLowest == 0); - { U32 const maxRep = dictMode == ZSTD_dictMatchState ? - (U32)(ip - prefixLowest + dictEnd - dictLowest) : - (U32)(ip - prefixLowest); + ip += (dictAndPrefixLength == 0); + if (dictMode == ZSTD_noDict) { + U32 const maxRep = (U32)(ip - prefixLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } + if (dictMode == ZSTD_dictMatchState) { + /* dictMatchState repCode checks don't currently handle repCode == 0 + * disabling. */ + assert(offset_1 <= dictAndPrefixLength); + assert(offset_2 <= dictAndPrefixLength); + } /* Main Search Loop */ while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ @@ -169,8 +175,7 @@ size_t ZSTD_compressBlock_fast_generic( while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2; - const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState - && repIndex2 < prefixLowestIndex ? + const BYTE* repMatch2 = repIndex2 < prefixLowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) From 298d24fa573842c7cc0c3530869817bc9ebe36f8 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Mon, 21 May 2018 20:12:11 -0400 Subject: [PATCH 107/288] Make loadedDictEnd an Index, not the Dict Len --- lib/compress/zstd_compress.c | 4 +++- lib/compress/zstd_compress_internal.h | 6 ++++++ lib/compress/zstd_fast.c | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e49046fcb..105cea44b 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1209,6 +1209,8 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, * in-place. We decide here which strategy to use. */ const int attachDict = ( pledgedSrcSize <= 8 KB || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) + && !params.forceWindow /* dictMatchState isn't correctly + * handled in _enforceMaxDist */ && cdict->cParams.strategy == ZSTD_fast && ZSTD_equivalentCParams(cctx->appliedParams.cParams, cdict->cParams); @@ -1244,7 +1246,7 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, cctx->blockState.matchState.window.base + cdictLen; ZSTD_window_clear(&cctx->blockState.matchState.window); } - cctx->blockState.matchState.loadedDictEnd = params.forceWindow ? 0 : cdictLen; + cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; } } else { DEBUGLOG(4, "copying dictionary into context"); diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index a61fc3742..a7666d5c5 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -594,14 +594,20 @@ MEM_STATIC U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog, * ZSTD_window_enforceMaxDist(): * Updates lowLimit so that: * (srcEnd - base) - lowLimit == maxDist + loadedDictEnd + * * This allows a simple check that index >= lowLimit to see if index is valid. * This must be called before a block compression call, with srcEnd as the block * source end. + * * If loadedDictEndPtr is not NULL, we set it to zero once we update lowLimit. * This is because dictionaries are allowed to be referenced as long as the last * byte of the dictionary is in the window, but once they are out of range, * they cannot be referenced. If loadedDictEndPtr is NULL, we use * loadedDictEnd == 0. + * + * In normal dict mode, the dict is between lowLimit and dictLimit. In + * dictMatchState mode, lowLimit and dictLimit are the same, and the dictionary + * is below them. forceWindow and dictMatchState are therefore incompatible. */ MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window, void const* srcEnd, U32 maxDist, diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 3bac2bddd..bf962a175 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -72,7 +72,7 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? - prefixLowestIndex - (U32)(dictEnd - dictBase) : + ms->loadedDictEnd - (U32)(dictEnd - dictBase) : 0; const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictLowest); From d9c7e67125d95d751e934870fdf611fbe0995934 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 23 May 2018 16:00:17 -0400 Subject: [PATCH 108/288] Assert that Dict and Current Window are Adjacent in Index Space --- lib/compress/zstd_compress.c | 5 +++++ lib/compress/zstd_fast.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 105cea44b..00f3e789d 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2210,6 +2210,11 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, ZSTD_resetSeqStore(&(zc->seqStore)); ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy; /* required for optimal parser to read stats from dictionary */ + /* a gap between an attached dict and the current window is not safe, + * they must remain adjacent, and when that stops being the case, the dict + * must be unset */ + assert(ms->dictMatchState == NULL || ms->loadedDictEnd == ms->window.dictLimit); + /* limited update after a very long match */ { const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index bf962a175..3bac2bddd 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -72,7 +72,7 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? - ms->loadedDictEnd - (U32)(dictEnd - dictBase) : + prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictLowest); From f2d0924b87e4dab590cb53f98bb33a21c4375119 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 23 May 2018 14:58:58 -0700 Subject: [PATCH 109/288] Variable declarations --- lib/common/entropy_common.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/common/entropy_common.c b/lib/common/entropy_common.c index 2edb6e9be..33fd04bd6 100644 --- a/lib/common/entropy_common.c +++ b/lib/common/entropy_common.c @@ -73,16 +73,16 @@ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* t int previous0 = 0; if (hbSize < 4) { - /* This function only works when hbSize >= 4 */ - char buffer[4]; - memset(buffer, 0, sizeof(buffer)); - memcpy(buffer, headerBuffer, hbSize); - size_t const countSize = FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr, - buffer, sizeof(buffer)); - if (FSE_isError(countSize)) return countSize; - if (countSize > hbSize) return ERROR(corruption_detected); - return countSize; - } + /* This function only works when hbSize >= 4 */ + char buffer[4]; + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, headerBuffer, hbSize); + { size_t const countSize = FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr, + buffer, sizeof(buffer)); + if (FSE_isError(countSize)) return countSize; + if (countSize > hbSize) return ERROR(corruption_detected); + return countSize; + } } assert(hbSize >= 4); bitStream = MEM_readLE32(ip); From 06b70179da2b8df06ab9a7d284e48f45f437b670 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 23 May 2018 18:02:30 -0700 Subject: [PATCH 110/288] Work around bug in zstd decoder (#1147) Work around bug in zstd decoder Pull request #1144 exercised a new path in the zstd decoder that proved to be buggy. Avoid the extremely rare bug by emitting an uncompressed block. --- lib/compress/zstd_compress.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 22c704f11..01bb3036c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1983,6 +1983,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, BYTE* op = ostart; size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; BYTE* seqHead; + BYTE* lastNCount = NULL; ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1< fse.litlengthCTable, sizeof(prevEntropy->fse.litlengthCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; + if (LLtype == set_compressed) + lastNCount = op; op += countSize; } } /* build CTable for Offsets */ @@ -2049,6 +2052,8 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, prevEntropy->fse.offcodeCTable, sizeof(prevEntropy->fse.offcodeCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; + if (Offtype == set_compressed) + lastNCount = op; op += countSize; } } /* build CTable for MatchLengths */ @@ -2063,6 +2068,8 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, prevEntropy->fse.matchlengthCTable, sizeof(prevEntropy->fse.matchlengthCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; + if (MLtype == set_compressed) + lastNCount = op; op += countSize; } } @@ -2077,6 +2084,21 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, longOffsets, bmi2); if (ZSTD_isError(bitstreamSize)) return bitstreamSize; op += bitstreamSize; + /* zstd versions <= 1.3.4 mistakenly report corruption when + * FSE_readNCount() recieves a buffer < 4 bytes. + * Fixed by https://github.com/facebook/zstd/pull/1146. + * This can happen when the last set_compressed table present is 2 + * bytes and the bitstream is only one byte. + * In this exceedingly rare case, we will simply emit an uncompressed + * block, since it isn't worth optimizing. + */ + if (lastNCount && (op - lastNCount) < 4) { + /* NCountSize >= 2 && bitstreamSize > 0 ==> lastCountSize == 3 */ + assert(op - lastNCount == 3); + DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.3.4 by " + "emitting an uncompressed block."); + return 0; + } } return op - ostart; @@ -2092,6 +2114,7 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, size_t const cSize = ZSTD_compressSequences_internal( seqStorePtr, prevEntropy, nextEntropy, cctxParams, dst, dstCapacity, workspace, bmi2); + if (cSize == 0) return 0; /* When srcSize <= dstCapacity, there is enough space to write a raw uncompressed block. * Since we ran out of space, block must be not compressible, so fall back to raw uncompressed block. */ From 776128d16f8020489da132f34bcd41346c74ea4a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 24 May 2018 13:59:11 -0700 Subject: [PATCH 111/288] fix corner case when requiring cost of an FSE symbol ensure that, when frequency[symbol]==0, result is (tableLog + 1) bits with both upper-bit and fractional-bit estimates. Also : enable BIT_DEBUG in /tests --- lib/common/fse.h | 26 ++++++++++++++++---------- lib/compress/fse_compress.c | 3 ++- tests/Makefile | 2 +- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 5a2344441..cd810c7d5 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -575,16 +575,22 @@ MEM_STATIC void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* statePt BIT_flushBits(bitC); } + +/* FSE_getMaxNbBits() : + * Approximate maximum cost of a symbol, in bits. + * Fractional get rounded up (i.e : a symbol with a normalized frequency of 3 gives the same result as a frequency of 2) + * note 1 : assume symbolValue is valid (<= maxSymbolValue) + * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */ MEM_STATIC U32 FSE_getMaxNbBits(const void* symbolTTPtr, U32 symbolValue) { const FSE_symbolCompressionTransform* symbolTT = (const FSE_symbolCompressionTransform*) symbolTTPtr; return (symbolTT[symbolValue].deltaNbBits + ((1<<16)-1)) >> 16; } -/* FSE_bitCost_b256() : - * Approximate symbol cost, - * provide fractional value, using fixed-point format (accuracyLog fractional bits) - * note: assume symbolValue is valid */ +/* FSE_bitCost() : + * Approximate symbol cost, as fractional value, using fixed-point format (accuracyLog fractional bits) + * note 1 : assume symbolValue is valid (<= maxSymbolValue) + * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */ MEM_STATIC U32 FSE_bitCost(const FSE_symbolCompressionTransform* symbolTT, U32 tableLog, U32 symbolValue, U32 accuracyLog) { U32 const minNbBits = symbolTT[symbolValue].deltaNbBits >> 16; @@ -592,13 +598,13 @@ MEM_STATIC U32 FSE_bitCost(const FSE_symbolCompressionTransform* symbolTT, U32 t assert(tableLog < 16); assert(accuracyLog < 31-tableLog); /* ensure enough room for renormalization double shift */ { U32 const tableSize = 1 << tableLog; + U32 const deltaFromThreshold = threshold - (symbolTT[symbolValue].deltaNbBits + tableSize); + U32 const normalizedDeltaFromThreshold = (deltaFromThreshold << accuracyLog) >> tableLog; /* linear interpolation (very approximate) */ + U32 const bitMultiplier = 1 << accuracyLog; assert(symbolTT[symbolValue].deltaNbBits + tableSize <= threshold); - { U32 const deltaFromThreshold = threshold - (symbolTT[symbolValue].deltaNbBits + tableSize); - U32 const normalizedDeltaFromThreshold = (deltaFromThreshold << accuracyLog) >> tableLog; /* linear interpolation (very approximate) */ - U32 const bitMultiplier = 1 << accuracyLog; - assert(normalizedDeltaFromThreshold <= bitMultiplier); - return (minNbBits+1)*bitMultiplier - normalizedDeltaFromThreshold; - } } + assert(normalizedDeltaFromThreshold <= bitMultiplier); + return (minNbBits+1)*bitMultiplier - normalizedDeltaFromThreshold; + } } diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index 5df92db45..ab2eb335f 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -100,6 +100,7 @@ size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsi if (((size_t)1 << tableLog) * sizeof(FSE_FUNCTION_TYPE) > wkspSize) return ERROR(tableLog_tooLarge); tableU16[-2] = (U16) tableLog; tableU16[-1] = (U16) maxSymbolValue; + assert(tableLog < 16); /* required for the threshold strategy to work */ /* For explanations on how to distribute symbol values over the table : * http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */ @@ -145,7 +146,7 @@ size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsi { case 0: /* filling nonetheless, for compatibility with FSE_getMaxNbBits() */ - symbolTT[s].deltaNbBits = (tableLog+1) << 16; + symbolTT[s].deltaNbBits = ((tableLog+1) << 16) - (1< Date: Wed, 23 May 2018 18:04:52 -0700 Subject: [PATCH 112/288] Small fixes to fuzz.py --- tests/fuzz/fuzz.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/fuzz/fuzz.py b/tests/fuzz/fuzz.py index b591e4f67..14ae8bca0 100755 --- a/tests/fuzz/fuzz.py +++ b/tests/fuzz/fuzz.py @@ -265,7 +265,7 @@ def build_parser(args): '--disable-fuzzing-mode', dest='fuzzing_mode', action='store_false', - help='Do not define FUZZING_BUILD_MORE_UNSAFE_FOR_PRODUCTION') + help='Do not define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION') parser.add_argument( '--enable-stateful-fuzzing', dest='stateful_fuzzing', @@ -399,7 +399,7 @@ def build(args): cppflags += ['-DSTATEFUL_FUZZING'] if args.fuzzing_mode: - cppflags += ['-DFUZZING_BUILD_MORE_UNSAFE_FOR_PRODUCTION'] + cppflags += ['-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION'] if args.lib_fuzzing_engine == 'libregression.a': targets = ['libregression.a'] + targets @@ -750,11 +750,10 @@ def zip_cmd(args): for target in args.TARGET: # Zip the seed_corpus seed_corpus = abs_join(CORPORA_DIR, "{}_seed_corpus".format(target)) - seeds = [abs_join(seed_corpus, f) for f in os.listdir(seed_corpus)] zip_file = "{}.zip".format(seed_corpus) - cmd = ["zip", "-q", "-j", "-9", zip_file] - print(' '.join(cmd + [abs_join(seed_corpus, '*')])) - subprocess.check_call(cmd + seeds) + cmd = ["zip", "-r", "-q", "-j", "-9", zip_file, "."] + print(' '.join(cmd)) + subprocess.check_call(cmd, cwd=seed_corpus) def list_cmd(args): From 2a9975f77b21de608654912d8a03dbf1a39ebea7 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 23 May 2018 18:22:32 -0700 Subject: [PATCH 113/288] Increase the maximum file size --- tests/fuzz/regression_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fuzz/regression_driver.c b/tests/fuzz/regression_driver.c index 2b714d29e..1553d436c 100644 --- a/tests/fuzz/regression_driver.c +++ b/tests/fuzz/regression_driver.c @@ -16,7 +16,7 @@ #include int main(int argc, char const **argv) { - size_t const kMaxFileSize = (size_t)1 << 20; + size_t const kMaxFileSize = (size_t)1 << 27; int const kFollowLinks = 1; char *fileNamesBuf = NULL; char const **files = argv + 1; From ac852abb8b23da70483a0a796c655855322950b0 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 23 May 2018 18:25:26 -0700 Subject: [PATCH 114/288] Define BIT_DEBUG for --debug --- tests/fuzz/fuzz.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/fuzz/fuzz.py b/tests/fuzz/fuzz.py index 14ae8bca0..bc6611fd2 100755 --- a/tests/fuzz/fuzz.py +++ b/tests/fuzz/fuzz.py @@ -248,7 +248,7 @@ def build_parser(args): dest='debug', type=int, default=1, - help='Set ZSTD_DEBUG (default: 1)') + help='Set ZSTD_DEBUG and BIT_DEBUG (default: 1)') parser.add_argument( '--force-memory-access', dest='memory_access', @@ -356,6 +356,7 @@ def build(args): cppflags += [ '-DZSTD_DEBUG={}'.format(args.debug), + '-DBIT_DEBUG={}'.format(args.debug), '-DMEM_FORCE_MEMORY_ACCESS={}'.format(args.memory_access), '-DFUZZ_RNG_SEED_SIZE={}'.format(args.fuzz_rng_seed_size), ] From fdd4d8510fcec17ff29477e7b243dadd8e7435f7 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 23 May 2018 18:46:38 -0700 Subject: [PATCH 115/288] Improve compiler detection to work on Mac --- tests/fuzz/fuzz.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/fuzz/fuzz.py b/tests/fuzz/fuzz.py index bc6611fd2..04d9c6008 100755 --- a/tests/fuzz/fuzz.py +++ b/tests/fuzz/fuzz.py @@ -147,15 +147,18 @@ def compiler_version(cc, cxx): """ cc_version_bytes = subprocess.check_output([cc, "--version"]) cxx_version_bytes = subprocess.check_output([cxx, "--version"]) - if cc_version_bytes.startswith(b'clang'): - assert(cxx_version_bytes.startswith(b'clang')) + compiler = None + version = None + if b'clang' in cc_version_bytes: + assert(b'clang' in cxx_version_bytes) compiler = 'clang' - if cc_version_bytes.startswith(b'gcc'): - assert(cxx_version_bytes.startswith(b'g++')) + elif b'gcc' in cc_version_bytes: + assert(b'gcc' in cxx_version_bytes) compiler = 'gcc' - version_regex = b'([0-9])+\.([0-9])+\.([0-9])+' - version_match = re.search(version_regex, cc_version_bytes) - version = tuple(int(version_match.group(i)) for i in range(1, 4)) + if compiler is not None: + version_regex = b'([0-9])+\.([0-9])+\.([0-9])+' + version_match = re.search(version_regex, cc_version_bytes) + version = tuple(int(version_match.group(i)) for i in range(1, 4)) return compiler, version From c10d1b4011c9a0c8d126635899c8416f050fb7b3 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Fri, 4 May 2018 15:59:27 -0400 Subject: [PATCH 116/288] Skeleton for In-Place Impl for ZSTD_dfast --- lib/compress/zstd_compress.c | 5 +++-- lib/compress/zstd_double_fast.c | 32 +++++++++++++++++++++++++++----- lib/compress/zstd_double_fast.h | 3 +++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 538319bcb..46a3fe541 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1211,7 +1211,7 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) && !params.forceWindow /* dictMatchState isn't correctly * handled in _enforceMaxDist */ - && cdict->cParams.strategy == ZSTD_fast + && cdict->cParams.strategy <= ZSTD_dfast && ZSTD_equivalentCParams(cctx->appliedParams.cParams, cdict->cParams); @@ -2192,7 +2192,8 @@ ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMo ZSTD_compressBlock_btopt_extDict, ZSTD_compressBlock_btultra_extDict }, { ZSTD_compressBlock_fast_dictMatchState /* default for 0 */, ZSTD_compressBlock_fast_dictMatchState, - NULL, NULL, NULL, NULL, NULL, NULL, NULL /* unimplemented as of yet */ } + ZSTD_compressBlock_doubleFast_dictMatchState, + NULL, NULL, NULL, NULL, NULL, NULL /* unimplemented as of yet */ } }; ZSTD_blockCompressor selectedCompressor; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 78aa0cbad..28c4f0248 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -52,7 +52,7 @@ FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_doubleFast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize, - U32 const mls /* template */) + U32 const mls /* template */, ZSTD_dictMode_e const dictMode) { U32* const hashLong = ms->hashTable; const U32 hBitsL = cParams->hashLog; @@ -69,6 +69,8 @@ size_t ZSTD_compressBlock_doubleFast_generic( U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; + (void)dictMode; + /* init */ ip += (ip==lowest); { U32 const maxRep = (U32)(ip-lowest); @@ -170,13 +172,33 @@ size_t ZSTD_compressBlock_doubleFast( { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 4); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 4, ZSTD_noDict); case 5 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 5); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 5, ZSTD_noDict); case 6 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 6); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 6, ZSTD_noDict); case 7 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 7); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 7, ZSTD_noDict); + } +} + + +size_t ZSTD_compressBlock_doubleFast_dictMatchState( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) +{ + const U32 mls = cParams->searchLength; + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 4, ZSTD_dictMatchState); + case 5 : + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 5, ZSTD_dictMatchState); + case 6 : + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 6, ZSTD_dictMatchState); + case 7 : + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 7, ZSTD_dictMatchState); } } diff --git a/lib/compress/zstd_double_fast.h b/lib/compress/zstd_double_fast.h index 3f5a24eb3..c475021d2 100644 --- a/lib/compress/zstd_double_fast.h +++ b/lib/compress/zstd_double_fast.h @@ -24,6 +24,9 @@ void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, size_t ZSTD_compressBlock_doubleFast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_doubleFast_dictMatchState( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); size_t ZSTD_compressBlock_doubleFast_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); From aacbbf4f9a2f993cb281707a21a41d8453a24865 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 15:22:36 -0400 Subject: [PATCH 117/288] Rename 'lowest' to 'localLowest' to Prepare to Introduce Dict Indices --- lib/compress/zstd_double_fast.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 28c4f0248..d1b926de9 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -62,8 +62,8 @@ size_t ZSTD_compressBlock_doubleFast_generic( const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ms->window.dictLimit; - const BYTE* const lowest = base + lowestIndex; + const U32 localLowestIndex = ms->window.dictLimit; + const BYTE* const localLowest = base + localLowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; @@ -72,8 +72,8 @@ size_t ZSTD_compressBlock_doubleFast_generic( (void)dictMode; /* init */ - ip += (ip==lowest); - { U32 const maxRep = (U32)(ip-lowest); + ip += (ip==localLowest); + { U32 const maxRep = (U32)(ip-localLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } @@ -98,24 +98,24 @@ size_t ZSTD_compressBlock_doubleFast_generic( ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else { U32 offset; - if ( (matchIndexL > lowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { + if ( (matchIndexL > localLowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; offset = (U32)(ip-matchLong); - while (((ip>anchor) & (matchLong>lowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ - } else if ( (matchIndexS > lowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { + while (((ip>anchor) & (matchLong>localLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ + } else if ( (matchIndexS > localLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); U32 const matchIndexL3 = hashLong[hl3]; const BYTE* matchL3 = base + matchIndexL3; hashLong[hl3] = current + 1; - if ( (matchIndexL3 > lowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { + if ( (matchIndexL3 > localLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; ip++; offset = (U32)(ip-matchL3); - while (((ip>anchor) & (matchL3>lowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ + while (((ip>anchor) & (matchL3>localLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ } else { mLength = ZSTD_count(ip+4, match+4, iend) + 4; offset = (U32)(ip-match); - while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + while (((ip>anchor) & (match>localLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } } else { ip += ((ip-anchor) >> kSearchStrength) + 1; From 7097a037492b9089fb43e88d50a6fc39ac5f3780 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 15:28:47 -0400 Subject: [PATCH 118/288] Add Necessary Dict Variables --- lib/compress/zstd_double_fast.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index d1b926de9..8c11d8ebe 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -69,6 +69,26 @@ size_t ZSTD_compressBlock_doubleFast_generic( U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; + const ZSTD_matchState_t* const dms = ms->dictMatchState; + const U32* const dictHashLong = dictMode == ZSTD_dictMatchState ? + dms->hashTable : NULL; + const U32* const dictHashSmall = dictMode == ZSTD_dictMatchState ? + dms->hashTable : NULL; + const U32 lowestDictIndex = dictMode == ZSTD_dictMatchState ? + dms->window.dictLimit : 0; + const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? + dms->window.base : NULL; + const BYTE* const dictLowest = dictMode == ZSTD_dictMatchState ? + dictBase + lowestDictIndex : NULL; + const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? + dms->window.nextSrc : NULL; + const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? + localLowestIndex - (U32)(dictEnd - dictBase) : + 0; + ptrdiff_t dictLowestLocalIndex = dictMode == ZSTD_dictMatchState ? + lowestDictIndex + dictIndexDelta : + localLowestIndex; + (void)dictMode; /* init */ From 8b241da4dfd69ab377753e984f8e50258487fdeb Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 15:23:22 -0400 Subject: [PATCH 119/288] Properly Initialize Repcode Values --- lib/compress/zstd_double_fast.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 8c11d8ebe..2bcd4b5d0 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -89,11 +89,13 @@ size_t ZSTD_compressBlock_doubleFast_generic( lowestDictIndex + dictIndexDelta : localLowestIndex; - (void)dictMode; + assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); /* init */ - ip += (ip==localLowest); - { U32 const maxRep = (U32)(ip-localLowest); + ip += (dictMode == ZSTD_noDict && ip == localLowest); + { U32 const maxRep = dictMode == ZSTD_dictMatchState ? + (U32)(ip - dictLowest) : + (U32)(ip - localLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } From 7a25f7ef5b10c4965a1700eaa44e067154e7ad7f Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 16:46:57 -0400 Subject: [PATCH 120/288] Existing Repcode Check Only Applies to noDict Case --- lib/compress/zstd_double_fast.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 2bcd4b5d0..0b8cba13e 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -112,8 +112,8 @@ size_t ZSTD_compressBlock_doubleFast_generic( const BYTE* match = base + matchIndexS; hashLong[h2] = hashSmall[h] = current; /* update hash tables */ - assert(offset_1 <= current); /* supposed guaranteed by construction */ - if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { + if (dictMode == ZSTD_noDict && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { + assert(offset_1 <= current); /* supposed guaranteed by construction */ /* favor repcode */ mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; @@ -162,7 +162,8 @@ size_t ZSTD_compressBlock_doubleFast_generic( hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base); /* check immediate repcode */ - while ( (ip <= ilimit) + while ( dictMode == ZSTD_noDict + && (ip <= ilimit) && ( (offset_2>0) & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { /* store sequence */ From 50c5b2bb9027ed3061dbb3b8d4ede12cac561c51 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 18:31:51 -0400 Subject: [PATCH 121/288] Find Dict Hash Table Matches --- lib/compress/zstd_double_fast.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 0b8cba13e..6bf5fb5eb 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -139,6 +139,36 @@ size_t ZSTD_compressBlock_doubleFast_generic( offset = (U32)(ip-match); while (((ip>anchor) & (match>localLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } + } else if (dictMode == ZSTD_dictMatchState) { + U32 const dictMatchIndexL = dictHashLong[h2]; + U32 const dictMatchIndexS = dictHashSmall[h]; + const BYTE* dictMatchL = dictBase + dictMatchIndexL; + const BYTE* dictMatchS = dictBase + dictMatchIndexS; + assert(dictMatchL < dictEnd); + assert(dictMatchS < dictEnd); + if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { + mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, localLowest) + 8; + offset = (U32)(current - dictMatchIndexL - dictIndexDelta); + while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ + } else if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { + size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); + U32 const dictMatchIndexL3 = dictHashLong[hl3]; + const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; + assert(dictMatchL3 < dictEnd); + if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip)) { + mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, localLowest) + 8; + ip++; + offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); + while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ + } else { + mLength = ZSTD_count_2segments(ip+4, dictMatchS+4, iend, dictEnd, istart) + 4; + offset = (U32)(current - dictMatchIndexS - dictIndexDelta); + while (((ip>anchor) & (dictMatchS>dictLowest)) && (ip[-1] == dictMatchS[-1])) { ip--; dictMatchS--; mLength++; } /* catch up */ + } + } else { + ip += ((ip-anchor) >> kSearchStrength) + 1; + continue; + } } else { ip += ((ip-anchor) >> kSearchStrength) + 1; continue; From 0998f10813efc40d42f7f4043183a81153032292 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 18:37:24 -0400 Subject: [PATCH 122/288] Implement First Repcode Check --- lib/compress/zstd_double_fast.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 6bf5fb5eb..beffe8661 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -110,9 +110,23 @@ size_t ZSTD_compressBlock_doubleFast_generic( U32 const matchIndexS = hashSmall[h]; const BYTE* matchLong = base + matchIndexL; const BYTE* match = base + matchIndexS; + const ptrdiff_t repIndex = (ptrdiff_t)current + 1 - offset_1; + const BYTE* repBase = (dictMode == ZSTD_dictMatchState + && repIndex < (ptrdiff_t)localLowestIndex) ? + dictBase - dictIndexDelta : base; + const BYTE* repMatch = repBase + repIndex; hashLong[h2] = hashSmall[h] = current; /* update hash tables */ - if (dictMode == ZSTD_noDict && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { + if (dictMode == ZSTD_dictMatchState + && (((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) + & (repIndex > dictLowestLocalIndex)) + && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { + const BYTE* repMatchEnd = repIndex < (ptrdiff_t)localLowestIndex ? dictEnd : iend; + mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; + ip++; + ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); + } else if ( dictMode == ZSTD_noDict + && (offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { assert(offset_1 <= current); /* supposed guaranteed by construction */ /* favor repcode */ mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; From 2313cca1b768575ce961a17f3289db4aa7cf1f74 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 18:46:33 -0400 Subject: [PATCH 123/288] Implement Second Repcode Check --- lib/compress/zstd_double_fast.c | 54 ++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index beffe8661..1645acfc2 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -206,20 +206,46 @@ size_t ZSTD_compressBlock_doubleFast_generic( hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base); /* check immediate repcode */ - while ( dictMode == ZSTD_noDict - && (ip <= ilimit) - && ( (offset_2>0) - & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { - /* store sequence */ - size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; - { U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */ - hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base); - hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base); - ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); - ip += rLength; - anchor = ip; - continue; /* faster when present ... (?) */ - } } } + if (dictMode == ZSTD_dictMatchState) { + while (ip <= ilimit) { + U32 const current2 = (U32)(ip-base); + ptrdiff_t const repIndex2 = (ptrdiff_t)current2 - offset_2; + const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState + && repIndex2 < (ptrdiff_t)localLowestIndex ? + dictBase - dictIndexDelta + repIndex2 : + base + repIndex2; + if ( (((U32)((localLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) + & (repIndex2 > dictLowestLocalIndex)) + && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { + const BYTE* const repEnd2 = repIndex2 < (ptrdiff_t)localLowestIndex ? dictEnd : iend; + size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; + U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ + ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); + hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; + hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; + ip += repLength2; + anchor = ip; + continue; + } + break; + } + } + + if (dictMode == ZSTD_noDict) { + while ( (ip <= ilimit) + && (ip - offset_2 >= istart) + && ( (offset_2>0) + & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { + /* store sequence */ + size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; + U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */ + hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base); + hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base); + ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); + ip += rLength; + anchor = ip; + continue; /* faster when present ... (?) */ + } } } } /* save reps for next block */ rep[0] = offset_1 ? offset_1 : offsetSaved; From b97ad3f457d8162e2b7cd71bc2d2db29ab578684 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 18:40:07 -0400 Subject: [PATCH 124/288] Port Changes Made to ZSTD_fast to ZSTD_dfast --- lib/compress/zstd_double_fast.c | 61 +++++++++++++++------------------ 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 1645acfc2..d415791be 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -62,8 +62,8 @@ size_t ZSTD_compressBlock_doubleFast_generic( const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 localLowestIndex = ms->window.dictLimit; - const BYTE* const localLowest = base + localLowestIndex; + const U32 prefixLowestIndex = ms->window.dictLimit; + const BYTE* const prefixLowest = base + prefixLowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; @@ -74,28 +74,25 @@ size_t ZSTD_compressBlock_doubleFast_generic( dms->hashTable : NULL; const U32* const dictHashSmall = dictMode == ZSTD_dictMatchState ? dms->hashTable : NULL; - const U32 lowestDictIndex = dictMode == ZSTD_dictMatchState ? + const U32 dictLowestIndex = dictMode == ZSTD_dictMatchState ? dms->window.dictLimit : 0; const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; const BYTE* const dictLowest = dictMode == ZSTD_dictMatchState ? - dictBase + lowestDictIndex : NULL; + dictBase + dictLowestIndex : NULL; const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? - localLowestIndex - (U32)(dictEnd - dictBase) : + prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; - ptrdiff_t dictLowestLocalIndex = dictMode == ZSTD_dictMatchState ? - lowestDictIndex + dictIndexDelta : - localLowestIndex; assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); /* init */ - ip += (dictMode == ZSTD_noDict && ip == localLowest); + ip += (dictMode == ZSTD_noDict && ip == prefixLowest); { U32 const maxRep = dictMode == ZSTD_dictMatchState ? (U32)(ip - dictLowest) : - (U32)(ip - localLowest); + (U32)(ip - prefixLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } @@ -110,48 +107,45 @@ size_t ZSTD_compressBlock_doubleFast_generic( U32 const matchIndexS = hashSmall[h]; const BYTE* matchLong = base + matchIndexL; const BYTE* match = base + matchIndexS; - const ptrdiff_t repIndex = (ptrdiff_t)current + 1 - offset_1; - const BYTE* repBase = (dictMode == ZSTD_dictMatchState - && repIndex < (ptrdiff_t)localLowestIndex) ? - dictBase - dictIndexDelta : base; - const BYTE* repMatch = repBase + repIndex; + const U32 repIndex = current + 1 - offset_1; + const BYTE* repMatch = (dictMode == ZSTD_dictMatchState + && repIndex < prefixLowestIndex) ? + dictBase + (repIndex - dictIndexDelta) : + base + repIndex; hashLong[h2] = hashSmall[h] = current; /* update hash tables */ if (dictMode == ZSTD_dictMatchState - && (((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) - & (repIndex > dictLowestLocalIndex)) + && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < (ptrdiff_t)localLowestIndex ? dictEnd : iend; + const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else if ( dictMode == ZSTD_noDict - && (offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { - assert(offset_1 <= current); /* supposed guaranteed by construction */ - /* favor repcode */ + && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else { U32 offset; - if ( (matchIndexL > localLowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { + if ( (matchIndexL > prefixLowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; offset = (U32)(ip-matchLong); - while (((ip>anchor) & (matchLong>localLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ - } else if ( (matchIndexS > localLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { + while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ + } else if ( (matchIndexS > prefixLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); U32 const matchIndexL3 = hashLong[hl3]; const BYTE* matchL3 = base + matchIndexL3; hashLong[hl3] = current + 1; - if ( (matchIndexL3 > localLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { + if ( (matchIndexL3 > prefixLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; ip++; offset = (U32)(ip-matchL3); - while (((ip>anchor) & (matchL3>localLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ + while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ } else { mLength = ZSTD_count(ip+4, match+4, iend) + 4; offset = (U32)(ip-match); - while (((ip>anchor) & (match>localLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } } else if (dictMode == ZSTD_dictMatchState) { U32 const dictMatchIndexL = dictHashLong[h2]; @@ -161,7 +155,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( assert(dictMatchL < dictEnd); assert(dictMatchS < dictEnd); if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { - mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, localLowest) + 8; + mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; offset = (U32)(current - dictMatchIndexL - dictIndexDelta); while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ } else if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { @@ -170,7 +164,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; assert(dictMatchL3 < dictEnd); if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip)) { - mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, localLowest) + 8; + mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; ip++; offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ @@ -209,15 +203,14 @@ size_t ZSTD_compressBlock_doubleFast_generic( if (dictMode == ZSTD_dictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); - ptrdiff_t const repIndex2 = (ptrdiff_t)current2 - offset_2; + U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState - && repIndex2 < (ptrdiff_t)localLowestIndex ? + && repIndex2 < prefixLowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; - if ( (((U32)((localLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) - & (repIndex2 > dictLowestLocalIndex)) + if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < (ptrdiff_t)localLowestIndex ? dictEnd : iend; + const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); From 2bfe43267e63718be3f7e499f8dd3c2f5dd5e89f Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 23 May 2018 12:51:09 -0400 Subject: [PATCH 125/288] Disallow Too-Long Repcodes When Using an Attached Dict --- lib/compress/zstd_double_fast.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index d415791be..711bf41ab 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -85,17 +85,23 @@ size_t ZSTD_compressBlock_doubleFast_generic( const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; + const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictLowest); assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); /* init */ - ip += (dictMode == ZSTD_noDict && ip == prefixLowest); - { U32 const maxRep = dictMode == ZSTD_dictMatchState ? - (U32)(ip - dictLowest) : - (U32)(ip - prefixLowest); + ip += (dictAndPrefixLength == 0); + if (dictMode == ZSTD_noDict) { + U32 const maxRep = (U32)(ip - prefixLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } + if (dictMode == ZSTD_dictMatchState) { + /* dictMatchState repCode checks don't currently handle repCode == 0 + * disabling. */ + assert(offset_1 <= dictAndPrefixLength); + assert(offset_2 <= dictAndPrefixLength); + } /* Main Search Loop */ while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ From ec7efe88f5dc9d86049651ce6d367d051fc00f12 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 23 May 2018 16:45:58 -0400 Subject: [PATCH 126/288] Fix Off-By-One Error --- lib/compress/zstd_double_fast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 711bf41ab..b862289bb 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -169,7 +169,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( U32 const dictMatchIndexL3 = dictHashLong[hl3]; const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; assert(dictMatchL3 < dictEnd); - if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip)) { + if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; ip++; offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); From 43606f9c8357ac959a86c937cc2032fa2c82d63f Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 23 May 2018 17:37:47 -0400 Subject: [PATCH 127/288] ... When I Said "HashTable", I Meant "ChainTable" --- lib/compress/zstd_double_fast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index b862289bb..3e1a1d3c6 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -73,7 +73,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( const U32* const dictHashLong = dictMode == ZSTD_dictMatchState ? dms->hashTable : NULL; const U32* const dictHashSmall = dictMode == ZSTD_dictMatchState ? - dms->hashTable : NULL; + dms->chainTable : NULL; const U32 dictLowestIndex = dictMode == ZSTD_dictMatchState ? dms->window.dictLimit : 0; const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? From 1850025156a2169bc0f1b022a9f3d5a6a18c9b05 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 16 May 2018 14:55:20 -0400 Subject: [PATCH 128/288] Refactor ZSTD_dfast to Use `goto`s --- lib/compress/zstd_double_fast.c | 146 ++++++++++++++++++-------------- 1 file changed, 84 insertions(+), 62 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 3e1a1d3c6..de1529006 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -106,6 +106,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( /* Main Search Loop */ while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ size_t mLength; + U32 offset; size_t const h2 = ZSTD_hashPtr(ip, hBitsL, 8); size_t const h = ZSTD_hashPtr(ip, hBitsS, mls); U32 const current = (U32)(ip-base); @@ -120,6 +121,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( base + repIndex; hashLong[h2] = hashSmall[h] = current; /* update hash tables */ + /* check dictMatchState repcode */ if (dictMode == ZSTD_dictMatchState && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { @@ -127,73 +129,93 @@ size_t ZSTD_compressBlock_doubleFast_generic( mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else if ( dictMode == ZSTD_noDict - && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { + goto _match_stored; + } + + /* check noDict repcode */ + if ( dictMode == ZSTD_noDict + && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else { - U32 offset; - if ( (matchIndexL > prefixLowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { - mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; - offset = (U32)(ip-matchLong); - while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ - } else if ( (matchIndexS > prefixLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { - size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); - U32 const matchIndexL3 = hashLong[hl3]; - const BYTE* matchL3 = base + matchIndexL3; - hashLong[hl3] = current + 1; - if ( (matchIndexL3 > prefixLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { - mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; - ip++; - offset = (U32)(ip-matchL3); - while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ - } else { - mLength = ZSTD_count(ip+4, match+4, iend) + 4; - offset = (U32)(ip-match); - while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - } - } else if (dictMode == ZSTD_dictMatchState) { - U32 const dictMatchIndexL = dictHashLong[h2]; - U32 const dictMatchIndexS = dictHashSmall[h]; - const BYTE* dictMatchL = dictBase + dictMatchIndexL; - const BYTE* dictMatchS = dictBase + dictMatchIndexS; - assert(dictMatchL < dictEnd); - assert(dictMatchS < dictEnd); - if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { - mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; - offset = (U32)(current - dictMatchIndexL - dictIndexDelta); - while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ - } else if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { - size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); - U32 const dictMatchIndexL3 = dictHashLong[hl3]; - const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; - assert(dictMatchL3 < dictEnd); - if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { - mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; - ip++; - offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); - while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ - } else { - mLength = ZSTD_count_2segments(ip+4, dictMatchS+4, iend, dictEnd, istart) + 4; - offset = (U32)(current - dictMatchIndexS - dictIndexDelta); - while (((ip>anchor) & (dictMatchS>dictLowest)) && (ip[-1] == dictMatchS[-1])) { ip--; dictMatchS--; mLength++; } /* catch up */ - } - } else { - ip += ((ip-anchor) >> kSearchStrength) + 1; - continue; - } - } else { - ip += ((ip-anchor) >> kSearchStrength) + 1; - continue; - } - - offset_2 = offset_1; - offset_1 = offset; - - ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + goto _match_stored; } + /* check prefix long match */ + if ( (matchIndexL > prefixLowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { + mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; + offset = (U32)(ip-matchLong); + while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ + goto _match_found; + } + + /* check prefix short match */ + if ( (matchIndexS > prefixLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { + size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); + U32 const matchIndexL3 = hashLong[hl3]; + const BYTE* matchL3 = base + matchIndexL3; + hashLong[hl3] = current + 1; + + /* check prefix + 1 long match */ + if ( (matchIndexL3 > prefixLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { + mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; + ip++; + offset = (U32)(ip-matchL3); + while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ + } else { + mLength = ZSTD_count(ip+4, match+4, iend) + 4; + offset = (U32)(ip-match); + while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + } + goto _match_found; + } + + /* check dictMatchState matches */ + if (dictMode == ZSTD_dictMatchState) { + U32 const dictMatchIndexL = dictHashLong[h2]; + U32 const dictMatchIndexS = dictHashSmall[h]; + const BYTE* dictMatchL = dictBase + dictMatchIndexL; + const BYTE* dictMatchS = dictBase + dictMatchIndexS; + assert(dictMatchL < dictEnd); + assert(dictMatchS < dictEnd); + + if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { + mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; + offset = (U32)(current - dictMatchIndexL - dictIndexDelta); + while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ + goto _match_found; + } + + if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { + size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); + U32 const dictMatchIndexL3 = dictHashLong[hl3]; + const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; + assert(dictMatchL3 < dictEnd); + if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip)) { + mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; + ip++; + offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); + while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ + } else { + mLength = ZSTD_count_2segments(ip+4, dictMatchS+4, iend, dictEnd, istart) + 4; + offset = (U32)(current - dictMatchIndexS - dictIndexDelta); + while (((ip>anchor) & (dictMatchS>dictLowest)) && (ip[-1] == dictMatchS[-1])) { ip--; dictMatchS--; mLength++; } /* catch up */ + } + + goto _match_found; + } + } + + ip += ((ip-anchor) >> kSearchStrength) + 1; + continue; + +_match_found: + offset_2 = offset_1; + offset_1 = offset; + + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + +_match_stored: /* match found */ ip += mLength; anchor = ip; From 88b733b380c99f0952d7ff2ea8ac6248b24a6a5a Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 16 May 2018 15:20:16 -0400 Subject: [PATCH 129/288] Interleave Prefix and Dict Searches --- lib/compress/zstd_double_fast.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index de1529006..cf58da161 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -149,6 +149,20 @@ size_t ZSTD_compressBlock_doubleFast_generic( goto _match_found; } + /* check dictMatchState long match */ + if (dictMode == ZSTD_dictMatchState) { + U32 const dictMatchIndexL = dictHashLong[h2]; + const BYTE* dictMatchL = dictBase + dictMatchIndexL; + assert(dictMatchL < dictEnd); + + if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { + mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; + offset = (U32)(current - dictMatchIndexL - dictIndexDelta); + while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ + goto _match_found; + } + } + /* check prefix short match */ if ( (matchIndexS > prefixLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); @@ -170,22 +184,12 @@ size_t ZSTD_compressBlock_doubleFast_generic( goto _match_found; } - /* check dictMatchState matches */ + /* check dictMatchState short match */ if (dictMode == ZSTD_dictMatchState) { - U32 const dictMatchIndexL = dictHashLong[h2]; U32 const dictMatchIndexS = dictHashSmall[h]; - const BYTE* dictMatchL = dictBase + dictMatchIndexL; const BYTE* dictMatchS = dictBase + dictMatchIndexS; - assert(dictMatchL < dictEnd); assert(dictMatchS < dictEnd); - if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { - mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; - offset = (U32)(current - dictMatchIndexL - dictIndexDelta); - while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ - goto _match_found; - } - if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); U32 const dictMatchIndexL3 = dictHashLong[hl3]; From 5b292b5685e6bd1b2278e37663d581c42e426f21 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 23 May 2018 16:43:33 -0400 Subject: [PATCH 130/288] Check Long + 1 Matches in Both Prefix and Dict in Bothe Short Match Paths --- lib/compress/zstd_double_fast.c | 88 +++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index cf58da161..401724715 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -111,7 +111,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( size_t const h = ZSTD_hashPtr(ip, hBitsS, mls); U32 const current = (U32)(ip-base); U32 const matchIndexL = hashLong[h2]; - U32 const matchIndexS = hashSmall[h]; + U32 matchIndexS = hashSmall[h]; const BYTE* matchLong = base + matchIndexL; const BYTE* match = base + matchIndexS; const U32 repIndex = current + 1 - offset_1; @@ -165,54 +165,68 @@ size_t ZSTD_compressBlock_doubleFast_generic( /* check prefix short match */ if ( (matchIndexS > prefixLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { - size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); - U32 const matchIndexL3 = hashLong[hl3]; - const BYTE* matchL3 = base + matchIndexL3; - hashLong[hl3] = current + 1; - - /* check prefix + 1 long match */ - if ( (matchIndexL3 > prefixLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { - mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; - ip++; - offset = (U32)(ip-matchL3); - while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ - } else { - mLength = ZSTD_count(ip+4, match+4, iend) + 4; - offset = (U32)(ip-match); - while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - } - goto _match_found; + goto _search_next_long; } /* check dictMatchState short match */ if (dictMode == ZSTD_dictMatchState) { U32 const dictMatchIndexS = dictHashSmall[h]; - const BYTE* dictMatchS = dictBase + dictMatchIndexS; - assert(dictMatchS < dictEnd); + match = dictBase + dictMatchIndexS; + matchIndexS = dictMatchIndexS + dictIndexDelta; - if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { - size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); - U32 const dictMatchIndexL3 = dictHashLong[hl3]; - const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; - assert(dictMatchL3 < dictEnd); - if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip)) { - mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; - ip++; - offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); - while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ - } else { - mLength = ZSTD_count_2segments(ip+4, dictMatchS+4, iend, dictEnd, istart) + 4; - offset = (U32)(current - dictMatchIndexS - dictIndexDelta); - while (((ip>anchor) & (dictMatchS>dictLowest)) && (ip[-1] == dictMatchS[-1])) { ip--; dictMatchS--; mLength++; } /* catch up */ - } - - goto _match_found; + if (match > dictLowest && MEM_read32(match) == MEM_read32(ip)) { + goto _search_next_long; } } ip += ((ip-anchor) >> kSearchStrength) + 1; continue; +_search_next_long: + + { + size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); + U32 const matchIndexL3 = hashLong[hl3]; + const BYTE* matchL3 = base + matchIndexL3; + hashLong[hl3] = current + 1; + + /* check prefix long +1 match */ + if ( (matchIndexL3 > prefixLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { + mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; + ip++; + offset = (U32)(ip-matchL3); + while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ + goto _match_found; + } + + /* check dict long +1 match */ + if (dictMode == ZSTD_dictMatchState) { + U32 const dictMatchIndexL3 = dictHashLong[hl3]; + const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; + assert(dictMatchL3 < dictEnd); + if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { + mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; + ip++; + offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); + while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ + goto _match_found; + } + } + } + + /* if no long +1 match, explore the short match we found */ + if (dictMode == ZSTD_dictMatchState && matchIndexS < prefixLowestIndex) { + mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, istart) + 4; + offset = (U32)(current - matchIndexS); + while (((ip>anchor) & (match>dictLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + } else { + mLength = ZSTD_count(ip+4, match+4, iend) + 4; + offset = (U32)(ip - match); + while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + } + + /* fall-through */ + _match_found: offset_2 = offset_1; offset_1 = offset; From e2c0e3d4371ec4b61a16609cacf1d517dc48cb36 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 25 May 2018 14:52:21 -0700 Subject: [PATCH 131/288] slightly nudge choices towards less sequences also slightly improve some strange detrimental corner cases. --- lib/compress/zstd_opt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 2a699c2ac..e66019aa8 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -282,6 +282,8 @@ ZSTD_getMatchPrice(U32 const offset, price += (ML_bits[mlCode] * BITCOST_MULTIPLIER) + (optPtr->matchLengthSumBasePrice - WEIGHT(optPtr->matchLengthFreq[mlCode], optLevel)); } + price += BITCOST_MULTIPLIER / 5; /* heuristic : make matches a bit more costly to favor less sequences -> faster decompression speed */ + DEBUGLOG(8, "ZSTD_getMatchPrice(ml:%u) = %u", matchLength, price); return price; } From 5f177f1c53d357313fa520a3c93aab2dad49a89d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 25 May 2018 15:19:52 -0700 Subject: [PATCH 132/288] btultra accepts blocks with poorer compression ratio zstd rejects blocks which do not compress by at least a certain amount. In which case, such block is simply emitted uncompressed (even if a little bit of compression could be achieved). This is better for decompression speed, hence for energy. The logic is controlled by ZSTD_minGain(). The rule is applied uniformly, at all compression levels. This change makes btultra accepts blocks with poor compression ratios. We presume that users of btultra mode prefers compression ratio over some decompress speed gains. The threshold for minimum gain is lowered for btultra from s>>6 (~1.5% minimum gain) to s>>7 (~0.8% minimum gain). This is a prudent change. Not sure if it's large enough. --- lib/compress/zstd_compress.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 538319bcb..fdb6a65f1 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1492,7 +1492,15 @@ static size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, cons } -static size_t ZSTD_minGain(size_t srcSize) { return (srcSize >> 6) + 2; } +/* ZSTD_minGain() : + * minimum compression required + * to generate a compress block or a compressed literals section. + * note : use same formula for both situations */ +static size_t ZSTD_minGain(size_t srcSize, ZSTD_strategy strat) +{ + U32 const minlog = (strat==ZSTD_btultra) ? 7 : 6; + return (srcSize >> minlog) + 2; +} static size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, ZSTD_hufCTables_t* nextHuf, @@ -1501,7 +1509,7 @@ static size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, const void* src, size_t srcSize, U32* workspace, const int bmi2) { - size_t const minGain = ZSTD_minGain(srcSize); + size_t const minGain = ZSTD_minGain(srcSize, strategy); size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB); BYTE* const ostart = (BYTE*)dst; U32 singleStream = srcSize < 256; @@ -2162,7 +2170,7 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, if (ZSTD_isError(cSize)) return cSize; /* Check compressibility */ - { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize); /* note : fixed formula, maybe should depend on compression level, or strategy */ + { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, cctxParams->cParams.strategy); if (cSize >= maxCSize) return 0; /* block not compressed */ } From a7fdceeccd0880f8804d6d4fe7ce6ab0c6cbb98b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 25 May 2018 17:41:16 -0700 Subject: [PATCH 133/288] changed dynamic fse threshold for offset recent experienced showed that default distribution table for offset can get it wrong pretty quickly with the nb of symbols, while it remains a reasonable choice much longer for lengths symbols. Changed the formula, so that dynamic threshold is now 32 symbols for offsets. It remains at 64 symbols for lengths. Detection based on defaultNormLog --- lib/compress/zstd_compress.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index df7cb8aa7..e6b363ae0 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1743,8 +1743,6 @@ ZSTD_selectEncodingType( ZSTD_defaultPolicy_e const isDefaultAllowed, ZSTD_strategy const strategy) { -#define MIN_SEQ_FOR_DYNAMIC_FSE 64 -#define MAX_SEQ_FOR_STATIC_FSE 1000 ZSTD_STATIC_ASSERT(ZSTD_defaultDisallowed == 0 && ZSTD_defaultAllowed != 0); if (mostFrequent == nbSeq) { *repeatMode = FSE_repeat_none; @@ -1761,11 +1759,14 @@ ZSTD_selectEncodingType( } if (strategy < ZSTD_lazy) { if (isDefaultAllowed) { - if ((*repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + size_t const staticFse_nbSeq_max = 1000; + size_t const dynamicFse_nbSeq_min = 1 << defaultNormLog; /* 32 for offset, 64 for lengths */ + assert(defaultNormLog >= 5 && defaultNormLog <= 6); /* xx_DEFAULTNORMLOG */ + if ((*repeatMode == FSE_repeat_valid) && (nbSeq < staticFse_nbSeq_max)) { DEBUGLOG(5, "Selected set_repeat"); return set_repeat; } - if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) { + if ((nbSeq < dynamicFse_nbSeq_min) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) { DEBUGLOG(5, "Selected set_basic"); /* The format allows default tables to be repeated, but it isn't useful. * When using simple heuristics to select encoding type, we don't want From e916c365a13813bbff58126dc1d361748e8bcdfb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 25 May 2018 20:43:09 -0700 Subject: [PATCH 134/288] fixed minor visual warning --- lib/compress/zstd_compress.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e6b363ae0..b95a9657e 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1760,13 +1760,15 @@ ZSTD_selectEncodingType( if (strategy < ZSTD_lazy) { if (isDefaultAllowed) { size_t const staticFse_nbSeq_max = 1000; - size_t const dynamicFse_nbSeq_min = 1 << defaultNormLog; /* 32 for offset, 64 for lengths */ + size_t const dynamicFse_nbSeq_min = (size_t)1 << defaultNormLog; /* 32 for offset, 64 for lengths */ assert(defaultNormLog >= 5 && defaultNormLog <= 6); /* xx_DEFAULTNORMLOG */ - if ((*repeatMode == FSE_repeat_valid) && (nbSeq < staticFse_nbSeq_max)) { + if ( (*repeatMode == FSE_repeat_valid) + && (nbSeq < staticFse_nbSeq_max) ) { DEBUGLOG(5, "Selected set_repeat"); return set_repeat; } - if ((nbSeq < dynamicFse_nbSeq_min) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) { + if ( (nbSeq < dynamicFse_nbSeq_min) + || (mostFrequent < (nbSeq >> (defaultNormLog-1))) ) { DEBUGLOG(5, "Selected set_basic"); /* The format allows default tables to be repeated, but it isn't useful. * When using simple heuristics to select encoding type, we don't want From 463a0fe38b4a49f9c54010221dc82e4074682b36 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 29 May 2018 14:07:25 -0700 Subject: [PATCH 135/288] simplified optimal parser removed "cached" structure. prices are now saved in the optimal table. Primarily done for simplification. Might improve speed by a little. But actually, and surprisingly, also improves ratio in some circumstances. --- lib/common/zstd_common.c | 2 +- lib/common/zstd_internal.h | 6 +- lib/compress/zstd_compress_internal.h | 2 +- lib/compress/zstd_opt.c | 267 +++++++++++--------------- lib/legacy/zstd_v04.c | 44 ++--- 5 files changed, 132 insertions(+), 189 deletions(-) diff --git a/lib/common/zstd_common.c b/lib/common/zstd_common.c index bccc94889..64aa8575a 100644 --- a/lib/common/zstd_common.c +++ b/lib/common/zstd_common.c @@ -49,7 +49,7 @@ const char* ZSTD_getErrorString(ZSTD_ErrorCode code) { return ERR_getErrorString /*! g_debuglog_enable : * turn on/off debug traces (global switch) */ #if defined(ZSTD_DEBUG) && (ZSTD_DEBUG >= 2) -int g_debuglog_enable = 1; +int g_debuglevel = ZSTD_DEBUG; #endif diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 981ce7f54..86d96b5a0 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -56,7 +56,7 @@ extern "C" { #undef DEBUGLOG #if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=2) # include -extern int g_debuglog_enable; +extern int g_debuglevel; /* recommended values for ZSTD_DEBUG display levels : * 1 : no display, enables assert() only * 2 : reserved for currently active debug path @@ -65,11 +65,11 @@ extern int g_debuglog_enable; * 5 : events once per block * 6 : events once per sequence (*very* verbose) */ # define RAWLOG(l, ...) { \ - if ((g_debuglog_enable) & (l<=ZSTD_DEBUG)) { \ + if (l<=g_debuglevel) { \ fprintf(stderr, __VA_ARGS__); \ } } # define DEBUGLOG(l, ...) { \ - if ((g_debuglog_enable) & (l<=ZSTD_DEBUG)) { \ + if (l<=g_debuglevel) { \ fprintf(stderr, __FILE__ ": " __VA_ARGS__); \ fprintf(stderr, " \n"); \ } } diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 516c752a3..87206c1ef 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -299,7 +299,7 @@ MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const v static const BYTE* g_start = NULL; if (g_start==NULL) g_start = (const BYTE*)literals; /* note : index only works for compression within a single segment */ { U32 const pos = (U32)((const BYTE*)literals - g_start); - DEBUGLOG(6, "Cpos%7u :%3u literals, match%3u bytes at dist.code%7u", + DEBUGLOG(6, "Cpos%7u :%3u literals, match%4u bytes at offCode%7u", pos, (U32)litLength, (U32)mlBase+MINMATCH, (U32)offsetCode); } #endif diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index e66019aa8..de745b6e6 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -189,10 +189,13 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */ /* dynamic statistics */ - { U32 price = litLength * optPtr->litSumBasePrice; - U32 u; - for (u=0; u < litLength; u++) - price -= WEIGHT(optPtr->litFreq[literals[u]], optLevel); + { U32 price, u; + for (u=0, price=0; u < litLength; u++) { + U32 const litWeight = WEIGHT(optPtr->litFreq[literals[u]], optLevel); + U32 litCost = optPtr->litSumBasePrice - litWeight; + //if (litCost < BITCOST_MULTIPLIER) litCost = BITCOST_MULTIPLIER; /* minimum 1 bit per symbol (huffman) */ + price += litCost; + } return price; } } @@ -209,17 +212,6 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP } } -/* ZSTD_litLengthPrice() : - * cost of the literal part of a sequence, - * including literals themselves, and literalLength symbol */ -static U32 ZSTD_fullLiteralsCost(const BYTE* const literals, U32 const litLength, - const optState_t* const optPtr, - int optLevel) -{ - return ZSTD_rawLiteralsCost(literals, litLength, optPtr, optLevel) - + ZSTD_litLengthPrice(litLength, optPtr, optLevel); -} - /* ZSTD_litLengthContribution() : * @return ( cost(litlength) - cost(0) ) * this value can then be added to rawLiteralsCost() @@ -288,6 +280,8 @@ ZSTD_getMatchPrice(U32 const offset, return price; } +/* ZSTD_updateStats() : + * assumption : literals + litLengtn <= iend */ static void ZSTD_updateStats(optState_t* const optPtr, U32 litLength, const BYTE* literals, U32 offsetCode, U32 matchLength) @@ -556,8 +550,8 @@ U32 ZSTD_insertBtAndGetAllMatches ( } } /* save longer solution */ if (repLen > bestLength) { - DEBUGLOG(8, "found rep-match %u of length %u", - repCode - ll0, (U32)repLen); + DEBUGLOG(8, "found repCode %u (ll0:%u, offset:%u) of length %u", + repCode, ll0, repOffset, repLen); bestLength = repLen; matches[mnum].off = repCode - ll0; matches[mnum].len = (U32)repLen; @@ -617,8 +611,8 @@ U32 ZSTD_insertBtAndGetAllMatches ( } if (matchLength > bestLength) { - DEBUGLOG(8, "found match of length %u at distance %u", - (U32)matchLength, current - matchIndex); + DEBUGLOG(8, "found match of length %u at distance %u (offCode=%u)", + (U32)matchLength, current - matchIndex, current - matchIndex + ZSTD_REP_MOVE); assert(matchEndIdx > matchIndex); if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; @@ -706,60 +700,9 @@ repcodes_t ZSTD_updateRep(U32 const rep[3], U32 const offset, U32 const ll0) } -typedef struct { - const BYTE* anchor; - U32 litlen; - U32 rawLitCost; -} cachedLiteralPrice_t; - -static U32 ZSTD_rawLiteralsCost_cached( - cachedLiteralPrice_t* const cachedLitPrice, - const BYTE* const anchor, U32 const litlen, - const optState_t* const optStatePtr, - int optLevel) +static U32 ZSTD_totalLen(ZSTD_optimal_t sol) { - U32 startCost; - U32 remainingLength; - const BYTE* startPosition; - - if (anchor == cachedLitPrice->anchor) { - startCost = cachedLitPrice->rawLitCost; - startPosition = anchor + cachedLitPrice->litlen; - assert(litlen >= cachedLitPrice->litlen); - remainingLength = litlen - cachedLitPrice->litlen; - } else { - startCost = 0; - startPosition = anchor; - remainingLength = litlen; - } - - { U32 const rawLitCost = startCost + ZSTD_rawLiteralsCost(startPosition, remainingLength, optStatePtr, optLevel); - cachedLitPrice->anchor = anchor; - cachedLitPrice->litlen = litlen; - cachedLitPrice->rawLitCost = rawLitCost; - return rawLitCost; - } -} - -static U32 ZSTD_fullLiteralsCost_cached( - cachedLiteralPrice_t* const cachedLitPrice, - const BYTE* const anchor, U32 const litlen, - const optState_t* const optStatePtr, - int optLevel) -{ - return ZSTD_rawLiteralsCost_cached(cachedLitPrice, anchor, litlen, optStatePtr, optLevel) - + ZSTD_litLengthPrice(litlen, optStatePtr, optLevel); -} - -static int ZSTD_literalsContribution_cached( - cachedLiteralPrice_t* const cachedLitPrice, - const BYTE* const anchor, U32 const litlen, - const optState_t* const optStatePtr, - int optLevel) -{ - int const contribution = ZSTD_rawLiteralsCost_cached(cachedLitPrice, anchor, litlen, optStatePtr, optLevel) - + ZSTD_litLengthContribution(litlen, optStatePtr, optLevel); - return contribution; + return sol.litlen + sol.mlen; } FORCE_INLINE_TEMPLATE size_t @@ -784,7 +727,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, ZSTD_optimal_t* const opt = optStatePtr->priceTable; ZSTD_match_t* const matches = optStatePtr->matchTable; - cachedLiteralPrice_t cachedLitPrice; + ZSTD_optimal_t lastSequence; /* init */ DEBUGLOG(5, "ZSTD_compressBlock_opt_generic"); @@ -792,12 +735,10 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, ms->nextToUpdate3 = ms->nextToUpdate; ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize, optLevel); ip += (ip==prefixStart); - memset(&cachedLitPrice, 0, sizeof(cachedLitPrice)); /* Match Loop */ while (ip < ilimit) { U32 cur, last_pos = 0; - U32 best_mlen, best_off; /* find first match */ { U32 const litlen = (U32)(ip - anchor); @@ -807,27 +748,29 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, /* initialize opt[0] */ { U32 i ; for (i=0; i immediate encoding */ { U32 const maxML = matches[nbMatches-1].len; U32 const maxOffset = matches[nbMatches-1].off; - DEBUGLOG(7, "found %u matches of maxLength=%u and maxOffset=%u at cPos=%u => start new serie", + DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffCode=%u at cPos=%u => start new serie", nbMatches, maxML, maxOffset, (U32)(ip-prefixStart)); if (maxML > sufficient_len) { - best_mlen = maxML; - best_off = maxOffset; - DEBUGLOG(7, "large match (%u>%u), immediate encoding", - best_mlen, sufficient_len); + lastSequence.litlen = litlen; + lastSequence.mlen = maxML; + lastSequence.off = maxOffset; + DEBUGLOG(6, "large match (%u>%u), immediate encoding", + maxML, sufficient_len); cur = 0; - last_pos = 1; + last_pos = ZSTD_totalLen(lastSequence); goto _shortestPath; } } /* set prices for first matches starting position == 0 */ - { U32 const literalsPrice = ZSTD_fullLiteralsCost_cached(&cachedLitPrice, anchor, litlen, optStatePtr, optLevel); + { U32 const literalsPrice = opt[0].price + ZSTD_litLengthPrice(0, optStatePtr, optLevel); U32 pos; U32 matchNb; for (pos = 1; pos < minMatch; pos++) { @@ -857,27 +800,28 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, for (cur = 1; cur <= last_pos; cur++) { const BYTE* const inr = ip + cur; assert(cur < ZSTD_OPT_NUM); + DEBUGLOG(7, "cPos:%zi==rPos:%u", inr-istart, cur) /* Fix current position with one literal if cheaper */ - { U32 const litlen = (opt[cur-1].mlen == 1) ? opt[cur-1].litlen + 1 : 1; - int price; /* note : contribution can be negative */ - if (cur > litlen) { - price = opt[cur - litlen].price + ZSTD_literalsContribution(inr-litlen, litlen, optStatePtr, optLevel); - } else { - price = ZSTD_literalsContribution_cached(&cachedLitPrice, anchor, litlen, optStatePtr, optLevel); - } + { U32 const litlen = (opt[cur-1].mlen == 0) ? opt[cur-1].litlen + 1 : 1; + int const price = opt[cur-1].price + + ZSTD_rawLiteralsCost(ip+cur-1, 1, optStatePtr, optLevel) + + ZSTD_litLengthPrice(litlen, optStatePtr, optLevel) + - ZSTD_litLengthPrice(litlen-1, optStatePtr, optLevel); assert(price < 1000000000); /* overflow check */ if (price <= opt[cur].price) { - DEBUGLOG(7, "rPos:%u : better price (%.2f<=%.2f) using literal", - cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price)); - opt[cur].mlen = 1; + DEBUGLOG(7, "cPos:%zi==rPos:%u : better price (%.2f<=%.2f) using literal (ll==%u) (hist:%u,%u,%u)", + inr-istart, cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price), litlen, + opt[cur-1].rep[0], opt[cur-1].rep[1], opt[cur-1].rep[2]); + opt[cur].mlen = 0; opt[cur].off = 0; opt[cur].litlen = litlen; opt[cur].price = price; memcpy(opt[cur].rep, opt[cur-1].rep, sizeof(opt[cur].rep)); } else { - DEBUGLOG(7, "rPos:%u : literal would cost more (%.2f>%.2f)", - cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price)); + DEBUGLOG(7, "cPos:%zi==rPos:%u : literal would cost more (%.2f>%.2f) (hist:%u,%u,%u)", + inr-istart, cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price), + opt[cur].rep[0], opt[cur].rep[1], opt[cur].rep[2]); } } @@ -892,10 +836,10 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, continue; /* skip unpromising positions; about ~+6% speed, -0.01 ratio */ } - { U32 const ll0 = (opt[cur].mlen != 1); - U32 const litlen = (opt[cur].mlen == 1) ? opt[cur].litlen : 0; - U32 const previousPrice = (cur > litlen) ? opt[cur-litlen].price : 0; - U32 const basePrice = previousPrice + ZSTD_fullLiteralsCost(inr-litlen, litlen, optStatePtr, optLevel); + { U32 const ll0 = (opt[cur].mlen != 0); + U32 const litlen = (opt[cur].mlen == 0) ? opt[cur].litlen : 0; + U32 const previousPrice = opt[cur].price; + U32 const basePrice = previousPrice + ZSTD_litLengthPrice(0, optStatePtr, optLevel); U32 const nbMatches = ZSTD_BtGetAllMatches(ms, cParams, inr, iend, extDict, opt[cur].rep, ll0, matches, minMatch); U32 matchNb; if (!nbMatches) { @@ -904,14 +848,17 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, } { U32 const maxML = matches[nbMatches-1].len; - DEBUGLOG(7, "rPos:%u, found %u matches, of maxLength=%u", - cur, nbMatches, maxML); + DEBUGLOG(7, "cPos:%zi==rPos:%u, found %u matches, of maxLength=%u", + inr-istart, cur, nbMatches, maxML); if ( (maxML > sufficient_len) || (cur + maxML >= ZSTD_OPT_NUM) ) { - best_mlen = maxML; - best_off = matches[nbMatches-1].off; - last_pos = cur + 1; + lastSequence.mlen = maxML; + lastSequence.off = matches[nbMatches-1].off; + lastSequence.litlen = litlen; + cur -= (opt[cur].mlen==0) ? opt[cur].litlen : 0; /* last sequence is actually only literals, fix cur to last match - note : may underflow, in which case, it's first sequence, and it's okay */ + last_pos = cur + ZSTD_totalLen(lastSequence); + if (cur > ZSTD_OPT_NUM) cur = 0; /* underflow => first match */ goto _shortestPath; } } @@ -923,7 +870,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, U32 const startML = (matchNb>0) ? matches[matchNb-1].len+1 : minMatch; U32 mlen; - DEBUGLOG(7, "testing match %u => offCode=%u, mlen=%u, llen=%u", + DEBUGLOG(7, "testing match %u => offCode=%4u, mlen=%2u, llen=%2u", matchNb, matches[matchNb].off, lastML, litlen); for (mlen = lastML; mlen >= startML; mlen--) { /* scan downward */ @@ -931,8 +878,8 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, int const price = basePrice + ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel); if ((pos > last_pos) || (price < opt[pos].price)) { - DEBUGLOG(7, "rPos:%u => new better price (%.2f<%.2f)", - pos, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price)); + DEBUGLOG(7, "rPos:%u (ml=%2u) => new better price (%.2f<%.2f)", + pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price)); while (last_pos < pos) { opt[last_pos+1].price = ZSTD_MAX_PRICE; last_pos++; } /* fill empty positions */ opt[pos].mlen = mlen; opt[pos].off = offset; @@ -941,63 +888,79 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, ZSTD_STATIC_ASSERT(sizeof(opt[pos].rep) == sizeof(repHistory)); memcpy(opt[pos].rep, &repHistory, sizeof(repHistory)); } else { + DEBUGLOG(7, "rPos:%u (ml=%2u) => new price is worse (%.2f>=%.2f)", + pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price)); if (optLevel==0) break; /* early update abort; gets ~+10% speed for about -0.01 ratio loss */ } } } } } /* for (cur = 1; cur <= last_pos; cur++) */ - best_mlen = opt[last_pos].mlen; - best_off = opt[last_pos].off; - cur = last_pos - best_mlen; + lastSequence = opt[last_pos]; + cur = last_pos > ZSTD_totalLen(lastSequence) ? last_pos - ZSTD_totalLen(lastSequence) : 0; /* single sequence, and it starts before `ip` */ + assert(cur < ZSTD_OPT_NUM); /* control overflow*/ _shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */ - assert(opt[0].mlen == 1); + assert(opt[0].mlen == 0); - /* reverse traversal */ - DEBUGLOG(7, "start reverse traversal (last_pos:%u, cur:%u)", - last_pos, cur); - { U32 selectedMatchLength = best_mlen; - U32 selectedOffset = best_off; - U32 pos = cur; - while (1) { - U32 const mlen = opt[pos].mlen; - U32 const off = opt[pos].off; - opt[pos].mlen = selectedMatchLength; - opt[pos].off = selectedOffset; - selectedMatchLength = mlen; - selectedOffset = off; - if (mlen > pos) break; - pos -= mlen; - } } + { U32 const storeEnd = cur + 1; + U32 storeStart = storeEnd; + U32 seqPos = cur; - /* save sequences */ - { U32 pos; - for (pos=0; pos < last_pos; ) { - U32 const llen = (U32)(ip - anchor); - U32 const mlen = opt[pos].mlen; - U32 const offset = opt[pos].off; - if (mlen == 1) { ip++; pos++; continue; } /* literal position => move on */ - pos += mlen; ip += mlen; + DEBUGLOG(6, "start reverse traversal (last_pos:%u, cur:%u)", + last_pos, cur); + assert(storeEnd < ZSTD_OPT_NUM); + DEBUGLOG(6, "last sequence copied into pos=%u (llen=%u,mlen=%u,ofc=%u)", + storeEnd, lastSequence.litlen, lastSequence.mlen, lastSequence.off); + opt[storeEnd] = lastSequence; + while (seqPos > 0) { + U32 const backDist = ZSTD_totalLen(opt[seqPos]); + storeStart--; + DEBUGLOG(6, "sequence from rPos=%u copied into pos=%u (llen=%u,mlen=%u,ofc=%u)", + seqPos, storeStart, opt[seqPos].litlen, opt[seqPos].mlen, opt[seqPos].off); + opt[storeStart] = opt[seqPos]; + seqPos = (seqPos > backDist) ? seqPos - backDist : 0; + } - /* repcodes update : like ZSTD_updateRep(), but update in place */ - if (offset >= ZSTD_REP_NUM) { /* full offset */ - rep[2] = rep[1]; - rep[1] = rep[0]; - rep[0] = offset - ZSTD_REP_MOVE; - } else { /* repcode */ - U32 const repCode = offset + (llen==0); - if (repCode) { /* note : if repCode==0, no change */ - U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode]; - if (repCode >= 2) rep[2] = rep[1]; + /* save sequences */ + DEBUGLOG(6, "sending selected sequences into seqStore") + { U32 storePos; + for (storePos=storeStart; storePos <= storeEnd; storePos++) { + U32 const llen = opt[storePos].litlen; + U32 const mlen = opt[storePos].mlen; + U32 const offCode = opt[storePos].off; + U32 const advance = llen + mlen; + DEBUGLOG(6, "considering seq starting at %zi, llen=%u, mlen=%u", + anchor - istart, llen, mlen); + + if (mlen==0) { /* only literals => must be last "sequence", actually starting a new stream of sequences */ + assert(storePos == storeEnd); /* must be last sequence */ + ip = anchor + llen; /* last "sequence" is a bunch of literals => don't progress anchor */ + continue; /* will finish */ + } + + /* repcodes update : like ZSTD_updateRep(), but update in place */ + if (offCode >= ZSTD_REP_NUM) { /* full offset */ + rep[2] = rep[1]; rep[1] = rep[0]; - rep[0] = currentOffset; - } } + rep[0] = offCode - ZSTD_REP_MOVE; + } else { /* repcode */ + U32 const repCode = offCode + (llen==0); + if (repCode) { /* note : if repCode==0, no change */ + U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode]; + if (repCode >= 2) rep[2] = rep[1]; + rep[1] = rep[0]; + rep[0] = currentOffset; + } } + + assert(anchor + llen <= iend); + ZSTD_updateStats(optStatePtr, llen, anchor, offCode, mlen); + ZSTD_storeSeq(seqStore, llen, anchor, offCode, mlen-MINMATCH); + anchor += advance; + ip = anchor; + } } + ZSTD_setBasePrices(optStatePtr, optLevel); + } - ZSTD_updateStats(optStatePtr, llen, anchor, offset, mlen); - ZSTD_storeSeq(seqStore, llen, anchor, offset, mlen-MINMATCH); - anchor = ip; - } } - ZSTD_setBasePrices(optStatePtr, optLevel); } /* while (ip < ilimit) */ /* Return the last literals size */ diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c index fb6d1d4b1..0dd7c4f2c 100644 --- a/lib/legacy/zstd_v04.c +++ b/lib/legacy/zstd_v04.c @@ -9,14 +9,19 @@ */ -/*- Dependencies -*/ + /****************************************** + * Includes + ******************************************/ +#include /* size_t, ptrdiff_t */ +#include /* memcpy */ + #include "zstd_v04.h" #include "error_private.h" /* ****************************************************************** - mem.h -****************************************************************** */ + * mem.h + *******************************************************************/ #ifndef MEM_H_MODULE #define MEM_H_MODULE @@ -24,12 +29,6 @@ extern "C" { #endif -/****************************************** -* Includes -******************************************/ -#include /* size_t, ptrdiff_t */ -#include /* memcpy */ - /****************************************** * Compiler-specific @@ -87,7 +86,7 @@ extern "C" { #if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=2) # include -extern int g_debuglog_enable; +extern int g_debuglevel; /* recommended values for ZSTD_DEBUG display levels : * 1 : no display, enables assert() only * 2 : reserved for currently active debug path @@ -96,11 +95,11 @@ extern int g_debuglog_enable; * 5 : events once per block * 6 : events once per sequence (*very* verbose) */ # define RAWLOG(l, ...) { \ - if ((g_debuglog_enable) & (l<=ZSTD_DEBUG)) { \ + if (l<=g_debuglevel) { \ fprintf(stderr, __VA_ARGS__); \ } } # define DEBUGLOG(l, ...) { \ - if ((g_debuglog_enable) & (l<=ZSTD_DEBUG)) { \ + if (l<=g_debuglevel) { \ fprintf(stderr, __FILE__ ": " __VA_ARGS__); \ fprintf(stderr, " \n"); \ } } @@ -266,14 +265,6 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr) #ifndef ZSTD_STATIC_H #define ZSTD_STATIC_H -/* The objects defined into this file shall be considered experimental. - * They are not considered stable, as their prototype may change in the future. - * You can use them for tests, provide feedback, or if you can endure risks of future changes. - */ - -#if defined (__cplusplus) -extern "C" { -#endif /* ************************************* * Types @@ -360,9 +351,6 @@ static size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t maxDstS */ -#if defined (__cplusplus) -} -#endif #endif /* ZSTD_STATIC_H */ @@ -375,10 +363,6 @@ static size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t maxDstS #ifndef ZSTD_CCOMMON_H_MODULE #define ZSTD_CCOMMON_H_MODULE -#if defined (__cplusplus) -extern "C" { -#endif - /* ************************************* * Common macros ***************************************/ @@ -450,10 +434,6 @@ static void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length) } -#if defined (__cplusplus) -} -#endif - /* ****************************************************************** FSE : Finite State Entropy coder @@ -2991,7 +2971,7 @@ static size_t ZSTD_execSequence(BYTE* op, } else { - ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8); /* works even if matchLength < 8 */ + ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8); /* works even if matchLength < 8, but must be signed */ } return sequenceLength; } From 809f2f93229b3de57ea4dfab01feab2e699a7327 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 29 May 2018 15:29:55 -0700 Subject: [PATCH 136/288] minor update of literal cost function just assert() there is no negative cost evaluation for literals --- lib/compress/zstd_opt.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index de745b6e6..46a5395ac 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -189,12 +189,11 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */ /* dynamic statistics */ - { U32 price, u; - for (u=0, price=0; u < litLength; u++) { - U32 const litWeight = WEIGHT(optPtr->litFreq[literals[u]], optLevel); - U32 litCost = optPtr->litSumBasePrice - litWeight; - //if (litCost < BITCOST_MULTIPLIER) litCost = BITCOST_MULTIPLIER; /* minimum 1 bit per symbol (huffman) */ - price += litCost; + { U32 price = litLength * optPtr->litSumBasePrice; + U32 u; + for (u=0; u < litLength; u++) { + assert(WEIGHT(optPtr->litFreq[literals[u]], optLevel) <= optPtr->litSumBasePrice); /* literal cost should never be negative */ + price -= WEIGHT(optPtr->litFreq[literals[u]], optLevel); } return price; } From a4c9c4defe90f192390e0d48a3ea542abcf1720c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 31 May 2018 10:47:44 -0700 Subject: [PATCH 137/288] update Zstandard format specification answering a few questions from IETF RFC Discuss stage. --- doc/zstd_compression_format.md | 79 +++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index d95475443..7fcc1f629 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.7 (30/04/18) +0.2.8 (30/05/18) Introduction @@ -27,6 +27,8 @@ that is independent of CPU type, operating system, file system and character set, suitable for file compression, pipe and streaming compression, using the [Zstandard algorithm](http://www.zstandard.org). +The text of the specification assumes a basic background in programming +at the level of bits and other primitive data representations. The data can be produced or consumed, even for an arbitrarily long sequentially presented input data stream, @@ -39,11 +41,6 @@ for detection of data corruption. The data format defined by this specification does not attempt to allow random access to compressed data. -This specification is intended for use by implementers of software -to compress data into Zstandard format and/or decompress data from Zstandard format. -The text of the specification assumes a basic background in programming -at the level of bits and other primitive data representations. - Unless otherwise indicated below, a compliant compressor must produce data sets that conform to the specifications presented here. @@ -57,6 +54,16 @@ Whenever it does not support a parameter defined in the compressed stream, it must produce a non-ambiguous error code and associated error message explaining which parameter is unsupported. +This specification is intended for use by implementers of software +to compress data into Zstandard format and/or decompress data from Zstandard format. +The Zstandard format is supported by an open source reference implementation, +which also contains some useful validation tool, +such as `decodeCorpus`, which generate random valid frames, +that a compliant decoder should be able to decode, +or provide a meaningful error code explaining why it cannot. + + + ### Overall conventions In this document: - square brackets i.e. `[` and `]` are used to indicate optional fields or parameters. @@ -92,14 +99,14 @@ Overview Frames ------ Zstandard compressed data is made of one or more __frames__. -Each frame is independent and can be decompressed indepedently of other frames. +Each frame is independent and can be decompressed independently of other frames. The decompressed content of multiple concatenated frames is the concatenation of each frame decompressed content. There are two frame formats defined by Zstandard: Zstandard frames and Skippable frames. Zstandard frames contain compressed data, while -skippable frames contain no data and can be used for metadata. +skippable frames contain custom user metadata. ## Zstandard frames The structure of a single Zstandard frame is following: @@ -630,15 +637,8 @@ They follow the same enumeration : - `Predefined_Mode` : A predefined FSE distribution table is used, defined in [default distributions](#default-distributions). No distribution table will be present. -- `RLE_Mode` : The table description consists of a single byte. - This code will be repeated for all sequences. -- `Repeat_Mode` : The table used in the previous `Compressed_Block` with `Number_of_Sequences > 0` will be used again, - or if this is the first block, table in the dictionary will be used - No distribution table will be present. - Note that this includes `RLE_mode`, so if `Repeat_Mode` follows `RLE_Mode`, the same symbol will be repeated. - Note that this also includes `Predefined_Mode`. - If this mode is used without any previous sequence table in the frame - (or [dictionary](#dictionary-format)) to repeat, this should be treated as corruption. +- `RLE_Mode` : The table description consists of a single byte, which contain symbol's value. + This symbol will be used for all sequences. - `FSE_Compressed_Mode` : standard FSE compression. A distribution table will be present. The format of this distribution table is described in [FSE Table Description](#fse-table-description). @@ -646,6 +646,13 @@ They follow the same enumeration : and the maximum accuracy log for the offsets table is 8. `FSE_Compressed_Mode` must not be used when only one symbol is present, `RLE_Mode` should be used instead (although any other mode will work). +- `Repeat_Mode` : The table used in the previous `Compressed_Block` with `Number_of_Sequences > 0` will be used again, + or if this is the first block, table in the dictionary will be used. + Note that this includes `RLE_mode`, so if `Repeat_Mode` follows `RLE_Mode`, the same symbol will be repeated. + It also includes `Predefined_Mode`, in which case `Repeat_Mode` will have same outcome as `Predefined_Mode`. + No distribution table will be present. + If this mode is used without any previous sequence table in the frame + (nor [dictionary](#dictionary-format)) to repeat, this should be treated as corruption. #### The codes for literals lengths, match lengths, and offsets. @@ -903,16 +910,22 @@ Skippable Frames |:--------------:|:------------:|:-----------:| | 4 bytes | 4 bytes | n bytes | -Skippable frames allow the insertion of user-defined data +Skippable frames allow the insertion of user-defined metadata 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. [LZ4]:http://www.lz4.org +From a compliant decoder perspective, skippable frames need just be skipped, +and their content ignored, resuming decoding after the skippable frame. + +It can be noted that a skippable frame +can be used to watermark a stream of concatenated frames +embedding any kind of tracking information (even just an UUID). +User wary of such usage should scan the stream of concatenated frames +in an attempt to detect such frame for analysis or removal. + __`Magic_Number`__ 4 Bytes, __little-endian__ format. @@ -1196,14 +1209,15 @@ which describes how to decode the list of weights. the top four bits and the second taking the bottom four (e.g. the following operations could be used to read the weights: `Weight[0] = (Byte[0] >> 4), Weight[1] = (Byte[0] & 0xf)`, etc.). - The full representation occupies `((Number_of_Symbols+1)/2)` bytes, - meaning it uses a last full byte even if `Number_of_Symbols` is odd. + The full representation occupies `Ceiling(Number_of_Symbols/2)` bytes, + meaning it uses only full bytes even if `Number_of_Symbols` is odd. `Number_of_Symbols = headerByte - 127`. Note that maximum `Number_of_Symbols` is 255-127 = 128. - A larger series must necessarily use FSE compression. + If any present literal has a value > 128, raw header mode is not possible. + It's necessary to use FSE compression. - if `headerByte` < 128 : - the series of weights is compressed by FSE. + the series of weights is compressed using FSE. The length of the FSE-compressed series is equal to `headerByte` (0-127). ##### Finite State Entropy (FSE) compression of Huffman weights @@ -1235,18 +1249,19 @@ The number of symbols to decode is determined by tracking bitStream overflow condition: If updating state after decoding a symbol would require more bits than remain in the stream, it is assumed that extra bits are 0. Then, -the symbols for each of the final states are decoded and the process is complete. +symbols for each of the final states are decoded and the process is complete. ##### Conversion from weights to Huffman prefix codes All present symbols shall now have a `Weight` value. -It is possible to transform weights into Number_of_Bits, using this formula: +It is possible to transform weights into` Number_of_Bits`, using this formula: ``` -Number_of_Bits = Number_of_Bits ? Max_Number_of_Bits + 1 - Weight : 0 +Number_of_Bits = Weight ? Max_Number_of_Bits + 1 - Weight : 0 ``` -Symbols are sorted by `Weight`. Within same `Weight`, symbols keep natural order. +Symbols are sorted by `Weight`. +Within same `Weight`, symbols keep natural sequential order. Symbols with a `Weight` of zero are removed. -Then, starting from lowest weight, prefix codes are distributed in order. +Then, starting from lowest weight, prefix codes are distributed in sequential order. __Example__ : Let's presume the following list of weights has been decoded : @@ -1255,7 +1270,7 @@ Let's presume the following list of weights has been decoded : | -------- | --- | --- | --- | --- | --- | --- | | `Weight` | 4 | 3 | 2 | 0 | 1 | 1 | -Sorted by weight and then natural order, +Sorted by weight and then natural sequential order, it gives the following distribution : | Literal | 3 | 4 | 5 | 2 | 1 | 0 | @@ -1265,6 +1280,7 @@ it gives the following distribution : | prefix codes | N/A | 0000| 0001| 001 | 01 | 1 | ### Huffman-coded Streams + Given a Huffman decoding table, it's possible to decode a Huffman-coded stream. @@ -1554,6 +1570,7 @@ to crosscheck that an implementation build its decoding tables correctly. Version changes --------------- +- 0.2.8 : clarifications for IETF RFC discuss - 0.2.7 : clarifications from IETF RFC review, by Vijay Gurbani and Nick Terrell - 0.2.6 : fixed an error in huffman example, by Ulrich Kunitz - 0.2.5 : minor typos and clarifications From 9b979d0e33a93ff20d04e98cb788a208f86a1f97 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 31 May 2018 11:12:18 -0700 Subject: [PATCH 138/288] minor : improved API code comment Extend guarantee that ZSTD_getFrameContentSize() will delivering the decompressed size to any single-pass compression function. Answer #1156 --- lib/zstd.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/zstd.h b/lib/zstd.h index 57d3e5f7a..6f4b071e6 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -96,7 +96,7 @@ ZSTDLIB_API size_t ZSTD_decompress( void* dst, size_t dstCapacity, * `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 + * @return : - decompressed size of `src` frame content, 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". @@ -106,7 +106,8 @@ ZSTDLIB_API size_t ZSTD_decompress( void* dst, size_t dstCapacity, * 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 3 : decompressed size is always present when compression is completed using single-pass functions, + * such as ZSTD_compress(), ZSTD_compressCCtx() ZSTD_compress_usingDict() or ZSTD_compress_usingCDict(). * 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. @@ -123,8 +124,7 @@ ZSTDLIB_API unsigned long long ZSTD_getFrameContentSize(const void *src, size_t * Both functions work the same way, but ZSTD_getDecompressedSize() blends * "empty", "unknown" and "error" results to the same return value (0), * while ZSTD_getFrameContentSize() gives them separate return values. - * `src` is the start of a zstd compressed frame. - * @return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise. */ + * @return : decompressed size of `src` frame content _if known and not empty_, 0 otherwise. */ ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); From f86796639ede24a5ae83ad24a0906e708e33c1af Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 31 May 2018 16:55:50 -0400 Subject: [PATCH 139/288] Remove Incorrect and Extraneous Repcode Bounds Check --- lib/compress/zstd_double_fast.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 401724715..fbd354043 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -272,7 +272,6 @@ _match_stored: if (dictMode == ZSTD_noDict) { while ( (ip <= ilimit) - && (ip - offset_2 >= istart) && ( (offset_2>0) & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { /* store sequence */ From 48deab92de4c507720903b959c2223b985a2a831 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Fri, 25 May 2018 15:19:37 -0400 Subject: [PATCH 140/288] Allow Different Dict Attachment Cut-Offs for Different Strategies --- lib/compress/zstd_compress.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 46a3fe541..68beea0c9 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1207,7 +1207,18 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, /* We have a choice between copying the dictionary context into the working * context, or referencing the dictionary context from the working context * in-place. We decide here which strategy to use. */ - const int attachDict = ( pledgedSrcSize <= 8 KB + const U64 attachDictSizeCutoffs[(unsigned)ZSTD_btultra+1] = { + 8 KB, /* unused */ + 8 KB, /* ZSTD_fast */ + 16 KB, /* ZSTD_dfast */ + 16 KB, /* ZSTD_greedy */ + 16 KB, /* ZSTD_lazy */ + 16 KB, /* ZSTD_lazy2 */ + 16 KB, /* ZSTD_btlazy2 */ + 16 KB, /* ZSTD_btopt */ + 16 KB /* ZSTD_btultra */ + }; + const int attachDict = ( pledgedSrcSize <= attachDictSizeCutoffs[cdict->cParams.strategy] || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) && !params.forceWindow /* dictMatchState isn't correctly * handled in _enforceMaxDist */ From 140f59d38e3d9b4009dada57939cb2d31048530c Mon Sep 17 00:00:00 2001 From: George Lu Date: Thu, 31 May 2018 15:29:35 -0700 Subject: [PATCH 141/288] Added --format=zstd title --- programs/zstdcli.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 28bfdc539..f69e3d83a 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -145,6 +145,7 @@ static int usage_advanced(const char* programName) #ifdef UTIL_HAS_CREATEFILELIST DISPLAY( " -r : operate recursively on directories \n"); #endif + DISPLAY( "--format=zstd : compress files to the .zstd format \n"); #ifdef ZSTD_GZCOMPRESS DISPLAY( "--format=gzip : compress files to the .gz format \n"); #endif @@ -500,6 +501,7 @@ int main(int argCount, const char* argv[]) if (!strcmp(argument, "--rm")) { FIO_setRemoveSrcFile(1); continue; } if (!strcmp(argument, "--priority=rt")) { setRealTimePrio = 1; continue; } if (!strcmp(argument, "--single-thread")) { nbWorkers = 0; singleThread = 1; continue; } + if (!strcmp(argument, "--format=zstd")) { suffix = ZSTD_EXTENSION; FIO_setCompressionType(FIO_zstdCompression); continue; } #ifdef ZSTD_GZCOMPRESS if (!strcmp(argument, "--format=gzip")) { suffix = GZ_EXTENSION; FIO_setCompressionType(FIO_gzipCompression); continue; } #endif From 5ff30fe2e5871fc9c9bef7978b22ec39ffd87def Mon Sep 17 00:00:00 2001 From: George Lu Date: Thu, 31 May 2018 16:13:36 -0700 Subject: [PATCH 142/288] Unknown Suffix Error Changed so only compiled formats are printed in list of supported extensions --- programs/fileio.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index d5b389e1e..a1da4bc3b 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -1754,8 +1754,18 @@ int FIO_decompressMultipleFilenames(const char** srcNamesTable, unsigned nbFiles && strcmp(suffixPtr, ZSTD_EXTENSION) && strcmp(suffixPtr, LZMA_EXTENSION) && strcmp(suffixPtr, LZ4_EXTENSION)) ) { - DISPLAYLEVEL(1, "zstd: %s: unknown suffix (%s/%s/%s/%s/%s expected) -- ignored \n", - srcFileName, GZ_EXTENSION, XZ_EXTENSION, ZSTD_EXTENSION, LZMA_EXTENSION, LZ4_EXTENSION); + char suffixlist[50] = ZSTD_EXTENSION; + #ifdef ZSTD_GZCOMPRESS + strcat(suffixlist, "/" GZ_EXTENSION); + #endif + #ifdef ZSTD_LZMACOMPRESS + strcat(suffixlist, "/" XZ_EXTENSION "/" LZMA_EXTENSION); + #endif + #ifdef ZSTD_LZ4COMPRESS + strcat(suffixlist, "/" LZ4_EXTENSION); + #endif + DISPLAYLEVEL(1, "zstd: %s: unknown suffix (%s expected) -- ignored \n", + srcFileName, suffixlist); skippedFiles++; continue; } else { From cec205c8421413e90dd052237a1a18fbe09852b1 Mon Sep 17 00:00:00 2001 From: George Lu Date: Thu, 31 May 2018 17:39:36 -0700 Subject: [PATCH 143/288] copy paste --- tests/paramgrill.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 3172ab067..2cd41e837 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -177,6 +177,35 @@ BMK_benchParam(BMK_result_t* resultPtr, resultPtr->cSpeed = 0.; resultPtr->dSpeed = 0.; + /* parse arg, wip */ + if (longCommandWArg(&argument, "--zstd=")) { if (!parseCompressionParameters(argument, &compressionParams)) CLEAN_RETURN(badusage(programName)); continue; } + +/* +static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressionParameters* params) +{ + for ( ; ;) { + if (longCommandWArg(&stringPtr, "windowLog=") || longCommandWArg(&stringPtr, "wlog=")) { params->windowLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "chainLog=") || longCommandWArg(&stringPtr, "clog=")) { params->chainLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "hashLog=") || longCommandWArg(&stringPtr, "hlog=")) { params->hashLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "searchLog=") || longCommandWArg(&stringPtr, "slog=")) { params->searchLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "searchLength=") || longCommandWArg(&stringPtr, "slen=")) { params->searchLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "targetLength=") || longCommandWArg(&stringPtr, "tlen=")) { params->targetLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "strategy=") || longCommandWArg(&stringPtr, "strat=")) { params->strategy = (ZSTD_strategy)(readU32FromChar(&stringPtr)); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "overlapLog=") || longCommandWArg(&stringPtr, "ovlog=")) { g_overlapLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "ldmHashLog=") || longCommandWArg(&stringPtr, "ldmhlog=")) { g_ldmHashLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "ldmSearchLength=") || longCommandWArg(&stringPtr, "ldmslen=")) { g_ldmMinMatch = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "ldmBucketSizeLog=") || longCommandWArg(&stringPtr, "ldmblog=")) { g_ldmBucketSizeLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "ldmHashEveryLog=") || longCommandWArg(&stringPtr, "ldmhevery=")) { g_ldmHashEveryLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + DISPLAYLEVEL(4, "invalid compression parameter \n"); + return 0; + } + + DISPLAYLEVEL(4, "windowLog=%d, chainLog=%d, hashLog=%d, searchLog=%d \n", params->windowLog, params->chainLog, params->hashLog, params->searchLog); + DISPLAYLEVEL(4, "searchLength=%d, targetLength=%d, strategy=%d \n", params->searchLength, params->targetLength, params->strategy); + if (stringPtr[0] != 0) return 0; /* check the end of string */ + return 1; +} +*/ /* Memory allocation & restrictions */ snprintf(name, 30, "Sw%02uc%02uh%02us%02ul%1ut%03uS%1u", Wlog, Clog, Hlog, Slog, Slength, Tlength, strat); if (!compressedBuffer || !resultBuffer || !blockTable) { From c9b1068298ec8c92b611ed9d6c98a3d8c45f0b10 Mon Sep 17 00:00:00 2001 From: George Lu Date: Thu, 31 May 2018 17:47:29 -0700 Subject: [PATCH 144/288] removed strcats --- programs/fileio.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index a1da4bc3b..b1d2a2415 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -1754,16 +1754,17 @@ int FIO_decompressMultipleFilenames(const char** srcNamesTable, unsigned nbFiles && strcmp(suffixPtr, ZSTD_EXTENSION) && strcmp(suffixPtr, LZMA_EXTENSION) && strcmp(suffixPtr, LZ4_EXTENSION)) ) { - char suffixlist[50] = ZSTD_EXTENSION; - #ifdef ZSTD_GZCOMPRESS - strcat(suffixlist, "/" GZ_EXTENSION); + const char* suffixlist = ZSTD_EXTENSION + #ifdef ZSTD_GZCOMPRESS + "/" GZ_EXTENSION #endif #ifdef ZSTD_LZMACOMPRESS - strcat(suffixlist, "/" XZ_EXTENSION "/" LZMA_EXTENSION); + "/" XZ_EXTENSION "/" LZMA_EXTENSION #endif #ifdef ZSTD_LZ4COMPRESS - strcat(suffixlist, "/" LZ4_EXTENSION); + "/" LZ4_EXTENSION #endif + ; DISPLAYLEVEL(1, "zstd: %s: unknown suffix (%s expected) -- ignored \n", srcFileName, suffixlist); skippedFiles++; From 547096d67255e3ebd26910a3a9455c33d01fb154 Mon Sep 17 00:00:00 2001 From: George Lu Date: Thu, 31 May 2018 18:03:52 -0700 Subject: [PATCH 145/288] update man --- programs/zstd.1.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programs/zstd.1.md b/programs/zstd.1.md index 22f7d0425..4b3818141 100644 --- a/programs/zstd.1.md +++ b/programs/zstd.1.md @@ -164,7 +164,8 @@ the last one takes effect. * `--format=FORMAT`: compress and decompress in other formats. If compiled with support, zstd can compress to or decompress from other compression algorithm - formats. Possibly available options are `gzip`, `xz`, `lzma`, and `lz4`. + formats. Possibly available options are `zstd`, `gzip`, `xz`, `lzma`, and `lz4`. + If no such format is provided, `zstd` is the default. * `-h`/`-H`, `--help`: display help/long help and exit * `-V`, `--version`: From 8984cc93d6b35ee9558c335a68070586cebf4d44 Mon Sep 17 00:00:00 2001 From: George Lu Date: Thu, 31 May 2018 18:04:05 -0700 Subject: [PATCH 146/288] update display --- programs/zstdcli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index f69e3d83a..0fa7ef325 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -145,7 +145,7 @@ static int usage_advanced(const char* programName) #ifdef UTIL_HAS_CREATEFILELIST DISPLAY( " -r : operate recursively on directories \n"); #endif - DISPLAY( "--format=zstd : compress files to the .zstd format \n"); + DISPLAY( "--format=zstd : compress files to the .zstd format (default) \n"); #ifdef ZSTD_GZCOMPRESS DISPLAY( "--format=gzip : compress files to the .gz format \n"); #endif From cfc3451dcc6a98ed7c66284b6b9bd4be700e0ef2 Mon Sep 17 00:00:00 2001 From: George Lu Date: Fri, 1 Jun 2018 09:52:25 -0700 Subject: [PATCH 147/288] Added Test Case --- tests/playTests.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index 200de4bd9..1433122ed 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -485,6 +485,11 @@ $ZSTD -bi0 --fast tmp1 $ECHO "with recursive and quiet modes" $ZSTD -rqi1b1e2 tmp1 +$ECHO "\n===> zstd compatibility tests " + +./datagen > tmp +$ZSTD --format=zstd tmp 2> tmplog +grep "zst" tmplog > $INTOVOID || die "--format=zstd not supported" $ECHO "\n===> gzip compatibility tests " From 53ea32cabea62e40caf797bbf74c287ae71ac90e Mon Sep 17 00:00:00 2001 From: George Lu Date: Fri, 1 Jun 2018 10:43:06 -0700 Subject: [PATCH 148/288] Suffix list test --- tests/playTests.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index 200de4bd9..e2dbccbf5 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -620,6 +620,23 @@ else $ECHO "lz4 mode not supported" fi +$ECHO "\n===> suffix list test" + +$ZSTD -d tmp.abc 2> tmplg || echo + +if [ $GZIPMODE -ne 1 ]; then + grep ".gz" tmplg > $INTOVOID && die "Unsupported suffix listed" +fi + +if [ $LZMAMODE -ne 1 ]; then + grep ".lzma" tmplg > $INTOVOID && die "Unsupported suffix listed" + grep ".xz" tmplg > $INTOVOID && die "Unsupported suffix listed" +fi + +if [ $LZ4MODE -ne 1 ]; then + grep ".lz4" tmplg > $INTOVOID && die "Unsupported suffix listed" +fi + $ECHO "\n===> zstd round-trip tests " roundTripTest From 41249bf34b5a4bba37834c03a0077497f5438d88 Mon Sep 17 00:00:00 2001 From: George Lu Date: Fri, 1 Jun 2018 10:54:51 -0700 Subject: [PATCH 149/288] Modified Tests Changed format as per suggestion and added second test --- tests/playTests.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index 1433122ed..e0779ad2c 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -488,8 +488,9 @@ $ZSTD -rqi1b1e2 tmp1 $ECHO "\n===> zstd compatibility tests " ./datagen > tmp -$ZSTD --format=zstd tmp 2> tmplog -grep "zst" tmplog > $INTOVOID || die "--format=zstd not supported" +rm -f tmp.zst +$ZSTD --format=zstd -f tmp +test -f tmp.zst $ECHO "\n===> gzip compatibility tests " @@ -527,6 +528,12 @@ else $ECHO "gzip mode not supported" fi +if [ $GZIPMODE -eq 1 ]; then + ./datagen > tmp + rm -f tmp.zst + $ZSTD --format=gzip --format=zstd -f tmp + test -f tmp.zst +fi $ECHO "\n===> xz compatibility tests " From c9b4d20f02eb50d156aaae6cf2d3787c36dd71d5 Mon Sep 17 00:00:00 2001 From: George Lu Date: Fri, 1 Jun 2018 12:39:39 -0700 Subject: [PATCH 150/288] Added new --zstd= format --- tests/paramgrill.c | 63 ++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 2cd41e837..68c573f83 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -127,6 +127,19 @@ U32 FUZ_rand(U32* src) return rand32 >> 5; } +/** 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. + * from zstdcli.c + */ +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; +} /*-******************************************************* * Bench functions @@ -177,35 +190,6 @@ BMK_benchParam(BMK_result_t* resultPtr, resultPtr->cSpeed = 0.; resultPtr->dSpeed = 0.; - /* parse arg, wip */ - if (longCommandWArg(&argument, "--zstd=")) { if (!parseCompressionParameters(argument, &compressionParams)) CLEAN_RETURN(badusage(programName)); continue; } - -/* -static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressionParameters* params) -{ - for ( ; ;) { - if (longCommandWArg(&stringPtr, "windowLog=") || longCommandWArg(&stringPtr, "wlog=")) { params->windowLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "chainLog=") || longCommandWArg(&stringPtr, "clog=")) { params->chainLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "hashLog=") || longCommandWArg(&stringPtr, "hlog=")) { params->hashLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "searchLog=") || longCommandWArg(&stringPtr, "slog=")) { params->searchLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "searchLength=") || longCommandWArg(&stringPtr, "slen=")) { params->searchLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "targetLength=") || longCommandWArg(&stringPtr, "tlen=")) { params->targetLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "strategy=") || longCommandWArg(&stringPtr, "strat=")) { params->strategy = (ZSTD_strategy)(readU32FromChar(&stringPtr)); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "overlapLog=") || longCommandWArg(&stringPtr, "ovlog=")) { g_overlapLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "ldmHashLog=") || longCommandWArg(&stringPtr, "ldmhlog=")) { g_ldmHashLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "ldmSearchLength=") || longCommandWArg(&stringPtr, "ldmslen=")) { g_ldmMinMatch = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "ldmBucketSizeLog=") || longCommandWArg(&stringPtr, "ldmblog=")) { g_ldmBucketSizeLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "ldmHashEveryLog=") || longCommandWArg(&stringPtr, "ldmhevery=")) { g_ldmHashEveryLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - DISPLAYLEVEL(4, "invalid compression parameter \n"); - return 0; - } - - DISPLAYLEVEL(4, "windowLog=%d, chainLog=%d, hashLog=%d, searchLog=%d \n", params->windowLog, params->chainLog, params->hashLog, params->searchLog); - DISPLAYLEVEL(4, "searchLength=%d, targetLength=%d, strategy=%d \n", params->searchLength, params->targetLength, params->strategy); - if (stringPtr[0] != 0) return 0; /* check the end of string */ - return 1; -} -*/ /* Memory allocation & restrictions */ snprintf(name, 30, "Sw%02uc%02uh%02us%02ul%1ut%03uS%1u", Wlog, Clog, Hlog, Slog, Slength, Tlength, strat); if (!compressedBuffer || !resultBuffer || !blockTable) { @@ -1001,7 +985,25 @@ int main(int argc, const char** argv) if(!strcmp(argument,"--no-seed")) { g_noSeed = 1; continue; } /* Decode command (note : aggregated commands are allowed) */ - if (argument[0]=='-') { + if (longCommandWArg(&argument, "--zstd=")) { + g_singleRun = 1; + g_params = ZSTD_getCParams(2, g_blockSize, 0); + for ( ; ;) { + if (longCommandWArg(&argument, "windowLog=") || longCommandWArg(&argument, "wlog=")) { g_params.windowLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; } + if (longCommandWArg(&argument, "chainLog=") || longCommandWArg(&argument, "clog=")) { g_params.chainLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; } + if (longCommandWArg(&argument, "hashLog=") || longCommandWArg(&argument, "hlog=")) { g_params.hashLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; } + if (longCommandWArg(&argument, "searchLog=") || longCommandWArg(&argument, "slog=")) { g_params.searchLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; } + if (longCommandWArg(&argument, "searchLength=") || longCommandWArg(&argument, "slen=")) { g_params.searchLength = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; } + if (longCommandWArg(&argument, "targetLength=") || longCommandWArg(&argument, "tlen=")) { g_params.targetLength = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; } + if (longCommandWArg(&argument, "strategy=") || longCommandWArg(&argument, "strat=")) { g_params.strategy = (ZSTD_strategy)(readU32FromChar(&argument)); if (argument[0]==',') { argument++; continue; } else break; } + DISPLAY("invalid compression parameter \n"); + return 1; + } + + if (argument[0] != 0) return 1; /* check the end of string */ + break; + //if not return, success + } else if (argument[0]=='-') { argument++; while (argument[0]!=0) { @@ -1080,6 +1082,7 @@ int main(int argc, const char** argv) } break; } + break; /* target level1 speed objective, in MB/s */ From 110ec9079d5a2d423432d09754bf7c8e065aa000 Mon Sep 17 00:00:00 2001 From: George Lu Date: Fri, 1 Jun 2018 12:45:02 -0700 Subject: [PATCH 151/288] Remove echo --- tests/playTests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index e2dbccbf5..51f55b9d8 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -622,7 +622,7 @@ fi $ECHO "\n===> suffix list test" -$ZSTD -d tmp.abc 2> tmplg || echo +! $ZSTD -d tmp.abc 2> tmplg if [ $GZIPMODE -ne 1 ]; then grep ".gz" tmplg > $INTOVOID && die "Unsupported suffix listed" From ae6d1fd3fac9972700f3d68b68a19f558b459a05 Mon Sep 17 00:00:00 2001 From: George Lu Date: Fri, 1 Jun 2018 13:54:08 -0700 Subject: [PATCH 152/288] Add Error Print --- tests/paramgrill.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 68c573f83..58be12c53 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -1000,7 +1000,10 @@ int main(int argc, const char** argv) return 1; } - if (argument[0] != 0) return 1; /* check the end of string */ + if (argument[0] != 0) { + DISPLAY("invvalid --zstd= format\n"); + return 1; /* check the end of string */ + } break; //if not return, success } else if (argument[0]=='-') { From e355f0a5801f44544ae0871696b332de7865d42a Mon Sep 17 00:00:00 2001 From: George Lu Date: Fri, 1 Jun 2018 14:27:53 -0700 Subject: [PATCH 153/288] Added Level Option --- tests/paramgrill.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 58be12c53..ab1c45d5d 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -996,12 +996,13 @@ int main(int argc, const char** argv) if (longCommandWArg(&argument, "searchLength=") || longCommandWArg(&argument, "slen=")) { g_params.searchLength = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; } if (longCommandWArg(&argument, "targetLength=") || longCommandWArg(&argument, "tlen=")) { g_params.targetLength = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; } if (longCommandWArg(&argument, "strategy=") || longCommandWArg(&argument, "strat=")) { g_params.strategy = (ZSTD_strategy)(readU32FromChar(&argument)); if (argument[0]==',') { argument++; continue; } else break; } + if (longCommandWArg(&argument, "level=") || longCommandWArg(&argument, "lvl=")) { g_params = ZSTD_getCParams(readU32FromChar(&argument), g_blockSize, 0); if (argument[0]==',') { argument++; continue; } else break; } DISPLAY("invalid compression parameter \n"); return 1; } if (argument[0] != 0) { - DISPLAY("invvalid --zstd= format\n"); + DISPLAY("invalid --zstd= format\n"); return 1; /* check the end of string */ } break; From 5e586aa0253fd2b0096cf1c3e3f3457540339bfe Mon Sep 17 00:00:00 2001 From: George Lu Date: Fri, 1 Jun 2018 18:02:56 -0700 Subject: [PATCH 154/288] -O# with no file fails --- tests/paramgrill.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index ab1c45d5d..580e84abd 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -889,6 +889,7 @@ int optimizeForSize(const char* inFileName, U32 targetSpeed) /* end summary */ BMK_printWinner(stdout, 99, winner.result, winner.params, benchedSize); + BMK_translateAdvancedParams(winner.params); DISPLAY("grillParams size - optimizer completed \n"); /* clean up*/ @@ -1005,7 +1006,6 @@ int main(int argc, const char** argv) DISPLAY("invalid --zstd= format\n"); return 1; /* check the end of string */ } - break; //if not return, success } else if (argument[0]=='-') { argument++; @@ -1114,7 +1114,12 @@ int main(int argc, const char** argv) } if (filenamesStart==0) { - result = benchSample(); + if (optimizer) { + DISPLAY("Optimizer Expects File") + return 1; + } else { + result = benchSample(); + } } else { if (optimizer) { result = optimizeForSize(input_filename, targetSpeed); From 2108decb41c953a2edcc3a8cb7becceb3981bd7a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Jun 2018 15:18:32 -0700 Subject: [PATCH 155/288] Fixed a nasty corruption bug recently introduce into the new dictionary mode. The bug could be reproduced with this command : ./zstreamtest -v --opaqueapi --no-big-tests -s4092 -t639 error was in function ZSTD_count_2segments() : the beginning of the 2nd segment corresponds to prefixStart and not the beginning of the current block (istart == src). This would result in comparing the wrong byte. --- lib/compress/zstd_compress.c | 3 ++ lib/compress/zstd_compress_internal.h | 5 +++ lib/compress/zstd_double_fast.c | 10 +++--- lib/compress/zstd_fast.c | 14 ++++---- lib/decompress/zstd_decompress.c | 50 +++++++++++++++------------ tests/zstreamtest.c | 16 ++++++--- 6 files changed, 58 insertions(+), 40 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 2f66acab3..7a30d5525 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -581,6 +581,7 @@ size_t ZSTD_CCtxParam_getParameter( size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params) { + DEBUGLOG(4, "ZSTD_CCtx_setParametersUsingCCtxParams"); if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); if (cctx->cdict) return ERROR(stage_wrong); @@ -1226,6 +1227,8 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, && ZSTD_equivalentCParams(cctx->appliedParams.cParams, cdict->cParams); + DEBUGLOG(4, "ZSTD_resetCCtx_usingCDict (pledgedSrcSize=%u)", (U32)pledgedSrcSize); + { unsigned const windowLog = params.cParams.windowLog; assert(windowLog != 0); diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 87206c1ef..fd072f309 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -435,6 +435,11 @@ ZSTD_count_2segments(const BYTE* ip, const BYTE* match, const BYTE* const vEnd = MIN( ip + (mEnd - match), iEnd); size_t const matchLength = ZSTD_count(ip, match, vEnd); if (match + matchLength != mEnd) return matchLength; + DEBUGLOG(7, "ZSTD_count_2segments: found a 2-parts match (current length==%zu)", matchLength); + DEBUGLOG(7, "distance from match beginning to end dictionary = %zi", mEnd - match); + DEBUGLOG(7, "distance from current pos to end buffer = %zi", iEnd - ip); + DEBUGLOG(7, "next byte : ip==%02X, istart==%02X", ip[matchLength], *iStart); + DEBUGLOG(7, "final match length = %zu", matchLength + ZSTD_count(ip+matchLength, iStart, iEnd)); return matchLength + ZSTD_count(ip+matchLength, iStart, iEnd); } diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index fbd354043..8db622a63 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -126,7 +126,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; - mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; + mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); goto _match_stored; @@ -183,7 +183,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( continue; _search_next_long: - + { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); U32 const matchIndexL3 = hashLong[hl3]; @@ -213,10 +213,10 @@ _search_next_long: } } } - + /* if no long +1 match, explore the short match we found */ if (dictMode == ZSTD_dictMatchState && matchIndexS < prefixLowestIndex) { - mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, istart) + 4; + mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, prefixLowest) + 4; offset = (U32)(current - matchIndexS); while (((ip>anchor) & (match>dictLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } else { @@ -257,7 +257,7 @@ _match_stored: if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; + size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixLowest) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 3bac2bddd..a2fdec612 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -54,7 +54,7 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* ip = istart; const BYTE* anchor = istart; const U32 prefixLowestIndex = ms->window.dictLimit; - const BYTE* const prefixLowest = base + prefixLowestIndex; + const BYTE* const prefixStart = base + prefixLowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; @@ -74,7 +74,7 @@ size_t ZSTD_compressBlock_fast_generic( const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; - const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictLowest); + const U32 dictAndPrefixLength = (U32)(ip - prefixStart + dictEnd - dictLowest); assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); @@ -86,7 +86,7 @@ size_t ZSTD_compressBlock_fast_generic( /* init */ ip += (dictAndPrefixLength == 0); if (dictMode == ZSTD_noDict) { - U32 const maxRep = (U32)(ip - prefixLowest); + U32 const maxRep = (U32)(ip - prefixStart); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } @@ -115,7 +115,7 @@ size_t ZSTD_compressBlock_fast_generic( && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; - mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; + mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else if ( dictMode == ZSTD_noDict @@ -136,7 +136,7 @@ size_t ZSTD_compressBlock_fast_generic( } else { /* found a dict match */ U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); - mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, istart) + 4; + mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, prefixStart) + 4; while (((ip>anchor) & (dictMatch>dictLowest)) && (ip[-1] == dictMatch[-1])) { ip--; dictMatch--; mLength++; @@ -154,7 +154,7 @@ size_t ZSTD_compressBlock_fast_generic( /* found a regular match */ U32 const offset = (U32)(ip-match); mLength = ZSTD_count(ip+4, match+4, iend) + 4; - while (((ip>anchor) & (match>prefixLowest)) + while (((ip>anchor) & (match>prefixStart)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; @@ -181,7 +181,7 @@ size_t ZSTD_compressBlock_fast_generic( if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; + size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 32a213089..030b74839 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -115,8 +115,8 @@ struct ZSTD_DCtx_s const HUF_DTable* HUFptr; 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 */ + const void* prefixStart; /* start of current segment */ + const void* virtualStart; /* virtual start of previous segment if it was just before current one */ const void* dictEnd; /* end of previous segment */ size_t expected; ZSTD_frameHeader fParams; @@ -1076,7 +1076,7 @@ HINT_INLINE size_t ZSTD_execSequence(BYTE* op, BYTE* const oend, seq_t sequence, const BYTE** litPtr, const BYTE* const litLimit, - const BYTE* const base, const BYTE* const vBase, const BYTE* const dictEnd) + const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd) { BYTE* const oLitEnd = op + sequence.litLength; size_t const sequenceLength = sequence.litLength + sequence.matchLength; @@ -1088,7 +1088,7 @@ size_t ZSTD_execSequence(BYTE* op, /* check */ if (oMatchEnd>oend) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ if (iLitEnd > litLimit) return ERROR(corruption_detected); /* over-read beyond lit buffer */ - if (oLitEnd>oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, base, vBase, dictEnd); + if (oLitEnd>oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd); /* copy Literals */ ZSTD_copy8(op, *litPtr); @@ -1098,21 +1098,25 @@ size_t ZSTD_execSequence(BYTE* op, *litPtr = iLitEnd; /* update for next sequence */ /* copy Match */ - if (sequence.offset > (size_t)(oLitEnd - base)) { + if (sequence.offset > (size_t)(oLitEnd - prefixStart)) { /* offset beyond prefix -> go into extDict */ - if (sequence.offset > (size_t)(oLitEnd - vBase)) + if (sequence.offset > (size_t)(oLitEnd - virtualStart)) return ERROR(corruption_detected); - match = dictEnd + (match - base); + match = dictEnd + (match - prefixStart); if (match + sequence.matchLength <= dictEnd) { memmove(oLitEnd, match, sequence.matchLength); return sequenceLength; } /* span extDict & currentPrefixSegment */ + DEBUGLOG(2, "ZSTD_execSequence: found a 2-segments match") { size_t const length1 = dictEnd - match; + DEBUGLOG(2, "first part (extDict) is %zu bytes long", length1); memmove(oLitEnd, match, length1); op = oLitEnd + length1; sequence.matchLength -= length1; - match = base; + DEBUGLOG(2, "second part (prefix) is %zu bytes long", sequence.matchLength); + match = prefixStart; + DEBUGLOG(2, "first byte of 2nd part : %02X", *prefixStart); if (op > oend_w || sequence.matchLength < MINMATCH) { U32 i; for (i = 0; i < sequence.matchLength; ++i) op[i] = match[i]; @@ -1355,10 +1359,10 @@ ZSTD_decompressSequences_body( ZSTD_DCtx* dctx, BYTE* op = ostart; const BYTE* litPtr = dctx->litPtr; const BYTE* const litEnd = litPtr + dctx->litSize; - const BYTE* const base = (const BYTE*) (dctx->base); - const BYTE* const vBase = (const BYTE*) (dctx->vBase); + const BYTE* const prefixStart = (const BYTE*) (dctx->prefixStart); + const BYTE* const vBase = (const BYTE*) (dctx->virtualStart); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); - DEBUGLOG(5, "ZSTD_decompressSequences"); + DEBUGLOG(5, "ZSTD_decompressSequences_body"); /* Regen sequences */ if (nbSeq) { @@ -1373,14 +1377,14 @@ ZSTD_decompressSequences_body( ZSTD_DCtx* dctx, for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) { nbSeq--; { seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset); - size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, base, vBase, dictEnd); + size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, prefixStart, vBase, dictEnd); DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize); if (ZSTD_isError(oneSeqSize)) return oneSeqSize; op += oneSeqSize; } } /* check if reached exact end */ - DEBUGLOG(5, "ZSTD_decompressSequences: after decode loop, remaining nbSeq : %i", nbSeq); + DEBUGLOG(5, "ZSTD_decompressSequences_body: after decode loop, remaining nbSeq : %i", nbSeq); if (nbSeq) return ERROR(corruption_detected); /* save reps for next block */ { U32 i; for (i=0; i entropy.rep[i] = (U32)(seqState.prevOffset[i]); } @@ -1499,8 +1503,8 @@ ZSTD_decompressSequencesLong_body( BYTE* op = ostart; const BYTE* litPtr = dctx->litPtr; const BYTE* const litEnd = litPtr + dctx->litSize; - const BYTE* const prefixStart = (const BYTE*) (dctx->base); - const BYTE* const dictStart = (const BYTE*) (dctx->vBase); + const BYTE* const prefixStart = (const BYTE*) (dctx->prefixStart); + const BYTE* const dictStart = (const BYTE*) (dctx->virtualStart); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); /* Regen sequences */ @@ -1702,8 +1706,8 @@ static void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst) { if (dst != dctx->previousDstEnd) { /* not contiguous */ dctx->dictEnd = dctx->previousDstEnd; - dctx->vBase = (const char*)dst - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->base)); - dctx->base = dst; + dctx->virtualStart = (const char*)dst - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->prefixStart)); + dctx->prefixStart = dst; dctx->previousDstEnd = dst; } } @@ -2171,8 +2175,8 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c static size_t ZSTD_refDictContent(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) { dctx->dictEnd = dctx->previousDstEnd; - dctx->vBase = (const char*)dict - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->base)); - dctx->base = dict; + dctx->virtualStart = (const char*)dict - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->prefixStart)); + dctx->prefixStart = dict; dctx->previousDstEnd = (const char*)dict + dictSize; return 0; } @@ -2276,8 +2280,8 @@ size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx) dctx->stage = ZSTDds_getFrameHeaderSize; dctx->decodedSize = 0; dctx->previousDstEnd = NULL; - dctx->base = NULL; - dctx->vBase = NULL; + dctx->prefixStart = NULL; + dctx->virtualStart = NULL; dctx->dictEnd = NULL; dctx->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ dctx->litEntropy = dctx->fseEntropy = 0; @@ -2327,8 +2331,8 @@ size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dstDCtx, const ZSTD_DDict* ddi CHECK_F( ZSTD_decompressBegin(dstDCtx) ); if (ddict) { /* support begin on NULL */ dstDCtx->dictID = ddict->dictID; - dstDCtx->base = ddict->dictContent; - dstDCtx->vBase = ddict->dictContent; + dstDCtx->prefixStart = ddict->dictContent; + dstDCtx->virtualStart = ddict->dictContent; dstDCtx->dictEnd = (const BYTE*)ddict->dictContent + ddict->dictSize; dstDCtx->previousDstEnd = dstDCtx->dictEnd; if (ddict->entropyPresent) { diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index ffed955a2..425fbab39 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -1671,10 +1671,13 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double /* compression init */ CHECK_Z( ZSTD_CCtx_loadDictionary(zc, NULL, 0) ); /* cancel previous dict /*/ if ((FUZ_rand(&lseed)&1) /* at beginning, to keep same nb of rand */ - && oldTestLog /* at least one test happened */ && resetAllowed) { + && oldTestLog /* at least one test happened */ + && resetAllowed) { + /* just set a compression level */ maxTestSize = FUZ_randomLength(&lseed, oldTestLog+2); if (maxTestSize >= srcBufferSize) maxTestSize = srcBufferSize-1; { int const compressionLevel = (FUZ_rand(&lseed) % 5) + 1; + DISPLAYLEVEL(5, "t%u : compression level : %i \n", testNb, compressionLevel); CHECK_Z (setCCtxParameter(zc, cctxParams, ZSTD_p_compressionLevel, compressionLevel, useOpaqueAPI) ); } } else { @@ -1698,6 +1701,8 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? ZSTD_CONTENTSIZE_UNKNOWN : maxTestSize; ZSTD_compressionParameters cParams = ZSTD_getCParams(cLevel, pledgedSrcSize, dictSize); static const U32 windowLogMax = 24; + if (dictSize) + DISPLAYLEVEL(5, "t%u: with dictionary of size : %zu \n", testNb, dictSize); /* mess with compression parameters */ cParams.windowLog += (FUZ_rand(&lseed) & 3) - 1; @@ -1707,7 +1712,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double cParams.searchLog += (FUZ_rand(&lseed) & 3) - 1; cParams.searchLength += (FUZ_rand(&lseed) & 3) - 1; cParams.targetLength = (U32)((cParams.targetLength + 1 ) * (0.5 + ((double)(FUZ_rand(&lseed) & 127) / 128))); - cParams = ZSTD_adjustCParams(cParams, 0, 0); + cParams = ZSTD_adjustCParams(cParams, pledgedSrcSize, dictSize); if (FUZ_rand(&lseed) & 1) { DISPLAYLEVEL(5, "t%u: windowLog : %u \n", testNb, cParams.windowLog); @@ -1766,7 +1771,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double /* Apply parameters */ if (useOpaqueAPI) { - DISPLAYLEVEL(6," t%u: applying CCtxParams \n", testNb); + DISPLAYLEVEL(5, "t%u: applying CCtxParams \n", testNb); CHECK_Z (ZSTD_CCtx_setParametersUsingCCtxParams(zc, cctxParams) ); } @@ -1832,7 +1837,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double } } crcOrig = XXH64_digest(&xxhState); cSize = outBuff.pos; - DISPLAYLEVEL(5, "Frame completed : %u bytes \n", (U32)cSize); + DISPLAYLEVEL(5, "Frame completed : %zu bytes \n", cSize); } CHECK(badParameters(zc, savedParams), "CCtx params are wrong"); @@ -1842,7 +1847,8 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double DISPLAYLEVEL(5, "resetting DCtx (dict:%08X) \n", (U32)(size_t)dict); CHECK_Z( ZSTD_resetDStream(zd) ); } else { - DISPLAYLEVEL(5, "using dict of size %u \n", (U32)dictSize); + if (dictSize) + DISPLAYLEVEL(5, "using dictionary of size %zu \n", dictSize); CHECK_Z( ZSTD_initDStream_usingDict(zd, dict, dictSize) ); } { size_t decompressionResult = 1; From 65de25a463072b84ce98fe18c07f2718ed588d54 Mon Sep 17 00:00:00 2001 From: George Lu Date: Mon, 4 Jun 2018 09:56:29 -0700 Subject: [PATCH 156/288] Created Macros --- lib/Makefile | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/Makefile b/lib/Makefile index 3a160f88c..92b5a19f5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -31,7 +31,41 @@ FLAGS = $(CPPFLAGS) $(CFLAGS) ZSTD_FILES := $(sort $(wildcard common/*.c compress/*.c decompress/*.c dictBuilder/*.c deprecated/*.c)) +ZSTDCOMMON_FILES := $(sort $(wildcard common/*.c)) +ZSTDCOMP_FILES := $(sort $(wildcard compress/*.c)) +ZSTDDECOMP_FILES := $(sort $(wildcard decompress/*.c)) +ZDICT_FILES := $(sort $(wildcard dictBuilder/*.c)) +ZDEPR_FILES := $(sort $(wildcard deprecated/*.c)) +ZSTD_FILES := $(ZSTDCOMMON_FILES) + ZSTD_LEGACY_SUPPORT ?= 4 +ZSTD_LIB_COMPRESSION ?= 1 +ZSTD_LIB_DECOMPRESSION ?= 1 +ZSTD_LIB_DICTBUILDER ?= 1 +ZSTD_LIB_DEPRECATED ?= 1 +ifeq ($(ZSTD_LIB_COMPRESSION), 0) + ZSTD_LIB_DICTBUILDER = 0 +endif + +ifeq ($(ZSTD_LIB_DECOMPRESSION), 0) + ZSTD_LEGACY_SUPPORT = 0 +endif + +ifneq ($(ZSTD_LIB_COMPRESSION), 0) + ZSTD_FILES += $(ZSTDCOMP_FILES) +endif + +ifneq ($(ZSTD_LIB_DECOMPRESSION), 0) + ZSTD_FILES += $(ZSTDDECOMP_FILES) +endif + +ifneq ($(ZSTD_DEPRECATED), 0) + ZSTD_FILE += $(ZDEPR_FILES) +endif + +ifneq ($(ZSTD_LIB_DICTBUILDER), 0) + ZSTD_FILE += $(ZDICT_FILES) +endif ifneq ($(ZSTD_LEGACY_SUPPORT), 0) ifeq ($(shell test $(ZSTD_LEGACY_SUPPORT) -lt 8; echo $$?), 0) From 6a617d70ed32e372b5efa306fef755b06d151bd6 Mon Sep 17 00:00:00 2001 From: George Lu Date: Mon, 4 Jun 2018 09:56:37 -0700 Subject: [PATCH 157/288] Documentation --- lib/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/README.md b/lib/README.md index 95196e467..75debe872 100644 --- a/lib/README.md +++ b/lib/README.md @@ -61,6 +61,10 @@ It's possible to compile only a limited set of features. Each version also provides an additional dedicated set of advanced API. For example, advanced API for version `v0.4` is exposed in `lib/legacy/zstd_v04.h` . Note : `lib/legacy` only supports _decoding_ legacy formats. +- Similarly, you can define `ZSTD_LIB_COMPRESSION, ZSTD_LIB_DECOMPRESSION`, `ZSTD_LIB_DICTBUILDER`, + and `ZSTD_LIB_DEPRECATED` as 0 to forgo compilation of the corresponding features. This will + also disable compilation of all dependencies (eg. `ZSTD_LIB_COMPRESSION=0` will also disable + dictBuilder). #### Multithreading support From ddf143ba6a387356789c2e64d5ac4a6f57c4709a Mon Sep 17 00:00:00 2001 From: George Lu Date: Mon, 4 Jun 2018 10:16:05 -0700 Subject: [PATCH 158/288] Update usage_advanced --- tests/paramgrill.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 580e84abd..c4b6932d0 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -952,6 +952,7 @@ static int usage_advanced(void) DISPLAY( " -i# : iteration loops [1-9](default : %i) \n", NBLOOPS); DISPLAY( " -O# : find Optimized parameters for # MB/s compression speed (default : 0) \n"); DISPLAY( " -S : Single run \n"); + DISPLAY( " --zstd : Single run, parameter selection same as zstdcli \n"); DISPLAY( " -P# : generated sample compressibility (default : %.1f%%) \n", COMPRESSIBILITY_DEFAULT * 100); return 0; } From 9437021d2feba9d5af884ff6044ba4f8ec694ba6 Mon Sep 17 00:00:00 2001 From: George Lu Date: Mon, 4 Jun 2018 13:32:41 -0700 Subject: [PATCH 159/288] Remove old file declaration --- lib/Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 92b5a19f5..30926a2f8 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -28,9 +28,6 @@ DEBUGFLAGS = -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ CFLAGS += $(DEBUGFLAGS) $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) - -ZSTD_FILES := $(sort $(wildcard common/*.c compress/*.c decompress/*.c dictBuilder/*.c deprecated/*.c)) - ZSTDCOMMON_FILES := $(sort $(wildcard common/*.c)) ZSTDCOMP_FILES := $(sort $(wildcard compress/*.c)) ZSTDDECOMP_FILES := $(sort $(wildcard decompress/*.c)) From 3f054dceb4d33bf7555d559100c14de8f0d672d3 Mon Sep 17 00:00:00 2001 From: George Lu Date: Mon, 4 Jun 2018 13:38:37 -0700 Subject: [PATCH 160/288] forgot \n, ; --- tests/paramgrill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index c4b6932d0..44e3cb37b 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -1116,7 +1116,7 @@ int main(int argc, const char** argv) if (filenamesStart==0) { if (optimizer) { - DISPLAY("Optimizer Expects File") + DISPLAY("Optimizer Expects File\n"); return 1; } else { result = benchSample(); From 609d72b0ca0ba533d7aaff330b5c46e5dc709a89 Mon Sep 17 00:00:00 2001 From: George Lu Date: Mon, 4 Jun 2018 14:33:21 -0700 Subject: [PATCH 161/288] Added Deprecated Dependencies --- lib/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Makefile b/lib/Makefile index 30926a2f8..00a51fdbf 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -42,10 +42,12 @@ ZSTD_LIB_DICTBUILDER ?= 1 ZSTD_LIB_DEPRECATED ?= 1 ifeq ($(ZSTD_LIB_COMPRESSION), 0) ZSTD_LIB_DICTBUILDER = 0 + ZSTD_DEPRECATED = 0 endif ifeq ($(ZSTD_LIB_DECOMPRESSION), 0) ZSTD_LEGACY_SUPPORT = 0 + ZSTD_DEPRECATED = 0 endif ifneq ($(ZSTD_LIB_COMPRESSION), 0) From 357c648c3f5919b8cc1c35885421ee04b79e87ce Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 4 Jun 2018 17:10:50 -0700 Subject: [PATCH 162/288] changed a few variable names to unify naming convention --- lib/compress/zstd_double_fast.c | 90 +++++++++++++++++---------------- lib/compress/zstd_fast.c | 85 ++++++++++++++++--------------- 2 files changed, 89 insertions(+), 86 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 8db622a63..7fc11eb48 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -74,18 +74,18 @@ size_t ZSTD_compressBlock_doubleFast_generic( dms->hashTable : NULL; const U32* const dictHashSmall = dictMode == ZSTD_dictMatchState ? dms->chainTable : NULL; - const U32 dictLowestIndex = dictMode == ZSTD_dictMatchState ? + const U32 dictStartIndex = dictMode == ZSTD_dictMatchState ? dms->window.dictLimit : 0; const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; - const BYTE* const dictLowest = dictMode == ZSTD_dictMatchState ? - dictBase + dictLowestIndex : NULL; + const BYTE* const dictStart = dictMode == ZSTD_dictMatchState ? + dictBase + dictStartIndex : NULL; const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; - const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictLowest); + const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictStart); assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); @@ -155,10 +155,10 @@ size_t ZSTD_compressBlock_doubleFast_generic( const BYTE* dictMatchL = dictBase + dictMatchIndexL; assert(dictMatchL < dictEnd); - if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { + if (dictMatchL > dictStart && MEM_read64(dictMatchL) == MEM_read64(ip)) { mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; offset = (U32)(current - dictMatchIndexL - dictIndexDelta); - while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ + while (((ip>anchor) & (dictMatchL>dictStart)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ goto _match_found; } } @@ -174,7 +174,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( match = dictBase + dictMatchIndexS; matchIndexS = dictMatchIndexS + dictIndexDelta; - if (match > dictLowest && MEM_read32(match) == MEM_read32(ip)) { + if (match > dictStart && MEM_read32(match) == MEM_read32(ip)) { goto _search_next_long; } } @@ -204,11 +204,11 @@ _search_next_long: U32 const dictMatchIndexL3 = dictHashLong[hl3]; const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; assert(dictMatchL3 < dictEnd); - if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { + if (dictMatchL3 > dictStart && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; ip++; offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); - while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ + while (((ip>anchor) & (dictMatchL3>dictStart)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ goto _match_found; } } @@ -218,7 +218,7 @@ _search_next_long: if (dictMode == ZSTD_dictMatchState && matchIndexS < prefixLowestIndex) { mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, prefixLowest) + 4; offset = (U32)(current - matchIndexS); - while (((ip>anchor) & (match>dictLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + while (((ip>anchor) & (match>dictStart)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } else { mLength = ZSTD_count(ip+4, match+4, iend) + 4; offset = (U32)(ip - match); @@ -343,18 +343,18 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic( U32 const hBitsL = cParams->hashLog; U32* const hashSmall = ms->chainTable; U32 const hBitsS = cParams->chainLog; - const BYTE* const base = ms->window.base; - const BYTE* const dictBase = ms->window.dictBase; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ms->window.lowLimit; - const BYTE* const dictStart = dictBase + lowestIndex; - const U32 dictLimit = ms->window.dictLimit; - const BYTE* const lowPrefixPtr = base + dictLimit; - const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; + const U32 prefixStartIndex = ms->window.dictLimit; + const BYTE* const base = ms->window.base; + const BYTE* const prefixStart = base + prefixStartIndex; + const U32 dictStartIndex = ms->window.lowLimit; + const BYTE* const dictBase = ms->window.dictBase; + const BYTE* const dictStart = dictBase + dictStartIndex; + const BYTE* const dictEnd = dictBase + prefixStartIndex; U32 offset_1=rep[0], offset_2=rep[1]; DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_extDict_generic (srcSize=%zu)", srcSize); @@ -363,57 +363,58 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic( while (ip < ilimit) { /* < instead of <=, because (ip+1) */ const size_t hSmall = ZSTD_hashPtr(ip, hBitsS, mls); const U32 matchIndex = hashSmall[hSmall]; - const BYTE* matchBase = matchIndex < dictLimit ? dictBase : base; + const BYTE* const matchBase = matchIndex < prefixStartIndex ? dictBase : base; const BYTE* match = matchBase + matchIndex; const size_t hLong = ZSTD_hashPtr(ip, hBitsL, 8); const U32 matchLongIndex = hashLong[hLong]; - const BYTE* matchLongBase = matchLongIndex < dictLimit ? dictBase : base; + const BYTE* const matchLongBase = matchLongIndex < prefixStartIndex ? dictBase : base; const BYTE* matchLong = matchLongBase + matchLongIndex; const U32 current = (U32)(ip-base); const U32 repIndex = current + 1 - offset_1; /* offset_1 expected <= current +1 */ - const BYTE* repBase = repIndex < dictLimit ? dictBase : base; - const BYTE* repMatch = repBase + repIndex; + const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base; + const BYTE* const repMatch = repBase + repIndex; size_t mLength; hashSmall[hSmall] = hashLong[hLong] = current; /* update hash table */ - if ( (((U32)((dictLimit-1) - repIndex) >= 3) /* intentional underflow */ & (repIndex > lowestIndex)) - && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < dictLimit ? dictEnd : iend; - mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, lowPrefixPtr) + 4; + if ((((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex doesn't overlap dict + prefix */ + & (repIndex > dictStartIndex)) + && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { + const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; + mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else { - if ((matchLongIndex > lowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) { - const BYTE* matchEnd = matchLongIndex < dictLimit ? dictEnd : iend; - const BYTE* lowMatchPtr = matchLongIndex < dictLimit ? dictStart : lowPrefixPtr; + if ((matchLongIndex > dictStartIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) { + const BYTE* const matchEnd = matchLongIndex < prefixStartIndex ? dictEnd : iend; + const BYTE* const lowMatchPtr = matchLongIndex < prefixStartIndex ? dictStart : prefixStart; U32 offset; - mLength = ZSTD_count_2segments(ip+8, matchLong+8, iend, matchEnd, lowPrefixPtr) + 8; + mLength = ZSTD_count_2segments(ip+8, matchLong+8, iend, matchEnd, prefixStart) + 8; offset = current - matchLongIndex; while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); - } else if ((matchIndex > lowestIndex) && (MEM_read32(match) == MEM_read32(ip))) { + } else if ((matchIndex > dictStartIndex) && (MEM_read32(match) == MEM_read32(ip))) { size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8); U32 const matchIndex3 = hashLong[h3]; - const BYTE* const match3Base = matchIndex3 < dictLimit ? dictBase : base; + const BYTE* const match3Base = matchIndex3 < prefixStartIndex ? dictBase : base; const BYTE* match3 = match3Base + matchIndex3; U32 offset; hashLong[h3] = current + 1; - if ( (matchIndex3 > lowestIndex) && (MEM_read64(match3) == MEM_read64(ip+1)) ) { - const BYTE* matchEnd = matchIndex3 < dictLimit ? dictEnd : iend; - const BYTE* lowMatchPtr = matchIndex3 < dictLimit ? dictStart : lowPrefixPtr; - mLength = ZSTD_count_2segments(ip+9, match3+8, iend, matchEnd, lowPrefixPtr) + 8; + if ( (matchIndex3 > dictStartIndex) && (MEM_read64(match3) == MEM_read64(ip+1)) ) { + const BYTE* const matchEnd = matchIndex3 < prefixStartIndex ? dictEnd : iend; + const BYTE* const lowMatchPtr = matchIndex3 < prefixStartIndex ? dictStart : prefixStart; + mLength = ZSTD_count_2segments(ip+9, match3+8, iend, matchEnd, prefixStart) + 8; ip++; offset = current+1 - matchIndex3; while (((ip>anchor) & (match3>lowMatchPtr)) && (ip[-1] == match3[-1])) { ip--; match3--; mLength++; } /* catch up */ } else { - const BYTE* matchEnd = matchIndex < dictLimit ? dictEnd : iend; - const BYTE* lowMatchPtr = matchIndex < dictLimit ? dictStart : lowPrefixPtr; - mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, lowPrefixPtr) + 4; + const BYTE* const matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend; + const BYTE* const lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart; + mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4; offset = current - matchIndex; while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } @@ -440,12 +441,13 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic( while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2; - const BYTE* repMatch2 = repIndex2 < dictLimit ? dictBase + repIndex2 : base + repIndex2; - if ( (((U32)((dictLimit-1) - repIndex2) >= 3) & (repIndex2 > lowestIndex)) /* intentional overflow */ - && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < dictLimit ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, lowPrefixPtr) + 4; - U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ + const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2; + if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) /* intentional overflow : ensure repIndex2 doesn't overlap dict + prefix */ + & (repIndex2 > dictStartIndex)) + && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { + const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; + size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; + U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index a2fdec612..57a70eeee 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -53,8 +53,8 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 prefixLowestIndex = ms->window.dictLimit; - const BYTE* const prefixStart = base + prefixLowestIndex; + const U32 prefixStartIndex = ms->window.dictLimit; + const BYTE* const prefixStart = base + prefixStartIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; @@ -63,25 +63,25 @@ size_t ZSTD_compressBlock_fast_generic( const ZSTD_matchState_t* const dms = ms->dictMatchState; const U32* const dictHashTable = dictMode == ZSTD_dictMatchState ? dms->hashTable : NULL; - const U32 dictLowestIndex = dictMode == ZSTD_dictMatchState ? + const U32 dictStartIndex = dictMode == ZSTD_dictMatchState ? dms->window.dictLimit : 0; const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; - const BYTE* const dictLowest = dictMode == ZSTD_dictMatchState ? - dictBase + dictLowestIndex : NULL; + const BYTE* const dictStart = dictMode == ZSTD_dictMatchState ? + dictBase + dictStartIndex : NULL; const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? - prefixLowestIndex - (U32)(dictEnd - dictBase) : + prefixStartIndex - (U32)(dictEnd - dictBase) : 0; - const U32 dictAndPrefixLength = (U32)(ip - prefixStart + dictEnd - dictLowest); + const U32 dictAndPrefixLength = (U32)(ip - prefixStart + dictEnd - dictStart); assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); /* otherwise, we would get index underflow when translating a dict index * into a local index */ assert(dictMode != ZSTD_dictMatchState - || prefixLowestIndex >= (U32)(dictEnd - dictBase)); + || prefixStartIndex >= (U32)(dictEnd - dictBase)); /* init */ ip += (dictAndPrefixLength == 0); @@ -106,15 +106,15 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* match = base + matchIndex; const U32 repIndex = current + 1 - offset_1; const BYTE* repMatch = (dictMode == ZSTD_dictMatchState - && repIndex < prefixLowestIndex) ? + && repIndex < prefixStartIndex) ? dictBase + (repIndex - dictIndexDelta) : base + repIndex; hashTable[h] = current; /* update hash table */ - if (dictMode == ZSTD_dictMatchState - && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) - && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; + if ( (dictMode == ZSTD_dictMatchState) + && ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */ + && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { + const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); @@ -123,12 +123,12 @@ size_t ZSTD_compressBlock_fast_generic( mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else if ( (matchIndex <= prefixLowestIndex) + } else if ( (matchIndex <= prefixStartIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { if (dictMode == ZSTD_dictMatchState) { U32 const dictMatchIndex = dictHashTable[h]; const BYTE* dictMatch = dictBase + dictMatchIndex; - if (dictMatchIndex <= dictLowestIndex || + if (dictMatchIndex <= dictStartIndex || MEM_read32(dictMatch) != MEM_read32(ip)) { assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; @@ -137,7 +137,7 @@ size_t ZSTD_compressBlock_fast_generic( /* found a dict match */ U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, prefixStart) + 4; - while (((ip>anchor) & (dictMatch>dictLowest)) + while (((ip>anchor) & (dictMatch>dictStart)) && (ip[-1] == dictMatch[-1])) { ip--; dictMatch--; mLength++; } /* catch up */ @@ -175,12 +175,12 @@ size_t ZSTD_compressBlock_fast_generic( while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2; - const BYTE* repMatch2 = repIndex2 < prefixLowestIndex ? + const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; - if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) + if ( ((U32)((prefixStartIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend; + const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); @@ -272,11 +272,11 @@ static size_t ZSTD_compressBlock_fast_extDict_generic( const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ms->window.lowLimit; - const BYTE* const dictStart = dictBase + lowestIndex; - const U32 dictLimit = ms->window.dictLimit; - const BYTE* const lowPrefixPtr = base + dictLimit; - const BYTE* const dictEnd = dictBase + dictLimit; + const U32 dictStartIndex = ms->window.lowLimit; + const BYTE* const dictStart = dictBase + dictStartIndex; + const U32 prefixStartIndex = ms->window.dictLimit; + const BYTE* const prefixStart = base + prefixStartIndex; + const BYTE* const dictEnd = dictBase + prefixStartIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; U32 offset_1=rep[0], offset_2=rep[1]; @@ -284,33 +284,34 @@ static size_t ZSTD_compressBlock_fast_extDict_generic( /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ const size_t h = ZSTD_hashPtr(ip, hlog, mls); - const U32 matchIndex = hashTable[h]; - const BYTE* matchBase = matchIndex < dictLimit ? dictBase : base; - const BYTE* match = matchBase + matchIndex; - const U32 current = (U32)(ip-base); - const U32 repIndex = current + 1 - offset_1; /* offset_1 expected <= current +1 */ - const BYTE* repBase = repIndex < dictLimit ? dictBase : base; - const BYTE* repMatch = repBase + repIndex; + const U32 matchIndex = hashTable[h]; + const BYTE* const matchBase = matchIndex < prefixStartIndex ? dictBase : base; + const BYTE* match = matchBase + matchIndex; + const U32 current = (U32)(ip-base); + const U32 repIndex = current + 1 - offset_1; + const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base; + const BYTE* const repMatch = repBase + repIndex; size_t mLength; hashTable[h] = current; /* update hash table */ + assert(offset_1 <= current +1); /* check repIndex */ - if ( (((U32)((dictLimit-1) - repIndex) >= 3) /* intentional underflow */ & (repIndex > lowestIndex)) + if ( (((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow */ & (repIndex > dictStartIndex)) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < dictLimit ? dictEnd : iend; - mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, lowPrefixPtr) + 4; + const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; + mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else { - if ( (matchIndex < lowestIndex) || + if ( (matchIndex < dictStartIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; continue; } - { const BYTE* matchEnd = matchIndex < dictLimit ? dictEnd : iend; - const BYTE* lowMatchPtr = matchIndex < dictLimit ? dictStart : lowPrefixPtr; + { const BYTE* matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend; + const BYTE* lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart; U32 offset; - mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, lowPrefixPtr) + 4; + mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4; while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ offset = current - matchIndex; offset_2 = offset_1; @@ -330,11 +331,11 @@ static size_t ZSTD_compressBlock_fast_extDict_generic( while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2; - const BYTE* repMatch2 = repIndex2 < dictLimit ? dictBase + repIndex2 : base + repIndex2; - if ( (((U32)((dictLimit-1) - repIndex2) >= 3) & (repIndex2 > lowestIndex)) /* intentional overflow */ + const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2; + if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (repIndex2 > dictStartIndex)) /* intentional overflow */ && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < dictLimit ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, lowPrefixPtr) + 4; + const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; + size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; From b3ef3148302231312c5582862cd89d5c16f0adb7 Mon Sep 17 00:00:00 2001 From: George Lu Date: Mon, 4 Jun 2018 17:19:06 -0700 Subject: [PATCH 163/288] Fix Typos --- lib/Makefile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 00a51fdbf..701cf7e9f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -40,14 +40,15 @@ ZSTD_LIB_COMPRESSION ?= 1 ZSTD_LIB_DECOMPRESSION ?= 1 ZSTD_LIB_DICTBUILDER ?= 1 ZSTD_LIB_DEPRECATED ?= 1 + ifeq ($(ZSTD_LIB_COMPRESSION), 0) ZSTD_LIB_DICTBUILDER = 0 - ZSTD_DEPRECATED = 0 + ZSTD_LIB_DEPRECATED = 0 endif ifeq ($(ZSTD_LIB_DECOMPRESSION), 0) ZSTD_LEGACY_SUPPORT = 0 - ZSTD_DEPRECATED = 0 + ZSTD_LIB_DEPRECATED = 0 endif ifneq ($(ZSTD_LIB_COMPRESSION), 0) @@ -58,12 +59,12 @@ ifneq ($(ZSTD_LIB_DECOMPRESSION), 0) ZSTD_FILES += $(ZSTDDECOMP_FILES) endif -ifneq ($(ZSTD_DEPRECATED), 0) - ZSTD_FILE += $(ZDEPR_FILES) +ifneq ($(ZSTD_LIB_DEPRECATED), 0) + ZSTD_FILES += $(ZDEPR_FILES) endif ifneq ($(ZSTD_LIB_DICTBUILDER), 0) - ZSTD_FILE += $(ZDICT_FILES) + ZSTD_FILES += $(ZDICT_FILES) endif ifneq ($(ZSTD_LEGACY_SUPPORT), 0) From 3d523c741be041f17c28e43b89ab6dfcaee281d2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 5 Jun 2018 11:23:18 -0700 Subject: [PATCH 164/288] added workSpaceTooLarge and workSpaceWasteful also : slightly increased speed of test fuzzer.16 --- lib/compress/zstd_compress.c | 73 ++++++++++++++++++--------- lib/compress/zstd_compress_internal.h | 6 ++- tests/fuzzer.c | 12 +++-- 3 files changed, 62 insertions(+), 29 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 7a30d5525..b87ae3819 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -695,7 +695,8 @@ size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams) /** ZSTD_clampCParams() : * make CParam values within valid range. * @return : valid CParams */ -static ZSTD_compressionParameters ZSTD_clampCParams(ZSTD_compressionParameters cParams) +static ZSTD_compressionParameters +ZSTD_clampCParams(ZSTD_compressionParameters cParams) { # define CLAMP(val,min,max) { \ if (val (U32)ZSTD_btultra) cParams.strategy = ZSTD_btultra; + if ((U32)(cParams.targetLength) < ZSTD_TARGETLENGTH_MIN) + cParams.targetLength = ZSTD_TARGETLENGTH_MIN; + CLAMP(cParams.strategy, ZSTD_fast, ZSTD_btultra); return cParams; } @@ -723,8 +725,11 @@ static U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat) optimize `cPar` for a given input (`srcSize` and `dictSize`). mostly downsizing to reduce memory consumption and initialization latency. Both `srcSize` and `dictSize` are optional (use 0 if unknown). - Note : cPar is considered validated at this stage. Use ZSTD_checkCParams() to ensure that condition. */ -ZSTD_compressionParameters ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize) + Note : cPar is assumed validated. Use ZSTD_checkCParams() to ensure this condition. */ +static ZSTD_compressionParameters +ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar, + unsigned long long srcSize, + size_t dictSize) { static const U64 minSrcSize = 513; /* (1<<9) + 1 */ static const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1); @@ -756,13 +761,18 @@ ZSTD_compressionParameters ZSTD_adjustCParams_internal(ZSTD_compressionParameter return cPar; } -ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize) +ZSTD_compressionParameters +ZSTD_adjustCParams(ZSTD_compressionParameters cPar, + unsigned long long srcSize, + size_t dictSize) { cPar = ZSTD_clampCParams(cPar); return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize); } -static size_t ZSTD_sizeof_matchState(ZSTD_compressionParameters const* cParams, const U32 forCCtx) +static size_t +ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams, + const U32 forCCtx) { size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog); size_t const hSize = ((size_t)1) << cParams->hashLog; @@ -848,12 +858,14 @@ size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams) return ZSTD_estimateCStreamSize_usingCCtxParams(¶ms); } -static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel) { +static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel) +{ ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0); return ZSTD_estimateCStreamSize_usingCParams(cParams); } -size_t ZSTD_estimateCStreamSize(int compressionLevel) { +size_t ZSTD_estimateCStreamSize(int compressionLevel) +{ int level; size_t memBudget = 0; for (level=1; level<=compressionLevel; level++) { @@ -1042,10 +1054,18 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms, return ptr; } +#define ZSTD_WORKSPACETOOLARGE_FACTOR 3 /* define "workspace is too large" as ZSTD_WORKSPACETOOLARGE_FACTOR times larger than needed */ +#define ZSTD_WORKSPACETOOLARGE_MAX 128 /* when workspace is continuously too large + * at least that number of times, + * context's memory usage is actually wasteful, + * because it's sized to handle a worst case scenario which rarely happens. + * In which case, resize it down to free some memory */ + /*! ZSTD_resetCCtx_internal() : note : `params` are assumed fully validated at this stage */ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, - ZSTD_CCtx_params params, U64 pledgedSrcSize, + ZSTD_CCtx_params params, + U64 pledgedSrcSize, ZSTD_compResetPolicy_e const crp, ZSTD_buffered_policy_e const zbuff) { @@ -1057,8 +1077,8 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, if (ZSTD_equivalentParams(zc->appliedParams, params, zc->inBuffSize, zc->blockSize, zbuff, pledgedSrcSize)) { - DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog1=%u, blockSize1=%u)", - zc->appliedParams.cParams.windowLog, (U32)zc->blockSize); + DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog1=%u, blockSize1=%zu)", + zc->appliedParams.cParams.windowLog, zc->blockSize); return ZSTD_continueCCtx(zc, params, pledgedSrcSize); } } DEBUGLOG(4, "ZSTD_equivalentParams()==0 -> reset CCtx"); @@ -1069,8 +1089,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams); assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog); assert(params.ldmParams.hashEveryLog < 32); - zc->ldmState.hashPower = - ZSTD_ldm_getHashPower(params.ldmParams.minMatchLength); + zc->ldmState.hashPower = ZSTD_ldm_getHashPower(params.ldmParams.minMatchLength); } { size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize)); @@ -1082,7 +1101,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, size_t const buffInSize = (zbuff==ZSTDb_buffered) ? windowSize + blockSize : 0; size_t const matchStateSize = ZSTD_sizeof_matchState(¶ms.cParams, /* forCCtx */ 1); size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(params.ldmParams, blockSize); - void* ptr; + void* ptr; /* used to partition workSpace */ /* Check if workSpace is large enough, alloc a new one if needed */ { size_t const entropySpace = HUF_WORKSPACE_SIZE; @@ -1094,14 +1113,19 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, size_t const neededSpace = entropySpace + blockStateSpace + ldmSpace + ldmSeqSpace + matchStateSize + tokenSpace + bufferSpace; - DEBUGLOG(4, "Need %uKB workspace, including %uKB for match state, and %uKB for buffers", - (U32)(neededSpace>>10), (U32)(matchStateSize>>10), (U32)(bufferSpace>>10)); - DEBUGLOG(4, "windowSize: %u - blockSize: %u", (U32)windowSize, (U32)blockSize); - if (zc->workSpaceSize < neededSpace) { /* too small : resize */ - DEBUGLOG(4, "Need to update workSpaceSize from %uK to %uK", - (unsigned)(zc->workSpaceSize>>10), - (unsigned)(neededSpace>>10)); + int const workSpaceTooSmall = zc->workSpaceSize < neededSpace; + int const workSpaceTooLarge = zc->workSpaceSize > ZSTD_WORKSPACETOOLARGE_FACTOR * neededSpace; + int const workSpaceWasteful = 0 && workSpaceTooLarge && (zc->workSpaceTooLarge > ZSTD_WORKSPACETOOLARGE_MAX); + DEBUGLOG(4, "Need %zuKB workspace, including %zuKB for match state, and %zuKB for buffers", + neededSpace>>10, matchStateSize>>10, bufferSpace>>10); + DEBUGLOG(4, "windowSize: %zu - blockSize: %zu", windowSize, blockSize); + + zc->workSpaceTooLarge = workSpaceTooLarge ? zc->workSpaceTooLarge+1 : 0; + if (workSpaceTooSmall || workSpaceWasteful) { + DEBUGLOG(4, "Need to resize workSpaceSize from %zuKB to %zuKB", + zc->workSpaceSize >> 10, + neededSpace >> 10); /* static cctx : no resize, error out */ if (zc->staticSize) return ERROR(memory_allocation); @@ -1110,9 +1134,12 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->workSpace = ZSTD_malloc(neededSpace, zc->customMem); if (zc->workSpace == NULL) return ERROR(memory_allocation); zc->workSpaceSize = neededSpace; + zc->workSpaceTooLarge = 0; ptr = zc->workSpace; - /* Statically sized space. entropyWorkspace never moves (but prev/next block swap places) */ + /* Statically sized space. + * entropyWorkspace never moves, + * though prev/next block swap places */ assert(((size_t)zc->workSpace & 3) == 0); /* ensure correct alignment */ assert(zc->workSpaceSize >= 2 * sizeof(ZSTD_compressedBlockState_t)); zc->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)zc->workSpace; diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index fd072f309..b2475aaef 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -27,6 +27,7 @@ extern "C" { #endif + /*-************************************* * Constants ***************************************/ @@ -37,7 +38,8 @@ extern "C" { It's not a big deal though : candidate will just be sorted again. Additionnally, candidate position 1 will be lost. But candidate 1 cannot hide a large tree of candidates, so it's a minimal loss. - The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled after table re-use with a different strategy */ + The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled after table re-use with a different strategy + Constant required by ZSTD_compressBlock_btlazy2() and ZSTD_reduceTable_internal() */ /*-************************************* @@ -204,6 +206,8 @@ struct ZSTD_CCtx_s { ZSTD_CCtx_params requestedParams; ZSTD_CCtx_params appliedParams; U32 dictID; + + int workSpaceTooLarge; void* workSpace; size_t workSpaceSize; size_t blockSize; diff --git a/tests/fuzzer.c b/tests/fuzzer.c index b0c425e1e..8dabcddbc 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -450,14 +450,16 @@ static int basicUnitTests(U32 seed, double compressibility) } DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(3, "test%3d : large window log smaller data : ", testNb++); + DISPLAYLEVEL(3, "test%3d : overflow protection with large windowLog : ", testNb++); { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); - ZSTD_parameters params = ZSTD_getParams(1, ZSTD_CONTENTSIZE_UNKNOWN, 0); - size_t const nbCompressions = (1U << 31) / CNBuffSize + 1; - size_t i; + ZSTD_parameters params = ZSTD_getParams(-9, ZSTD_CONTENTSIZE_UNKNOWN, 0); + size_t const nbCompressions = ((1U << 31) / CNBuffSize) + 1; /* ensure U32 overflow protection is triggered */ + size_t cnb; + assert(cctx != NULL); params.fParams.contentSizeFlag = 0; params.cParams.windowLog = ZSTD_WINDOWLOG_MAX; - for (i = 0; i < nbCompressions; ++i) { + for (cnb = 0; cnb < nbCompressions; ++cnb) { + DISPLAYLEVEL(6, "run %zu / %zu \n", cnb, nbCompressions); CHECK_Z( ZSTD_compressBegin_advanced(cctx, NULL, 0, params, ZSTD_CONTENTSIZE_UNKNOWN) ); /* re-use same parameters */ CHECK_Z( ZSTD_compressEnd(cctx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize) ); } From b2496ab60663992eb566dd221705da86fc9dafc7 Mon Sep 17 00:00:00 2001 From: George Lu Date: Tue, 5 Jun 2018 13:24:00 -0700 Subject: [PATCH 165/288] Partial compilation test? --- lib/log | 28 ++++++++++++++ tests/Makefile | 13 ++++++- tests/partial | Bin 0 -> 13159 bytes tests/partial.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 lib/log create mode 100755 tests/partial create mode 100644 tests/partial.c diff --git a/lib/log b/lib/log new file mode 100644 index 000000000..7f4d68a89 --- /dev/null +++ b/lib/log @@ -0,0 +1,28 @@ +entropy_common.o: +error_private.o: +fse_decompress.o: +pool.o: +threading.o: +xxhash.o: +zstd_common.o: +fse_compress.o: +huf_compress.o: +zstd_compress.o: +zstd_double_fast.o: +zstd_fast.o: +zstd_lazy.o: +zstd_ldm.o: +zstd_opt.o: +zstdmt_compress.o: +huf_decompress.o: +zstd_decompress.o: +zbuff_common.o: +zbuff_compress.o: +zbuff_decompress.o: +cover.o: +divsufsort.o: +zdict.o: +zstd_v04.o: +zstd_v05.o: +zstd_v06.o: +zstd_v07.o: diff --git a/tests/Makefile b/tests/Makefile index c4cbe1bdf..65474db4d 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -233,6 +233,14 @@ else $(CC) $(FLAGS) $< -o $@$(EXT) -Wl,-rpath=$(ZSTDDIR) $(ZSTDDIR)/libzstd.so # broken on Mac endif +partial : partial.c zstd-dll +ifneq (,$(filter Windows%,$(OS))) + cp $(ZSTDDIR)/dll/libzstd.dll . + $(CC) $(FLAGS) $< -o $@$(EXT) -DZSTD_DLL_IMPORT=1 libzstd.dll +else + $(CC) $(FLAGS) $< -o $@$(EXT) -Wl,-rpath=$(ZSTDDIR) $(ZSTDDIR)/libzstd.so # broken on Mac +endif + poolTests : poolTests.c $(ZSTDDIR)/common/pool.c $(ZSTDDIR)/common/threading.c $(ZSTDDIR)/common/zstd_common.c $(ZSTDDIR)/common/error_private.c $(CC) $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT) @@ -255,7 +263,7 @@ clean: zstreamtest$(EXT) zstreamtest32$(EXT) \ datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) \ symbols$(EXT) invalidDictionaries$(EXT) legacy$(EXT) poolTests$(EXT) \ - decodecorpus$(EXT) checkTag$(EXT) + decodecorpus$(EXT) checkTag$(EXT) partial$(EXT) @echo Cleaning completed @@ -387,6 +395,9 @@ test-invalidDictionaries: invalidDictionaries test-symbols: symbols $(QEMU_SYS) ./symbols +test-partial: partial + $(QEMU_SYS) ./partial + test-legacy: legacy $(QEMU_SYS) ./legacy diff --git a/tests/partial b/tests/partial new file mode 100755 index 0000000000000000000000000000000000000000..7abd3498a0a29ddbe82a832d9cd398ff54a791f0 GIT binary patch literal 13159 zcmb<-^>JfjWMqH=CI&kO5Kn^D0W1U|85n-Zfw^G9fx&`-lfi*Ol|hMtje&uIm4Sf) zrp^J%g3&)fhA}WOz-SJz2@DL(3=9k`3=9kwOb`JJCWr|zS_UG_0HdMCfZYbN4=Rmf zGf0ew6GSpFz-R^r1+V}}Kgg{Nd|)mE178A!0i_+F?(l%pFnu5{NS_2$p9EArjD7$z zn1O)-M#KCE@*4<;fDB+@U Hp~ znXZ|MUU9yj5jb8!=7ZF_`-OsS0vQ8xp9D1CMHnDy03`n=_wPz`wceYLHa7lv{%mW< z?uciLL3&{rYygo2$b2CzY7H4+F^Ve5&A`BbO+7 PZ9J7x^=@dZVhc_nG_$r;%U z@$n#u_~MepqLTRB#LPSfJv~rZFvQ2F=jP`@CE^*}eLS6< +! zGcYnRfzvW5oIoxrk<8=-g$O9NL1Lv+AU+d=4@eS}fA}Hk6%>Y`u!IUTNC-g0LCFOa zUNCV5BynW*8c5=xJP1?o(fo$Pqnq_9w*rGl>wyy17sd<>3?9u#I1a;9K hG&yu1MB9|G|~PI!6X|Ns9xKq@_& z-$;OL@!SDYw+kfXYIw4fMa84_5}3CWB<{#R^`J-VrF|ys4h)X`QxEI{3GN3GuKe2$ z7#?`B;Q#;sh6g;Fk4PK_>)OEN(R!(L1BA^CVlRQPSwQR=5H>4_-2-8>f!GaDC;ecQ zJn;QO^8@ym*&uV@dUjrn^XPo!(fs5?K#1q@0}AX83?9c1ZeRmZ#|u~-7+(DU_y7Nw zG(CR#7LXUf;jzPAAb}y&L)Sqdfx)Bs2cJjh({iTR!w@@K50rd~-4Bxb|A1e<0i pZ>oP`?C7~ z|Ns2*3@?izG&n{MBm9KH2RYiK^#K3W1N_?#9G(C(6`fzg4%Yb@;&@2Re1e2JIA)$f z*x;DC1!03@<{X3#j+sLc!<*qTb2x2+W4L4If5$M-&To#Po}E`6LwtHwE!Y(pLOptQ zL3FT>=2OGl9tZz2gHrSh)xZD$d$hhSk%PsNPv 9&-(vQFY|AWdJkQfMq#6fk=2Q1 x$*b^|2U{x z5C!tf9+3Lq|NpN533&1;F!^%vaX5lXXa;aSc?Tr^@BjZ)5beY#(9h(`C(+03%BRrF z;>xGd!|K6j(8lJ$XVJ{=%jb~8XW__a;K--p#HZlIC*i~=;KauP4o?+E1_q6P|Nnyu zC6H00cr*k?Ltr!nMq&s&fcEnaKxtSQfSOq#HjIXq?XbFr2PDbBz`(%*u1y$tp?no+ ze?=0?hxN0hp?p~XND9V>_NPGQKS&5B|MlPhd=P&IwBH2l^GPs6?1R-!JD~EQx)-D# zR1bq_NUg!Z0O}8b_@W>J3B%jI(DpE_F7SY=0o7k1Ca7HvqQyZ30|UbWsQobU6;M9R z-7x#0-B7sw|NcY#Qvp@~AIgV??*}Nq04fi)j^-4sAB%308#G?SpmZ9PMpx(V>};i= z5t>w*S5m5AqGzFJqH9zN<{8!*n&=sr=$U9j#2E-HHHIm*f%cOa7$D&)zy^u+c~I*? zt#1$$+)@BB(C~j2$mjwC0|(RROhEn6D`1j?y$r-* 3vXSXeXjN*I_qSoee62}%kq&7ejJQzb~Y1;k|L zVBf>Yz`)AE#xW6M06!B01N%Kj28J7~UnQ6r7&uOVhEBeL8$leP?8W-K8 v#?isVz`zSq%Qu~Yfq^ZUX(rf+`AiH9{2;$=W@2Cv05JqW zu4dp11xbf6?FUgDN0}HH1h2wXfRr%^Nw9@7u`x3+aHfOQgfjJl3N21K2nS>w2WJ+@ zs!%47dI1K$4hRoq4l@UPAp--0@K;6#21X~4uNZ|Vg4k9Nb`UcI1EUdy4Ql5zYJu6{ zVvs2f#AZ%oW?&GV$Hc(Elm_ys7=ySiGXn!GYr+a-#yR4K%nS?)&>V{-tjf&5U;q^c zrFT&1G0qW}WM*J+zz`M&4Gc1Rfh=YeKg7(y5CByI5`pNHumia{79@aP9x%@7W?^8E z1nGlGf%POwf^2JsmSvzag)vFWlbL~`8!80qlQSljvoJ77M?$LznFCO{*DMSSvW%gO zhO&$yoXnt636PnxjDaALAVxz521$??Cqk7UU|?WiOk$9G0E(Y!j5Amm7+85hQ7tFQ z$iToh9W25o_Z$>QGr$}nIcJc}OfZK--UXyFjOhXk0|PH8+7)aW85r1CfKsQzRuFR~ zh^Yt~Mq&yFg@OQ>fW!?0qadRypD;HwpEO^nxFDl|I0FNd7y~n(6}L6Fr!WHpvkC(@ zw;eY#9|Hpml*z%sz{=0S&CS5T2BJW!*o7IH`M9}3niz!{7&v(0CRlJYFmOV-te(Ok zE*H#v1_o|MMo(do4jx4YPhov-1_oX=29SY#P&KR|4!}4HTn40DQkKC`IEXP+Jc2utn}I<}mJz`P$xE{`7z#6qGcd?>HZd{^G8zgqFvxZ_ zF)}boOmvc9V30F`*v{%HEW^#fAdlcOFepGd+@8Yrk~WeI42q}@0SPgLF@nYikAPAR z<8MA*21Z6kPy+#!PxVWSi}W+|l5 VbRk2p>3OC4Ntq?Z3}7WWnMwNT z$;p^XO!O>Z`XMTzhQS6f8441MN-`63^pZizy0|1IGv7!r1C+XoOHxu&)ATYxLzg9$ z1*ydl;hfB*%zV8J&`^F=aY>3^258_HtPdi{z`)4_D;=0M8700*d=b8F07CPb`6HN% z^dfbmP!JOj10y2?Clf5oGT&ifWX@w`7K&ib^NM6Hk6>Y9R&8QoWRA9BuCQTYV%CiS zaUz&2yg(cq7DkRE%$X%9X6i;oG6^s+GD3!M8RFyf)6(Kg7~(zs =B&HXGB|-Ls V{i{g{>b4qjb zAm*hNCFZ7jq$Z}M76pU6kegV6Hi(^{lL88e;?xoxIx_RXR^%oYWv3P~fFc!Yc5!xQ zK|x|t4wCs`mE}d5C8?0mECvM?ILs7KybcPp;>5C4q(E~?EhtJ&PAo}HVTjMkPtIn5 zc+ZF-J~uxl6=ZLIX$d$UK _$`D_WU(65>adt5zVq7wl zOPoqGb5c@^7~t+n&PXg`fVdG9s*nhAD@sjeh%YMvc@Gvm;JEckO;1d&galPeYFc7x zPKh%-_R@+{Q{j;ZF~|`Vp$zePsTC!V6lZ1vbtE{=ffEfpG{9boPs;@ u;=}n^s(sn4GO!P?TSinp~1!!~mY? z0J#_v&0r6JL&haF8J6y0wd!$b{l~=suOsJ!N)%)a%i*O^wUtmB-3-_)B(h!*8>S9c zZTCU-!|LAIQ2u-<4bp>*(e+J(s)OlU1m(l3eq_BMHq0KFKA1f)eIPZ+7*-6fgEnGd z{>SA%2EF3S+>*p32EF2vA_$!UV`b)*q!tx0=;h^?r0O|3dFqxVrbBt@d8K+upro#w znZlq4;$ Rs1OUum5WY8 7by1P`JkKC!4Mv62C-1IEruE&@9v zF*h@rK`%YO1Wf3GZGo7WR9wuUmz 2Sd^HTo>~m!X6B{k!`O*Q zNkyq;FkVh(UMh@T4Du;Ne@=cfOcZJoa^#T3290Ncs%B6V9Ap4&-Wt?w1@S>WClC$8 zAhj?yhz4~=K^;e!e%QP)Y`z#WYYkEf!LWH=2# NaanOt* zC{AGdVdF{%p!#9sP$2!Fu{L!5eV{fXsOtcA5NsTd17s)z0|RV)4Jr+4n!%W08q`As zg(<9mW&kbm450cUsR^tUOn~O3K@1o@1 O+RehPz98z z85kH~<7Y7SP-9_Sh6Palu<$d0jyoDa^@BUEaMiHUAcO#D h_H1su=Oyo^aZmYmJXhw+5ZE& zzTpSRQ+V7DGv_s$eg^0`E(6p8SpEh134~$#VDuL>{RU9|22lM_3ZxZ`Vfw%v1_p*- zP&dN-51PgY$vHqZqN_(s4h;XH_M^KKrXN&3g3N XyUnGKdZcX`un31)vEK)D8fRZbP~7`3O+?iWWp*jULbdl}D3Aw;!w+ z2LW2$1Zxi=D`93}g0F)^6=!CEwO>%hS>WvwRB={#djnORjRDq {sRB=%TSb2{sF2(>W$5F+_8Q|qJ zk_0n@1Ou$xMG|IUW{_lnm8YoU=&c%vEHeYBH4b4Tlg!{1FUTSgHZub>lR>y}l9>T% zkt ar{0kRi1Ukp+U!i&M` zG1C<;e;op=7i8ezM6IDNfW S{KU809y42QVVM*>N8?rN9uq>JO(U|na z1`c~cGrQQ#>4Mq|S{4d&2WajWM9%{ISCYX4&7Gj>KTvq0kNhlysz)#PK>a9?dRTn{ z>bHQ*-ww7H#6ravak&2}$iGaO>E|O3_1w&$@Wiaw1VM9qs8&LFh}nbWqLSj0(zG7FXy0mAJ5>B_;`kt)S}e%%;J*NqWF^B_~e}YywqX_EJE?|Df#i~Ir&M6Iq@ka z`9;O?iKP_`pmD~W)RNQ`Ju^!SBL=LB;z5H`@!(;n_|&|TqDltH;8aR!Zf+$*JaniE z%!>z&X`v{Mk9P}l^mUDQ^>c}jhq@fbMp2x?5bqM{=jiL{%n*-k40ugAL%h3>zmua+ zyuX`Uuxm(sh@+E_E66zTvhn2NQm`8tqMSnA+{z40;xmg~i;D7#7~(xceB%)z6yoa) z3ZD?rP&5N*U<|Ub9H!3{t`9m2ix@^v1*ry2BIIR4RHG?H8V!d!!U# d3AD@w!my!dWKY@+0LjuMeq0!Sn9z6dL z4;uJ}c&W_57%mU>gc)1_>P%2jgQATg9z4Jfjhy(T;$o;1A!FJ|Yx@!20WI2(_bQ7I pf~HsJoW$bd)MAGC_z+)bSONh>7%cUoL>D+sFhsd{I)}g=1psMDmaqT- literal 0 HcmV?d00001 diff --git a/tests/partial.c b/tests/partial.c new file mode 100644 index 000000000..8756e6c78 --- /dev/null +++ b/tests/partial.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + + +#include +#include "zstd_errors.h" +//#define ZSTD_STATIC_LINKING_ONLY +#include "zstd.h" +#define ZBUFF_DISABLE_DEPRECATE_WARNINGS +#define ZBUFF_STATIC_LINKING_ONLY +#include "zbuff.h" +#define ZDICT_DISABLE_DEPRECATE_WARNINGS +#define ZDICT_STATIC_LINKING_ONLY +#include "zdict.h" + +/* +size_t ZSTD_compress(void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) __attribute__((weak)); //compress +size_t ZSTD_decompress( void* dst, size_t dstCapacity, const void* src, size_t compressedSize) __attribute__((weak)); //decompress +int weakfunc() __attribute__((weak)); //deprecated +int weakfunc() __attribute__((weak)); //dictbuilder +int weakfunc() __attribute__((weak)); //legacy +*/ +//size_t ZSTD_compress(void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) __attribute__((weak)); //compress +//size_t ZSTD_decompress( void* dst, size_t dstCapacity, const void* src, size_t compressedSize) __attribute__((weak)); //decompress +unsigned ZBUFF_isError(size_t errorCode) __attribute__((weak)); //deprecated +unsigned ZDICT_isError(size_t errorCode) __attribute__((weak)); +unsigned ZBUFFv07_isError(size_t errorCode) __attribute__((weak)); +unsigned ZBUFFv06_isError(size_t errorCode) __attribute__((weak)); +unsigned ZBUFFv05_isError(size_t errorCode) __attribute__((weak)); +unsigned ZBUFFv04_isError(size_t errorCode) __attribute__((weak)); +unsigned ZBUFFv03_isError(size_t errorCode) __attribute__((weak)); +unsigned ZBUFFv02_isError(size_t errorCode) __attribute__((weak)); +unsigned ZBUFFv01_isError(size_t errorCode) __attribute__((weak)); + +int checkCompress(void) { + if(ZSTD_compress) { + return 1; + } else { + return 0; + } +} + +int checkDecompress(void) { + if(ZSTD_decompress) { + return 1; + } else { + return 0; + } +} + +int checkDeprecated(void) { + if(ZBUFF_isError) { + return 1; + } else { + return 0; + } +} + +int checkDictBuilder(void) { + if(ZDICT_isError) { + return 1; + } else { + return 0; + } +} + +int checkLegacy(void) { + if(ZBUFFv01_isError) { + return 1; + } else if (ZBUFFv02_isError) { + return 2; + } else if (ZBUFFv03_isError) { + return 3; + } else if (ZBUFFv04_isError) { + return 4; + } else if (ZBUFFv05_isError) { + return 5; + } else if (ZBUFFv06_isError) { + return 6; + } else if (ZBUFFv07_isError) { + return 7; + } + return 0; +} + +int main(void){//int argc, const char** argv) { + //const void **symbol; + //(void)argc; + //(void)argv; + + printf("%d %d %d %d %d\n", checkCompress(), checkDecompress(), checkDeprecated(), checkDictBuilder(), checkLegacy()); + return 0; +} From 11d5bfdaa96029b1ecf03654db6969a0c3555a5d Mon Sep 17 00:00:00 2001 From: George Lu Date: Tue, 5 Jun 2018 13:55:36 -0700 Subject: [PATCH 166/288] Revert "Partial compilation test?" This reverts commit b2496ab60663992eb566dd221705da86fc9dafc7. --- lib/log | 28 -------------- tests/Makefile | 13 +------ tests/partial | Bin 13159 -> 0 bytes tests/partial.c | 100 ------------------------------------------------ 4 files changed, 1 insertion(+), 140 deletions(-) delete mode 100644 lib/log delete mode 100755 tests/partial delete mode 100644 tests/partial.c diff --git a/lib/log b/lib/log deleted file mode 100644 index 7f4d68a89..000000000 --- a/lib/log +++ /dev/null @@ -1,28 +0,0 @@ -entropy_common.o: -error_private.o: -fse_decompress.o: -pool.o: -threading.o: -xxhash.o: -zstd_common.o: -fse_compress.o: -huf_compress.o: -zstd_compress.o: -zstd_double_fast.o: -zstd_fast.o: -zstd_lazy.o: -zstd_ldm.o: -zstd_opt.o: -zstdmt_compress.o: -huf_decompress.o: -zstd_decompress.o: -zbuff_common.o: -zbuff_compress.o: -zbuff_decompress.o: -cover.o: -divsufsort.o: -zdict.o: -zstd_v04.o: -zstd_v05.o: -zstd_v06.o: -zstd_v07.o: diff --git a/tests/Makefile b/tests/Makefile index 65474db4d..c4cbe1bdf 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -233,14 +233,6 @@ else $(CC) $(FLAGS) $< -o $@$(EXT) -Wl,-rpath=$(ZSTDDIR) $(ZSTDDIR)/libzstd.so # broken on Mac endif -partial : partial.c zstd-dll -ifneq (,$(filter Windows%,$(OS))) - cp $(ZSTDDIR)/dll/libzstd.dll . - $(CC) $(FLAGS) $< -o $@$(EXT) -DZSTD_DLL_IMPORT=1 libzstd.dll -else - $(CC) $(FLAGS) $< -o $@$(EXT) -Wl,-rpath=$(ZSTDDIR) $(ZSTDDIR)/libzstd.so # broken on Mac -endif - poolTests : poolTests.c $(ZSTDDIR)/common/pool.c $(ZSTDDIR)/common/threading.c $(ZSTDDIR)/common/zstd_common.c $(ZSTDDIR)/common/error_private.c $(CC) $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT) @@ -263,7 +255,7 @@ clean: zstreamtest$(EXT) zstreamtest32$(EXT) \ datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) \ symbols$(EXT) invalidDictionaries$(EXT) legacy$(EXT) poolTests$(EXT) \ - decodecorpus$(EXT) checkTag$(EXT) partial$(EXT) + decodecorpus$(EXT) checkTag$(EXT) @echo Cleaning completed @@ -395,9 +387,6 @@ test-invalidDictionaries: invalidDictionaries test-symbols: symbols $(QEMU_SYS) ./symbols -test-partial: partial - $(QEMU_SYS) ./partial - test-legacy: legacy $(QEMU_SYS) ./legacy diff --git a/tests/partial b/tests/partial deleted file mode 100755 index 7abd3498a0a29ddbe82a832d9cd398ff54a791f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13159 zcmb<-^>JfjWMqH=CI&kO5Kn^D0W1U|85n-Zfw^G9fx&`-lfi*Ol|hMtje&uIm4Sf) zrp^J%g3&)fhA}WOz-SJz2@DL(3=9k`3=9kwOb`JJCWr|zS_UG_0HdMCfZYbN4=Rmf zGf0ew6GSpFz-R^r1+V}}Kgg{Nd|)mE178A!0i_+F?(l%pFnu5{NS_2$p9EArjD7$z zn1O)-M#KCE@*4<;fDB+@U Hp~ znXZ|MUU9yj5jb8!=7ZF_`-OsS0vQ8xp9D1CMHnDy03`n=_wPz`wceYLHa7lv{%mW< z?uciLL3&{rYygo2$b2CzY7H4+F^Ve5&A`BbO+7 PZ9J7x^=@dZVhc_nG_$r;%U z@$n#u_~MepqLTRB#LPSfJv~rZFvQ2F=jP`@CE^*}eLS6< +! zGcYnRfzvW5oIoxrk<8=-g$O9NL1Lv+AU+d=4@eS}fA}Hk6%>Y`u!IUTNC-g0LCFOa zUNCV5BynW*8c5=xJP1?o(fo$Pqnq_9w*rGl>wyy17sd<>3?9u#I1a;9K hG&yu1MB9|G|~PI!6X|Ns9xKq@_& z-$;OL@!SDYw+kfXYIw4fMa84_5}3CWB<{#R^`J-VrF|ys4h)X`QxEI{3GN3GuKe2$ z7#?`B;Q#;sh6g;Fk4PK_>)OEN(R!(L1BA^CVlRQPSwQR=5H>4_-2-8>f!GaDC;ecQ zJn;QO^8@ym*&uV@dUjrn^XPo!(fs5?K#1q@0}AX83?9c1ZeRmZ#|u~-7+(DU_y7Nw zG(CR#7LXUf;jzPAAb}y&L)Sqdfx)Bs2cJjh({iTR!w@@K50rd~-4Bxb|A1e<0i pZ>oP`?C7~ z|Ns2*3@?izG&n{MBm9KH2RYiK^#K3W1N_?#9G(C(6`fzg4%Yb@;&@2Re1e2JIA)$f z*x;DC1!03@<{X3#j+sLc!<*qTb2x2+W4L4If5$M-&To#Po}E`6LwtHwE!Y(pLOptQ zL3FT>=2OGl9tZz2gHrSh)xZD$d$hhSk%PsNPv 9&-(vQFY|AWdJkQfMq#6fk=2Q1 x$*b^|2U{x z5C!tf9+3Lq|NpN533&1;F!^%vaX5lXXa;aSc?Tr^@BjZ)5beY#(9h(`C(+03%BRrF z;>xGd!|K6j(8lJ$XVJ{=%jb~8XW__a;K--p#HZlIC*i~=;KauP4o?+E1_q6P|Nnyu zC6H00cr*k?Ltr!nMq&s&fcEnaKxtSQfSOq#HjIXq?XbFr2PDbBz`(%*u1y$tp?no+ ze?=0?hxN0hp?p~XND9V>_NPGQKS&5B|MlPhd=P&IwBH2l^GPs6?1R-!JD~EQx)-D# zR1bq_NUg!Z0O}8b_@W>J3B%jI(DpE_F7SY=0o7k1Ca7HvqQyZ30|UbWsQobU6;M9R z-7x#0-B7sw|NcY#Qvp@~AIgV??*}Nq04fi)j^-4sAB%308#G?SpmZ9PMpx(V>};i= z5t>w*S5m5AqGzFJqH9zN<{8!*n&=sr=$U9j#2E-HHHIm*f%cOa7$D&)zy^u+c~I*? zt#1$$+)@BB(C~j2$mjwC0|(RROhEn6D`1j?y$r-* 3vXSXeXjN*I_qSoee62}%kq&7ejJQzb~Y1;k|L zVBf>Yz`)AE#xW6M06!B01N%Kj28J7~UnQ6r7&uOVhEBeL8$leP?8W-K8 v#?isVz`zSq%Qu~Yfq^ZUX(rf+`AiH9{2;$=W@2Cv05JqW zu4dp11xbf6?FUgDN0}HH1h2wXfRr%^Nw9@7u`x3+aHfOQgfjJl3N21K2nS>w2WJ+@ zs!%47dI1K$4hRoq4l@UPAp--0@K;6#21X~4uNZ|Vg4k9Nb`UcI1EUdy4Ql5zYJu6{ zVvs2f#AZ%oW?&GV$Hc(Elm_ys7=ySiGXn!GYr+a-#yR4K%nS?)&>V{-tjf&5U;q^c zrFT&1G0qW}WM*J+zz`M&4Gc1Rfh=YeKg7(y5CByI5`pNHumia{79@aP9x%@7W?^8E z1nGlGf%POwf^2JsmSvzag)vFWlbL~`8!80qlQSljvoJ77M?$LznFCO{*DMSSvW%gO zhO&$yoXnt636PnxjDaALAVxz521$??Cqk7UU|?WiOk$9G0E(Y!j5Amm7+85hQ7tFQ z$iToh9W25o_Z$>QGr$}nIcJc}OfZK--UXyFjOhXk0|PH8+7)aW85r1CfKsQzRuFR~ zh^Yt~Mq&yFg@OQ>fW!?0qadRypD;HwpEO^nxFDl|I0FNd7y~n(6}L6Fr!WHpvkC(@ zw;eY#9|Hpml*z%sz{=0S&CS5T2BJW!*o7IH`M9}3niz!{7&v(0CRlJYFmOV-te(Ok zE*H#v1_o|MMo(do4jx4YPhov-1_oX=29SY#P&KR|4!}4HTn40DQkKC`IEXP+Jc2utn}I<}mJz`P$xE{`7z#6qGcd?>HZd{^G8zgqFvxZ_ zF)}boOmvc9V30F`*v{%HEW^#fAdlcOFepGd+@8Yrk~WeI42q}@0SPgLF@nYikAPAR z<8MA*21Z6kPy+#!PxVWSi}W+|l5 VbRk2p>3OC4Ntq?Z3}7WWnMwNT z$;p^XO!O>Z`XMTzhQS6f8441MN-`63^pZizy0|1IGv7!r1C+XoOHxu&)ATYxLzg9$ z1*ydl;hfB*%zV8J&`^F=aY>3^258_HtPdi{z`)4_D;=0M8700*d=b8F07CPb`6HN% z^dfbmP!JOj10y2?Clf5oGT&ifWX@w`7K&ib^NM6Hk6>Y9R&8QoWRA9BuCQTYV%CiS zaUz&2yg(cq7DkRE%$X%9X6i;oG6^s+GD3!M8RFyf)6(Kg7~(zs =B&HXGB|-Ls V{i{g{>b4qjb zAm*hNCFZ7jq$Z}M76pU6kegV6Hi(^{lL88e;?xoxIx_RXR^%oYWv3P~fFc!Yc5!xQ zK|x|t4wCs`mE}d5C8?0mECvM?ILs7KybcPp;>5C4q(E~?EhtJ&PAo}HVTjMkPtIn5 zc+ZF-J~uxl6=ZLIX$d$UK _$`D_WU(65>adt5zVq7wl zOPoqGb5c@^7~t+n&PXg`fVdG9s*nhAD@sjeh%YMvc@Gvm;JEckO;1d&galPeYFc7x zPKh%-_R@+{Q{j;ZF~|`Vp$zePsTC!V6lZ1vbtE{=ffEfpG{9boPs;@ u;=}n^s(sn4GO!P?TSinp~1!!~mY? z0J#_v&0r6JL&haF8J6y0wd!$b{l~=suOsJ!N)%)a%i*O^wUtmB-3-_)B(h!*8>S9c zZTCU-!|LAIQ2u-<4bp>*(e+J(s)OlU1m(l3eq_BMHq0KFKA1f)eIPZ+7*-6fgEnGd z{>SA%2EF3S+>*p32EF2vA_$!UV`b)*q!tx0=;h^?r0O|3dFqxVrbBt@d8K+upro#w znZlq4;$ Rs1OUum5WY8 7by1P`JkKC!4Mv62C-1IEruE&@9v zF*h@rK`%YO1Wf3GZGo7WR9wuUmz 2Sd^HTo>~m!X6B{k!`O*Q zNkyq;FkVh(UMh@T4Du;Ne@=cfOcZJoa^#T3290Ncs%B6V9Ap4&-Wt?w1@S>WClC$8 zAhj?yhz4~=K^;e!e%QP)Y`z#WYYkEf!LWH=2# NaanOt* zC{AGdVdF{%p!#9sP$2!Fu{L!5eV{fXsOtcA5NsTd17s)z0|RV)4Jr+4n!%W08q`As zg(<9mW&kbm450cUsR^tUOn~O3K@1o@1 O+RehPz98z z85kH~<7Y7SP-9_Sh6Palu<$d0jyoDa^@BUEaMiHUAcO#D h_H1su=Oyo^aZmYmJXhw+5ZE& zzTpSRQ+V7DGv_s$eg^0`E(6p8SpEh134~$#VDuL>{RU9|22lM_3ZxZ`Vfw%v1_p*- zP&dN-51PgY$vHqZqN_(s4h;XH_M^KKrXN&3g3N XyUnGKdZcX`un31)vEK)D8fRZbP~7`3O+?iWWp*jULbdl}D3Aw;!w+ z2LW2$1Zxi=D`93}g0F)^6=!CEwO>%hS>WvwRB={#djnORjRDq {sRB=%TSb2{sF2(>W$5F+_8Q|qJ zk_0n@1Ou$xMG|IUW{_lnm8YoU=&c%vEHeYBH4b4Tlg!{1FUTSgHZub>lR>y}l9>T% zkt ar{0kRi1Ukp+U!i&M` zG1C<;e;op=7i8ezM6IDNfW S{KU809y42QVVM*>N8?rN9uq>JO(U|na z1`c~cGrQQ#>4Mq|S{4d&2WajWM9%{ISCYX4&7Gj>KTvq0kNhlysz)#PK>a9?dRTn{ z>bHQ*-ww7H#6ravak&2}$iGaO>E|O3_1w&$@Wiaw1VM9qs8&LFh}nbWqLSj0(zG7FXy0mAJ5>B_;`kt)S}e%%;J*NqWF^B_~e}YywqX_EJE?|Df#i~Ir&M6Iq@ka z`9;O?iKP_`pmD~W)RNQ`Ju^!SBL=LB;z5H`@!(;n_|&|TqDltH;8aR!Zf+$*JaniE z%!>z&X`v{Mk9P}l^mUDQ^>c}jhq@fbMp2x?5bqM{=jiL{%n*-k40ugAL%h3>zmua+ zyuX`Uuxm(sh@+E_E66zTvhn2NQm`8tqMSnA+{z40;xmg~i;D7#7~(xceB%)z6yoa) z3ZD?rP&5N*U<|Ub9H!3{t`9m2ix@^v1*ry2BIIR4RHG?H8V!d!!U# d3AD@w!my!dWKY@+0LjuMeq0!Sn9z6dL z4;uJ}c&W_57%mU>gc)1_>P%2jgQATg9z4Jfjhy(T;$o;1A!FJ|Yx@!20WI2(_bQ7I pf~HsJoW$bd)MAGC_z+)bSONh>7%cUoL>D+sFhsd{I)}g=1psMDmaqT- diff --git a/tests/partial.c b/tests/partial.c deleted file mode 100644 index 8756e6c78..000000000 --- a/tests/partial.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under both the BSD-style license (found in the - * LICENSE file in the root directory of this source tree) and the GPLv2 (found - * in the COPYING file in the root directory of this source tree). - * You may select, at your option, one of the above-listed licenses. - */ - - -#include -#include "zstd_errors.h" -//#define ZSTD_STATIC_LINKING_ONLY -#include "zstd.h" -#define ZBUFF_DISABLE_DEPRECATE_WARNINGS -#define ZBUFF_STATIC_LINKING_ONLY -#include "zbuff.h" -#define ZDICT_DISABLE_DEPRECATE_WARNINGS -#define ZDICT_STATIC_LINKING_ONLY -#include "zdict.h" - -/* -size_t ZSTD_compress(void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) __attribute__((weak)); //compress -size_t ZSTD_decompress( void* dst, size_t dstCapacity, const void* src, size_t compressedSize) __attribute__((weak)); //decompress -int weakfunc() __attribute__((weak)); //deprecated -int weakfunc() __attribute__((weak)); //dictbuilder -int weakfunc() __attribute__((weak)); //legacy -*/ -//size_t ZSTD_compress(void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) __attribute__((weak)); //compress -//size_t ZSTD_decompress( void* dst, size_t dstCapacity, const void* src, size_t compressedSize) __attribute__((weak)); //decompress -unsigned ZBUFF_isError(size_t errorCode) __attribute__((weak)); //deprecated -unsigned ZDICT_isError(size_t errorCode) __attribute__((weak)); -unsigned ZBUFFv07_isError(size_t errorCode) __attribute__((weak)); -unsigned ZBUFFv06_isError(size_t errorCode) __attribute__((weak)); -unsigned ZBUFFv05_isError(size_t errorCode) __attribute__((weak)); -unsigned ZBUFFv04_isError(size_t errorCode) __attribute__((weak)); -unsigned ZBUFFv03_isError(size_t errorCode) __attribute__((weak)); -unsigned ZBUFFv02_isError(size_t errorCode) __attribute__((weak)); -unsigned ZBUFFv01_isError(size_t errorCode) __attribute__((weak)); - -int checkCompress(void) { - if(ZSTD_compress) { - return 1; - } else { - return 0; - } -} - -int checkDecompress(void) { - if(ZSTD_decompress) { - return 1; - } else { - return 0; - } -} - -int checkDeprecated(void) { - if(ZBUFF_isError) { - return 1; - } else { - return 0; - } -} - -int checkDictBuilder(void) { - if(ZDICT_isError) { - return 1; - } else { - return 0; - } -} - -int checkLegacy(void) { - if(ZBUFFv01_isError) { - return 1; - } else if (ZBUFFv02_isError) { - return 2; - } else if (ZBUFFv03_isError) { - return 3; - } else if (ZBUFFv04_isError) { - return 4; - } else if (ZBUFFv05_isError) { - return 5; - } else if (ZBUFFv06_isError) { - return 6; - } else if (ZBUFFv07_isError) { - return 7; - } - return 0; -} - -int main(void){//int argc, const char** argv) { - //const void **symbol; - //(void)argc; - //(void)argv; - - printf("%d %d %d %d %d\n", checkCompress(), checkDecompress(), checkDeprecated(), checkDictBuilder(), checkLegacy()); - return 0; -} From 51f20e99822766d5c4f27bbebaaa4ea0e7ee55e4 Mon Sep 17 00:00:00 2001 From: George Lu Date: Tue, 5 Jun 2018 14:10:29 -0700 Subject: [PATCH 167/288] circle test --- circle.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/circle.yml b/circle.yml index ed50d59e5..8c69f5944 100644 --- a/circle.yml +++ b/circle.yml @@ -41,6 +41,15 @@ test: if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C lib libzstd-nomt && make clean; fi : parallel: true + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then ZSTD_LIB_COMPRESSION=0 make -C lib libzstd.a; nm lib/libzstd.a | grep ".*\.o:" > tmplog; + ! grep "zstd_compress" tmplog && grep "zstd_decompress" tmplog && ! grep "dict" tmplog \ + && grep "zstd_v" tmplog && make clean && rm tmplog; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then ZSTD_LIB_DECOMPRESSION=0 make -C lib libzstd.a; nm lib/libzstd.a | grep ".*\.o:" > tmplog; + grep "zstd_compress" tmplog && ! grep "zstd_decompress" tmplog && grep "dict" tmplog && \ + ! grep "zstd_v" tmplog && make clean && rm tmplog; fi + : + parallel: true post: - echo Circle CI tests finished From 0fd450af7b9fa748620169b4a872c074cf950832 Mon Sep 17 00:00:00 2001 From: George Lu Date: Tue, 5 Jun 2018 14:41:41 -0700 Subject: [PATCH 168/288] Remove optimizations --- circle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 8c69f5944..df5f5547b 100644 --- a/circle.yml +++ b/circle.yml @@ -42,10 +42,10 @@ test: : parallel: true - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then ZSTD_LIB_COMPRESSION=0 make -C lib libzstd.a; nm lib/libzstd.a | grep ".*\.o:" > tmplog; + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then ZSTD_LIB_COMPRESSION=0 CFLAGS= make -C lib libzstd.a; nm lib/libzstd.a | grep ".*\.o:" > tmplog; ! grep "zstd_compress" tmplog && grep "zstd_decompress" tmplog && ! grep "dict" tmplog \ && grep "zstd_v" tmplog && make clean && rm tmplog; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then ZSTD_LIB_DECOMPRESSION=0 make -C lib libzstd.a; nm lib/libzstd.a | grep ".*\.o:" > tmplog; + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then ZSTD_LIB_DECOMPRESSION=0 CFLAGS= make -C lib libzstd.a; nm lib/libzstd.a | grep ".*\.o:" > tmplog; grep "zstd_compress" tmplog && ! grep "zstd_decompress" tmplog && grep "dict" tmplog && \ ! grep "zstd_v" tmplog && make clean && rm tmplog; fi : From f7392f3dc95e26922763bc5817ab98b0ee4fa696 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 5 Jun 2018 14:53:28 -0700 Subject: [PATCH 169/288] added test case --- doc/zstd_manual.html | 8 ++++---- lib/compress/zstd_compress.c | 6 ++++-- tests/fuzzer.c | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index aae6d0f37..3d107746b 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -82,7 +82,7 @@ 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 + @return : - decompressed size of `src` frame content, 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". @@ -92,7 +92,8 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize); 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 3 : decompressed size is always present when compression is completed using single-pass functions, + such as ZSTD_compress(), ZSTD_compressCCtx() ZSTD_compress_usingDict() or ZSTD_compress_usingCDict(). 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. @@ -107,8 +108,7 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize); Both functions work the same way, but ZSTD_getDecompressedSize() blends "empty", "unknown" and "error" results to the same return value (0), while ZSTD_getFrameContentSize() gives them separate return values. - `src` is the start of a zstd compressed frame. - @return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise. + @return : decompressed size of `src` frame content _if known and not empty_, 0 otherwise.
Helper functions
#define ZSTD_COMPRESSBOUND(srcSize) ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11)/* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */ diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b87ae3819..d9e3f8143 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1079,6 +1079,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zbuff, pledgedSrcSize)) { DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog1=%u, blockSize1=%zu)", zc->appliedParams.cParams.windowLog, zc->blockSize); + zc->workSpaceTooLarge += (zc->workSpaceTooLarge > 0); /* if it was too large, it still is */ return ZSTD_continueCCtx(zc, params, pledgedSrcSize); } } DEBUGLOG(4, "ZSTD_equivalentParams()==0 -> reset CCtx"); @@ -1116,12 +1117,13 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, int const workSpaceTooSmall = zc->workSpaceSize < neededSpace; int const workSpaceTooLarge = zc->workSpaceSize > ZSTD_WORKSPACETOOLARGE_FACTOR * neededSpace; - int const workSpaceWasteful = 0 && workSpaceTooLarge && (zc->workSpaceTooLarge > ZSTD_WORKSPACETOOLARGE_MAX); + int const workSpaceWasteful = workSpaceTooLarge && (zc->workSpaceTooLarge > ZSTD_WORKSPACETOOLARGE_MAX); + zc->workSpaceTooLarge = workSpaceTooLarge ? zc->workSpaceTooLarge+1 : 0; + DEBUGLOG(4, "Need %zuKB workspace, including %zuKB for match state, and %zuKB for buffers", neededSpace>>10, matchStateSize>>10, bufferSpace>>10); DEBUGLOG(4, "windowSize: %zu - blockSize: %zu", windowSize, blockSize); - zc->workSpaceTooLarge = workSpaceTooLarge ? zc->workSpaceTooLarge+1 : 0; if (workSpaceTooSmall || workSpaceWasteful) { DEBUGLOG(4, "Need to resize workSpaceSize from %zuKB to %zuKB", zc->workSpaceSize >> 10, diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 8dabcddbc..6e60b74cb 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -450,6 +450,7 @@ static int basicUnitTests(U32 seed, double compressibility) } DISPLAYLEVEL(3, "OK \n"); + /* this test is really too long, and should be made faster */ DISPLAYLEVEL(3, "test%3d : overflow protection with large windowLog : ", testNb++); { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); ZSTD_parameters params = ZSTD_getParams(-9, ZSTD_CONTENTSIZE_UNKNOWN, 0); @@ -467,6 +468,40 @@ static int basicUnitTests(U32 seed, double compressibility) } DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3d : size down context : ", testNb++); + { ZSTD_CCtx* const largeCCtx = ZSTD_createCCtx(); + assert(largeCCtx != NULL); + CHECK_Z( ZSTD_compressBegin(largeCCtx, 19) ); /* streaming implies ZSTD_CONTENTSIZE_UNKNOWN, which maximizes memory usage */ + CHECK_Z( ZSTD_compressEnd(largeCCtx, compressedBuffer, compressedBufferSize, CNBuffer, 1) ); + { size_t const largeCCtxSize = ZSTD_sizeof_CCtx(largeCCtx); /* size of context must be measured after compression */ + { ZSTD_CCtx* const smallCCtx = ZSTD_createCCtx(); + assert(smallCCtx != NULL); + CHECK_Z(ZSTD_compressCCtx(smallCCtx, compressedBuffer, compressedBufferSize, CNBuffer, 1, 1)); + { size_t const smallCCtxSize = ZSTD_sizeof_CCtx(smallCCtx); + DISPLAYLEVEL(5, "(large) %zuKB > 32*%zuKB (small) : ", + largeCCtxSize>>10, smallCCtxSize>>10); + assert(largeCCtxSize > 32* smallCCtxSize); /* note : "too large" definition is handled within zstd_compress.c . + * make this test case extreme, so that it doesn't depend on a possibly fluctuating definition */ + } + ZSTD_freeCCtx(smallCCtx); + } + { U32 const maxNbAttempts = 1100; /* nb of usages before triggering size down is handled within zstd_compress.c. + * currently defined as 128x, but could be adjusted in the future. + * make this test long enough so that it's not too much tied to the current definition within zstd_compress.c */ + U32 u; + for (u=0; uDate: Tue, 5 Jun 2018 15:20:34 -0700 Subject: [PATCH 170/288] Move stuff around test execution -> travis logic -> partialTests.sh --- .travis.yml | 1 + tests/partialTests.sh | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100755 tests/partialTests.sh diff --git a/.travis.yml b/.travis.yml index a9c1db525..8d1033022 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ matrix: - env: Cmd='make lz4install && make -C tests test-lz4' + - env: Cmd='bash tests/partialTests.sh' # tag-specific test - if: tag =~ ^v[0-9]\.[0-9] env: Cmd='make -C tests checkTag && tests/checkTag $TRAVIS_BRANCH' diff --git a/tests/partialTests.sh b/tests/partialTests.sh new file mode 100755 index 000000000..3ee3cc5bc --- /dev/null +++ b/tests/partialTests.sh @@ -0,0 +1,24 @@ +#!/bin/sh -e + +die() { + $ECHO "$@" 1>&2 + exit 1 +} + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +INTOVOID="/dev/null" +case "$OS" in + Windows*) + INTOVOID="NUL" + ;; +esac + +ZSTD_LIB_COMPRESSION=0 CFLAGS= make -C $DIR/../lib libzstd.a > $INTOVOID +nm $DIR/../lib/libzstd.a | grep ".*\.o:" > tmplog +! grep -q "zstd_compress" tmplog && grep -q "zstd_decompress" tmplog && ! grep -q "dict" tmplog && grep -q "zstd_v" tmplog && make clean && rm -f tmplog || die "Compression macro failed" + + +ZSTD_LIB_DECOMPRESSION=0 CFLAGS= make -C $DIR/../lib libzstd.a > $INTOVOID +nm $DIR/../lib/libzstd.a | grep ".*\.o:" > tmplog +grep -q "zstd_compress" tmplog && ! grep -q "zstd_decompress" tmplog && grep -q "dict" tmplog && ! grep -q "zstd_v" tmplog && make clean && rm -f tmplog || die "Decompression macro failed" \ No newline at end of file From 7892f4808091ab877b8833becb11085f4af73165 Mon Sep 17 00:00:00 2001 From: George Lu Date: Tue, 5 Jun 2018 15:21:31 -0700 Subject: [PATCH 171/288] Remove test from Circle --- circle.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/circle.yml b/circle.yml index df5f5547b..ed50d59e5 100644 --- a/circle.yml +++ b/circle.yml @@ -41,15 +41,6 @@ test: if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C lib libzstd-nomt && make clean; fi : parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then ZSTD_LIB_COMPRESSION=0 CFLAGS= make -C lib libzstd.a; nm lib/libzstd.a | grep ".*\.o:" > tmplog; - ! grep "zstd_compress" tmplog && grep "zstd_decompress" tmplog && ! grep "dict" tmplog \ - && grep "zstd_v" tmplog && make clean && rm tmplog; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then ZSTD_LIB_DECOMPRESSION=0 CFLAGS= make -C lib libzstd.a; nm lib/libzstd.a | grep ".*\.o:" > tmplog; - grep "zstd_compress" tmplog && ! grep "zstd_decompress" tmplog && grep "dict" tmplog && \ - ! grep "zstd_v" tmplog && make clean && rm tmplog; fi - : - parallel: true post: - echo Circle CI tests finished From eab626292c2536c6d406073832fbc62909a80554 Mon Sep 17 00:00:00 2001 From: George Lu Date: Wed, 6 Jun 2018 11:33:39 -0700 Subject: [PATCH 172/288] More Tests --- tests/partialTests.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/partialTests.sh b/tests/partialTests.sh index 3ee3cc5bc..34d8ea552 100755 --- a/tests/partialTests.sh +++ b/tests/partialTests.sh @@ -16,9 +16,21 @@ esac ZSTD_LIB_COMPRESSION=0 CFLAGS= make -C $DIR/../lib libzstd.a > $INTOVOID nm $DIR/../lib/libzstd.a | grep ".*\.o:" > tmplog -! grep -q "zstd_compress" tmplog && grep -q "zstd_decompress" tmplog && ! grep -q "dict" tmplog && grep -q "zstd_v" tmplog && make clean && rm -f tmplog || die "Compression macro failed" +! grep -q "zstd_compress" tmplog && grep -q "zstd_decompress" tmplog && ! grep -q "dict" tmplog && grep -q "zstd_v" tmplog && ! grep -q "zbuff" tmplog && make clean && rm -f tmplog || die "Compression macro failed" ZSTD_LIB_DECOMPRESSION=0 CFLAGS= make -C $DIR/../lib libzstd.a > $INTOVOID nm $DIR/../lib/libzstd.a | grep ".*\.o:" > tmplog -grep -q "zstd_compress" tmplog && ! grep -q "zstd_decompress" tmplog && grep -q "dict" tmplog && ! grep -q "zstd_v" tmplog && make clean && rm -f tmplog || die "Decompression macro failed" \ No newline at end of file +grep -q "zstd_compress" tmplog && ! grep -q "zstd_decompress" tmplog && grep -q "dict" tmplog && ! grep -q "zstd_v" tmplog && ! grep -q "zbuff" tmplog && make clean && rm -f tmplog || die "Decompression macro failed" + +ZSTD_LIB_DEPRECATED=0 CFLAGS= make -C $DIR/../lib libzstd.a > $INTOVOID +nm $DIR/../lib/libzstd.a | grep ".*\.o:" > tmplog +grep -q "zstd_compress" tmplog && grep -q "zstd_decompress" tmplog && grep -q "dict" tmplog && grep -q "zstd_v" tmplog && ! grep -q "zbuff" tmplog && make clean && rm -f tmplog || die "Deprecated macro failed" + +ZSTD_LIB_DICTBUILDER=0 CFLAGS= make -C $DIR/../lib libzstd.a > $INTOVOID +nm $DIR/../lib/libzstd.a | grep ".*\.o:" > tmplog +grep -q "zstd_compress" tmplog && grep -q "zstd_decompress" tmplog && ! grep -q "dict" tmplog && grep -q "zstd_v" tmplog && grep -q "zbuff" tmplog && make clean && rm -f tmplog || die "Dictbuilder macro failed" + +ZSTD_LIB_DECOMPRESSION=0 ZSTD_LIB_DICTBUILDER=0 CFLAGS= make -C $DIR/../lib libzstd.a > $INTOVOID +nm $DIR/../lib/libzstd.a | grep ".*\.o:" > tmplog +grep -q "zstd_compress" tmplog && ! grep -q "zstd_decompress" tmplog && ! grep -q "dict" tmplog && ! grep -q "zstd_v" tmplog && ! grep -q "zbuff" tmplog && make clean && rm -f tmplog || die "Multi-macro failed" \ No newline at end of file From 30ee23e9057be4889fe9174973d34653bcefc6d1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 6 Jun 2018 12:09:58 -0700 Subject: [PATCH 173/288] ensure seekable_format/examples generated libzstd.a when it's not already present in the expected directory --- contrib/seekable_format/examples/Makefile | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/contrib/seekable_format/examples/Makefile b/contrib/seekable_format/examples/Makefile index 1847aa7e7..6d9562df8 100644 --- a/contrib/seekable_format/examples/Makefile +++ b/contrib/seekable_format/examples/Makefile @@ -9,13 +9,16 @@ # This Makefile presumes libzstd is built, using `make` in / or /lib/ -LDFLAGS += ../../../lib/libzstd.a +ZSTDLIB_PATH = ../../../lib +ZSTDLIB_NAME = libzstd.a +ZSTDLIB = $(ZSTDLIB_PATH)/$(ZSTDLIB_NAME) + CPPFLAGS += -I../ -I../../../lib -I../../../lib/common CFLAGS ?= -O3 CFLAGS += -g -SEEKABLE_OBJS = ../zstdseek_compress.c ../zstdseek_decompress.c +SEEKABLE_OBJS = ../zstdseek_compress.c ../zstdseek_decompress.c $(ZSTDLIB) .PHONY: default all clean test @@ -23,6 +26,9 @@ default: all all: seekable_compression seekable_decompression parallel_processing +$(ZSTDLIB): + make -C $(ZSTDLIB_PATH) $(ZSTDLIB_NAME) + seekable_compression : seekable_compression.c $(SEEKABLE_OBJS) $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ From 79e83d1c169719aba67d4cb0b636873010ec4d5b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 6 Jun 2018 12:20:19 -0700 Subject: [PATCH 174/288] refactor travis CI test to add a `make all` target with gcc-6. Note : should fail at this stage, due to contrib/seekable_format --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a9c1db525..4959e97fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ addons: matrix: include: # Ubuntu 14.04 - - env: Cmd='make gcc6install && CC=gcc-6 make clean uasan-test-zstd' + - env: Cmd='make gcc6install && CC=gcc-6 make -j all && make clean && CC=gcc-6 make clean uasan-test-zstd' - env: Cmd='make gcc6install libc6install && CC=gcc-6 make clean uasan-test-zstd32' - env: Cmd='make gcc7install && CC=gcc-7 make clean uasan-test-zstd' - env: Cmd='make clang38install && CC=clang-3.8 make clean msan-test-zstd' @@ -26,8 +26,7 @@ matrix: - env: Cmd='make arminstall && make aarch64fuzz' - env: Cmd='make ppcinstall && make ppcfuzz' - env: Cmd='make ppcinstall && make ppc64fuzz' - - env: Cmd='make -j uasanregressiontest' - - env: Cmd='make -j msanregressiontest' + - env: Cmd='make -j uasanregressiontest && make clean && make -j msanregressiontest' - env: Cmd='make lz4install && make -C tests test-lz4' From 830fd4468f099358fb370e00f2b25ac0c0159043 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 6 Jun 2018 12:47:16 -0700 Subject: [PATCH 175/288] better make -j all behavior avoid concurrent compilation of libzstd --- Makefile | 24 +++++++----------------- doc/zstd_manual.html | 8 ++++---- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index 5756e630c..4f515bb95 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ endif default: lib-release zstd-release .PHONY: all -all: | allmost examples manual contrib +all: allmost examples manual contrib .PHONY: allmost allmost: allzstd @@ -35,8 +35,7 @@ allmost: allzstd #skip zwrapper, can't build that on alternate architectures without the proper zlib installed .PHONY: allzstd -allzstd: - $(MAKE) -C $(ZSTDDIR) all +allzstd: lib $(MAKE) -C $(PRGDIR) all $(MAKE) -C $(TESTDIR) all @@ -45,24 +44,15 @@ all32: $(MAKE) -C $(PRGDIR) zstd32 $(MAKE) -C $(TESTDIR) all32 -.PHONY: lib -lib: +.PHONY: lib lib-release +lib lib-release: @$(MAKE) -C $(ZSTDDIR) $@ -.PHONY: lib-release -lib-release: - @$(MAKE) -C $(ZSTDDIR) - -.PHONY: zstd -zstd: +.PHONY: zstd zstd-release +zstd zstd-release: @$(MAKE) -C $(PRGDIR) $@ cp $(PRGDIR)/zstd$(EXT) . -.PHONY: zstd-release -zstd-release: - @$(MAKE) -C $(PRGDIR) - cp $(PRGDIR)/zstd$(EXT) . - .PHONY: zstdmt zstdmt: @$(MAKE) -C $(PRGDIR) $@ @@ -85,7 +75,7 @@ shortest: check: shortest .PHONY: examples -examples: +examples: lib CPPFLAGS=-I../lib LDFLAGS=-L../lib $(MAKE) -C examples/ all .PHONY: manual diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index aae6d0f37..3d107746b 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -82,7 +82,7 @@ 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 + @return : - decompressed size of `src` frame content, 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". @@ -92,7 +92,8 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize); 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 3 : decompressed size is always present when compression is completed using single-pass functions, + such as ZSTD_compress(), ZSTD_compressCCtx() ZSTD_compress_usingDict() or ZSTD_compress_usingCDict(). 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. @@ -107,8 +108,7 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize); Both functions work the same way, but ZSTD_getDecompressedSize() blends "empty", "unknown" and "error" results to the same return value (0), while ZSTD_getFrameContentSize() gives them separate return values. - `src` is the start of a zstd compressed frame. - @return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise. + @return : decompressed size of `src` frame content _if known and not empty_, 0 otherwise.
#define ZSTD_COMPRESSBOUND(srcSize) ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11)/* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */ From 97c60cdf365eacc5504607600cd824748a70e298 Mon Sep 17 00:00:00 2001 From: Yann Collet
@@ -56,7 +57,9 @@unsigned ZSTD_versionNumber(void); /**< useful to check dll version */
-Simple API
+Default constant
+ +Simple API
size_t ZSTD_compress( void* dst, size_t dstCapacity, const void* src, size_t srcSize, @@ -117,7 +120,7 @@ unsigned ZSTD_isError(size_t code); /*!< tells if a `size_t` fun const char* ZSTD_getErrorName(size_t code);
When compressing many times, it is recommended to allocate a context just once, and re-use it for each successive compression operation. @@ -149,7 +152,7 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);Same as ZSTD_decompress(), requires an allocated ZSTD_DCtx (see ZSTD_createDCtx())
size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx,
void* dst, size_t dstCapacity,
@@ -171,7 +174,7 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
Note : When `dict == NULL || dictSize < 8` no dictionary is used.
ZSTD_CDict* ZSTD_createCDict(const void* dictBuffer, size_t dictSize,
int compressionLevel);
@@ -212,7 +215,7 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
Faster startup than ZSTD_decompress_usingDict(), recommended when same dictionary is used multiple times.
typedef struct ZSTD_inBuffer_s {
const void* src; /**< start of input buffer */
@@ -226,7 +229,7 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
size_t pos; /**< position where writing stopped. Will be updated. Necessarily 0 <= pos <= size */
} ZSTD_outBuffer;
+Streaming compression - HowTo
A ZSTD_CStream object is required to track streaming operation. Use ZSTD_createCStream() and ZSTD_freeCStream() to create/release resources. ZSTD_CStream objects can be reused multiple times on consecutive compression operations. @@ -285,7 +288,7 @@ size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output);
size_t ZSTD_CStreamOutSize(void); /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block in all circumstances. */
-Streaming decompression - HowTo
+Streaming decompression - HowTo
A ZSTD_DStream object is required to track streaming operations. Use ZSTD_createDStream() and ZSTD_freeDStream() to create/release resources. ZSTD_DStream objects can be re-used multiple times. @@ -318,14 +321,14 @@ 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. +START OF ADVANCED AND EXPERIMENTAL FUNCTIONS
The definitions in this section are considered experimental. 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.-Advanced types
+Advanced types
typedef enum { ZSTD_fast=1, ZSTD_dfast, ZSTD_greedy, ZSTD_lazy, ZSTD_lazy2, ZSTD_btlazy2, ZSTD_btopt, ZSTD_btultra } ZSTD_strategy; /* from faster to stronger */ @@ -362,7 +365,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB ZSTD_dlm_byRef, /**< Reference dictionary content -- the dictionary buffer must outlive its users. */ } ZSTD_dictLoadMethod_e;
-Frame size functions
+Frame size functions
size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize);`src` should point to the start of a ZSTD encoded frame or skippable frame @@ -395,12 +398,12 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB however it does mean that all frame data must be present and valid.
-ZSTD_frameHeaderSize() :
srcSize must be >= ZSTD_frameHeaderSize_prefix. +ZSTD_frameHeaderSize() :
srcSize must be >= ZSTD_frameHeaderSize_prefix. @return : size of the Frame Header, or an error code (if srcSize is too small)-Memory management
+Memory management
size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx); size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx); @@ -490,7 +493,7 @@ static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< t
-Advanced compression functions
+Advanced compression functions
ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);Create a digested dictionary for compression @@ -532,7 +535,7 @@ static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< t
Same as ZSTD_compress_usingCDict(), with fine-tune control over frame parameters
-Advanced decompression functions
+Advanced decompression functions
unsigned ZSTD_isFrame(const void* buffer, size_t size);Tells if the content of `buffer` starts with a valid Frame Identifier. @@ -572,7 +575,7 @@ static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< t When identifying the exact failure cause, it's possible to use ZSTD_getFrameHeader(), which will provide a more precise error code.
-Advanced streaming functions
+Advanced streaming functions
Advanced Streaming compression functions
size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);/**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */ size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/ @@ -604,14 +607,14 @@ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t di size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); /**< note : ddict is referenced, it must outlive decompression session */ size_t ZSTD_resetDStream(ZSTD_DStream* zds); /**< re-use decompression parameters from previous init; saves dictionary loading */
-Buffer-less and synchronous inner streaming functions
+Buffer-less and synchronous inner streaming functions
This is an advanced API, giving full control over buffer management, for users which need direct control over memory. But it's also a complex one, with several restrictions, documented below. Prefer normal streaming API for an easier experience.-Buffer-less streaming compression (synchronous mode)
+Buffer-less streaming compression (synchronous mode)
A ZSTD_CCtx object is required to track streaming operations. Use ZSTD_createCCtx() / ZSTD_freeCCtx() to manage resource. ZSTD_CCtx object can be re-used multiple times within successive compression operations. @@ -647,7 +650,7 @@ size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */ size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
-Buffer-less streaming decompression (synchronous mode)
+Buffer-less streaming decompression (synchronous mode)
A ZSTD_DCtx object is required to track streaming operations. Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it. A ZSTD_DCtx object can be re-used multiple times. @@ -738,7 +741,7 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
-New advanced API (experimental)
+New advanced API (experimental)
typedef enum { /* Opened question : should we have a format ZSTD_f_auto ? @@ -764,7 +767,7 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long /* compression parameters */ ZSTD_p_compressionLevel=100, /* Update all compression parameters according to pre-defined cLevel table * Default level is ZSTD_CLEVEL_DEFAULT==3. - * Special: value 0 means "do not change cLevel". + * Special: value 0 means default, which is controlled by ZSTD_CLEVEL_DEFAULT. * Note 1 : it's possible to pass a negative compression level by casting it to unsigned type. * Note 2 : setting a level sets all default values of other compression parameters. * Note 3 : setting compressionLevel automatically updates ZSTD_p_compressLiterals. */ @@ -1146,7 +1149,7 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx,
-ZSTD_getFrameHeader_advanced() :
same as ZSTD_getFrameHeader(), +ZSTD_getFrameHeader_advanced() :
same as ZSTD_getFrameHeader(), with added capability to select a format (like ZSTD_f_zstd1_magicless)@@ -1182,7 +1185,7 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx,
-Block level API
+Block level API
Frame metadata cost is typically ~18 bytes, which can be non-negligible for very small blocks (< 100 bytes). User will have to take in charge required information to regenerate data, such as compressed and content sizes. diff --git a/lib/common/fse.h b/lib/common/fse.h index 8d7033695..a5a6b6d4d 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -72,6 +72,7 @@ extern "C" { #define FSE_VERSION_NUMBER (FSE_VERSION_MAJOR *100*100 + FSE_VERSION_MINOR *100 + FSE_VERSION_RELEASE) FSE_PUBLIC_API unsigned FSE_versionNumber(void); /**< library version number; to be used when checking dll version */ + /*-**************************************** * FSE simple functions ******************************************/ @@ -129,7 +130,7 @@ FSE_PUBLIC_API size_t FSE_compress2 (void* dst, size_t dstSize, const void* src, ******************************************/ /*! FSE_compress() does the following: -1. count symbol occurrence from source[] into table count[] +1. count symbol occurrence from source[] into table count[] (see hist.h) 2. normalize counters so that sum(count[]) == Power_of_2 (2^tableLog) 3. save normalized counters to memory buffer using writeNCount() 4. build encoding table 'CTable' from normalized counters @@ -147,15 +148,6 @@ or to save and provide normalized distribution using external method. /* *** COMPRESSION *** */ -/*! FSE_count(): - Provides the precise count of each byte within a table 'count'. - 'count' is a table of unsigned int, of minimum size (*maxSymbolValuePtr+1). - *maxSymbolValuePtr will be updated if detected smaller than initial value. - @return : the count of the most frequent symbol (which is not identified). - if return == srcSize, there is only one symbol. - Can also return an error code, which can be tested with FSE_isError(). */ -FSE_PUBLIC_API size_t FSE_count(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize); - /*! FSE_optimalTableLog(): dynamically downsize 'tableLog' when conditions are met. It saves CPU time, by using smaller tables, while preserving or even improving compression ratio. @@ -167,7 +159,8 @@ FSE_PUBLIC_API unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize 'normalizedCounter' is a table of short, of minimum size (maxSymbolValue+1). @return : tableLog, or an errorCode, which can be tested using FSE_isError() */ -FSE_PUBLIC_API size_t FSE_normalizeCount(short* normalizedCounter, unsigned tableLog, const unsigned* count, size_t srcSize, unsigned maxSymbolValue); +FSE_PUBLIC_API size_t FSE_normalizeCount(short* normalizedCounter, unsigned tableLog, + const unsigned* count, size_t srcSize, unsigned maxSymbolValue); /*! FSE_NCountWriteBound(): Provides the maximum possible size of an FSE normalized table, given 'maxSymbolValue' and 'tableLog'. @@ -178,8 +171,9 @@ FSE_PUBLIC_API size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tab Compactly save 'normalizedCounter' into 'buffer'. @return : size of the compressed table, or an errorCode, which can be tested using FSE_isError(). */ -FSE_PUBLIC_API size_t FSE_writeNCount (void* buffer, size_t bufferSize, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); - +FSE_PUBLIC_API size_t FSE_writeNCount (void* buffer, size_t bufferSize, + const short* normalizedCounter, + unsigned maxSymbolValue, unsigned tableLog); /*! Constructor and Destructor of FSE_CTable. Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */ @@ -250,7 +244,9 @@ If there is an error, the function will return an ErrorCode (which can be tested @return : size read from 'rBuffer', or an errorCode, which can be tested using FSE_isError(). maxSymbolValuePtr[0] and tableLogPtr[0] will also be updated with their respective values */ -FSE_PUBLIC_API size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSymbolValuePtr, unsigned* tableLogPtr, const void* rBuffer, size_t rBuffSize); +FSE_PUBLIC_API size_t FSE_readNCount (short* normalizedCounter, + unsigned* maxSymbolValuePtr, unsigned* tableLogPtr, + const void* rBuffer, size_t rBuffSize); /*! Constructor and Destructor of FSE_DTable. Note that its size depends on 'tableLog' */ @@ -325,33 +321,8 @@ If there is an error, the function will return an error code, which can be teste /* ***************************************** -* FSE advanced API -*******************************************/ -/* FSE_count_wksp() : - * Same as FSE_count(), but using an externally provided scratch buffer. - * `workSpace` size must be table of >= `1024` unsigned - */ -size_t FSE_count_wksp(unsigned* count, unsigned* maxSymbolValuePtr, - const void* source, size_t sourceSize, unsigned* workSpace); - -/** FSE_countFast() : - * same as FSE_count(), but blindly trusts that all byte values within src are <= *maxSymbolValuePtr - */ -size_t FSE_countFast(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize); - -/* FSE_countFast_wksp() : - * Same as FSE_countFast(), but using an externally provided scratch buffer. - * `workSpace` must be a table of minimum `1024` unsigned - */ -size_t FSE_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned* workSpace); - -/*! FSE_count_simple() : - * Same as FSE_countFast(), but does not use any additional memory (not even on stack). - * This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr` (presuming it's also the size of `count`). -*/ -size_t FSE_count_simple(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize); - - + * FSE advanced API + ***************************************** */ unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus); /**< same as FSE_optimalTableLog(), which used `minus==2` */ diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index eeff3f2cd..07b3ab89b 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -1,6 +1,6 @@ /* ****************************************************************** FSE : Finite State Entropy encoder - Copyright (C) 2013-2015, Yann Collet. + Copyright (C) 2013-present, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) @@ -37,9 +37,11 @@ ****************************************************************/ #include
/* malloc, free, qsort */ #include /* memcpy, memset */ -#include /* printf (debug) */ -#include "bitstream.h" #include "compiler.h" +#include "mem.h" /* U32, U16, etc. */ +#include "debug.h" /* assert, DEBUGLOG */ +#include "hist.h" /* HIST_count_wksp */ +#include "bitstream.h" #define FSE_STATIC_LINKING_ONLY #include "fse.h" #include "error_private.h" @@ -49,7 +51,6 @@ * Error Management ****************************************************************/ #define FSE_isError ERR_isError -#define FSE_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c) /* use only *after* variable declarations */ /* ************************************************************** @@ -190,8 +191,9 @@ size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned #ifndef FSE_COMMONDEFS_ONLY + /*-************************************************************** -* FSE NCount encoding-decoding +* FSE NCount encoding ****************************************************************/ size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog) { @@ -299,159 +301,6 @@ size_t FSE_writeNCount (void* buffer, size_t bufferSize, const short* normalized } - -/*-************************************************************** -* Counting histogram -****************************************************************/ -/*! FSE_count_simple - This function counts byte values within `src`, and store the histogram into table `count`. - It doesn't use any additional memory. - But this function is unsafe : it doesn't check that all values within `src` can fit into `count`. - For this reason, prefer using a table `count` with 256 elements. - @return : count of most numerous element. -*/ -size_t FSE_count_simple(unsigned* count, unsigned* maxSymbolValuePtr, - const void* src, size_t srcSize) -{ - const BYTE* ip = (const BYTE*)src; - const BYTE* const end = ip + srcSize; - unsigned maxSymbolValue = *maxSymbolValuePtr; - unsigned max=0; - - memset(count, 0, (maxSymbolValue+1)*sizeof(*count)); - if (srcSize==0) { *maxSymbolValuePtr = 0; return 0; } - - while (ip max) max = count[s]; } - - return (size_t)max; -} - - -/* FSE_count_parallel_wksp() : - * Same as FSE_count_parallel(), but using an externally provided scratch buffer. - * `workSpace` size must be a minimum of `1024 * sizeof(unsigned)`. - * @return : largest histogram frequency, or an error code (notably when histogram would be larger than *maxSymbolValuePtr). */ -static size_t FSE_count_parallel_wksp( - unsigned* count, unsigned* maxSymbolValuePtr, - const void* source, size_t sourceSize, - unsigned checkMax, unsigned* const workSpace) -{ - const BYTE* ip = (const BYTE*)source; - const BYTE* const iend = ip+sourceSize; - unsigned maxSymbolValue = *maxSymbolValuePtr; - unsigned max=0; - U32* const Counting1 = workSpace; - U32* const Counting2 = Counting1 + 256; - U32* const Counting3 = Counting2 + 256; - U32* const Counting4 = Counting3 + 256; - - memset(workSpace, 0, 4*256*sizeof(unsigned)); - - /* safety checks */ - if (!sourceSize) { - memset(count, 0, maxSymbolValue + 1); - *maxSymbolValuePtr = 0; - return 0; - } - if (!maxSymbolValue) maxSymbolValue = 255; /* 0 == default */ - - /* by stripes of 16 bytes */ - { U32 cached = MEM_read32(ip); ip += 4; - while (ip < iend-15) { - U32 c = cached; cached = MEM_read32(ip); ip += 4; - Counting1[(BYTE) c ]++; - Counting2[(BYTE)(c>>8) ]++; - Counting3[(BYTE)(c>>16)]++; - Counting4[ c>>24 ]++; - c = cached; cached = MEM_read32(ip); ip += 4; - Counting1[(BYTE) c ]++; - Counting2[(BYTE)(c>>8) ]++; - Counting3[(BYTE)(c>>16)]++; - Counting4[ c>>24 ]++; - c = cached; cached = MEM_read32(ip); ip += 4; - Counting1[(BYTE) c ]++; - Counting2[(BYTE)(c>>8) ]++; - Counting3[(BYTE)(c>>16)]++; - Counting4[ c>>24 ]++; - c = cached; cached = MEM_read32(ip); ip += 4; - Counting1[(BYTE) c ]++; - Counting2[(BYTE)(c>>8) ]++; - Counting3[(BYTE)(c>>16)]++; - Counting4[ c>>24 ]++; - } - ip-=4; - } - - /* finish last symbols */ - while (ip maxSymbolValue; s--) { - Counting1[s] += Counting2[s] + Counting3[s] + Counting4[s]; - if (Counting1[s]) return ERROR(maxSymbolValue_tooSmall); - } } - - { U32 s; - if (maxSymbolValue > 255) maxSymbolValue = 255; - for (s=0; s<=maxSymbolValue; s++) { - count[s] = Counting1[s] + Counting2[s] + Counting3[s] + Counting4[s]; - if (count[s] > max) max = count[s]; - } } - - while (!count[maxSymbolValue]) maxSymbolValue--; - *maxSymbolValuePtr = maxSymbolValue; - return (size_t)max; -} - -/* FSE_countFast_wksp() : - * Same as FSE_countFast(), but using an externally provided scratch buffer. - * `workSpace` size must be table of >= `1024` unsigned */ -size_t FSE_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr, - const void* source, size_t sourceSize, - unsigned* workSpace) -{ - if (sourceSize < 1500) /* heuristic threshold */ - return FSE_count_simple(count, maxSymbolValuePtr, source, sourceSize); - return FSE_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 0, workSpace); -} - -/* fast variant (unsafe : won't check if src contains values beyond count[] limit) */ -size_t FSE_countFast(unsigned* count, unsigned* maxSymbolValuePtr, - const void* source, size_t sourceSize) -{ - unsigned tmpCounters[1024]; - return FSE_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, tmpCounters); -} - -/* FSE_count_wksp() : - * Same as FSE_count(), but using an externally provided scratch buffer. - * `workSpace` size must be table of >= `1024` unsigned */ -size_t FSE_count_wksp(unsigned* count, unsigned* maxSymbolValuePtr, - const void* source, size_t sourceSize, unsigned* workSpace) -{ - if (*maxSymbolValuePtr < 255) - return FSE_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 1, workSpace); - *maxSymbolValuePtr = 255; - return FSE_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, workSpace); -} - -size_t FSE_count(unsigned* count, unsigned* maxSymbolValuePtr, - const void* src, size_t srcSize) -{ - unsigned tmpCounters[1024]; - return FSE_count_wksp(count, maxSymbolValuePtr, src, srcSize, tmpCounters); -} - - - /*-************************************************************** * FSE Compression Code ****************************************************************/ @@ -645,11 +494,11 @@ size_t FSE_normalizeCount (short* normalizedCounter, unsigned tableLog, U32 s; U32 nTotal = 0; for (s=0; s<=maxSymbolValue; s++) - printf("%3i: %4i \n", s, normalizedCounter[s]); + RAWLOG(2, "%3i: %4i \n", s, normalizedCounter[s]); for (s=0; s<=maxSymbolValue; s++) nTotal += abs(normalizedCounter[s]); if (nTotal != (1U< not compressible */ if (maxCount < (srcSize >> 7)) return 0; /* Heuristic : not compressible enough */ @@ -851,7 +700,7 @@ typedef struct { size_t FSE_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog) { fseWkspMax_t scratchBuffer; - FSE_STATIC_ASSERT(sizeof(scratchBuffer) >= FSE_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)); /* compilation failures here means scratchBuffer is not large enough */ + DEBUG_STATIC_ASSERT(sizeof(scratchBuffer) >= FSE_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)); /* compilation failures here means scratchBuffer is not large enough */ if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); return FSE_compress_wksp(dst, dstCapacity, src, srcSize, maxSymbolValue, tableLog, &scratchBuffer, sizeof(scratchBuffer)); } diff --git a/lib/compress/hist.c b/lib/compress/hist.c new file mode 100644 index 000000000..da3b749db --- /dev/null +++ b/lib/compress/hist.c @@ -0,0 +1,200 @@ +/* ****************************************************************** + hist : Histogram functions + part of Finite State Entropy project + Copyright (C) 2013-present, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy + - Public forum : https://groups.google.com/forum/#!forum/lz4c +****************************************************************** */ + +/* --- dependencies --- */ +#include "mem.h" /* U32, BYTE, etc. */ +#include "debug.h" /* assert, DEBUGLOG */ +#include "error_private.h" /* ERROR */ +#include "hist.h" + + +/* --- Error management --- */ +unsigned HIST_isError(size_t code) { return ERR_isError(code); } + +/*-************************************************************** + * Histogram functions + ****************************************************************/ +/*! HIST_count_simple : + Counts byte values within `src`, storing histogram into table `count`. + Doesn't use any additional memory, very limited stack usage. + But unsafe : doesn't check that all values within `src` fit into `count`. + For this reason, prefer using a table `count` of size 256. + @return : count of most numerous element. + this function doesn't produce any error (i.e. it must succeed). +*/ +unsigned HIST_count_simple(unsigned* count, unsigned* maxSymbolValuePtr, + const void* src, size_t srcSize) +{ + const BYTE* ip = (const BYTE*)src; + const BYTE* const end = ip + srcSize; + unsigned maxSymbolValue = *maxSymbolValuePtr; + unsigned largestCount=0; + + memset(count, 0, (maxSymbolValue+1) * sizeof(*count)); + if (srcSize==0) { *maxSymbolValuePtr = 0; return 0; } + + while (ip largestCount) largestCount = count[s]; + } + + return largestCount; +} + + +/* HIST_count_parallel_wksp() : + * Same as HIST_count_parallel(), but using an externally provided scratch buffer. + * `workSpace` size must be a table of size >= HIST_WKSP_SIZE_U32. + * @return : largest histogram frequency, + * or an error code (notably when histogram would be larger than *maxSymbolValuePtr). */ +static size_t HIST_count_parallel_wksp( + unsigned* count, unsigned* maxSymbolValuePtr, + const void* source, size_t sourceSize, + unsigned checkMax, + unsigned* const workSpace) +{ + const BYTE* ip = (const BYTE*)source; + const BYTE* const iend = ip+sourceSize; + unsigned maxSymbolValue = *maxSymbolValuePtr; + unsigned max=0; + U32* const Counting1 = workSpace; + U32* const Counting2 = Counting1 + 256; + U32* const Counting3 = Counting2 + 256; + U32* const Counting4 = Counting3 + 256; + + memset(workSpace, 0, 4*256*sizeof(unsigned)); + + /* safety checks */ + if (!sourceSize) { + memset(count, 0, maxSymbolValue + 1); + *maxSymbolValuePtr = 0; + return 0; + } + if (!maxSymbolValue) maxSymbolValue = 255; /* 0 == default */ + + /* by stripes of 16 bytes */ + { U32 cached = MEM_read32(ip); ip += 4; + while (ip < iend-15) { + U32 c = cached; cached = MEM_read32(ip); ip += 4; + Counting1[(BYTE) c ]++; + Counting2[(BYTE)(c>>8) ]++; + Counting3[(BYTE)(c>>16)]++; + Counting4[ c>>24 ]++; + c = cached; cached = MEM_read32(ip); ip += 4; + Counting1[(BYTE) c ]++; + Counting2[(BYTE)(c>>8) ]++; + Counting3[(BYTE)(c>>16)]++; + Counting4[ c>>24 ]++; + c = cached; cached = MEM_read32(ip); ip += 4; + Counting1[(BYTE) c ]++; + Counting2[(BYTE)(c>>8) ]++; + Counting3[(BYTE)(c>>16)]++; + Counting4[ c>>24 ]++; + c = cached; cached = MEM_read32(ip); ip += 4; + Counting1[(BYTE) c ]++; + Counting2[(BYTE)(c>>8) ]++; + Counting3[(BYTE)(c>>16)]++; + Counting4[ c>>24 ]++; + } + ip-=4; + } + + /* finish last symbols */ + while (ip maxSymbolValue; s--) { + Counting1[s] += Counting2[s] + Counting3[s] + Counting4[s]; + if (Counting1[s]) return ERROR(maxSymbolValue_tooSmall); + } } + + { U32 s; + if (maxSymbolValue > 255) maxSymbolValue = 255; + for (s=0; s<=maxSymbolValue; s++) { + count[s] = Counting1[s] + Counting2[s] + Counting3[s] + Counting4[s]; + if (count[s] > max) max = count[s]; + } } + + while (!count[maxSymbolValue]) maxSymbolValue--; + *maxSymbolValuePtr = maxSymbolValue; + return (size_t)max; +} + +/* HIST_countFast_wksp() : + * Same as HIST_countFast(), but using an externally provided scratch buffer. + * `workSpace` size must be table of >= HIST_WKSP_SIZE_U32 unsigned */ +size_t HIST_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr, + const void* source, size_t sourceSize, + unsigned* workSpace) +{ + if (sourceSize < 1500) /* heuristic threshold */ + return HIST_count_simple(count, maxSymbolValuePtr, source, sourceSize); + return HIST_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 0, workSpace); +} + +/* fast variant (unsafe : won't check if src contains values beyond count[] limit) */ +size_t HIST_countFast(unsigned* count, unsigned* maxSymbolValuePtr, + const void* source, size_t sourceSize) +{ + unsigned tmpCounters[HIST_WKSP_SIZE_U32]; + return HIST_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, tmpCounters); +} + +/* HIST_count_wksp() : + * Same as HIST_count(), but using an externally provided scratch buffer. + * `workSpace` size must be table of >= HIST_WKSP_SIZE_U32 unsigned */ +size_t HIST_count_wksp(unsigned* count, unsigned* maxSymbolValuePtr, + const void* source, size_t sourceSize, unsigned* workSpace) +{ + if (*maxSymbolValuePtr < 255) + return HIST_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 1, workSpace); + *maxSymbolValuePtr = 255; + return HIST_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, workSpace); +} + +size_t HIST_count(unsigned* count, unsigned* maxSymbolValuePtr, + const void* src, size_t srcSize) +{ + unsigned tmpCounters[HIST_WKSP_SIZE_U32]; + return HIST_count_wksp(count, maxSymbolValuePtr, src, srcSize, tmpCounters); +} diff --git a/lib/compress/hist.h b/lib/compress/hist.h new file mode 100644 index 000000000..293b188bc --- /dev/null +++ b/lib/compress/hist.h @@ -0,0 +1,90 @@ +/* ****************************************************************** + hist : Histogram functions + part of Finite State Entropy project + Copyright (C) 2013-present, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy + - Public forum : https://groups.google.com/forum/#!forum/lz4c +****************************************************************** */ + +/* --- dependencies --- */ +#include /* size_t */ + + +/* --- simple histogram functions --- */ + +/*! HIST_count(): + * Provides the precise count of each byte within a table 'count'. + * 'count' is a table of unsigned int, of minimum size (*maxSymbolValuePtr+1). + * Updates *maxSymbolValuePtr with actual largest symbol value detected. + * @return : count of the most frequent symbol (which isn't identified). + * or an error code, which can be tested using HIST_isError(). + * note : if return == srcSize, there is only one symbol. + */ +size_t HIST_count(unsigned* count, unsigned* maxSymbolValuePtr, + const void* src, size_t srcSize); + +unsigned HIST_isError(size_t code); /*< tells if a return value is an error code */ + + +/* --- advanced histogram functions --- */ + +#define HIST_WKSP_SIZE_U32 1024 +/** HIST_count_wksp() : + * Same as HIST_count(), but using an externally provided scratch buffer. + * Benefit is this function will use very little stack space. + * `workSpace` must be a table of unsigned of size >= HIST_WKSP_SIZE_U32 + */ +size_t HIST_count_wksp(unsigned* count, unsigned* maxSymbolValuePtr, + const void* src, size_t srcSize, + unsigned* workSpace); + +/** HIST_countFast() : + * same as HIST_count(), but blindly trusts that all byte values within src are <= *maxSymbolValuePtr. + * This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr` + */ +size_t HIST_countFast(unsigned* count, unsigned* maxSymbolValuePtr, + const void* src, size_t srcSize); + +/** HIST_countFast_wksp() : + * Same as HIST_countFast(), but using an externally provided scratch buffer. + * `workSpace` must be a table of unsigned of size >= HIST_WKSP_SIZE_U32 + */ +size_t HIST_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr, + const void* src, size_t srcSize, + unsigned* workSpace); + +/*! HIST_count_simple() : + * Same as HIST_countFast(), but does not use any additional memory (not even on stack). + * This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr`. + * It is also a bit slower for large inputs. + * This function doesn't produce any error (i.e. it must succeed). + */ +unsigned HIST_count_simple(unsigned* count, unsigned* maxSymbolValuePtr, + const void* src, size_t srcSize); diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index b927efcc2..2e5319187 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -45,8 +45,9 @@ ****************************************************************/ #include /* memcpy, memset */ #include /* printf (debug) */ -#include "bitstream.h" #include "compiler.h" +#include "bitstream.h" +#include "hist.h" #define FSE_STATIC_LINKING_ONLY /* FSE_optimalTableLog_internal */ #include "fse.h" /* header compression */ #define HUF_STATIC_LINKING_ONLY @@ -100,9 +101,9 @@ size_t HUF_compressWeights (void* dst, size_t dstSize, const void* weightTable, if (wtSize <= 1) return 0; /* Not compressible */ /* Scan input and build symbol stats */ - { CHECK_V_F(maxCount, FSE_count_simple(count, &maxSymbolValue, weightTable, wtSize) ); + { unsigned const maxCount = HIST_count_simple(count, &maxSymbolValue, weightTable, wtSize); /* never fails */ if (maxCount == wtSize) return 1; /* only a single symbol in src : rle */ - if (maxCount == 1) return 0; /* each symbol present maximum once => not compressible */ + if (maxCount == 1) return 0; /* each symbol present maximum once => not compressible */ } tableLog = FSE_optimalTableLog(tableLog, wtSize, maxSymbolValue); @@ -667,7 +668,7 @@ static size_t HUF_compress_internal ( } /* Scan input and build symbol stats */ - { CHECK_V_F(largest, FSE_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, table->count) ); + { CHECK_V_F(largest, HIST_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, table->count) ); if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */ if (largest <= (srcSize >> 7)+1) return 0; /* heuristic : probably not compressible enough */ } diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d79e9c74b..9c4e54c28 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -14,6 +14,7 @@ #include /* memset */ #include "cpu.h" #include "mem.h" +#include "hist.h" /* HIST_countFast_wksp */ #define FSE_STATIC_LINKING_ONLY /* FSE_encodeSymbol */ #include "fse.h" #define HUF_STATIC_LINKING_ONLY @@ -2109,7 +2110,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, ZSTD_seqToCodes(seqStorePtr); /* build CTable for Literal Lengths */ { U32 max = MaxLL; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, workspace); + size_t const mostFrequent = HIST_countFast_wksp(count, &max, llCodeTable, nbSeq, workspace); /* can't fail */ DEBUGLOG(5, "Building LL table"); nextEntropy->fse.litlength_repeatMode = prevEntropy->fse.litlength_repeatMode; LLtype = ZSTD_selectEncodingType(&nextEntropy->fse.litlength_repeatMode, count, max, mostFrequent, nbSeq, LLFSELog, prevEntropy->fse.litlengthCTable, LL_defaultNorm, LL_defaultNormLog, ZSTD_defaultAllowed, strategy); @@ -2126,7 +2127,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, } } /* build CTable for Offsets */ { U32 max = MaxOff; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, workspace); + size_t const mostFrequent = HIST_countFast_wksp(count, &max, ofCodeTable, nbSeq, workspace); /* can't fail */ /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */ ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed; DEBUGLOG(5, "Building OF table"); @@ -2144,7 +2145,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, } } /* build CTable for MatchLengths */ { U32 max = MaxML; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, workspace); + size_t const mostFrequent = HIST_countFast_wksp(count, &max, mlCodeTable, nbSeq, workspace); /* can't fail */ DEBUGLOG(5, "Building ML table"); nextEntropy->fse.matchlength_repeatMode = prevEntropy->fse.matchlength_repeatMode; MLtype = ZSTD_selectEncodingType(&nextEntropy->fse.matchlength_repeatMode, count, max, mostFrequent, nbSeq, MLFSELog, prevEntropy->fse.matchlengthCTable, ML_defaultNorm, ML_defaultNormLog, ZSTD_defaultAllowed, strategy); diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 46a5395ac..91ae4ae28 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -9,6 +9,7 @@ */ #include "zstd_compress_internal.h" +#include "hist.h" #include "zstd_opt.h" @@ -142,7 +143,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, assert(optPtr->litFreq != NULL); { unsigned lit = MaxLit; - FSE_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */ + HIST_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */ } optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1); diff --git a/tests/decodecorpus.c b/tests/decodecorpus.c index 407653119..936d307f4 100644 --- a/tests/decodecorpus.c +++ b/tests/decodecorpus.c @@ -437,7 +437,8 @@ static size_t writeHufHeader(U32* seed, HUF_CElt* hufTable, void* dst, size_t ds U32 count[HUF_SYMBOLVALUE_MAX+1]; /* Scan input and build symbol stats */ - { size_t const largest = FSE_count_wksp (count, &maxSymbolValue, (const BYTE*)src, srcSize, WKSP); + { size_t const largest = HIST_count_wksp (count, &maxSymbolValue, (const BYTE*)src, srcSize, WKSP); + assert(!HIST_isError(largest)); if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 0; } /* single symbol, rle */ if (largest <= (srcSize >> 7)+1) return 0; /* Fast heuristic : not compressible enough */ } @@ -834,7 +835,8 @@ static size_t writeSequences(U32* seed, frame_t* frame, seqStore_t* seqStorePtr, /* CTable for Literal Lengths */ { U32 max = MaxLL; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, WKSP); + size_t const mostFrequent = HIST_countFast_wksp(count, &max, llCodeTable, nbSeq, WKSP); /* cannot fail */ + assert(!HIST_isError(mostFrequent)); if (mostFrequent == nbSeq) { /* do RLE if we have the chance */ *op++ = llCodeTable[0]; @@ -865,7 +867,8 @@ static size_t writeSequences(U32* seed, frame_t* frame, seqStore_t* seqStorePtr, /* CTable for Offsets */ /* see Literal Lengths for descriptions of mode choices */ { U32 max = MaxOff; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, WKSP); + size_t const mostFrequent = HIST_countFast_wksp(count, &max, ofCodeTable, nbSeq, WKSP); /* cannot fail */ + assert(!HIST_isError(mostFrequent)); if (mostFrequent == nbSeq) { *op++ = ofCodeTable[0]; FSE_buildCTable_rle(CTable_OffsetBits, (BYTE)max); @@ -892,7 +895,8 @@ static size_t writeSequences(U32* seed, frame_t* frame, seqStore_t* seqStorePtr, /* CTable for MatchLengths */ /* see Literal Lengths for descriptions of mode choices */ { U32 max = MaxML; - size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, WKSP); + size_t const mostFrequent = HIST_countFast_wksp(count, &max, mlCodeTable, nbSeq, WKSP); /* cannot fail */ + assert(!HIST_isError(mostFrequent)); if (mostFrequent == nbSeq) { *op++ = *mlCodeTable; FSE_buildCTable_rle(CTable_MatchLength, (BYTE)max); From fc682263d0bcc44715c85e936fbef4bb8cdda9ab Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 13 Jun 2018 20:02:33 -0400 Subject: [PATCH 228/288] fixed g_debuglevel variable name in debug.h --- lib/common/debug.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/common/debug.h b/lib/common/debug.h index 1b81f229c..95a3fcabe 100644 --- a/lib/common/debug.h +++ b/lib/common/debug.h @@ -93,18 +93,18 @@ extern "C" { #if (DEBUGLEVEL>=2) # include -extern int g_debug_level; /* here, this variable is only declared, +extern int g_debuglevel; /* here, this variable is only declared, it actually lives in debug.c, and is shared by the whole process. It's typically used to enable very verbose levels on selective conditions (such as position in src) */ # define RAWLOG(l, ...) { \ - if (l<=g_debug_level) { \ + if (l<=g_debuglevel) { \ fprintf(stderr, __VA_ARGS__); \ } } # define DEBUGLOG(l, ...) { \ - if (l<=g_debug_level) { \ + if (l<=g_debuglevel) { \ fprintf(stderr, __FILE__ ": " __VA_ARGS__); \ fprintf(stderr, " \n"); \ } } From 7fee966f02ddd037f001096158281273a61c442d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 14 Jun 2018 10:22:24 -0400 Subject: [PATCH 229/288] fix dctx initialization within ZSTD_decompress in stack mode when ZSTD_HEAPMODE=0 (which is not default). Also : added an associated test (test-fuzzer-stackmode) run on travis CI fix #1186 --- .travis.yml | 5 +++++ lib/decompress/zstd_decompress.c | 5 +++-- tests/Makefile | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b01805bee..8c5eb70f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,14 +20,18 @@ matrix: - env: Cmd='make clang38install && CC=clang-3.8 make clean msan-fuzztest' - env: Cmd='make clang38install && CC=clang-3.8 make clean tsan-test-zstream' + - env: Cmd='make -C tests test-fuzzer-stackmode' + - env: Cmd='make valgrindinstall && make -C tests clean valgrindTest' - env: Cmd='make arminstall && make armfuzz' + # Following test is disabled, as there is a bug in Travis' ld # preventing aarch64 compilation to complete. # > collect2: error: ld terminated with signal 11 [Segmentation fault], core dumped # to be re-enabled in a few commit, as it's possible that a random code change circumvent the ld bug # - env: Cmd='make arminstall && make aarch64fuzz' + - env: Cmd='make ppcinstall && make ppcfuzz' - env: Cmd='make ppcinstall && make ppc64fuzz' - env: Cmd='make -j uasanregressiontest && make clean && make -j msanregressiontest' @@ -35,6 +39,7 @@ matrix: - env: Cmd='make lz4install && make -C tests test-lz4' - env: Cmd='bash tests/libzstd_partial_builds.sh' + # tag-specific test - if: tag =~ ^v[0-9]\.[0-9] env: Cmd='make -C tests checkTag && tests/checkTag $TRAVIS_BRANCH' diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 030b74839..f65d85662 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -192,6 +192,8 @@ static void ZSTD_initDCtx_internal(ZSTD_DCtx* dctx) dctx->inBuffSize = 0; dctx->outBuffSize = 0; dctx->streamStage = zdss_init; + dctx->legacyContext = NULL; + dctx->previousLegacyVersion = 0; dctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); } @@ -215,8 +217,6 @@ ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem) { ZSTD_DCtx* const dctx = (ZSTD_DCtx*)ZSTD_malloc(sizeof(*dctx), customMem); if (!dctx) return NULL; dctx->customMem = customMem; - dctx->legacyContext = NULL; - dctx->previousLegacyVersion = 0; ZSTD_initDCtx_internal(dctx); return dctx; } @@ -1996,6 +1996,7 @@ size_t ZSTD_decompress(void* dst, size_t dstCapacity, const void* src, size_t sr return regenSize; #else /* stack mode */ ZSTD_DCtx dctx; + ZSTD_initDCtx_internal(&dctx); return ZSTD_decompressDCtx(&dctx, dst, dstCapacity, src, srcSize); #endif } diff --git a/tests/Makefile b/tests/Makefile index c1482bc9c..792ee9efe 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -360,6 +360,9 @@ test-fullbench32: fullbench32 datagen test-fuzzer: fuzzer $(QEMU_SYS) ./fuzzer -v $(FUZZERTEST) $(FUZZER_FLAGS) +test-fuzzer-stackmode: MOREFLAGS += -DZSTD_HEAPMODE=0 +test-fuzzer-stackmode: test-fuzzer + test-fuzzer32: fuzzer32 $(QEMU_SYS) ./fuzzer32 -v $(FUZZERTEST) $(FUZZER_FLAGS) From 0c654d22c87a78fb2ff4c28341345a35714606b1 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 14 Jun 2018 14:53:36 -0400 Subject: [PATCH 230/288] Force Inline BtFindBestMatch --- lib/compress/zstd_lazy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index fba5dd679..078d20360 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -356,7 +356,7 @@ static size_t ZSTD_DUBT_findBestMatch ( /** ZSTD_BtFindBestMatch() : Tree updater, providing best match */ -static size_t ZSTD_BtFindBestMatch ( +FORCE_INLINE_TEMPLATE size_t ZSTD_BtFindBestMatch ( ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iLimit, size_t* offsetPtr, From a09af5eb6b6af43f319c63889a3ce65a92fb42a4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 14 Jun 2018 15:08:43 -0400 Subject: [PATCH 231/288] renamed all HUF_decompress*X2*() functions into *X1 to underline they generate one symbol per decoding operation. The new naming scheme will make it easier to introduce an *X3 variant. --- lib/common/huf.h | 36 +++---- lib/decompress/huf_decompress.c | 179 +++++++++++++++---------------- lib/decompress/zstd_decompress.c | 2 +- 3 files changed, 108 insertions(+), 109 deletions(-) diff --git a/lib/common/huf.h b/lib/common/huf.h index 1f46fda2b..fc19bccad 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -1,7 +1,7 @@ /* ****************************************************************** - Huffman coder, part of New Generation Entropy library - header file - Copyright (C) 2013-2016, Yann Collet. + huff0 huffman codec, + part of Finite State Entropy library + Copyright (C) 2013-present, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) @@ -163,7 +163,7 @@ HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, /* static allocation of HUF's DTable */ typedef U32 HUF_DTable; #define HUF_DTABLE_SIZE(maxTableLog) (1 + (1<<(maxTableLog))) -#define HUF_CREATE_STATIC_DTABLEX2(DTable, maxTableLog) \ +#define HUF_CREATE_STATIC_DTABLEX1(DTable, maxTableLog) \ HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = { ((U32)((maxTableLog)-1) * 0x01000001) } #define HUF_CREATE_STATIC_DTABLEX4(DTable, maxTableLog) \ HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = { ((U32)(maxTableLog) * 0x01000001) } @@ -172,14 +172,14 @@ typedef U32 HUF_DTable; /* **************************************** * Advanced decompression functions ******************************************/ -size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ +size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ size_t HUF_decompress4X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< decodes RLE and uncompressed */ size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< considers RLE and uncompressed as errors */ size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< considers RLE and uncompressed as errors */ -size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ -size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ +size_t HUF_decompress4X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ +size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ size_t HUF_decompress4X4_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ @@ -252,7 +252,7 @@ U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue); /* * HUF_decompress() does the following: - * 1. select the decompression algorithm (X2, X4) based on pre-computed heuristics + * 1. select the decompression algorithm (X1, X4) based on pre-computed heuristics * 2. build Huffman table from save, using HUF_readDTableX?() * 3. decode 1 or 4 segments in parallel using HUF_decompress?X?_usingDTable() */ @@ -260,13 +260,13 @@ U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue); /** HUF_selectDecoder() : * Tells which decoder is likely to decode faster, * based on a set of pre-computed metrics. - * @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 . + * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X4 . * Assumption : 0 < dstSize <= 128 KB */ U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize); /** * The minimum workspace size for the `workSpace` used in - * HUF_readDTableX2_wksp() and HUF_readDTableX4_wksp(). + * HUF_readDTableX1_wksp() and HUF_readDTableX4_wksp(). * * The space used depends on HUF_TABLELOG_MAX, ranging from ~1500 bytes when * HUF_TABLE_LOG_MAX=12 to ~1850 bytes when HUF_TABLE_LOG_MAX=15. @@ -277,13 +277,13 @@ U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize); #define HUF_DECOMPRESS_WORKSPACE_SIZE (2 << 10) #define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32)) -size_t HUF_readDTableX2 (HUF_DTable* DTable, const void* src, size_t srcSize); -size_t HUF_readDTableX2_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); +size_t HUF_readDTableX1 (HUF_DTable* DTable, const void* src, size_t srcSize); +size_t HUF_readDTableX1_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); size_t HUF_readDTableX4 (HUF_DTable* DTable, const void* src, size_t srcSize); size_t HUF_readDTableX4_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); -size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); +size_t HUF_decompress4X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); size_t HUF_decompress4X4_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); @@ -305,25 +305,25 @@ size_t HUF_compress1X_repeat(void* dst, size_t dstSize, void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */ HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2); -size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ +size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */ size_t HUF_decompress1X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); size_t HUF_decompress1X_DCtx_wksp (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); -size_t HUF_decompress1X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ -size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ +size_t HUF_decompress1X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ +size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ size_t HUF_decompress1X4_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); /**< automatic selection of sing or double symbol decoder, based on DTable */ -size_t HUF_decompress1X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); +size_t HUF_decompress1X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); size_t HUF_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); /* BMI2 variants. * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0. */ size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2); -size_t HUF_decompress1X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2); +size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2); size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2); size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2); diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index dc722e59b..e46bbbc1d 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -1,6 +1,7 @@ /* ****************************************************************** - Huffman decoder, part of New Generation Entropy library - Copyright (C) 2013-2016, Yann Collet. + huff0 huffman decoder, + part of Finite State Entropy library + Copyright (C) 2013-present, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) @@ -29,16 +30,15 @@ You can contact the author at : - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy - - Public forum : https://groups.google.com/forum/#!forum/lz4c ****************************************************************** */ /* ************************************************************** * Dependencies ****************************************************************/ #include /* memcpy, memset */ -#include "bitstream.h" /* BIT_* */ #include "compiler.h" -#include "fse.h" /* header compression */ +#include "bitstream.h" /* BIT_* */ +#include "fse.h" /* to compress headers */ #define HUF_STATIC_LINKING_ONLY #include "huf.h" #include "error_private.h" @@ -48,7 +48,6 @@ * Error Management ****************************************************************/ #define HUF_isError ERR_isError -#define HUF_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c) /* use only *after* variable declarations */ #define CHECK_F(f) { size_t const err_ = (f); if (HUF_isError(err_)) return err_; } @@ -75,15 +74,15 @@ static DTableDesc HUF_getDTableDesc(const HUF_DTable* table) /*-***************************/ /* single-symbol decoding */ /*-***************************/ -typedef struct { BYTE byte; BYTE nbBits; } HUF_DEltX2; /* single-symbol decoding */ +typedef struct { BYTE byte; BYTE nbBits; } HUF_DEltX1; /* single-symbol decoding */ -size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize) +size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize) { U32 tableLog = 0; U32 nbSymbols = 0; size_t iSize; void* const dtPtr = DTable + 1; - HUF_DEltX2* const dt = (HUF_DEltX2*)dtPtr; + HUF_DEltX1* const dt = (HUF_DEltX1*)dtPtr; U32* rankVal; BYTE* huffWeight; @@ -96,7 +95,7 @@ size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge); - HUF_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable)); + DEBUG_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable)); /* memset(huffWeight, 0, sizeof(huffWeight)); */ /* is not necessary, even though some analyzer complain ... */ iSize = HUF_readStats(huffWeight, HUF_SYMBOLVALUE_MAX + 1, rankVal, &nbSymbols, &tableLog, src, srcSize); @@ -124,7 +123,7 @@ size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize U32 const w = huffWeight[n]; U32 const length = (1 << w) >> 1; U32 u; - HUF_DEltX2 D; + HUF_DEltX1 D; D.byte = (BYTE)n; D.nbBits = (BYTE)(tableLog + 1 - w); for (u = rankVal[w]; u < rankVal[w] + length; u++) dt[u] = D; @@ -134,17 +133,17 @@ size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize return iSize; } -size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize) +size_t HUF_readDTableX1(HUF_DTable* DTable, const void* src, size_t srcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_readDTableX2_wksp(DTable, src, srcSize, + return HUF_readDTableX1_wksp(DTable, src, srcSize, workSpace, sizeof(workSpace)); } typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX4; /* double-symbols decoding */ FORCE_INLINE_TEMPLATE BYTE -HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, const U32 dtLog) +HUF_decodeSymbolX1(BIT_DStream_t* Dstream, const HUF_DEltX1* dt, const U32 dtLog) { size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ BYTE const c = dt[val].byte; @@ -152,44 +151,44 @@ HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, const U32 dtLog return c; } -#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) \ - *ptr++ = HUF_decodeSymbolX2(DStreamPtr, dt, dtLog) +#define HUF_DECODE_SYMBOLX1_0(ptr, DStreamPtr) \ + *ptr++ = HUF_decodeSymbolX1(DStreamPtr, dt, dtLog) -#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \ +#define HUF_DECODE_SYMBOLX1_1(ptr, DStreamPtr) \ if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ - HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) + HUF_DECODE_SYMBOLX1_0(ptr, DStreamPtr) -#define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \ +#define HUF_DECODE_SYMBOLX1_2(ptr, DStreamPtr) \ if (MEM_64bits()) \ - HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) + HUF_DECODE_SYMBOLX1_0(ptr, DStreamPtr) HINT_INLINE size_t -HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog) +HUF_decodeStreamX1(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX1* const dt, const U32 dtLog) { BYTE* const pStart = p; /* up to 4 symbols at a time */ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-3)) { - HUF_DECODE_SYMBOLX2_2(p, bitDPtr); - HUF_DECODE_SYMBOLX2_1(p, bitDPtr); - HUF_DECODE_SYMBOLX2_2(p, bitDPtr); - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + HUF_DECODE_SYMBOLX1_2(p, bitDPtr); + HUF_DECODE_SYMBOLX1_1(p, bitDPtr); + HUF_DECODE_SYMBOLX1_2(p, bitDPtr); + HUF_DECODE_SYMBOLX1_0(p, bitDPtr); } /* [0-3] symbols remaining */ if (MEM_32bits()) while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd)) - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + HUF_DECODE_SYMBOLX1_0(p, bitDPtr); /* no more data to retrieve from bitstream, no need to reload */ while (p < pEnd) - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + HUF_DECODE_SYMBOLX1_0(p, bitDPtr); return pEnd-pStart; } FORCE_INLINE_TEMPLATE size_t -HUF_decompress1X2_usingDTable_internal_body( +HUF_decompress1X1_usingDTable_internal_body( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) @@ -197,14 +196,14 @@ HUF_decompress1X2_usingDTable_internal_body( BYTE* op = (BYTE*)dst; BYTE* const oend = op + dstSize; const void* dtPtr = DTable + 1; - const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; + const HUF_DEltX1* const dt = (const HUF_DEltX1*)dtPtr; BIT_DStream_t bitD; DTableDesc const dtd = HUF_getDTableDesc(DTable); U32 const dtLog = dtd.tableLog; CHECK_F( BIT_initDStream(&bitD, cSrc, cSrcSize) ); - HUF_decodeStreamX2(op, &bitD, oend, dt, dtLog); + HUF_decodeStreamX1(op, &bitD, oend, dt, dtLog); if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected); @@ -212,7 +211,7 @@ HUF_decompress1X2_usingDTable_internal_body( } FORCE_INLINE_TEMPLATE size_t -HUF_decompress4X2_usingDTable_internal_body( +HUF_decompress4X1_usingDTable_internal_body( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) @@ -224,7 +223,7 @@ HUF_decompress4X2_usingDTable_internal_body( BYTE* const ostart = (BYTE*) dst; BYTE* const oend = ostart + dstSize; const void* const dtPtr = DTable + 1; - const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; + const HUF_DEltX1* const dt = (const HUF_DEltX1*)dtPtr; /* Init */ BIT_DStream_t bitD1; @@ -260,22 +259,22 @@ HUF_decompress4X2_usingDTable_internal_body( /* up to 16 symbols per loop (4 symbols per stream) in 64-bit mode */ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); while ( (endSignal==BIT_DStream_unfinished) && (op4<(oend-3)) ) { - HUF_DECODE_SYMBOLX2_2(op1, &bitD1); - HUF_DECODE_SYMBOLX2_2(op2, &bitD2); - HUF_DECODE_SYMBOLX2_2(op3, &bitD3); - HUF_DECODE_SYMBOLX2_2(op4, &bitD4); - HUF_DECODE_SYMBOLX2_1(op1, &bitD1); - HUF_DECODE_SYMBOLX2_1(op2, &bitD2); - HUF_DECODE_SYMBOLX2_1(op3, &bitD3); - HUF_DECODE_SYMBOLX2_1(op4, &bitD4); - HUF_DECODE_SYMBOLX2_2(op1, &bitD1); - HUF_DECODE_SYMBOLX2_2(op2, &bitD2); - HUF_DECODE_SYMBOLX2_2(op3, &bitD3); - HUF_DECODE_SYMBOLX2_2(op4, &bitD4); - HUF_DECODE_SYMBOLX2_0(op1, &bitD1); - HUF_DECODE_SYMBOLX2_0(op2, &bitD2); - HUF_DECODE_SYMBOLX2_0(op3, &bitD3); - HUF_DECODE_SYMBOLX2_0(op4, &bitD4); + HUF_DECODE_SYMBOLX1_2(op1, &bitD1); + HUF_DECODE_SYMBOLX1_2(op2, &bitD2); + HUF_DECODE_SYMBOLX1_2(op3, &bitD3); + HUF_DECODE_SYMBOLX1_2(op4, &bitD4); + HUF_DECODE_SYMBOLX1_1(op1, &bitD1); + HUF_DECODE_SYMBOLX1_1(op2, &bitD2); + HUF_DECODE_SYMBOLX1_1(op3, &bitD3); + HUF_DECODE_SYMBOLX1_1(op4, &bitD4); + HUF_DECODE_SYMBOLX1_2(op1, &bitD1); + HUF_DECODE_SYMBOLX1_2(op2, &bitD2); + HUF_DECODE_SYMBOLX1_2(op3, &bitD3); + HUF_DECODE_SYMBOLX1_2(op4, &bitD4); + HUF_DECODE_SYMBOLX1_0(op1, &bitD1); + HUF_DECODE_SYMBOLX1_0(op2, &bitD2); + HUF_DECODE_SYMBOLX1_0(op3, &bitD3); + HUF_DECODE_SYMBOLX1_0(op4, &bitD4); BIT_reloadDStream(&bitD1); BIT_reloadDStream(&bitD2); BIT_reloadDStream(&bitD3); @@ -291,10 +290,10 @@ HUF_decompress4X2_usingDTable_internal_body( /* note : op4 supposed already verified within main loop */ /* finish bitStreams one by one */ - HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog); - HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog); - HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog); - HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog); + HUF_decodeStreamX1(op1, &bitD1, opStart2, dt, dtLog); + HUF_decodeStreamX1(op2, &bitD2, opStart3, dt, dtLog); + HUF_decodeStreamX1(op3, &bitD3, opStart4, dt, dtLog); + HUF_decodeStreamX1(op4, &bitD4, oend, dt, dtLog); /* check */ { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); @@ -532,96 +531,96 @@ typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize, #endif -X(HUF_decompress1X2_usingDTable_internal) -X(HUF_decompress4X2_usingDTable_internal) +X(HUF_decompress1X1_usingDTable_internal) +X(HUF_decompress4X1_usingDTable_internal) X(HUF_decompress1X4_usingDTable_internal) X(HUF_decompress4X4_usingDTable_internal) #undef X -size_t HUF_decompress1X2_usingDTable( +size_t HUF_decompress1X1_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 0) return ERROR(GENERIC); - return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); + return HUF_decompress1X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } -size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, +size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize) { const BYTE* ip = (const BYTE*) cSrc; - size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize); + size_t const hSize = HUF_readDTableX1_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); + return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); } -size_t HUF_decompress1X2_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, +size_t HUF_decompress1X1_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress1X2_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, + return HUF_decompress1X1_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, workSpace, sizeof(workSpace)); } -size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) +size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { - HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX); - return HUF_decompress1X2_DCtx (DTable, dst, dstSize, cSrc, cSrcSize); + HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); + return HUF_decompress1X1_DCtx (DTable, dst, dstSize, cSrc, cSrcSize); } -size_t HUF_decompress4X2_usingDTable( +size_t HUF_decompress4X1_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 0) return ERROR(GENERIC); - return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); + return HUF_decompress4X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } -static size_t HUF_decompress4X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, +static size_t HUF_decompress4X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) { const BYTE* ip = (const BYTE*) cSrc; - size_t const hSize = HUF_readDTableX2_wksp (dctx, cSrc, cSrcSize, + size_t const hSize = HUF_readDTableX1_wksp (dctx, cSrc, cSrcSize, workSpace, wkspSize); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); + return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); } -size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, +size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize) { - return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, 0); + return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, 0); } -size_t HUF_decompress4X2_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) +size_t HUF_decompress4X1_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, + return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, sizeof(workSpace)); } -size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) +size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { - HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX); - return HUF_decompress4X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); + HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); + return HUF_decompress4X1_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); } @@ -752,7 +751,7 @@ size_t HUF_readDTableX4_wksp(HUF_DTable* DTable, const void* src, rankStart = rankStart0 + 1; memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1)); - HUF_STATIC_ASSERT(sizeof(HUF_DEltX4) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */ + DEBUG_STATIC_ASSERT(sizeof(HUF_DEltX4) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */ if (maxTableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); /* memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */ @@ -922,7 +921,7 @@ size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, { DTableDesc const dtd = HUF_getDTableDesc(DTable); return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : - HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); + HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, @@ -931,7 +930,7 @@ size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, { DTableDesc const dtd = HUF_getDTableDesc(DTable); return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : - HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); + HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } @@ -960,7 +959,7 @@ static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, qu /** HUF_selectDecoder() : * Tells which decoder is likely to decode faster, * based on a set of pre-computed metrics. - * @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 . + * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X4 . * Assumption : 0 < dstSize <= 128 KB */ U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize) { @@ -980,7 +979,7 @@ typedef size_t (*decompressionAlgo)(void* dst, size_t dstSize, const void* cSrc, size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { - static const decompressionAlgo decompress[2] = { HUF_decompress4X2, HUF_decompress4X4 }; + static const decompressionAlgo decompress[2] = { HUF_decompress4X1, HUF_decompress4X4 }; /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); @@ -1003,7 +1002,7 @@ size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); return algoNb ? HUF_decompress4X4_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) : - HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) ; + HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) ; } } @@ -1026,7 +1025,7 @@ size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); return algoNb ? HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize): - HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); + HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); } } @@ -1043,7 +1042,7 @@ size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); return algoNb ? HUF_decompress1X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize): - HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, + HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); } } @@ -1061,26 +1060,26 @@ size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const voi { DTableDesc const dtd = HUF_getDTableDesc(DTable); return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : - HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); } -size_t HUF_decompress1X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) +size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) { const BYTE* ip = (const BYTE*) cSrc; - size_t const hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize); + size_t const hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); + return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); } size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2) { DTableDesc const dtd = HUF_getDTableDesc(DTable); return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : - HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); } size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) @@ -1091,6 +1090,6 @@ size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t ds { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); return algoNb ? HUF_decompress4X4_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2) : - HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); + HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); } } diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 030b74839..1f50b22a3 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -595,7 +595,7 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, HUF_decompress1X_usingDTable_bmi2(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr, dctx->bmi2) : HUF_decompress4X_usingDTable_bmi2(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr, dctx->bmi2) ) : ( singleStream ? - HUF_decompress1X2_DCtx_wksp_bmi2(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, + HUF_decompress1X1_DCtx_wksp_bmi2(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->entropy.workspace, sizeof(dctx->entropy.workspace), dctx->bmi2) : HUF_decompress4X_hufOnly_wksp_bmi2(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->entropy.workspace, sizeof(dctx->entropy.workspace), dctx->bmi2)))) From 1adf84ccb723cb27df8195a872b08ccc3b9cee38 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 14 Jun 2018 15:17:03 -0400 Subject: [PATCH 232/288] renamed all HUF_decompress*X4*() functions into *X2 to underline they generate up to 2 symbols per decoding, in preparation for a future *X3 variant. --- lib/common/huf.h | 28 ++--- lib/decompress/huf_decompress.c | 178 +++++++++++++++---------------- lib/decompress/zstd_decompress.c | 2 +- 3 files changed, 104 insertions(+), 104 deletions(-) diff --git a/lib/common/huf.h b/lib/common/huf.h index fc19bccad..de9464111 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -165,7 +165,7 @@ typedef U32 HUF_DTable; #define HUF_DTABLE_SIZE(maxTableLog) (1 + (1<<(maxTableLog))) #define HUF_CREATE_STATIC_DTABLEX1(DTable, maxTableLog) \ HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = { ((U32)((maxTableLog)-1) * 0x01000001) } -#define HUF_CREATE_STATIC_DTABLEX4(DTable, maxTableLog) \ +#define HUF_CREATE_STATIC_DTABLEX2(DTable, maxTableLog) \ HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = { ((U32)(maxTableLog) * 0x01000001) } @@ -173,15 +173,15 @@ typedef U32 HUF_DTable; * Advanced decompression functions ******************************************/ size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ -size_t HUF_decompress4X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ +size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< decodes RLE and uncompressed */ size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< considers RLE and uncompressed as errors */ size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< considers RLE and uncompressed as errors */ size_t HUF_decompress4X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ -size_t HUF_decompress4X4_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ -size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ +size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ +size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ /* **************************************** @@ -252,7 +252,7 @@ U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue); /* * HUF_decompress() does the following: - * 1. select the decompression algorithm (X1, X4) based on pre-computed heuristics + * 1. select the decompression algorithm (X1, X2) based on pre-computed heuristics * 2. build Huffman table from save, using HUF_readDTableX?() * 3. decode 1 or 4 segments in parallel using HUF_decompress?X?_usingDTable() */ @@ -260,13 +260,13 @@ U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue); /** HUF_selectDecoder() : * Tells which decoder is likely to decode faster, * based on a set of pre-computed metrics. - * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X4 . + * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X2 . * Assumption : 0 < dstSize <= 128 KB */ U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize); /** * The minimum workspace size for the `workSpace` used in - * HUF_readDTableX1_wksp() and HUF_readDTableX4_wksp(). + * HUF_readDTableX1_wksp() and HUF_readDTableX2_wksp(). * * The space used depends on HUF_TABLELOG_MAX, ranging from ~1500 bytes when * HUF_TABLE_LOG_MAX=12 to ~1850 bytes when HUF_TABLE_LOG_MAX=15. @@ -279,12 +279,12 @@ U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize); size_t HUF_readDTableX1 (HUF_DTable* DTable, const void* src, size_t srcSize); size_t HUF_readDTableX1_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); -size_t HUF_readDTableX4 (HUF_DTable* DTable, const void* src, size_t srcSize); -size_t HUF_readDTableX4_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); +size_t HUF_readDTableX2 (HUF_DTable* DTable, const void* src, size_t srcSize); +size_t HUF_readDTableX2_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); size_t HUF_decompress4X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); -size_t HUF_decompress4X4_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); +size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); /* ====================== */ @@ -306,18 +306,18 @@ size_t HUF_compress1X_repeat(void* dst, size_t dstSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2); size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ -size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */ +size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */ size_t HUF_decompress1X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); size_t HUF_decompress1X_DCtx_wksp (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); size_t HUF_decompress1X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ -size_t HUF_decompress1X4_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ -size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ +size_t HUF_decompress1X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ +size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); /**< automatic selection of sing or double symbol decoder, based on DTable */ size_t HUF_decompress1X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); -size_t HUF_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); +size_t HUF_decompress1X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); /* BMI2 variants. * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0. diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index e46bbbc1d..43d93c067 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -140,7 +140,7 @@ size_t HUF_readDTableX1(HUF_DTable* DTable, const void* src, size_t srcSize) workSpace, sizeof(workSpace)); } -typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX4; /* double-symbols decoding */ +typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX2; /* double-symbols decoding */ FORCE_INLINE_TEMPLATE BYTE HUF_decodeSymbolX1(BIT_DStream_t* Dstream, const HUF_DEltX1* dt, const U32 dtLog) @@ -306,7 +306,7 @@ HUF_decompress4X1_usingDTable_internal_body( FORCE_INLINE_TEMPLATE U32 -HUF_decodeSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) +HUF_decodeSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog) { size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ memcpy(op, dt+val, 2); @@ -315,7 +315,7 @@ HUF_decodeSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const } FORCE_INLINE_TEMPLATE U32 -HUF_decodeLastSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, const U32 dtLog) +HUF_decodeLastSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog) { size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ memcpy(op, dt+val, 1); @@ -330,46 +330,46 @@ HUF_decodeLastSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DEltX4* dt, c return 1; } -#define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) \ - ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) +#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) \ + ptr += HUF_decodeSymbolX2(ptr, DStreamPtr, dt, dtLog) -#define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \ +#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \ if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \ - ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) + ptr += HUF_decodeSymbolX2(ptr, DStreamPtr, dt, dtLog) -#define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \ +#define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \ if (MEM_64bits()) \ - ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) + ptr += HUF_decodeSymbolX2(ptr, DStreamPtr, dt, dtLog) HINT_INLINE size_t -HUF_decodeStreamX4(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, - const HUF_DEltX4* const dt, const U32 dtLog) +HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, + const HUF_DEltX2* const dt, const U32 dtLog) { BYTE* const pStart = p; /* up to 8 symbols at a time */ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) { - HUF_DECODE_SYMBOLX4_2(p, bitDPtr); - HUF_DECODE_SYMBOLX4_1(p, bitDPtr); - HUF_DECODE_SYMBOLX4_2(p, bitDPtr); - HUF_DECODE_SYMBOLX4_0(p, bitDPtr); + HUF_DECODE_SYMBOLX2_2(p, bitDPtr); + HUF_DECODE_SYMBOLX2_1(p, bitDPtr); + HUF_DECODE_SYMBOLX2_2(p, bitDPtr); + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); } /* closer to end : up to 2 symbols at a time */ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2)) - HUF_DECODE_SYMBOLX4_0(p, bitDPtr); + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); while (p <= pEnd-2) - HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ if (p < pEnd) - p += HUF_decodeLastSymbolX4(p, bitDPtr, dt, dtLog); + p += HUF_decodeLastSymbolX2(p, bitDPtr, dt, dtLog); return p-pStart; } FORCE_INLINE_TEMPLATE size_t -HUF_decompress1X4_usingDTable_internal_body( +HUF_decompress1X2_usingDTable_internal_body( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) @@ -383,9 +383,9 @@ HUF_decompress1X4_usingDTable_internal_body( { BYTE* const ostart = (BYTE*) dst; BYTE* const oend = ostart + dstSize; const void* const dtPtr = DTable+1; /* force compiler to not use strict-aliasing */ - const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr; + const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; DTableDesc const dtd = HUF_getDTableDesc(DTable); - HUF_decodeStreamX4(ostart, &bitD, oend, dt, dtd.tableLog); + HUF_decodeStreamX2(ostart, &bitD, oend, dt, dtd.tableLog); } /* check */ @@ -397,7 +397,7 @@ HUF_decompress1X4_usingDTable_internal_body( FORCE_INLINE_TEMPLATE size_t -HUF_decompress4X4_usingDTable_internal_body( +HUF_decompress4X2_usingDTable_internal_body( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) @@ -408,7 +408,7 @@ HUF_decompress4X4_usingDTable_internal_body( BYTE* const ostart = (BYTE*) dst; BYTE* const oend = ostart + dstSize; const void* const dtPtr = DTable+1; - const HUF_DEltX4* const dt = (const HUF_DEltX4*)dtPtr; + const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; /* Init */ BIT_DStream_t bitD1; @@ -444,22 +444,22 @@ HUF_decompress4X4_usingDTable_internal_body( /* 16-32 symbols per loop (4-8 symbols per stream) */ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); for ( ; (endSignal==BIT_DStream_unfinished) & (op4<(oend-(sizeof(bitD4.bitContainer)-1))) ; ) { - HUF_DECODE_SYMBOLX4_2(op1, &bitD1); - HUF_DECODE_SYMBOLX4_2(op2, &bitD2); - HUF_DECODE_SYMBOLX4_2(op3, &bitD3); - HUF_DECODE_SYMBOLX4_2(op4, &bitD4); - HUF_DECODE_SYMBOLX4_1(op1, &bitD1); - HUF_DECODE_SYMBOLX4_1(op2, &bitD2); - HUF_DECODE_SYMBOLX4_1(op3, &bitD3); - HUF_DECODE_SYMBOLX4_1(op4, &bitD4); - HUF_DECODE_SYMBOLX4_2(op1, &bitD1); - HUF_DECODE_SYMBOLX4_2(op2, &bitD2); - HUF_DECODE_SYMBOLX4_2(op3, &bitD3); - HUF_DECODE_SYMBOLX4_2(op4, &bitD4); - HUF_DECODE_SYMBOLX4_0(op1, &bitD1); - HUF_DECODE_SYMBOLX4_0(op2, &bitD2); - HUF_DECODE_SYMBOLX4_0(op3, &bitD3); - HUF_DECODE_SYMBOLX4_0(op4, &bitD4); + HUF_DECODE_SYMBOLX2_2(op1, &bitD1); + HUF_DECODE_SYMBOLX2_2(op2, &bitD2); + HUF_DECODE_SYMBOLX2_2(op3, &bitD3); + HUF_DECODE_SYMBOLX2_2(op4, &bitD4); + HUF_DECODE_SYMBOLX2_1(op1, &bitD1); + HUF_DECODE_SYMBOLX2_1(op2, &bitD2); + HUF_DECODE_SYMBOLX2_1(op3, &bitD3); + HUF_DECODE_SYMBOLX2_1(op4, &bitD4); + HUF_DECODE_SYMBOLX2_2(op1, &bitD1); + HUF_DECODE_SYMBOLX2_2(op2, &bitD2); + HUF_DECODE_SYMBOLX2_2(op3, &bitD3); + HUF_DECODE_SYMBOLX2_2(op4, &bitD4); + HUF_DECODE_SYMBOLX2_0(op1, &bitD1); + HUF_DECODE_SYMBOLX2_0(op2, &bitD2); + HUF_DECODE_SYMBOLX2_0(op3, &bitD3); + HUF_DECODE_SYMBOLX2_0(op4, &bitD4); endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); } @@ -471,10 +471,10 @@ HUF_decompress4X4_usingDTable_internal_body( /* note : op4 already verified within main loop */ /* finish bitStreams one by one */ - HUF_decodeStreamX4(op1, &bitD1, opStart2, dt, dtLog); - HUF_decodeStreamX4(op2, &bitD2, opStart3, dt, dtLog); - HUF_decodeStreamX4(op3, &bitD3, opStart4, dt, dtLog); - HUF_decodeStreamX4(op4, &bitD4, oend, dt, dtLog); + HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog); + HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog); + HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog); + HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog); /* check */ { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); @@ -533,8 +533,8 @@ typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize, X(HUF_decompress1X1_usingDTable_internal) X(HUF_decompress4X1_usingDTable_internal) -X(HUF_decompress1X4_usingDTable_internal) -X(HUF_decompress4X4_usingDTable_internal) +X(HUF_decompress1X2_usingDTable_internal) +X(HUF_decompress4X2_usingDTable_internal) #undef X @@ -629,14 +629,14 @@ size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cS /* *************************/ typedef struct { BYTE symbol; BYTE weight; } sortedSymbol_t; -/* HUF_fillDTableX4Level2() : +/* HUF_fillDTableX2Level2() : * `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */ -static void HUF_fillDTableX4Level2(HUF_DEltX4* DTable, U32 sizeLog, const U32 consumed, +static void HUF_fillDTableX2Level2(HUF_DEltX2* DTable, U32 sizeLog, const U32 consumed, const U32* rankValOrigin, const int minWeight, const sortedSymbol_t* sortedSymbols, const U32 sortedListSize, U32 nbBitsBaseline, U16 baseSeq) { - HUF_DEltX4 DElt; + HUF_DEltX2 DElt; U32 rankVal[HUF_TABLELOG_MAX + 1]; /* get pre-calculated rankVal */ @@ -674,7 +674,7 @@ static void HUF_fillDTableX4Level2(HUF_DEltX4* DTable, U32 sizeLog, const U32 co typedef U32 rankValCol_t[HUF_TABLELOG_MAX + 1]; typedef rankValCol_t rankVal_t[HUF_TABLELOG_MAX]; -static void HUF_fillDTableX4(HUF_DEltX4* DTable, const U32 targetLog, +static void HUF_fillDTableX2(HUF_DEltX2* DTable, const U32 targetLog, const sortedSymbol_t* sortedList, const U32 sortedListSize, const U32* rankStart, rankVal_t rankValOrigin, const U32 maxWeight, const U32 nbBitsBaseline) @@ -699,12 +699,12 @@ static void HUF_fillDTableX4(HUF_DEltX4* DTable, const U32 targetLog, int minWeight = nbBits + scaleLog; if (minWeight < 1) minWeight = 1; sortedRank = rankStart[minWeight]; - HUF_fillDTableX4Level2(DTable+start, targetLog-nbBits, nbBits, + HUF_fillDTableX2Level2(DTable+start, targetLog-nbBits, nbBits, rankValOrigin[nbBits], minWeight, sortedList+sortedRank, sortedListSize-sortedRank, nbBitsBaseline, symbol); } else { - HUF_DEltX4 DElt; + HUF_DEltX2 DElt; MEM_writeLE16(&(DElt.sequence), symbol); DElt.nbBits = (BYTE)(nbBits); DElt.length = 1; @@ -716,7 +716,7 @@ static void HUF_fillDTableX4(HUF_DEltX4* DTable, const U32 targetLog, } } -size_t HUF_readDTableX4_wksp(HUF_DTable* DTable, const void* src, +size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize) { @@ -725,7 +725,7 @@ size_t HUF_readDTableX4_wksp(HUF_DTable* DTable, const void* src, U32 const maxTableLog = dtd.maxTableLog; size_t iSize; void* dtPtr = DTable+1; /* force compiler to avoid strict-aliasing */ - HUF_DEltX4* const dt = (HUF_DEltX4*)dtPtr; + HUF_DEltX2* const dt = (HUF_DEltX2*)dtPtr; U32 *rankStart; rankValCol_t* rankVal; @@ -751,7 +751,7 @@ size_t HUF_readDTableX4_wksp(HUF_DTable* DTable, const void* src, rankStart = rankStart0 + 1; memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1)); - DEBUG_STATIC_ASSERT(sizeof(HUF_DEltX4) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */ + DEBUG_STATIC_ASSERT(sizeof(HUF_DEltX2) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */ if (maxTableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); /* memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */ @@ -805,7 +805,7 @@ size_t HUF_readDTableX4_wksp(HUF_DTable* DTable, const void* src, rankValPtr[w] = rankVal0[w] >> consumed; } } } } - HUF_fillDTableX4(dt, maxTableLog, + HUF_fillDTableX2(dt, maxTableLog, sortedSymbol, sizeOfSort, rankStart0, rankVal, maxW, tableLog+1); @@ -816,98 +816,98 @@ size_t HUF_readDTableX4_wksp(HUF_DTable* DTable, const void* src, return iSize; } -size_t HUF_readDTableX4(HUF_DTable* DTable, const void* src, size_t srcSize) +size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_readDTableX4_wksp(DTable, src, srcSize, + return HUF_readDTableX2_wksp(DTable, src, srcSize, workSpace, sizeof(workSpace)); } -size_t HUF_decompress1X4_usingDTable( +size_t HUF_decompress1X2_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 1) return ERROR(GENERIC); - return HUF_decompress1X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); + return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } -size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, +size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize) { const BYTE* ip = (const BYTE*) cSrc; - size_t const hSize = HUF_readDTableX4_wksp(DCtx, cSrc, cSrcSize, + size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress1X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); + return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); } -size_t HUF_decompress1X4_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, +size_t HUF_decompress1X2_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress1X4_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, + return HUF_decompress1X2_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, workSpace, sizeof(workSpace)); } -size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) +size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { - HUF_CREATE_STATIC_DTABLEX4(DTable, HUF_TABLELOG_MAX); - return HUF_decompress1X4_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); + HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX); + return HUF_decompress1X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); } -size_t HUF_decompress4X4_usingDTable( +size_t HUF_decompress4X2_usingDTable( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable) { DTableDesc dtd = HUF_getDTableDesc(DTable); if (dtd.tableType != 1) return ERROR(GENERIC); - return HUF_decompress4X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); + return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } -static size_t HUF_decompress4X4_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, +static size_t HUF_decompress4X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) { const BYTE* ip = (const BYTE*) cSrc; - size_t hSize = HUF_readDTableX4_wksp(dctx, cSrc, cSrcSize, + size_t hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress4X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); + return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); } -size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, +size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize) { - return HUF_decompress4X4_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, /* bmi2 */ 0); + return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, /* bmi2 */ 0); } -size_t HUF_decompress4X4_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, +size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, + return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, sizeof(workSpace)); } -size_t HUF_decompress4X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) +size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { - HUF_CREATE_STATIC_DTABLEX4(DTable, HUF_TABLELOG_MAX); - return HUF_decompress4X4_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); + HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX); + return HUF_decompress4X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); } @@ -920,7 +920,7 @@ size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const HUF_DTable* DTable) { DTableDesc const dtd = HUF_getDTableDesc(DTable); - return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : + return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } @@ -929,7 +929,7 @@ size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const HUF_DTable* DTable) { DTableDesc const dtd = HUF_getDTableDesc(DTable); - return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : + return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); } @@ -959,7 +959,7 @@ static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, qu /** HUF_selectDecoder() : * Tells which decoder is likely to decode faster, * based on a set of pre-computed metrics. - * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X4 . + * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X2 . * Assumption : 0 < dstSize <= 128 KB */ U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize) { @@ -979,7 +979,7 @@ typedef size_t (*decompressionAlgo)(void* dst, size_t dstSize, const void* cSrc, size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) { - static const decompressionAlgo decompress[2] = { HUF_decompress4X1, HUF_decompress4X4 }; + static const decompressionAlgo decompress[2] = { HUF_decompress4X1, HUF_decompress4X2 }; /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); @@ -1001,7 +1001,7 @@ size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const if (cSrcSize == 1) { memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */ { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); - return algoNb ? HUF_decompress4X4_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) : + return algoNb ? HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) : HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) ; } } @@ -1024,7 +1024,7 @@ size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, 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): + return algoNb ? HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize): HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); } } @@ -1040,7 +1040,7 @@ size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, if (cSrcSize == 1) { memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */ { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); - return algoNb ? HUF_decompress1X4_DCtx_wksp(dctx, dst, dstSize, cSrc, + return algoNb ? HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize): HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); @@ -1059,7 +1059,7 @@ size_t HUF_decompress1X_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2) { DTableDesc const dtd = HUF_getDTableDesc(DTable); - return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : + return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); } @@ -1078,7 +1078,7 @@ size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstS size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2) { DTableDesc const dtd = HUF_getDTableDesc(DTable); - return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : + return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); } @@ -1089,7 +1089,7 @@ size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t ds if (cSrcSize == 0) return ERROR(corruption_detected); { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); - return algoNb ? HUF_decompress4X4_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2) : + return algoNb ? HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2) : HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); } } diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 1f50b22a3..3f41a43d5 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2193,7 +2193,7 @@ static size_t ZSTD_loadEntropy(ZSTD_entropyDTables_t* entropy, const void* const dictPtr += 8; /* skip header = magic + dictID */ - { size_t const hSize = HUF_readDTableX4_wksp( + { size_t const hSize = HUF_readDTableX2_wksp( entropy->hufTable, dictPtr, dictEnd - dictPtr, entropy->workspace, sizeof(entropy->workspace)); if (HUF_isError(hSize)) return ERROR(dictionary_corrupted); From bf30b9caf4d8afa907fd1b3424475fccbd48a915 Mon Sep 17 00:00:00 2001 From: Ryan Schmidt Date: Thu, 14 Jun 2018 15:05:33 -0500 Subject: [PATCH 233/288] Add CXXFLAGS to ALL_LDFLAGS (#1178) pzstd requires C++11, which older C++ standard libraries like libstdc++ as used on OS X 10.8 and earlier don't support. The user might address this by setting "CXXFLAGS=-stdlib=libc++". This flag must be used both at compile time and at link time. Asking the user to also put the flag in LDFLAGS is undesirable because then the flag would also be used when linking C code, which would be inappropriate. --- contrib/pzstd/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile index e15795f72..14b932297 100644 --- a/contrib/pzstd/Makefile +++ b/contrib/pzstd/Makefile @@ -42,7 +42,7 @@ PZSTD_LDFLAGS = EXTRA_FLAGS = ALL_CFLAGS = $(EXTRA_FLAGS) $(CPPFLAGS) $(PZSTD_CPPFLAGS) $(CFLAGS) $(PZSTD_CFLAGS) ALL_CXXFLAGS = $(EXTRA_FLAGS) $(CPPFLAGS) $(PZSTD_CPPFLAGS) $(CXXFLAGS) $(PZSTD_CXXFLAGS) -ALL_LDFLAGS = $(EXTRA_FLAGS) $(LDFLAGS) $(PZSTD_LDFLAGS) +ALL_LDFLAGS = $(EXTRA_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(PZSTD_LDFLAGS) # gtest libraries need to go before "-lpthread" because they depend on it. From 78e5e2887ec6ee98b557070092ab46d160a7e875 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 14 Jun 2018 15:51:31 -0400 Subject: [PATCH 234/288] Visual Studio project blind fix --- build/VS2008/fullbench/fullbench.vcproj | 4 ++++ build/VS2008/fuzzer/fuzzer.vcproj | 4 ++++ build/VS2008/zstd/zstd.vcproj | 4 ++++ build/VS2008/zstdlib/zstdlib.vcproj | 4 ++++ build/VS2010/fullbench/fullbench.vcxproj | 1 + build/VS2010/fuzzer/fuzzer.vcxproj | 1 + build/VS2010/libzstd-dll/libzstd-dll.vcxproj | 1 + build/VS2010/libzstd/libzstd.vcxproj | 1 + build/VS2010/zstd/zstd.vcxproj | 1 + 9 files changed, 21 insertions(+) diff --git a/build/VS2008/fullbench/fullbench.vcproj b/build/VS2008/fullbench/fullbench.vcproj index 715ea2579..2ce7f74e7 100644 --- a/build/VS2008/fullbench/fullbench.vcproj +++ b/build/VS2008/fullbench/fullbench.vcproj @@ -332,6 +332,10 @@ RelativePath="..\..\..\lib\common\entropy_common.c" > + + diff --git a/build/VS2008/fuzzer/fuzzer.vcproj b/build/VS2008/fuzzer/fuzzer.vcproj index 1421619a1..f6ea1f855 100644 --- a/build/VS2008/fuzzer/fuzzer.vcproj +++ b/build/VS2008/fuzzer/fuzzer.vcproj @@ -352,6 +352,10 @@ RelativePath="..\..\..\lib\common\entropy_common.c" > ++ diff --git a/build/VS2008/zstd/zstd.vcproj b/build/VS2008/zstd/zstd.vcproj index dbd211c06..5d9f68327 100644 --- a/build/VS2008/zstd/zstd.vcproj +++ b/build/VS2008/zstd/zstd.vcproj @@ -356,6 +356,10 @@ RelativePath="..\..\..\lib\common\entropy_common.c" > ++ diff --git a/build/VS2008/zstdlib/zstdlib.vcproj b/build/VS2008/zstdlib/zstdlib.vcproj index 340a4cd89..7234b02ac 100644 --- a/build/VS2008/zstdlib/zstdlib.vcproj +++ b/build/VS2008/zstdlib/zstdlib.vcproj @@ -348,6 +348,10 @@ RelativePath="..\..\..\lib\common\entropy_common.c" > ++ diff --git a/build/VS2010/fullbench/fullbench.vcxproj b/build/VS2010/fullbench/fullbench.vcxproj index d0cbcae98..2ec8c4f61 100644 --- a/build/VS2010/fullbench/fullbench.vcxproj +++ b/build/VS2010/fullbench/fullbench.vcxproj @@ -156,6 +156,7 @@ + diff --git a/build/VS2010/fuzzer/fuzzer.vcxproj b/build/VS2010/fuzzer/fuzzer.vcxproj index 6fe327209..ac400028b 100644 --- a/build/VS2010/fuzzer/fuzzer.vcxproj +++ b/build/VS2010/fuzzer/fuzzer.vcxproj @@ -158,6 +158,7 @@ + diff --git a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj index 2d04c6935..db9fdedbb 100644 --- a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj +++ b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj @@ -22,6 +22,7 @@ + diff --git a/build/VS2010/libzstd/libzstd.vcxproj b/build/VS2010/libzstd/libzstd.vcxproj index c01a5d179..9550b03cf 100644 --- a/build/VS2010/libzstd/libzstd.vcxproj +++ b/build/VS2010/libzstd/libzstd.vcxproj @@ -22,6 +22,7 @@ + diff --git a/build/VS2010/zstd/zstd.vcxproj b/build/VS2010/zstd/zstd.vcxproj index ace343465..36a265a33 100644 --- a/build/VS2010/zstd/zstd.vcxproj +++ b/build/VS2010/zstd/zstd.vcxproj @@ -20,6 +20,7 @@ + From 6901c94cd67c9a6ca0a3f2bc78abf6e3e210d216 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 14 Jun 2018 19:47:05 -0400 Subject: [PATCH 235/288] avoid duplicate code comments when a function is decribed in hist.h, do not describe it again in hist.c to avoid future doc synchronization issues. --- lib/compress/hist.c | 13 ++++--------- lib/compress/hist.h | 8 +++++--- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/compress/hist.c b/lib/compress/hist.c index da3b749db..16524756b 100644 --- a/lib/compress/hist.c +++ b/lib/compress/hist.c @@ -46,14 +46,6 @@ unsigned HIST_isError(size_t code) { return ERR_isError(code); } /*-************************************************************** * Histogram functions ****************************************************************/ -/*! HIST_count_simple : - Counts byte values within `src`, storing histogram into table `count`. - Doesn't use any additional memory, very limited stack usage. - But unsafe : doesn't check that all values within `src` fit into `count`. - For this reason, prefer using a table `count` of size 256. - @return : count of most numerous element. - this function doesn't produce any error (i.e. it must succeed). -*/ unsigned HIST_count_simple(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize) { @@ -83,7 +75,10 @@ unsigned HIST_count_simple(unsigned* count, unsigned* maxSymbolValuePtr, /* HIST_count_parallel_wksp() : - * Same as HIST_count_parallel(), but using an externally provided scratch buffer. + * store histogram into 4 intermediate tables, recombined at the end. + * this design makes better use of OoO cpus, + * and is noticeably faster when some values are heavily repeated. + * But it needs some additional workspace for intermediate tables. * `workSpace` size must be a table of size >= HIST_WKSP_SIZE_U32. * @return : largest histogram frequency, * or an error code (notably when histogram would be larger than *maxSymbolValuePtr). */ diff --git a/lib/compress/hist.h b/lib/compress/hist.h index 293b188bc..788470da7 100644 --- a/lib/compress/hist.h +++ b/lib/compress/hist.h @@ -81,10 +81,12 @@ size_t HIST_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr, unsigned* workSpace); /*! HIST_count_simple() : - * Same as HIST_countFast(), but does not use any additional memory (not even on stack). - * This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr`. + * Same as HIST_countFast(), this function is unsafe, + * and will segfault if any value within `src` is `> *maxSymbolValuePtr`. * It is also a bit slower for large inputs. - * This function doesn't produce any error (i.e. it must succeed). + * However, it does not need any additional memory (not even on stack). + * @return : count of the most frequent symbol. + * Note this function doesn't produce any error (i.e. it must succeed). */ unsigned HIST_count_simple(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize); From 507bef196deb994676f06d1c498dcdfd859cc168 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 14 Jun 2018 19:48:40 -0400 Subject: [PATCH 236/288] added debug.h for cmake --- build/cmake/lib/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index 2cdfde0f3..ade946daf 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -55,6 +55,7 @@ SET(Sources SET(Headers ${LIBRARY_DIR}/zstd.h + ${LIBRARY_DIR}/common/debug.h ${LIBRARY_DIR}/common/pool.h ${LIBRARY_DIR}/common/threading.h ${LIBRARY_DIR}/common/bitstream.h From b7e5ebef2aa4d86215d664fbf7fb351a94e4a3eb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 14 Jun 2018 20:41:50 -0400 Subject: [PATCH 237/288] grouped X2 function together --- lib/decompress/huf_decompress.c | 685 ++++++++++++++++---------------- 1 file changed, 343 insertions(+), 342 deletions(-) diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 43d93c067..a696261bd 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -140,8 +140,6 @@ size_t HUF_readDTableX1(HUF_DTable* DTable, const void* src, size_t srcSize) workSpace, sizeof(workSpace)); } -typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX2; /* double-symbols decoding */ - FORCE_INLINE_TEMPLATE BYTE HUF_decodeSymbolX1(BIT_DStream_t* Dstream, const HUF_DEltX1* dt, const U32 dtLog) { @@ -305,6 +303,344 @@ HUF_decompress4X1_usingDTable_internal_body( } +typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize, + const void *cSrc, + size_t cSrcSize, + const HUF_DTable *DTable); +#if DYNAMIC_BMI2 + +#define HUF_DGEN(fn) \ + \ + static size_t fn##_default( \ + void* dst, size_t dstSize, \ + const void* cSrc, size_t cSrcSize, \ + const HUF_DTable* DTable) \ + { \ + return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ + } \ + \ + static TARGET_ATTRIBUTE("bmi2") size_t fn##_bmi2( \ + void* dst, size_t dstSize, \ + const void* cSrc, size_t cSrcSize, \ + const HUF_DTable* DTable) \ + { \ + return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ + } \ + \ + static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ + size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + { \ + if (bmi2) { \ + return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \ + } \ + return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ + } + +#else + +#define HUF_DGEN(fn) \ + static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ + size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + { \ + (void)bmi2; \ + return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ + } + +#endif + +HUF_DGEN(HUF_decompress1X1_usingDTable_internal) +HUF_DGEN(HUF_decompress4X1_usingDTable_internal) + + + +size_t HUF_decompress1X1_usingDTable( + void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + const HUF_DTable* DTable) +{ + DTableDesc dtd = HUF_getDTableDesc(DTable); + if (dtd.tableType != 0) return ERROR(GENERIC); + return HUF_decompress1X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); +} + +size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + void* workSpace, size_t wkspSize) +{ + const BYTE* ip = (const BYTE*) cSrc; + + size_t const hSize = HUF_readDTableX1_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize); + if (HUF_isError(hSize)) return hSize; + if (hSize >= cSrcSize) return ERROR(srcSize_wrong); + ip += hSize; cSrcSize -= hSize; + + return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); +} + + +size_t HUF_decompress1X1_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize) +{ + U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; + return HUF_decompress1X1_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, + workSpace, sizeof(workSpace)); +} + +size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) +{ + HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); + return HUF_decompress1X1_DCtx (DTable, dst, dstSize, cSrc, cSrcSize); +} + +size_t HUF_decompress4X1_usingDTable( + void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + const HUF_DTable* DTable) +{ + DTableDesc dtd = HUF_getDTableDesc(DTable); + if (dtd.tableType != 0) return ERROR(GENERIC); + return HUF_decompress4X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); +} + +static size_t HUF_decompress4X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + void* workSpace, size_t wkspSize, int bmi2) +{ + const BYTE* ip = (const BYTE*) cSrc; + + size_t const hSize = HUF_readDTableX1_wksp (dctx, cSrc, cSrcSize, + workSpace, wkspSize); + if (HUF_isError(hSize)) return hSize; + if (hSize >= cSrcSize) return ERROR(srcSize_wrong); + ip += hSize; cSrcSize -= hSize; + + return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); +} + +size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + void* workSpace, size_t wkspSize) +{ + return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, 0); +} + + +size_t HUF_decompress4X1_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) +{ + U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; + return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, + workSpace, sizeof(workSpace)); +} +size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) +{ + HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); + return HUF_decompress4X1_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); +} + + +/* *************************/ +/* double-symbols decoding */ +/* *************************/ + +typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX2; /* double-symbols decoding */ +typedef struct { BYTE symbol; BYTE weight; } sortedSymbol_t; +typedef U32 rankValCol_t[HUF_TABLELOG_MAX + 1]; +typedef rankValCol_t rankVal_t[HUF_TABLELOG_MAX]; + + +/* HUF_fillDTableX2Level2() : + * `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */ +static void HUF_fillDTableX2Level2(HUF_DEltX2* DTable, U32 sizeLog, const U32 consumed, + const U32* rankValOrigin, const int minWeight, + const sortedSymbol_t* sortedSymbols, const U32 sortedListSize, + U32 nbBitsBaseline, U16 baseSeq) +{ + HUF_DEltX2 DElt; + U32 rankVal[HUF_TABLELOG_MAX + 1]; + + /* get pre-calculated rankVal */ + memcpy(rankVal, rankValOrigin, sizeof(rankVal)); + + /* fill skipped values */ + if (minWeight>1) { + U32 i, skipSize = rankVal[minWeight]; + MEM_writeLE16(&(DElt.sequence), baseSeq); + DElt.nbBits = (BYTE)(consumed); + DElt.length = 1; + for (i = 0; i < skipSize; i++) + DTable[i] = DElt; + } + + /* fill DTable */ + { U32 s; for (s=0; s = 1 */ + + rankVal[weight] += length; + } } +} + + +static void HUF_fillDTableX2(HUF_DEltX2* DTable, const U32 targetLog, + const sortedSymbol_t* sortedList, const U32 sortedListSize, + const U32* rankStart, rankVal_t rankValOrigin, const U32 maxWeight, + const U32 nbBitsBaseline) +{ + U32 rankVal[HUF_TABLELOG_MAX + 1]; + const int scaleLog = nbBitsBaseline - targetLog; /* note : targetLog >= srcLog, hence scaleLog <= 1 */ + const U32 minBits = nbBitsBaseline - maxWeight; + U32 s; + + memcpy(rankVal, rankValOrigin, sizeof(rankVal)); + + /* fill DTable */ + for (s=0; s = minBits) { /* enough room for a second symbol */ + U32 sortedRank; + int minWeight = nbBits + scaleLog; + if (minWeight < 1) minWeight = 1; + sortedRank = rankStart[minWeight]; + HUF_fillDTableX2Level2(DTable+start, targetLog-nbBits, nbBits, + rankValOrigin[nbBits], minWeight, + sortedList+sortedRank, sortedListSize-sortedRank, + nbBitsBaseline, symbol); + } else { + HUF_DEltX2 DElt; + MEM_writeLE16(&(DElt.sequence), symbol); + DElt.nbBits = (BYTE)(nbBits); + DElt.length = 1; + { U32 const end = start + length; + U32 u; + for (u = start; u < end; u++) DTable[u] = DElt; + } } + rankVal[weight] += length; + } +} + +size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, + size_t srcSize, void* workSpace, + size_t wkspSize) +{ + U32 tableLog, maxW, sizeOfSort, nbSymbols; + DTableDesc dtd = HUF_getDTableDesc(DTable); + U32 const maxTableLog = dtd.maxTableLog; + size_t iSize; + void* dtPtr = DTable+1; /* force compiler to avoid strict-aliasing */ + HUF_DEltX2* const dt = (HUF_DEltX2*)dtPtr; + U32 *rankStart; + + rankValCol_t* rankVal; + U32* rankStats; + U32* rankStart0; + sortedSymbol_t* sortedSymbol; + BYTE* weightList; + size_t spaceUsed32 = 0; + + rankVal = (rankValCol_t *)((U32 *)workSpace + spaceUsed32); + spaceUsed32 += (sizeof(rankValCol_t) * HUF_TABLELOG_MAX) >> 2; + rankStats = (U32 *)workSpace + spaceUsed32; + spaceUsed32 += HUF_TABLELOG_MAX + 1; + rankStart0 = (U32 *)workSpace + spaceUsed32; + spaceUsed32 += HUF_TABLELOG_MAX + 2; + sortedSymbol = (sortedSymbol_t *)workSpace + (spaceUsed32 * sizeof(U32)) / sizeof(sortedSymbol_t); + spaceUsed32 += HUF_ALIGN(sizeof(sortedSymbol_t) * (HUF_SYMBOLVALUE_MAX + 1), sizeof(U32)) >> 2; + weightList = (BYTE *)((U32 *)workSpace + spaceUsed32); + spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; + + if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge); + + rankStart = rankStart0 + 1; + memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1)); + + DEBUG_STATIC_ASSERT(sizeof(HUF_DEltX2) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */ + if (maxTableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); + /* memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */ + + iSize = HUF_readStats(weightList, HUF_SYMBOLVALUE_MAX + 1, rankStats, &nbSymbols, &tableLog, src, srcSize); + if (HUF_isError(iSize)) return iSize; + + /* check result */ + if (tableLog > maxTableLog) return ERROR(tableLog_tooLarge); /* DTable can't fit code depth */ + + /* find maxWeight */ + for (maxW = tableLog; rankStats[maxW]==0; maxW--) {} /* necessarily finds a solution before 0 */ + + /* Get start index of each weight */ + { U32 w, nextRankStart = 0; + for (w=1; w > consumed; + } } } } + + HUF_fillDTableX2(dt, maxTableLog, + sortedSymbol, sizeOfSort, + rankStart0, rankVal, maxW, + tableLog+1); + + dtd.tableLog = (BYTE)maxTableLog; + dtd.tableType = 1; + memcpy(DTable, &dtd, sizeof(dtd)); + return iSize; +} + +size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize) +{ + U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; + return HUF_readDTableX2_wksp(DTable, src, srcSize, + workSpace, sizeof(workSpace)); +} + + FORCE_INLINE_TEMPLATE U32 HUF_decodeSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog) { @@ -485,343 +821,8 @@ HUF_decompress4X2_usingDTable_internal_body( } } - -typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize, - const void *cSrc, - size_t cSrcSize, - const HUF_DTable *DTable); -#if DYNAMIC_BMI2 - -#define X(fn) \ - \ - static size_t fn##_default( \ - void* dst, size_t dstSize, \ - const void* cSrc, size_t cSrcSize, \ - const HUF_DTable* DTable) \ - { \ - return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ - } \ - \ - static TARGET_ATTRIBUTE("bmi2") size_t fn##_bmi2( \ - void* dst, size_t dstSize, \ - const void* cSrc, size_t cSrcSize, \ - const HUF_DTable* DTable) \ - { \ - return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ - } \ - \ - static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ - { \ - if (bmi2) { \ - return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \ - } \ - return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ - } - -#else - -#define X(fn) \ - static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ - { \ - (void)bmi2; \ - return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ - } - -#endif - -X(HUF_decompress1X1_usingDTable_internal) -X(HUF_decompress4X1_usingDTable_internal) -X(HUF_decompress1X2_usingDTable_internal) -X(HUF_decompress4X2_usingDTable_internal) - -#undef X - - -size_t HUF_decompress1X1_usingDTable( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc dtd = HUF_getDTableDesc(DTable); - if (dtd.tableType != 0) return ERROR(GENERIC); - return HUF_decompress1X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -} - -size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) -{ - const BYTE* ip = (const BYTE*) cSrc; - - size_t const hSize = HUF_readDTableX1_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize); - if (HUF_isError(hSize)) return hSize; - if (hSize >= cSrcSize) return ERROR(srcSize_wrong); - ip += hSize; cSrcSize -= hSize; - - return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); -} - - -size_t HUF_decompress1X1_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress1X1_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); - return HUF_decompress1X1_DCtx (DTable, dst, dstSize, cSrc, cSrcSize); -} - -size_t HUF_decompress4X1_usingDTable( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc dtd = HUF_getDTableDesc(DTable); - if (dtd.tableType != 0) return ERROR(GENERIC); - return HUF_decompress4X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -} - -static size_t HUF_decompress4X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize, int bmi2) -{ - const BYTE* ip = (const BYTE*) cSrc; - - size_t const hSize = HUF_readDTableX1_wksp (dctx, cSrc, cSrcSize, - workSpace, wkspSize); - if (HUF_isError(hSize)) return hSize; - if (hSize >= cSrcSize) return ERROR(srcSize_wrong); - ip += hSize; cSrcSize -= hSize; - - return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); -} - -size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) -{ - return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, 0); -} - - -size_t HUF_decompress4X1_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} -size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); - return HUF_decompress4X1_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); -} - - -/* *************************/ -/* double-symbols decoding */ -/* *************************/ -typedef struct { BYTE symbol; BYTE weight; } sortedSymbol_t; - -/* HUF_fillDTableX2Level2() : - * `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */ -static void HUF_fillDTableX2Level2(HUF_DEltX2* DTable, U32 sizeLog, const U32 consumed, - const U32* rankValOrigin, const int minWeight, - const sortedSymbol_t* sortedSymbols, const U32 sortedListSize, - U32 nbBitsBaseline, U16 baseSeq) -{ - HUF_DEltX2 DElt; - U32 rankVal[HUF_TABLELOG_MAX + 1]; - - /* get pre-calculated rankVal */ - memcpy(rankVal, rankValOrigin, sizeof(rankVal)); - - /* fill skipped values */ - if (minWeight>1) { - U32 i, skipSize = rankVal[minWeight]; - MEM_writeLE16(&(DElt.sequence), baseSeq); - DElt.nbBits = (BYTE)(consumed); - DElt.length = 1; - for (i = 0; i < skipSize; i++) - DTable[i] = DElt; - } - - /* fill DTable */ - { U32 s; for (s=0; s = 1 */ - - rankVal[weight] += length; - } } -} - -typedef U32 rankValCol_t[HUF_TABLELOG_MAX + 1]; -typedef rankValCol_t rankVal_t[HUF_TABLELOG_MAX]; - -static void HUF_fillDTableX2(HUF_DEltX2* DTable, const U32 targetLog, - const sortedSymbol_t* sortedList, const U32 sortedListSize, - const U32* rankStart, rankVal_t rankValOrigin, const U32 maxWeight, - const U32 nbBitsBaseline) -{ - U32 rankVal[HUF_TABLELOG_MAX + 1]; - const int scaleLog = nbBitsBaseline - targetLog; /* note : targetLog >= srcLog, hence scaleLog <= 1 */ - const U32 minBits = nbBitsBaseline - maxWeight; - U32 s; - - memcpy(rankVal, rankValOrigin, sizeof(rankVal)); - - /* fill DTable */ - for (s=0; s = minBits) { /* enough room for a second symbol */ - U32 sortedRank; - int minWeight = nbBits + scaleLog; - if (minWeight < 1) minWeight = 1; - sortedRank = rankStart[minWeight]; - HUF_fillDTableX2Level2(DTable+start, targetLog-nbBits, nbBits, - rankValOrigin[nbBits], minWeight, - sortedList+sortedRank, sortedListSize-sortedRank, - nbBitsBaseline, symbol); - } else { - HUF_DEltX2 DElt; - MEM_writeLE16(&(DElt.sequence), symbol); - DElt.nbBits = (BYTE)(nbBits); - DElt.length = 1; - { U32 const end = start + length; - U32 u; - for (u = start; u < end; u++) DTable[u] = DElt; - } } - rankVal[weight] += length; - } -} - -size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, - size_t srcSize, void* workSpace, - size_t wkspSize) -{ - U32 tableLog, maxW, sizeOfSort, nbSymbols; - DTableDesc dtd = HUF_getDTableDesc(DTable); - U32 const maxTableLog = dtd.maxTableLog; - size_t iSize; - void* dtPtr = DTable+1; /* force compiler to avoid strict-aliasing */ - HUF_DEltX2* const dt = (HUF_DEltX2*)dtPtr; - U32 *rankStart; - - rankValCol_t* rankVal; - U32* rankStats; - U32* rankStart0; - sortedSymbol_t* sortedSymbol; - BYTE* weightList; - size_t spaceUsed32 = 0; - - rankVal = (rankValCol_t *)((U32 *)workSpace + spaceUsed32); - spaceUsed32 += (sizeof(rankValCol_t) * HUF_TABLELOG_MAX) >> 2; - rankStats = (U32 *)workSpace + spaceUsed32; - spaceUsed32 += HUF_TABLELOG_MAX + 1; - rankStart0 = (U32 *)workSpace + spaceUsed32; - spaceUsed32 += HUF_TABLELOG_MAX + 2; - sortedSymbol = (sortedSymbol_t *)workSpace + (spaceUsed32 * sizeof(U32)) / sizeof(sortedSymbol_t); - spaceUsed32 += HUF_ALIGN(sizeof(sortedSymbol_t) * (HUF_SYMBOLVALUE_MAX + 1), sizeof(U32)) >> 2; - weightList = (BYTE *)((U32 *)workSpace + spaceUsed32); - spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; - - if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge); - - rankStart = rankStart0 + 1; - memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1)); - - DEBUG_STATIC_ASSERT(sizeof(HUF_DEltX2) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */ - if (maxTableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); - /* memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */ - - iSize = HUF_readStats(weightList, HUF_SYMBOLVALUE_MAX + 1, rankStats, &nbSymbols, &tableLog, src, srcSize); - if (HUF_isError(iSize)) return iSize; - - /* check result */ - if (tableLog > maxTableLog) return ERROR(tableLog_tooLarge); /* DTable can't fit code depth */ - - /* find maxWeight */ - for (maxW = tableLog; rankStats[maxW]==0; maxW--) {} /* necessarily finds a solution before 0 */ - - /* Get start index of each weight */ - { U32 w, nextRankStart = 0; - for (w=1; w > consumed; - } } } } - - HUF_fillDTableX2(dt, maxTableLog, - sortedSymbol, sizeOfSort, - rankStart0, rankVal, maxW, - tableLog+1); - - dtd.tableLog = (BYTE)maxTableLog; - dtd.tableType = 1; - memcpy(DTable, &dtd, sizeof(dtd)); - return iSize; -} - -size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_readDTableX2_wksp(DTable, src, srcSize, - workSpace, sizeof(workSpace)); -} +HUF_DGEN(HUF_decompress1X2_usingDTable_internal) +HUF_DGEN(HUF_decompress4X2_usingDTable_internal) size_t HUF_decompress1X2_usingDTable( void* dst, size_t dstSize, @@ -911,9 +912,9 @@ size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cS } -/* ********************************/ -/* Generic decompression selector */ -/* ********************************/ +/* ***********************************/ +/* Universal decompression selectors */ +/* ***********************************/ size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, From 3841dbac84ae8d2e2d288a200fd18c28fafed0ae Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 14 Jun 2018 16:24:18 -0700 Subject: [PATCH 238/288] Adjust advanced parameters to source size In the new advanced API, adjust the parameters even if they are explicitly set. This mainly applies to the `windowLog`, and accordingly the `hashLog` and `chainLog`, when the source size is known. --- lib/compress/zstd_compress.c | 33 +++++++++++++++++---------------- lib/compress/zstd_ldm.c | 10 ++++++---- lib/compress/zstdmt_compress.c | 1 - programs/fileio.c | 7 ++++--- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 807b1df5b..0bf6c4c3f 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -150,21 +150,6 @@ size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs) /* private API call, for dictBuilder only */ const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStore); } -ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams( - const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize) -{ - ZSTD_compressionParameters cParams = ZSTD_getCParams(CCtxParams->compressionLevel, srcSizeHint, dictSize); - if (CCtxParams->ldmParams.enableLdm) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG; - if (CCtxParams->cParams.windowLog) cParams.windowLog = CCtxParams->cParams.windowLog; - if (CCtxParams->cParams.hashLog) cParams.hashLog = CCtxParams->cParams.hashLog; - if (CCtxParams->cParams.chainLog) cParams.chainLog = CCtxParams->cParams.chainLog; - if (CCtxParams->cParams.searchLog) cParams.searchLog = CCtxParams->cParams.searchLog; - if (CCtxParams->cParams.searchLength) cParams.searchLength = CCtxParams->cParams.searchLength; - if (CCtxParams->cParams.targetLength) cParams.targetLength = CCtxParams->cParams.targetLength; - if (CCtxParams->cParams.strategy) cParams.strategy = CCtxParams->cParams.strategy; - return cParams; -} - static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams( ZSTD_compressionParameters cParams) { @@ -761,6 +746,22 @@ ZSTD_adjustCParams(ZSTD_compressionParameters cPar, return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize); } +ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams( + const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize) +{ + ZSTD_compressionParameters cParams = ZSTD_getCParams(CCtxParams->compressionLevel, srcSizeHint, dictSize); + if (CCtxParams->ldmParams.enableLdm) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG; + if (CCtxParams->cParams.windowLog) cParams.windowLog = CCtxParams->cParams.windowLog; + if (CCtxParams->cParams.hashLog) cParams.hashLog = CCtxParams->cParams.hashLog; + if (CCtxParams->cParams.chainLog) cParams.chainLog = CCtxParams->cParams.chainLog; + if (CCtxParams->cParams.searchLog) cParams.searchLog = CCtxParams->cParams.searchLog; + if (CCtxParams->cParams.searchLength) cParams.searchLength = CCtxParams->cParams.searchLength; + if (CCtxParams->cParams.targetLength) cParams.targetLength = CCtxParams->cParams.targetLength; + if (CCtxParams->cParams.strategy) cParams.strategy = CCtxParams->cParams.strategy; + assert(!ZSTD_checkCParams(cParams)); + return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize); +} + static size_t ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams, const U32 forCCtx) @@ -1078,7 +1079,6 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, if (params.ldmParams.enableLdm) { /* Adjust long distance matching parameters */ - params.ldmParams.windowLog = params.cParams.windowLog; ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams); assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog); assert(params.ldmParams.hashEveryLog < 32); @@ -3638,6 +3638,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, params.cParams = ZSTD_getCParamsFromCCtxParams( &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/); + #ifdef ZSTD_MULTITHREAD if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) { params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */ diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index 819c62aa3..215f55cf4 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -9,6 +9,7 @@ #include "zstd_ldm.h" +#include "debug.h" #include "zstd_fast.h" /* ZSTD_fillHashTable() */ #include "zstd_double_fast.h" /* ZSTD_fillDoubleHashTable() */ @@ -20,7 +21,7 @@ void ZSTD_ldm_adjustParameters(ldmParams_t* params, ZSTD_compressionParameters const* cParams) { - U32 const windowLog = cParams->windowLog; + params->windowLog = cParams->windowLog; ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX); DEBUGLOG(4, "ZSTD_ldm_adjustParameters"); if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG; @@ -33,12 +34,13 @@ void ZSTD_ldm_adjustParameters(ldmParams_t* params, params->minMatchLength = minMatch; } if (params->hashLog == 0) { - params->hashLog = MAX(ZSTD_HASHLOG_MIN, windowLog - LDM_HASH_RLOG); + params->hashLog = MAX(ZSTD_HASHLOG_MIN, params->windowLog - LDM_HASH_RLOG); assert(params->hashLog <= ZSTD_HASHLOG_MAX); } if (params->hashEveryLog == 0) { - params->hashEveryLog = - windowLog < params->hashLog ? 0 : windowLog - params->hashLog; + params->hashEveryLog = params->windowLog < params->hashLog + ? 0 + : params->windowLog - params->hashLog; } params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog); } diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 0c59c6a72..d93c1262f 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -429,7 +429,6 @@ static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* /* Adjust parameters */ if (params.ldmParams.enableLdm) { DEBUGLOG(4, "LDM window size = %u KB", (1U << params.cParams.windowLog) >> 10); - params.ldmParams.windowLog = params.cParams.windowLog; ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams); assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog); assert(params.ldmParams.hashEveryLog < 32); diff --git a/programs/fileio.c b/programs/fileio.c index e8d25ab9d..d9e972040 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -759,8 +759,9 @@ FIO_compressZstdFrame(const cRess_t* ressPtr, DISPLAYLEVEL(6, "compression using zstd format \n"); /* init */ - if (fileSize != UTIL_FILESIZE_UNKNOWN) - ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize); + if (fileSize != UTIL_FILESIZE_UNKNOWN) { + CHECK(ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize)); + } (void)compressionLevel; (void)srcFileName; /* Main compression loop */ @@ -1755,7 +1756,7 @@ int FIO_decompressMultipleFilenames(const char** srcNamesTable, unsigned nbFiles && strcmp(suffixPtr, LZMA_EXTENSION) && strcmp(suffixPtr, LZ4_EXTENSION)) ) { const char* suffixlist = ZSTD_EXTENSION - #ifdef ZSTD_GZCOMPRESS + #ifdef ZSTD_GZCOMPRESS "/" GZ_EXTENSION #endif #ifdef ZSTD_LZMACOMPRESS From 1d0fcde45d9833a318f88af8608f4612e1e4c7b9 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 15 Jun 2018 07:36:54 -0700 Subject: [PATCH 239/288] Use debug.h in fileio.c --- programs/fileio.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index d9e972040..a7cd295bb 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -36,6 +36,7 @@ # include #endif +#include "debug.h" #include "mem.h" #include "fileio.h" #include "util.h" @@ -101,21 +102,6 @@ static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; #define MIN(a,b) ((a) < (b) ? (a) : (b)) -/*-************************************* -* Debug -***************************************/ -#if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=1) -# include -#else -# ifndef assert -# define assert(condition) ((void)0) -# endif -#endif - -#ifndef ZSTD_DEBUG -# define ZSTD_DEBUG 0 -#endif -#define DEBUGLOG(l,...) if (l<=ZSTD_DEBUG) DISPLAY(__VA_ARGS__); #define EXM_THROW(error, ...) \ { \ DISPLAYLEVEL(1, "zstd: "); \ From c9e8ee93a7c2d30819b760c54397448baf4c19aa Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 18 Jun 2018 19:20:37 -0700 Subject: [PATCH 240/288] removed specific --opaqueapi test from zstreamtest. This test is now integrated within --newapi, which dynamically switches between the 2 modes randomly. The main outcome is reduced testing time. --- tests/Makefile | 1 - tests/zstreamtest.c | 82 +++++++++++++++++++++++---------------------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index 095e419b8..4bd43ea1f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -376,7 +376,6 @@ test-zstream: zstreamtest $(QEMU_SYS) ./zstreamtest -v $(ZSTREAM_TESTTIME) $(FUZZER_FLAGS) $(QEMU_SYS) ./zstreamtest --mt -t1 $(ZSTREAM_TESTTIME) $(FUZZER_FLAGS) $(QEMU_SYS) ./zstreamtest --newapi -t1 $(ZSTREAM_TESTTIME) $(FUZZER_FLAGS) - $(QEMU_SYS) ./zstreamtest --opaqueapi -t1 $(ZSTREAM_TESTTIME) $(FUZZER_FLAGS) test-zstream32: zstreamtest32 $(QEMU_SYS) ./zstreamtest32 $(ZSTREAM_TESTTIME) $(FUZZER_FLAGS) diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 425fbab39..dc341d13f 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -10,8 +10,8 @@ /*-************************************ -* Compiler specific -**************************************/ + * Compiler specific + **************************************/ #ifdef _MSC_VER /* Visual Studio */ # define _CRT_SECURE_NO_WARNINGS /* fgets */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ @@ -20,8 +20,8 @@ /*-************************************ -* Includes -**************************************/ + * Includes + **************************************/ #include /* free */ #include /* fgets, sscanf */ #include /* strcmp */ @@ -40,8 +40,8 @@ /*-************************************ -* Constants -**************************************/ + * Constants + **************************************/ #define KB *(1U<<10) #define MB *(1U<<20) #define GB *(1U<<30) @@ -54,8 +54,8 @@ static const U32 prime32 = 2654435761U; /*-************************************ -* Display Macros -**************************************/ + * Display Macros + **************************************/ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { \ DISPLAY(__VA_ARGS__); \ @@ -74,8 +74,8 @@ static U64 g_clockTime = 0; /*-******************************************************* -* Fuzzer functions -*********************************************************/ + * Check macros + *********************************************************/ #undef MIN #undef MAX #define MIN(a,b) ((a)<(b)?(a):(b)) @@ -126,8 +126,8 @@ unsigned int FUZ_rand(unsigned int* seedPtr) /*====================================================== -* Basic Unit tests -======================================================*/ + * Basic Unit tests + *======================================================*/ typedef struct { void* start; @@ -1284,8 +1284,9 @@ _output_error: } -/* Multi-threading version of fuzzer Tests */ -static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double compressibility, int bigTests) +/* fuzzing ZSTDMT_* interface */ +static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, + double compressibility, int bigTests) { const U32 maxSrcLog = bigTests ? 24 : 22; static const U32 maxSampleLog = 19; @@ -1559,7 +1560,7 @@ _output_error: * Otherwise, sets the param in zc. */ static size_t setCCtxParameter(ZSTD_CCtx* zc, ZSTD_CCtx_params* cctxParams, ZSTD_cParameter param, unsigned value, - U32 useOpaqueAPI) + int useOpaqueAPI) { if (useOpaqueAPI) { return ZSTD_CCtxParam_setParameter(cctxParams, param, value); @@ -1569,7 +1570,8 @@ static size_t setCCtxParameter(ZSTD_CCtx* zc, ZSTD_CCtx_params* cctxParams, } /* Tests for ZSTD_compress_generic() API */ -static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double compressibility, int bigTests, U32 const useOpaqueAPI) +static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, + double compressibility, int bigTests) { U32 const maxSrcLog = bigTests ? 24 : 22; static const U32 maxSampleLog = 19; @@ -1622,6 +1624,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double /* test loop */ for ( ; (testNb <= nbTests) || (UTIL_clockSpanMicro(startClock) < g_clockTime) ; testNb++ ) { U32 lseed; + int opaqueAPI; const BYTE* srcBuffer; size_t totalTestSize, totalGenSize, cSize; XXH64_state_t xxhState; @@ -1636,6 +1639,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double FUZ_rand(&coreSeed); lseed = coreSeed ^ prime32; DISPLAYLEVEL(5, " *** Test %u *** \n", testNb); + opaqueAPI = FUZ_rand(&lseed) & 1; /* states full reset (deliberately not synchronized) */ /* some issues can only happen when reusing states */ @@ -1678,7 +1682,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double if (maxTestSize >= srcBufferSize) maxTestSize = srcBufferSize-1; { int const compressionLevel = (FUZ_rand(&lseed) % 5) + 1; DISPLAYLEVEL(5, "t%u : compression level : %i \n", testNb, compressionLevel); - CHECK_Z (setCCtxParameter(zc, cctxParams, ZSTD_p_compressionLevel, compressionLevel, useOpaqueAPI) ); + CHECK_Z (setCCtxParameter(zc, cctxParams, ZSTD_p_compressionLevel, compressionLevel, opaqueAPI) ); } } else { U32 const testLog = FUZ_rand(&lseed) % maxSrcLog; @@ -1716,39 +1720,39 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double if (FUZ_rand(&lseed) & 1) { DISPLAYLEVEL(5, "t%u: windowLog : %u \n", testNb, cParams.windowLog); - CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_windowLog, cParams.windowLog, useOpaqueAPI) ); + CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_windowLog, cParams.windowLog, opaqueAPI) ); assert(cParams.windowLog >= ZSTD_WINDOWLOG_MIN); /* guaranteed by ZSTD_adjustCParams() */ windowLogMalus = (cParams.windowLog - ZSTD_WINDOWLOG_MIN) / 5; } if (FUZ_rand(&lseed) & 1) { DISPLAYLEVEL(5, "t%u: hashLog : %u \n", testNb, cParams.hashLog); - CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_hashLog, cParams.hashLog, useOpaqueAPI) ); + CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_hashLog, cParams.hashLog, opaqueAPI) ); } if (FUZ_rand(&lseed) & 1) { DISPLAYLEVEL(5, "t%u: chainLog : %u \n", testNb, cParams.chainLog); - CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_chainLog, cParams.chainLog, useOpaqueAPI) ); + CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_chainLog, cParams.chainLog, opaqueAPI) ); } - if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_searchLog, cParams.searchLog, useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_minMatch, cParams.searchLength, useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_targetLength, cParams.targetLength, useOpaqueAPI) ); + if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_searchLog, cParams.searchLog, opaqueAPI) ); + if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_minMatch, cParams.searchLength, opaqueAPI) ); + if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_targetLength, cParams.targetLength, opaqueAPI) ); /* mess with long distance matching parameters */ if (bigTests) { - if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_enableLongDistanceMatching, FUZ_rand(&lseed) & 63, useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmHashLog, FUZ_randomClampedLength(&lseed, ZSTD_HASHLOG_MIN, 23), useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmMinMatch, FUZ_randomClampedLength(&lseed, ZSTD_LDM_MINMATCH_MIN, ZSTD_LDM_MINMATCH_MAX), useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmBucketSizeLog, FUZ_randomClampedLength(&lseed, 0, ZSTD_LDM_BUCKETSIZELOG_MAX), useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmHashEveryLog, FUZ_randomClampedLength(&lseed, 0, ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN), useOpaqueAPI) ); + if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_enableLongDistanceMatching, FUZ_rand(&lseed) & 63, opaqueAPI) ); + if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmHashLog, FUZ_randomClampedLength(&lseed, ZSTD_HASHLOG_MIN, 23), opaqueAPI) ); + if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmMinMatch, FUZ_randomClampedLength(&lseed, ZSTD_LDM_MINMATCH_MIN, ZSTD_LDM_MINMATCH_MAX), opaqueAPI) ); + if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmBucketSizeLog, FUZ_randomClampedLength(&lseed, 0, ZSTD_LDM_BUCKETSIZELOG_MAX), opaqueAPI) ); + if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmHashEveryLog, FUZ_randomClampedLength(&lseed, 0, ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN), opaqueAPI) ); } /* mess with frame parameters */ if (FUZ_rand(&lseed) & 1) { U32 const checksumFlag = FUZ_rand(&lseed) & 1; DISPLAYLEVEL(5, "t%u: frame checksum : %u \n", testNb, checksumFlag); - CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_checksumFlag, checksumFlag, useOpaqueAPI) ); + CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_checksumFlag, checksumFlag, opaqueAPI) ); } - if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_dictIDFlag, FUZ_rand(&lseed) & 1, useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_contentSizeFlag, FUZ_rand(&lseed) & 1, useOpaqueAPI) ); + if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_dictIDFlag, FUZ_rand(&lseed) & 1, opaqueAPI) ); + if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_contentSizeFlag, FUZ_rand(&lseed) & 1, opaqueAPI) ); if (FUZ_rand(&lseed) & 1) { DISPLAYLEVEL(5, "t%u: pledgedSrcSize : %u \n", testNb, (U32)pledgedSrcSize); CHECK_Z( ZSTD_CCtx_setPledgedSrcSize(zc, pledgedSrcSize) ); @@ -1759,18 +1763,18 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double U32 const nbThreadsAdjusted = (windowLogMalus < nbThreadsCandidate) ? nbThreadsCandidate - windowLogMalus : 1; U32 const nbThreads = MIN(nbThreadsAdjusted, nbThreadsMax); DISPLAYLEVEL(5, "t%u: nbThreads : %u \n", testNb, nbThreads); - CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_nbWorkers, nbThreads, useOpaqueAPI) ); + CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_nbWorkers, nbThreads, opaqueAPI) ); if (nbThreads > 1) { U32 const jobLog = FUZ_rand(&lseed) % (testLog+1); - CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_overlapSizeLog, FUZ_rand(&lseed) % 10, useOpaqueAPI) ); - CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_jobSize, (U32)FUZ_rLogLength(&lseed, jobLog), useOpaqueAPI) ); + CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_overlapSizeLog, FUZ_rand(&lseed) % 10, opaqueAPI) ); + CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_jobSize, (U32)FUZ_rLogLength(&lseed, jobLog), opaqueAPI) ); } } - if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_forceMaxWindow, FUZ_rand(&lseed) & 1, useOpaqueAPI) ); + if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_forceMaxWindow, FUZ_rand(&lseed) & 1, opaqueAPI) ); /* Apply parameters */ - if (useOpaqueAPI) { + if (opaqueAPI) { DISPLAYLEVEL(5, "t%u: applying CCtxParams \n", testNb); CHECK_Z (ZSTD_CCtx_setParametersUsingCCtxParams(zc, cctxParams) ); } @@ -1783,7 +1787,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double } if (dict && dictSize) { /* test that compression parameters are rejected (correctly) after loading a non-NULL dictionary */ - if (useOpaqueAPI) { + if (opaqueAPI) { size_t const setError = ZSTD_CCtx_setParametersUsingCCtxParams(zc, cctxParams); CHECK(!ZSTD_isError(setError), "ZSTD_CCtx_setParametersUsingCCtxParams should have failed"); } else { @@ -1962,7 +1966,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]; - U32 useOpaqueAPI = 0; int argNb; /* Check command line */ @@ -1975,7 +1978,6 @@ int main(int argc, const char** argv) if (!strcmp(argument, "--mt")) { selected_api=mt_api; testNb += !testNb; continue; } if (!strcmp(argument, "--newapi")) { selected_api=advanced_api; testNb += !testNb; continue; } - if (!strcmp(argument, "--opaqueapi")) { selected_api=advanced_api; testNb += !testNb; useOpaqueAPI = 1; continue; } if (!strcmp(argument, "--no-big-tests")) { bigTests=0; continue; } argument++; @@ -2091,7 +2093,7 @@ int main(int argc, const char** argv) result = fuzzerTests_MT(seed, nbTests, testNb, ((double)proba) / 100, bigTests); break; case advanced_api : - result = fuzzerTests_newAPI(seed, nbTests, testNb, ((double)proba) / 100, bigTests, useOpaqueAPI); + result = fuzzerTests_newAPI(seed, nbTests, testNb, ((double)proba) / 100, bigTests); break; default : assert(0); /* impossible */ From 1c714fda3fe0932faff5c1a535a2f4a597b8d0bb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 18 Jun 2018 20:46:39 -0700 Subject: [PATCH 241/288] introduced POOL_resize() not complete yet : finalize behavior in case of unfinished expansion --- lib/common/pool.c | 89 +++++++++++++++++++++++++++++++++++++---------- lib/common/pool.h | 37 ++++++++++---------- 2 files changed, 88 insertions(+), 38 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index 773488b07..6795f25eb 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -33,8 +33,9 @@ typedef struct POOL_job_s { struct POOL_ctx_s { ZSTD_customMem customMem; /* Keep track of the threads */ - ZSTD_pthread_t *threads; - size_t numThreads; + ZSTD_pthread_t* threads; + size_t threadCapacity; + size_t threadLimit; /* The queue is a circular buffer */ POOL_job *queue; @@ -58,10 +59,10 @@ struct POOL_ctx_s { }; /* POOL_thread() : - Work thread for the thread pool. - Waits for jobs and executes them. - @returns : NULL on failure else non-null. -*/ + * Work thread for the thread pool. + * Waits for jobs and executes them. + * @returns : NULL on failure else non-null. + */ static void* POOL_thread(void* opaque) { POOL_ctx* const ctx = (POOL_ctx*)opaque; if (!ctx) { return NULL; } @@ -103,16 +104,17 @@ POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) { return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem); } -POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem) { +POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, + ZSTD_customMem customMem) { POOL_ctx* ctx; - /* Check the parameters */ + /* Check parameters */ if (!numThreads) { return NULL; } /* Allocate the context and zero initialize */ ctx = (POOL_ctx*)ZSTD_calloc(sizeof(POOL_ctx), customMem); if (!ctx) { return NULL; } /* Initialize the job queue. - * It needs one extra space since one space is wasted to differentiate empty - * and full queues. + * It needs one extra space since one space is wasted to differentiate + * empty and full queues. */ ctx->queueSize = queueSize + 1; ctx->queue = (POOL_job*)ZSTD_malloc(ctx->queueSize * sizeof(POOL_job), customMem); @@ -126,7 +128,7 @@ POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customM ctx->shutdown = 0; /* Allocate space for the thread handles */ ctx->threads = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), customMem); - ctx->numThreads = 0; + ctx->threadCapacity = 0; ctx->customMem = customMem; /* Check for errors */ if (!ctx->threads || !ctx->queue) { POOL_free(ctx); return NULL; } @@ -134,11 +136,12 @@ POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customM { size_t i; for (i = 0; i < numThreads; ++i) { if (ZSTD_pthread_create(&ctx->threads[i], NULL, &POOL_thread, ctx)) { - ctx->numThreads = i; + ctx->threadCapacity = i; POOL_free(ctx); return NULL; } } - ctx->numThreads = numThreads; + ctx->threadCapacity = numThreads; + ctx->threadLimit = numThreads; } return ctx; } @@ -156,8 +159,8 @@ static void POOL_join(POOL_ctx* ctx) { ZSTD_pthread_cond_broadcast(&ctx->queuePopCond); /* Join all of the threads */ { size_t i; - for (i = 0; i < ctx->numThreads; ++i) { - ZSTD_pthread_join(ctx->threads[i], NULL); + for (i = 0; i < ctx->threadCapacity; ++i) { + ZSTD_pthread_join(ctx->threads[i], NULL); /* note : could fail */ } } } @@ -172,24 +175,72 @@ void POOL_free(POOL_ctx *ctx) { ZSTD_free(ctx, ctx->customMem); } + + size_t POOL_sizeof(POOL_ctx *ctx) { if (ctx==NULL) return 0; /* supports sizeof NULL */ return sizeof(*ctx) + ctx->queueSize * sizeof(POOL_job) - + ctx->numThreads * sizeof(ZSTD_pthread_t); + + ctx->threadCapacity * sizeof(ZSTD_pthread_t); +} + + +/* note : only works if no job is running ! + * return : 1 on success, 0 on failure */ +static int POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) +{ + if (ctx->numThreadsBusy > 0) return 0; + if (numThreads <= ctx->threadCapacity) { + ctx->threadLimit = numThreads; + return 1; + } + /* numThreads > threadCapacity */ + { ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem); + if (!threadPool) return 0; + /* Initialize additional threads */ + { size_t threadId; + for (threadId = ctx->threadCapacity; threadId < numThreads; ++threadId) { + if (ZSTD_pthread_create(&threadPool[threadId], NULL, &POOL_thread, ctx)) { + break; + } } + if (threadId != numThreads) { /* not all threads successfully init */ + /* how to destroy existing threads ? */ + /* POOL_join destroy all existing threads, not just newly created ones */ + return 0; + } + } + /* replace existing thread pool */ + memcpy(threadPool, ctx->threads, ctx->threadCapacity); + ZSTD_free(ctx->threads, ctx->customMem); + ctx->threads = threadPool; + } + ctx->threadCapacity = numThreads; + ctx->threadLimit = numThreads; + return 1; +} + +/* return : 1 on success, 0 on failure */ +int POOL_resize(POOL_ctx* ctx, size_t numThreads) +{ + int result; + if (!ctx) return 0; + ZSTD_pthread_mutex_lock(&ctx->queueMutex); + result = POOL_resize_internal(ctx, numThreads); + ZSTD_pthread_mutex_unlock(&ctx->queueMutex); + return result; } /** * 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. + * When queueSize is 1 (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 || + return ctx->numThreadsBusy == ctx->threadLimit || !ctx->queueEmpty; } } diff --git a/lib/common/pool.h b/lib/common/pool.h index a57e9b4fa..dba28859c 100644 --- a/lib/common/pool.h +++ b/lib/common/pool.h @@ -30,40 +30,39 @@ typedef struct POOL_ctx_s POOL_ctx; */ POOL_ctx* POOL_create(size_t numThreads, size_t queueSize); -POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem); +POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, + ZSTD_customMem customMem); /*! POOL_free() : - Free a thread pool returned by POOL_create(). -*/ + * Free a thread pool returned by POOL_create(). + */ void POOL_free(POOL_ctx* ctx); /*! POOL_sizeof() : - return memory usage of pool returned by POOL_create(). -*/ + * @return threadpool memory usage + * note : compatible with NULL (returns 0 in this case) + */ size_t POOL_sizeof(POOL_ctx* ctx); /*! POOL_function : - The function type that can be added to a thread pool. -*/ + * The function type that can be added to a thread pool. + */ typedef void (*POOL_function)(void*); -/*! POOL_add_function : - The function type for a generic thread pool add function. -*/ -typedef void (*POOL_add_function)(void*, POOL_function, void*); /*! POOL_add() : - Add the job `function(opaque)` to the thread pool. `ctx` must be valid. - Possibly blocks until there is room in the queue. - Note : The function may be executed asynchronously, so `opaque` must live until the function has been completed. -*/ + * Add the job `function(opaque)` to the thread pool. `ctx` must be valid. + * Possibly blocks until there is room in the queue. + * Note : The function may be executed asynchronously, + * therefore, `opaque` must live until function has been completed. + */ void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque); /*! POOL_tryAdd() : - Add the job `function(opaque)` to the thread pool if a worker is available. - return immediately otherwise. - @return : 1 if successful, 0 if not. -*/ + * Add the job `function(opaque)` to thread pool _if_ a worker is available. + * Returns immediately even if not (does not block). + * @return : 1 if successful, 0 if not. + */ int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque); From 5bac1db28f383d0d6e05296f157f7dcfdf881452 Mon Sep 17 00:00:00 2001 From: Topher Lubaway Date: Tue, 19 Jun 2018 09:56:37 -0700 Subject: [PATCH 242/288] Tests to verify piped input to `--list` exits 1 I'm following the pattern that i saw in the rest of the test file please tell me if i am using the wrong conventions --- tests/playTests.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index 48001c2eb..3b872d281 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -729,6 +729,9 @@ $ECHO "\n===> zstd --list/-l error detection tests " ! $ZSTD -lv tmp1* ! $ZSTD --list -v tmp2 tmp12.zst +$ECHO "\n===> zstd --list/-l exits 1 when stdin is piped in" +! echo "piped STDIN" | $ZSTD --list + $ECHO "\n===> zstd --list/-l test with null files " ./datagen -g0 > tmp5 $ZSTD tmp5 From 93c3184d44e5cbef4ef48be29be7e063dc72a466 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 13 Jun 2018 16:54:31 -0400 Subject: [PATCH 243/288] Attach Dicts when Using ZSTD_btopt and ZSTD_btultra --- lib/compress/zstd_compress.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c071c5738..75338326d 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1245,7 +1245,6 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) && !params.forceWindow /* dictMatchState isn't correctly * handled in _enforceMaxDist */ - && cdict->cParams.strategy <= ZSTD_btlazy2 && ZSTD_equivalentCParams(cctx->appliedParams.cParams, cdict->cParams); From d5d8240967eb5db5f7f67902854ab192610e7885 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 13 Jun 2018 17:10:02 -0400 Subject: [PATCH 244/288] Convert `extDict` Flag to `dictMode` Enum --- lib/compress/zstd_opt.c | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 91ae4ae28..00e0e68e6 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -362,7 +362,7 @@ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE* static U32 ZSTD_insertBt1( ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iend, - U32 const mls, U32 const extDict) + U32 const mls, const ZSTD_dictMode_e dictMode) { U32* const hashTable = ms->hashTable; U32 const hashLog = cParams->hashLog; @@ -425,8 +425,8 @@ static U32 ZSTD_insertBt1( } #endif - if ((!extDict) || (matchIndex+matchLength >= dictLimit)) { - assert(matchIndex+matchLength >= dictLimit); /* might be wrong if extDict is incorrectly set to 0 */ + if ((dictMode != ZSTD_extDict) || (matchIndex+matchLength >= dictLimit)) { + assert(matchIndex+matchLength >= dictLimit); /* might be wrong if actually extDict */ match = base + matchIndex; matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend); } else { @@ -472,16 +472,16 @@ FORCE_INLINE_TEMPLATE void ZSTD_updateTree_internal( ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iend, - const U32 mls, const U32 extDict) + const U32 mls, const ZSTD_dictMode_e dictMode) { const BYTE* const base = ms->window.base; U32 const target = (U32)(ip - base); U32 idx = ms->nextToUpdate; - DEBUGLOG(5, "ZSTD_updateTree_internal, from %u to %u (extDict:%u)", - idx, target, extDict); + DEBUGLOG(5, "ZSTD_updateTree_internal, from %u to %u (dictMode:%u)", + idx, target, dictMode); while(idx < target) - idx += ZSTD_insertBt1(ms, cParams, base+idx, iend, mls, extDict); + idx += ZSTD_insertBt1(ms, cParams, base+idx, iend, mls, dictMode); ms->nextToUpdate = target; } @@ -489,13 +489,13 @@ void ZSTD_updateTree( ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* ip, const BYTE* iend) { - ZSTD_updateTree_internal(ms, cParams, ip, iend, cParams->searchLength, 0 /*extDict*/); + ZSTD_updateTree_internal(ms, cParams, ip, iend, cParams->searchLength, ZSTD_noDict); } FORCE_INLINE_TEMPLATE U32 ZSTD_insertBtAndGetAllMatches ( ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, - const BYTE* const ip, const BYTE* const iLimit, int const extDict, + const BYTE* const ip, const BYTE* const iLimit, const ZSTD_dictMode_e dictMode, U32 rep[ZSTD_REP_NUM], U32 const ll0, ZSTD_match_t* matches, const U32 lengthToBeat, U32 const mls /* template */) { @@ -542,7 +542,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( } else { /* repIndex < dictLimit || repIndex >= current */ const BYTE* const repMatch = dictBase + repIndex; assert(current >= windowLow); - if ( extDict /* this case only valid in extDict mode */ + if ( dictMode == ZSTD_extDict /* this case only valid in extDict mode */ && ( ((repOffset-1) /*intentional overflow*/ < current - windowLow) /* equivalent to `current > repIndex >= windowLow` */ & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */) && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) { @@ -567,7 +567,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( if ((matchIndex3 > windowLow) & (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) { size_t mlen; - if ((!extDict) /*static*/ || (matchIndex3 >= dictLimit)) { + if ((dictMode != ZSTD_extDict) /*static*/ || (matchIndex3 >= dictLimit)) { const BYTE* const match = base + matchIndex3; mlen = ZSTD_count(ip, match, iLimit); } else { @@ -599,7 +599,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( const BYTE* match; assert(current > matchIndex); - if ((!extDict) || (matchIndex+matchLength >= dictLimit)) { + if ((dictMode != ZSTD_extDict) || (matchIndex+matchLength >= dictLimit)) { assert(matchIndex+matchLength >= dictLimit); /* ensure the condition is correct when !extDict */ match = base + matchIndex; matchLength += ZSTD_count(ip+matchLength, match+matchLength, iLimit); @@ -651,22 +651,22 @@ U32 ZSTD_insertBtAndGetAllMatches ( FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches ( ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, - const BYTE* ip, const BYTE* const iHighLimit, int const extDict, + const BYTE* ip, const BYTE* const iHighLimit, const ZSTD_dictMode_e dictMode, U32 rep[ZSTD_REP_NUM], U32 const ll0, ZSTD_match_t* matches, U32 const lengthToBeat) { U32 const matchLengthSearch = cParams->searchLength; DEBUGLOG(8, "ZSTD_BtGetAllMatches"); if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */ - ZSTD_updateTree_internal(ms, cParams, ip, iHighLimit, matchLengthSearch, extDict); + ZSTD_updateTree_internal(ms, cParams, ip, iHighLimit, matchLengthSearch, dictMode); switch(matchLengthSearch) { - case 3 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 3); + case 3 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 3); default : - case 4 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 4); - case 5 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 5); + case 4 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 4); + case 5 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 5); case 7 : - case 6 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, extDict, rep, ll0, matches, lengthToBeat, 6); + case 6 : return ZSTD_insertBtAndGetAllMatches(ms, cParams, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 6); } } @@ -711,7 +711,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, U32 rep[ZSTD_REP_NUM], const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize, - const int optLevel, const int extDict) + const int optLevel, const ZSTD_dictMode_e dictMode) { optState_t* const optStatePtr = &ms->opt; const BYTE* const istart = (const BYTE*)src; @@ -743,7 +743,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, /* find first match */ { U32 const litlen = (U32)(ip - anchor); U32 const ll0 = !litlen; - U32 const nbMatches = ZSTD_BtGetAllMatches(ms, cParams, ip, iend, extDict, rep, ll0, matches, minMatch); + U32 const nbMatches = ZSTD_BtGetAllMatches(ms, cParams, ip, iend, dictMode, rep, ll0, matches, minMatch); if (!nbMatches) { ip++; continue; } /* initialize opt[0] */ @@ -840,7 +840,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, U32 const litlen = (opt[cur].mlen == 0) ? opt[cur].litlen : 0; U32 const previousPrice = opt[cur].price; U32 const basePrice = previousPrice + ZSTD_litLengthPrice(0, optStatePtr, optLevel); - U32 const nbMatches = ZSTD_BtGetAllMatches(ms, cParams, inr, iend, extDict, opt[cur].rep, ll0, matches, minMatch); + U32 const nbMatches = ZSTD_BtGetAllMatches(ms, cParams, inr, iend, dictMode, opt[cur].rep, ll0, matches, minMatch); U32 matchNb; if (!nbMatches) { DEBUGLOG(7, "rPos:%u : no match found", cur); @@ -973,7 +973,7 @@ size_t ZSTD_compressBlock_btopt( const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock_btopt"); - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, 0 /*extDict*/); + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, ZSTD_extDict); } @@ -1019,7 +1019,7 @@ size_t ZSTD_compressBlock_btultra( assert(ms->nextToUpdate >= ms->window.dictLimit && ms->nextToUpdate <= ms->window.dictLimit + 1); memcpy(tmpRep, rep, sizeof(tmpRep)); - ZSTD_compressBlock_opt_generic(ms, seqStore, tmpRep, cParams, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/); /* generate stats into ms->opt*/ + ZSTD_compressBlock_opt_generic(ms, seqStore, tmpRep, cParams, src, srcSize, 2 /*optLevel*/, ZSTD_noDict); /* generate stats into ms->opt*/ ZSTD_resetSeqStore(seqStore); /* invalidate first scan from history */ ms->window.base -= srcSize; @@ -1031,19 +1031,19 @@ size_t ZSTD_compressBlock_btultra( ZSTD_upscaleStats(&ms->opt); } #endif - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, 0 /*extDict*/); + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, ZSTD_noDict); } size_t ZSTD_compressBlock_btopt_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) { - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, 1 /*extDict*/); + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, ZSTD_extDict); } size_t ZSTD_compressBlock_btultra_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) { - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, 1 /*extDict*/); + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, ZSTD_extDict); } From ccbf067973e3510ebd52c711ce0e969cb04852cd Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 13 Jun 2018 17:10:37 -0400 Subject: [PATCH 245/288] Add _dictMatchState Functions --- lib/compress/zstd_compress.c | 3 ++- lib/compress/zstd_opt.c | 14 ++++++++++++++ lib/compress/zstd_opt.h | 7 +++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 75338326d..01ba01bda 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2254,7 +2254,8 @@ ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMo ZSTD_compressBlock_lazy_dictMatchState, ZSTD_compressBlock_lazy2_dictMatchState, ZSTD_compressBlock_btlazy2_dictMatchState, - NULL, NULL /* unimplemented as of yet */ } + ZSTD_compressBlock_btopt_dictMatchState, + ZSTD_compressBlock_btultra_dictMatchState } }; ZSTD_blockCompressor selectedCompressor; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 00e0e68e6..aa0ed6627 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -1034,6 +1034,20 @@ size_t ZSTD_compressBlock_btultra( return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, ZSTD_noDict); } +size_t ZSTD_compressBlock_btopt_dictMatchState( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) +{ + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, ZSTD_dictMatchState); +} + +size_t ZSTD_compressBlock_btultra_dictMatchState( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) +{ + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 2 /*optLevel*/, ZSTD_dictMatchState); +} + size_t ZSTD_compressBlock_btopt_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index b8dc389f3..63dbe79a8 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -28,6 +28,13 @@ size_t ZSTD_compressBlock_btultra( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_btopt_dictMatchState( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_btultra_dictMatchState( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); + size_t ZSTD_compressBlock_btopt_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); From 1e03377bde37d8f16136af97f3ef4b437bde806f Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 14 Jun 2018 13:23:17 -0400 Subject: [PATCH 246/288] Implement RepCode Check --- lib/compress/zstd_opt.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index aa0ed6627..1bb9bec20 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -524,6 +524,13 @@ U32 ZSTD_insertBtAndGetAllMatches ( U32 mnum = 0; U32 nbCompares = 1U << cParams->searchLog; + const ZSTD_matchState_t* dms = ms->dictMatchState; + const BYTE* const dmsBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; + const BYTE* const dmsEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; + U32 const dmsHighLimit = dictMode == ZSTD_dictMatchState ? (U32)(dmsEnd - dmsBase) : 0; + U32 const dmsLowLimit = dictMode == ZSTD_dictMatchState ? dms->window.lowLimit : 0; + U32 const dmsIndexDelta = dictMode == ZSTD_dictMatchState ? windowLow - dmsHighLimit : 0; + size_t bestLength = lengthToBeat-1; DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches: current=%u", current); @@ -547,7 +554,14 @@ U32 ZSTD_insertBtAndGetAllMatches ( & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */) && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) { repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dictEnd, prefixStart) + minMatch; - } } + } + if (dictMode == ZSTD_dictMatchState + && ( ((repOffset-1) /*intentional overflow*/ < current - windowLow) /* equivalent to `current > repIndex >= windowLow` */ + & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */)) { + const BYTE* const repMatch = dmsBase + repIndex - dmsIndexDelta; + if (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) { + repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dictEnd, prefixStart) + minMatch; + } } } /* save longer solution */ if (repLen > bestLength) { DEBUGLOG(8, "found repCode %u (ll0:%u, offset:%u) of length %u", From a075864756ebee79cb8969ee2ec8cf1e08e57103 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 14 Jun 2018 13:33:59 -0400 Subject: [PATCH 247/288] Switch `!= ZSTD_extDict` to `== ZSTD_noDict` --- lib/compress/zstd_opt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 1bb9bec20..20a8ac2e0 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -425,7 +425,7 @@ static U32 ZSTD_insertBt1( } #endif - if ((dictMode != ZSTD_extDict) || (matchIndex+matchLength >= dictLimit)) { + if ((dictMode == ZSTD_noDict) || (matchIndex+matchLength >= dictLimit)) { assert(matchIndex+matchLength >= dictLimit); /* might be wrong if actually extDict */ match = base + matchIndex; matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend); @@ -581,7 +581,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( if ((matchIndex3 > windowLow) & (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) { size_t mlen; - if ((dictMode != ZSTD_extDict) /*static*/ || (matchIndex3 >= dictLimit)) { + if ((dictMode == ZSTD_noDict) /*static*/ || (matchIndex3 >= dictLimit)) { const BYTE* const match = base + matchIndex3; mlen = ZSTD_count(ip, match, iLimit); } else { @@ -613,7 +613,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( const BYTE* match; assert(current > matchIndex); - if ((dictMode != ZSTD_extDict) || (matchIndex+matchLength >= dictLimit)) { + if ((dictMode == ZSTD_noDict) || (matchIndex+matchLength >= dictLimit)) { assert(matchIndex+matchLength >= dictLimit); /* ensure the condition is correct when !extDict */ match = base + matchIndex; matchLength += ZSTD_count(ip+matchLength, match+matchLength, iLimit); From ce743312e2e91c35de209616dec550bf65b1849d Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 14 Jun 2018 14:52:46 -0400 Subject: [PATCH 248/288] Fix Typo --- lib/compress/zstd_opt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 20a8ac2e0..f638c150a 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -560,7 +560,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */)) { const BYTE* const repMatch = dmsBase + repIndex - dmsIndexDelta; if (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) { - repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dictEnd, prefixStart) + minMatch; + repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dmsEnd, prefixStart) + minMatch; } } } /* save longer solution */ if (repLen > bestLength) { From ade8586ce6175d1cd2df9e3902d79197f3a309c4 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 14 Jun 2018 14:53:04 -0400 Subject: [PATCH 249/288] Find `mls == 3` Matches --- lib/compress/zstd_opt.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index f638c150a..974802e17 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -603,7 +603,36 @@ U32 ZSTD_insertBtAndGetAllMatches ( (ip+mlen == iLimit) ) { /* best possible length */ ms->nextToUpdate = current+1; /* skip insertion */ return 1; - } } } } + } + } + } else if (dictMode == ZSTD_dictMatchState) { + /* should we perform this search even if the working ctx search + * found a match? */ + U32 const hashLog3 = ms->hashLog3; + size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3); + U32 const dmsMatchIndex3 = dms->hashTable3[hash3]; + if (dmsMatchIndex3 >= dmsLowLimit) { + const BYTE* const match = dmsBase + dmsMatchIndex3; + size_t mlen = ZSTD_count_2segments(ip, match, iLimit, dmsEnd, prefixStart); + if (mlen >= mls) { + U32 const matchIndex3 = dmsMatchIndex3 + dmsIndexDelta; + DEBUGLOG(8, "found small dms match with hlog3, of length %u", + (U32)mlen); + bestLength = mlen; + assert(current > matchIndex3); + assert(mnum==0); /* no prior solution */ + matches[0].off = (current - matchIndex3) + ZSTD_REP_MOVE; + matches[0].len = (U32)mlen; + mnum = 1; + if ( (mlen > sufficient_len) | + (ip+mlen == iLimit) ) { /* best possible length */ + ms->nextToUpdate = current+1; /* skip insertion */ + return 1; + } + } + } + } + } hashTable[h] = current; /* Update Hash Table */ From 64348a15f1327307d710d5fc3c05095befd26543 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 14 Jun 2018 15:53:46 -0400 Subject: [PATCH 250/288] Misc Fixes --- lib/compress/zstd_opt.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 974802e17..8d90ee7ff 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -547,7 +547,9 @@ U32 ZSTD_insertBtAndGetAllMatches ( repLen = (U32)ZSTD_count(ip+minMatch, ip+minMatch-repOffset, iLimit) + minMatch; } } else { /* repIndex < dictLimit || repIndex >= current */ - const BYTE* const repMatch = dictBase + repIndex; + const BYTE* const repMatch = dictMode == ZSTD_dictMatchState ? + dmsBase + repIndex - dmsIndexDelta : + dictBase + repIndex; assert(current >= windowLow); if ( dictMode == ZSTD_extDict /* this case only valid in extDict mode */ && ( ((repOffset-1) /*intentional overflow*/ < current - windowLow) /* equivalent to `current > repIndex >= windowLow` */ @@ -557,11 +559,10 @@ U32 ZSTD_insertBtAndGetAllMatches ( } if (dictMode == ZSTD_dictMatchState && ( ((repOffset-1) /*intentional overflow*/ < current - windowLow) /* equivalent to `current > repIndex >= windowLow` */ - & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */)) { - const BYTE* const repMatch = dmsBase + repIndex - dmsIndexDelta; - if (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) { - repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dmsEnd, prefixStart) + minMatch; - } } } + & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */) + && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) { + repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dmsEnd, prefixStart) + minMatch; + } } /* save longer solution */ if (repLen > bestLength) { DEBUGLOG(8, "found repCode %u (ll0:%u, offset:%u) of length %u", @@ -577,7 +578,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( /* HC3 match finder */ if ((mls == 3) /*static*/ && (bestLength < mls)) { - U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, ip); + U32 matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, ip); if ((matchIndex3 > windowLow) & (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) { size_t mlen; @@ -615,7 +616,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( const BYTE* const match = dmsBase + dmsMatchIndex3; size_t mlen = ZSTD_count_2segments(ip, match, iLimit, dmsEnd, prefixStart); if (mlen >= mls) { - U32 const matchIndex3 = dmsMatchIndex3 + dmsIndexDelta; + matchIndex3 = dmsMatchIndex3 + dmsIndexDelta; DEBUGLOG(8, "found small dms match with hlog3, of length %u", (U32)mlen); bestLength = mlen; @@ -642,7 +643,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( const BYTE* match; assert(current > matchIndex); - if ((dictMode == ZSTD_noDict) || (matchIndex+matchLength >= dictLimit)) { + if ((dictMode == ZSTD_noDict) || (dictMode == ZSTD_dictMatchState) || (matchIndex+matchLength >= dictLimit)) { assert(matchIndex+matchLength >= dictLimit); /* ensure the condition is correct when !extDict */ match = base + matchIndex; matchLength += ZSTD_count(ip+matchLength, match+matchLength, iLimit); From 2091f34e9e30f801e281b8000e26c9660786eb15 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 14 Jun 2018 15:54:03 -0400 Subject: [PATCH 251/288] Find Proper Matches --- lib/compress/zstd_opt.c | 47 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 8d90ee7ff..bd740f854 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -664,9 +664,10 @@ U32 ZSTD_insertBtAndGetAllMatches ( matches[mnum].off = (current - matchIndex) + ZSTD_REP_MOVE; matches[mnum].len = (U32)matchLength; mnum++; - if (matchLength > ZSTD_OPT_NUM) break; - if (ip+matchLength == iLimit) { /* equal : no way to know if inf or sup */ - break; /* drop, to preserve bt consistency (miss a little bit of compression) */ + if ( matchLength > ZSTD_OPT_NUM + | ip+matchLength == iLimit /* equal : no way to know if inf or sup */) { + if (dictMode == ZSTD_dictMatchState) nbCompares = 0; /* break should also skip searching dms */ + break; /* drop, to preserve bt consistency (miss a little bit of compression) */ } } @@ -687,6 +688,46 @@ U32 ZSTD_insertBtAndGetAllMatches ( *smallerPtr = *largerPtr = 0; + commonLengthSmaller = commonLengthLarger = 0; + + if (dictMode == ZSTD_dictMatchState && nbCompares) { + U32 dictMatchIndex = dms->hashTable[h]; + const U32* const dmsBt = dms->chainTable; + while (nbCompares-- && (dictMatchIndex > dmsLowLimit)) { + const U32* const nextPtr = dmsBt + 2*(dictMatchIndex & btMask); + size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ + const BYTE* match = dmsBase + dictMatchIndex; + matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iLimit, dmsEnd, prefixStart); + if (dictMatchIndex+matchLength >= dmsHighLimit) + match = base + dictMatchIndex + dmsIndexDelta; /* to prepare for next usage of match[matchLength] */ + + if (matchLength > bestLength) { + matchIndex = dictMatchIndex + dmsIndexDelta; + if (matchLength > matchEndIdx - matchIndex) + matchEndIdx = matchIndex + (U32)matchLength; + bestLength = matchLength; + matches[mnum].off = (current - matchIndex) + ZSTD_REP_MOVE; + matches[mnum].len = (U32)matchLength; + mnum++; + if ( matchLength > ZSTD_OPT_NUM + | ip+matchLength == iLimit /* equal : no way to know if inf or sup */) { + break; /* drop, to guarantee consistency (miss a little bit of compression) */ + } + } + + if (match[matchLength] < ip[matchLength]) { + if (dictMatchIndex <= btLow) { break; } /* beyond tree size, stop the search */ + commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ + dictMatchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ + } else { + /* match is larger than current */ + if (dictMatchIndex <= btLow) { break; } /* beyond tree size, stop the search */ + commonLengthLarger = matchLength; + dictMatchIndex = nextPtr[0]; + } + } + } + assert(matchEndIdx > current+8); ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ return mnum; From 4bb79f9c559e7111d9a5c2c4a025565e8937904b Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 14 Jun 2018 16:59:49 -0400 Subject: [PATCH 252/288] Misc Changes --- lib/compress/zstd_opt.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index bd740f854..ea80d7805 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -612,7 +612,8 @@ U32 ZSTD_insertBtAndGetAllMatches ( U32 const hashLog3 = ms->hashLog3; size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3); U32 const dmsMatchIndex3 = dms->hashTable3[hash3]; - if (dmsMatchIndex3 >= dmsLowLimit) { + matchIndex3 = dmsMatchIndex3 + dmsIndexDelta; + if ((dmsMatchIndex3 >= dmsLowLimit) & (current - matchIndex3 < (1<<18))) { const BYTE* const match = dmsBase + dmsMatchIndex3; size_t mlen = ZSTD_count_2segments(ip, match, iLimit, dmsEnd, prefixStart); if (mlen >= mls) { @@ -664,8 +665,8 @@ U32 ZSTD_insertBtAndGetAllMatches ( matches[mnum].off = (current - matchIndex) + ZSTD_REP_MOVE; matches[mnum].len = (U32)matchLength; mnum++; - if ( matchLength > ZSTD_OPT_NUM - | ip+matchLength == iLimit /* equal : no way to know if inf or sup */) { + if ( (matchLength > ZSTD_OPT_NUM) + | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) { if (dictMode == ZSTD_dictMatchState) nbCompares = 0; /* break should also skip searching dms */ break; /* drop, to preserve bt consistency (miss a little bit of compression) */ } @@ -709,19 +710,18 @@ U32 ZSTD_insertBtAndGetAllMatches ( matches[mnum].off = (current - matchIndex) + ZSTD_REP_MOVE; matches[mnum].len = (U32)matchLength; mnum++; - if ( matchLength > ZSTD_OPT_NUM - | ip+matchLength == iLimit /* equal : no way to know if inf or sup */) { + if ( (matchLength > ZSTD_OPT_NUM) + | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) { break; /* drop, to guarantee consistency (miss a little bit of compression) */ } } + if (dictMatchIndex <= btLow) { break; } /* beyond tree size, stop the search */ if (match[matchLength] < ip[matchLength]) { - if (dictMatchIndex <= btLow) { break; } /* beyond tree size, stop the search */ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ dictMatchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ } else { /* match is larger than current */ - if (dictMatchIndex <= btLow) { break; } /* beyond tree size, stop the search */ commonLengthLarger = matchLength; dictMatchIndex = nextPtr[0]; } From 87fe4788a36019fcfbc0c1feb31341ab85b6bd72 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 14 Jun 2018 20:59:29 -0400 Subject: [PATCH 253/288] Fix Compression Ratio Regression #1 --- lib/compress/zstd_opt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index ea80d7805..c22ddeb53 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -425,7 +425,7 @@ static U32 ZSTD_insertBt1( } #endif - if ((dictMode == ZSTD_noDict) || (matchIndex+matchLength >= dictLimit)) { + if ((dictMode == ZSTD_noDict) || (dictMode == ZSTD_dictMatchState) || (matchIndex+matchLength >= dictLimit)) { assert(matchIndex+matchLength >= dictLimit); /* might be wrong if actually extDict */ match = base + matchIndex; matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend); @@ -530,6 +530,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( U32 const dmsHighLimit = dictMode == ZSTD_dictMatchState ? (U32)(dmsEnd - dmsBase) : 0; U32 const dmsLowLimit = dictMode == ZSTD_dictMatchState ? dms->window.lowLimit : 0; U32 const dmsIndexDelta = dictMode == ZSTD_dictMatchState ? windowLow - dmsHighLimit : 0; + U32 const dmsBtLow = dictMode == ZSTD_dictMatchState || btMask >= dmsHighLimit ? 0 : dmsHighLimit - btMask; size_t bestLength = lengthToBeat-1; DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches: current=%u", current); @@ -551,15 +552,14 @@ U32 ZSTD_insertBtAndGetAllMatches ( dmsBase + repIndex - dmsIndexDelta : dictBase + repIndex; assert(current >= windowLow); - if ( dictMode == ZSTD_extDict /* this case only valid in extDict mode */ + if ( dictMode == ZSTD_extDict && ( ((repOffset-1) /*intentional overflow*/ < current - windowLow) /* equivalent to `current > repIndex >= windowLow` */ & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */) && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) { repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dictEnd, prefixStart) + minMatch; } if (dictMode == ZSTD_dictMatchState - && ( ((repOffset-1) /*intentional overflow*/ < current - windowLow) /* equivalent to `current > repIndex >= windowLow` */ - & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */) + && ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow : do not test positions overlapping 2 memory segments */ && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) { repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dmsEnd, prefixStart) + minMatch; } } @@ -582,7 +582,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( if ((matchIndex3 > windowLow) & (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) { size_t mlen; - if ((dictMode == ZSTD_noDict) /*static*/ || (matchIndex3 >= dictLimit)) { + if ((dictMode == ZSTD_noDict) /*static*/ || (dictMode == ZSTD_dictMatchState) /*static*/ || (matchIndex3 >= dictLimit)) { const BYTE* const match = base + matchIndex3; mlen = ZSTD_count(ip, match, iLimit); } else { @@ -716,7 +716,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( } } - if (dictMatchIndex <= btLow) { break; } /* beyond tree size, stop the search */ + if (dictMatchIndex <= dmsBtLow) { break; } /* beyond tree size, stop the search */ if (match[matchLength] < ip[matchLength]) { commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ dictMatchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ From f0a13bcd68466f230ac5bc4dc92854779b5bc285 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Fri, 15 Jun 2018 14:08:58 -0400 Subject: [PATCH 254/288] Make Sure Position 0 Gets Into the Tree --- lib/compress/zstd_compress.c | 1 + lib/compress/zstd_opt.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 01ba01bda..216196ed6 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -966,6 +966,7 @@ static void ZSTD_invalidateMatchState(ZSTD_matchState_t* ms) ZSTD_window_clear(&ms->window); ms->nextToUpdate = ms->window.dictLimit + 1; + ms->nextToUpdate3 = ms->window.dictLimit + 1; ms->loadedDictEnd = 0; ms->opt.litLengthSum = 0; /* force reset of btopt stats */ ms->dictMatchState = NULL; diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index c22ddeb53..92805b9b2 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -384,6 +384,7 @@ static U32 ZSTD_insertBt1( U32* largerPtr = smallerPtr + 1; U32 dummy32; /* to be nullified at the end */ U32 const windowLow = ms->window.lowLimit; + U32 const matchLow = windowLow ? windowLow : 1; U32 matchEndIdx = current+8+1; size_t bestLength = 8; U32 nbCompares = 1U << cParams->searchLog; @@ -399,7 +400,7 @@ static U32 ZSTD_insertBt1( assert(ip <= iend-8); /* required for h calculation */ hashTable[h] = current; /* Update Hash Table */ - while (nbCompares-- && (matchIndex > windowLow)) { + while (nbCompares-- && (matchIndex >= matchLow)) { U32* const nextPtr = bt + 2*(matchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ assert(matchIndex < current); @@ -517,6 +518,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( const BYTE* const prefixStart = base + dictLimit; U32 const btLow = btMask >= current ? 0 : current - btMask; U32 const windowLow = ms->window.lowLimit; + U32 const matchLow = windowLow ? windowLow : 1; U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = bt + 2*(current&btMask) + 1; U32 matchEndIdx = current+8+1; /* farthest referenced position of any match => detects repetitive patterns */ @@ -638,7 +640,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( hashTable[h] = current; /* Update Hash Table */ - while (nbCompares-- && (matchIndex > windowLow)) { + while (nbCompares-- && (matchIndex >= matchLow)) { U32* const nextPtr = bt + 2*(matchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ const BYTE* match; From de639502aa84bfa2ec46e1c8befdc583232332ed Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 19 Jun 2018 15:13:30 -0400 Subject: [PATCH 255/288] Update Dict Attachment Cut-Offs --- lib/compress/zstd_compress.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 216196ed6..3ab0f9509 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1239,8 +1239,8 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, 32 KB, /* ZSTD_lazy */ 32 KB, /* ZSTD_lazy2 */ 32 KB, /* ZSTD_btlazy2 */ - 256 KB, /* ZSTD_btopt */ - 256 KB /* ZSTD_btultra */ + 32 KB, /* ZSTD_btopt */ + 8 KB /* ZSTD_btultra */ }; const int attachDict = ( pledgedSrcSize <= attachDictSizeCutoffs[cdict->cParams.strategy] || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) From 03c39c540b28a2f323d96105ec01bc5b000debc8 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 19 Jun 2018 15:36:33 -0400 Subject: [PATCH 256/288] Fix Incorrect Param --- lib/compress/zstd_opt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 92805b9b2..d494982a3 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -1060,7 +1060,7 @@ size_t ZSTD_compressBlock_btopt( const ZSTD_compressionParameters* cParams, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock_btopt"); - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, ZSTD_extDict); + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, cParams, src, srcSize, 0 /*optLevel*/, ZSTD_noDict); } From 4567c571994428ce19710b9b03ff3b28cfaa7c38 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 19 Jun 2018 16:03:12 -0700 Subject: [PATCH 257/288] finalized POOL_resize() POOL_ctx* POOL_resize(POOL_ctx* ctx, size_t numThreads) The function may fail, and returns a NULL pointer in this case. --- lib/common/pool.c | 50 +++++++++++++++++++++++++---------------------- lib/common/pool.h | 10 ++++++++++ 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index 6795f25eb..e64833f87 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -185,18 +185,21 @@ size_t POOL_sizeof(POOL_ctx *ctx) { } -/* note : only works if no job is running ! - * return : 1 on success, 0 on failure */ -static int POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) +/* note : only works if no job is running ! */ +static POOL_ctx* POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) { - if (ctx->numThreadsBusy > 0) return 0; + if (ctx->numThreadsBusy > 0) return NULL; if (numThreads <= ctx->threadCapacity) { ctx->threadLimit = numThreads; - return 1; + return ctx; } /* numThreads > threadCapacity */ { ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem); - if (!threadPool) return 0; + if (!threadPool) return NULL; + /* replace existing thread pool */ + memcpy(threadPool, ctx->threads, ctx->threadCapacity); + ZSTD_free(ctx->threads, ctx->customMem); + ctx->threads = threadPool; /* Initialize additional threads */ { size_t threadId; for (threadId = ctx->threadCapacity; threadId < numThreads; ++threadId) { @@ -204,30 +207,26 @@ static int POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) break; } } if (threadId != numThreads) { /* not all threads successfully init */ - /* how to destroy existing threads ? */ - /* POOL_join destroy all existing threads, not just newly created ones */ - return 0; - } - } - /* replace existing thread pool */ - memcpy(threadPool, ctx->threads, ctx->threadCapacity); - ZSTD_free(ctx->threads, ctx->customMem); - ctx->threads = threadPool; - } + ctx->threadCapacity = threadId; + return NULL; /* will release the pool */ + } } } + /* successfully expanded */ ctx->threadCapacity = numThreads; ctx->threadLimit = numThreads; - return 1; + return ctx; } -/* return : 1 on success, 0 on failure */ -int POOL_resize(POOL_ctx* ctx, size_t numThreads) +/* @return : a working pool on success, NULL on failure + * note : starting context is considered consumed. */ +POOL_ctx* POOL_resize(POOL_ctx* ctx, size_t numThreads) { - int result; - if (!ctx) return 0; + POOL_ctx* newCtx; + if (ctx==NULL) return NULL; ZSTD_pthread_mutex_lock(&ctx->queueMutex); - result = POOL_resize_internal(ctx, numThreads); + newCtx = POOL_resize_internal(ctx, numThreads); ZSTD_pthread_mutex_unlock(&ctx->queueMutex); - return result; + if (newCtx!=ctx) POOL_free(ctx); + return newCtx; } /** @@ -314,6 +313,11 @@ void POOL_free(POOL_ctx* ctx) { (void)ctx; } +POOL_ctx* POOL_resize(POOL_ctx* ctx, size_t numThreads) { + (void)numThreads; + return ctx; +} + void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque) { (void)ctx; function(opaque); diff --git a/lib/common/pool.h b/lib/common/pool.h index dba28859c..00ea76631 100644 --- a/lib/common/pool.h +++ b/lib/common/pool.h @@ -38,6 +38,16 @@ POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, */ void POOL_free(POOL_ctx* ctx); +/*! POOL_resize() : + * Expands or shrinks pool's number of threads. + * This is more efficient than releasing and creating a new context. + * @return : a new pool context on success, NULL on failure + * note : new pool context might have same address as original one, but it's not guaranteed. + * consider starting context as consumed, only rely on returned one. + * note 2 : only numThreads can be resized, queueSize is unchanged. + */ +POOL_ctx* POOL_resize(POOL_ctx* ctx, size_t numThreads); + /*! POOL_sizeof() : * @return threadpool memory usage * note : compatible with NULL (returns 0 in this case) From 066fbbfe1c8f849420ff9f10711bd1d11b8ce15b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 19 Jun 2018 17:28:56 -0700 Subject: [PATCH 258/288] make zstdmt resize its context when nbThreads change. Technically, it only expands. But when instructed to use less threads, the thread pool will limit nb of concurrent threads. --- lib/compress/zstd_compress.c | 6 +-- lib/compress/zstdmt_compress.c | 98 ++++++++++++++++++++++++++-------- lib/compress/zstdmt_compress.h | 5 -- 3 files changed, 77 insertions(+), 32 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c071c5738..d268cef08 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3645,13 +3645,9 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, } if (params.nbWorkers > 0) { /* mt context creation */ - if (cctx->mtctx == NULL || (params.nbWorkers != ZSTDMT_getNbWorkers(cctx->mtctx))) { + if (cctx->mtctx == NULL) { DEBUGLOG(4, "ZSTD_compress_generic: creating new mtctx for nbWorkers=%u", params.nbWorkers); - if (cctx->mtctx != NULL) - DEBUGLOG(4, "ZSTD_compress_generic: previous nbWorkers was %u", - ZSTDMT_getNbWorkers(cctx->mtctx)); - ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbWorkers, cctx->customMem); if (cctx->mtctx == NULL) return ERROR(memory_allocation); } diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 0c59c6a72..4a2a4cdd5 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -159,6 +159,25 @@ static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* const bufPool, size_t const ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); } + +static ZSTDMT_bufferPool* ZSTDMT_expandBufferPool(ZSTDMT_bufferPool* srcBufPool, U32 nbWorkers) +{ + unsigned const maxNbBuffers = 2*nbWorkers + 3; + if (srcBufPool==NULL) return NULL; + if (srcBufPool->totalBuffers >= maxNbBuffers) /* good enough */ + return srcBufPool; + /* need a larger buffer pool */ + { ZSTD_customMem const cMem = srcBufPool->cMem; + size_t const bSize = srcBufPool->bufferSize; /* forward parameters */ + ZSTDMT_bufferPool* newBufPool; + ZSTDMT_freeBufferPool(srcBufPool); + newBufPool = ZSTDMT_createBufferPool(nbWorkers, cMem); + if (newBufPool==NULL) return newBufPool; + ZSTDMT_setBufferSize(newBufPool, bSize); + return newBufPool; + } +} + /** ZSTDMT_getBuffer() : * assumption : bufPool must be valid * @return : a buffer, with start pointer and size @@ -309,6 +328,10 @@ static void ZSTDMT_freeSeqPool(ZSTDMT_seqPool* seqPool) ZSTDMT_freeBufferPool(seqPool); } +static ZSTDMT_seqPool* ZSTDMT_expandSeqPool(ZSTDMT_seqPool* pool, U32 nbWorkers) +{ + return ZSTDMT_expandBufferPool(pool, nbWorkers); +} /* ===== CCtx Pool ===== */ @@ -354,6 +377,18 @@ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbWorkers, return cctxPool; } +static ZSTDMT_CCtxPool* ZSTDMT_expandCCtxPool(ZSTDMT_CCtxPool* srcPool, + unsigned nbWorkers) +{ + if (srcPool==NULL) return NULL; + if (nbWorkers <= srcPool->totalCCtx) return srcPool; /* good enough */ + /* need a larger cctx pool */ + { ZSTD_customMem const cMem = srcPool->cMem; + ZSTDMT_freeCCtxPool(srcPool); + return ZSTDMT_createCCtxPool(nbWorkers, cMem); + } +} + /* only works during initialization phase, not during compression */ static size_t ZSTDMT_sizeof_CCtxPool(ZSTDMT_CCtxPool* cctxPool) { @@ -745,9 +780,9 @@ struct ZSTDMT_CCtx_s { ZSTD_CCtx_params params; size_t targetSectionSize; size_t targetPrefixSize; - roundBuff_t roundBuff; + int jobReady; /* 1 => one job is already prepared, but pool has shortage of workers. Don't create a new job. */ inBuff_t inBuff; - int jobReady; /* 1 => one job is already prepared, but pool has shortage of workers. Don't create another one. */ + roundBuff_t roundBuff; serialState_t serial; unsigned singleBlockingThread; unsigned jobIDMask; @@ -798,6 +833,20 @@ static ZSTDMT_jobDescription* ZSTDMT_createJobsTable(U32* nbJobsPtr, ZSTD_custom return jobTable; } +static size_t ZSTDMT_expandJobsTable (ZSTDMT_CCtx* mtctx, U32 nbWorkers) { + U32 nbJobs = nbWorkers + 2; + if (nbJobs > mtctx->jobIDMask+1) { /* need more job capacity */ + ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem); + mtctx->jobIDMask = 0; + mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, mtctx->cMem); + if (mtctx->jobs==NULL) return ERROR(memory_allocation); + assert((nbJobs != 0) && ((nbJobs & (nbJobs - 1)) == 0)); /* ensure nbJobs is a power of 2 */ + mtctx->jobIDMask = nbJobs - 1; + } + return 0; +} + + /* ZSTDMT_CCtxParam_setNbWorkers(): * Internal use only */ size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers) @@ -964,6 +1013,25 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) return jobParams; } + +/* ZSTDMT_resize() : + * @return : error code if fails, 0 on success */ +static size_t ZSTDMT_resize(ZSTDMT_CCtx* mtctx, unsigned nbWorkers) +{ + mtctx->factory = POOL_resize(mtctx->factory, nbWorkers); + if (mtctx->factory == NULL) return ERROR(memory_allocation); + CHECK_F( ZSTDMT_expandJobsTable(mtctx, nbWorkers) ); + mtctx->bufPool = ZSTDMT_expandBufferPool(mtctx->bufPool, nbWorkers); + if (mtctx->bufPool == NULL) return ERROR(memory_allocation); + mtctx->cctxPool = ZSTDMT_expandCCtxPool(mtctx->cctxPool, nbWorkers); + if (mtctx->cctxPool == NULL) return ERROR(memory_allocation); + mtctx->seqPool = ZSTDMT_expandSeqPool(mtctx->seqPool, nbWorkers); + if (mtctx->seqPool == NULL) return ERROR(memory_allocation); + ZSTDMT_CCtxParam_setNbWorkers(&mtctx->params, nbWorkers); + return 0; +} + + /*! ZSTDMT_updateCParams_whileCompressing() : * Updates only a selected set of compression parameters, to remain compatible with current frame. * New parameters will be applied to next compression job. */ @@ -980,15 +1048,6 @@ void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_p } } -/* ZSTDMT_getNbWorkers(): - * @return nb threads currently active in mtctx. - * mtctx must be valid */ -unsigned ZSTDMT_getNbWorkers(const ZSTDMT_CCtx* mtctx) -{ - assert(mtctx != NULL); - return mtctx->params.nbWorkers; -} - /* ZSTDMT_getFrameProgression(): * tells how much data has been consumed (input) and produced (output) for current frame. * able to count progression inside worker threads. @@ -1089,15 +1148,7 @@ static size_t ZSTDMT_compress_advanced_internal( if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params)) return ERROR(memory_allocation); - if (nbJobs > mtctx->jobIDMask+1) { /* enlarge job table */ - U32 jobsTableSize = nbJobs; - ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem); - mtctx->jobIDMask = 0; - mtctx->jobs = ZSTDMT_createJobsTable(&jobsTableSize, mtctx->cMem); - if (mtctx->jobs==NULL) return ERROR(memory_allocation); - assert((jobsTableSize != 0) && ((jobsTableSize & (jobsTableSize - 1)) == 0)); /* ensure jobsTableSize is a power of 2 */ - mtctx->jobIDMask = jobsTableSize - 1; - } + CHECK_F( ZSTDMT_expandJobsTable(mtctx, nbJobs) ); /* only expands if necessary */ { unsigned u; for (u=0; u cctxPool->totalCCtx); - /* params are supposed to be fully validated at this point */ + + /* params supposed partially fully validated at this point */ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ - assert(mtctx->cctxPool->totalCCtx == params.nbWorkers); /* init */ + if (params.nbWorkers != mtctx->params.nbWorkers) + ZSTDMT_resize(mtctx, params.nbWorkers); + if (params.jobSize == 0) { params.jobSize = 1U << ZSTDMT_computeTargetJobLog(params); } diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index f79e3b441..4249a82de 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -126,11 +126,6 @@ size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorker * New parameters will be applied to next compression job. */ void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* cctxParams); -/* ZSTDMT_getNbWorkers(): - * @return nb threads currently active in mtctx. - * mtctx must be valid */ -unsigned ZSTDMT_getNbWorkers(const ZSTDMT_CCtx* mtctx); - /* ZSTDMT_getFrameProgression(): * tells how much data has been consumed (input) and produced (output) for current frame. * able to count progression inside worker threads. From 166901dc726691e31de143b9f570693256a1c953 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 19 Jun 2018 18:07:18 -0700 Subject: [PATCH 259/288] reduced POOL_resize() restriction It's not necessary to ensure that no job is ongoing. The pool is only expanded, existing threads are preserved. In case of error, the only option is to return NULL and terminate the thread pool anyway. --- lib/common/pool.c | 1 - lib/compress/zstdmt_compress.c | 2 +- lib/decompress/zstd_decompress.c | 4 ---- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index e64833f87..a0e2bedf0 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -188,7 +188,6 @@ size_t POOL_sizeof(POOL_ctx *ctx) { /* note : only works if no job is running ! */ static POOL_ctx* POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) { - if (ctx->numThreadsBusy > 0) return NULL; if (numThreads <= ctx->threadCapacity) { ctx->threadLimit = numThreads; return ctx; diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 4a2a4cdd5..bbbdc5cd4 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1280,7 +1280,7 @@ size_t ZSTDMT_initCStream_internal( /* init */ if (params.nbWorkers != mtctx->params.nbWorkers) - ZSTDMT_resize(mtctx, params.nbWorkers); + CHECK_F( ZSTDMT_resize(mtctx, params.nbWorkers) ); if (params.jobSize == 0) { params.jobSize = 1U << ZSTDMT_computeTargetJobLog(params); diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 148c87f9d..4de98a8e6 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1108,15 +1108,11 @@ size_t ZSTD_execSequence(BYTE* op, return sequenceLength; } /* span extDict & currentPrefixSegment */ - DEBUGLOG(2, "ZSTD_execSequence: found a 2-segments match") { size_t const length1 = dictEnd - match; - DEBUGLOG(2, "first part (extDict) is %zu bytes long", length1); memmove(oLitEnd, match, length1); op = oLitEnd + length1; sequence.matchLength -= length1; - DEBUGLOG(2, "second part (prefix) is %zu bytes long", sequence.matchLength); match = prefixStart; - DEBUGLOG(2, "first byte of 2nd part : %02X", *prefixStart); if (op > oend_w || sequence.matchLength < MINMATCH) { U32 i; for (i = 0; i < sequence.matchLength; ++i) op[i] = match[i]; From 62469c9f41747ba78a8c616d84d7b36ffc2b7b41 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 19 Jun 2018 20:14:03 -0700 Subject: [PATCH 260/288] fixed wrong size in pthread struct transfer --- lib/common/pool.c | 11 +++++------ tests/zstreamtest.c | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index a0e2bedf0..7f9219e36 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -196,19 +196,18 @@ static POOL_ctx* POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) { ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem); if (!threadPool) return NULL; /* replace existing thread pool */ - memcpy(threadPool, ctx->threads, ctx->threadCapacity); + memcpy(threadPool, ctx->threads, ctx->threadCapacity * sizeof(ctx->threads[0])); ZSTD_free(ctx->threads, ctx->customMem); ctx->threads = threadPool; /* Initialize additional threads */ { size_t threadId; for (threadId = ctx->threadCapacity; threadId < numThreads; ++threadId) { if (ZSTD_pthread_create(&threadPool[threadId], NULL, &POOL_thread, ctx)) { - break; + ctx->threadCapacity = threadId; + ctx->threadLimit = threadId; + return NULL; /* will release the pool */ } } - if (threadId != numThreads) { /* not all threads successfully init */ - ctx->threadCapacity = threadId; - return NULL; /* will release the pool */ - } } } + } } /* successfully expanded */ ctx->threadCapacity = numThreads; ctx->threadLimit = numThreads; diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 425fbab39..fba99aa34 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -1643,13 +1643,13 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double DISPLAYLEVEL(5, "Creating new context \n"); ZSTD_freeCCtx(zc); zc = ZSTD_createCCtx(); - CHECK(zc==NULL, "ZSTD_createCCtx allocation error"); - resetAllowed=0; + CHECK(zc == NULL, "ZSTD_createCCtx allocation error"); + resetAllowed = 0; } if ((FUZ_rand(&lseed) & 0xFF) == 132) { ZSTD_freeDStream(zd); zd = ZSTD_createDStream(); - CHECK(zd==NULL, "ZSTD_createDStream allocation error"); + CHECK(zd == NULL, "ZSTD_createDStream allocation error"); ZSTD_initDStream_usingDict(zd, NULL, 0); /* ensure at least one init */ } From ae0b7ffa0a0edee2342f97115f37c23febc9746b Mon Sep 17 00:00:00 2001 From: cyan4973 Date: Wed, 20 Jun 2018 09:45:02 -0700 Subject: [PATCH 261/288] made Visual Studio compatible with DEBUGLEVEL >= 2 --- build/VS2010/fullbench/fullbench.vcxproj | 3 ++- build/VS2010/fuzzer/fuzzer.vcxproj | 1 + build/VS2010/libzstd-dll/libzstd-dll.vcxproj | 1 + build/VS2010/libzstd/libzstd.vcxproj | 1 + build/VS2010/zstd/zstd.vcxproj | 3 ++- lib/compress/zstdmt_compress.c | 4 ++-- programs/windres/zstd32.res | Bin 1044 -> 1044 bytes programs/windres/zstd64.res | Bin 1044 -> 1044 bytes 8 files changed, 9 insertions(+), 4 deletions(-) diff --git a/build/VS2010/fullbench/fullbench.vcxproj b/build/VS2010/fullbench/fullbench.vcxproj index 2ec8c4f61..19faf92cb 100644 --- a/build/VS2010/fullbench/fullbench.vcxproj +++ b/build/VS2010/fullbench/fullbench.vcxproj @@ -156,13 +156,14 @@ - + + diff --git a/build/VS2010/fuzzer/fuzzer.vcxproj b/build/VS2010/fuzzer/fuzzer.vcxproj index ac400028b..f0d1ab066 100644 --- a/build/VS2010/fuzzer/fuzzer.vcxproj +++ b/build/VS2010/fuzzer/fuzzer.vcxproj @@ -156,6 +156,7 @@ + diff --git a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj index db9fdedbb..92d518d3d 100644 --- a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj +++ b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj @@ -20,6 +20,7 @@ + diff --git a/build/VS2010/libzstd/libzstd.vcxproj b/build/VS2010/libzstd/libzstd.vcxproj index 9550b03cf..c306fcec9 100644 --- a/build/VS2010/libzstd/libzstd.vcxproj +++ b/build/VS2010/libzstd/libzstd.vcxproj @@ -20,6 +20,7 @@ + diff --git a/build/VS2010/zstd/zstd.vcxproj b/build/VS2010/zstd/zstd.vcxproj index 36a265a33..4af281326 100644 --- a/build/VS2010/zstd/zstd.vcxproj +++ b/build/VS2010/zstd/zstd.vcxproj @@ -19,14 +19,15 @@ + - + diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 0c59c6a72..a348361a0 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -37,7 +37,7 @@ #define ZSTD_RESIZE_SEQPOOL 0 /* ====== Debug ====== */ -#if defined(DEBUGLEVEL) && (DEBUGLEVEL>=2) +#if defined(DEBUGLEVEL) && (DEBUGLEVEL>=2) && !defined(_MSC_VER) # include # include @@ -47,7 +47,7 @@ unsigned debug_u; \ for (debug_u=0; debug_u<(n); debug_u++) \ RAWLOG(l, "%02X ", ((const unsigned char*)(p))[debug_u]); \ - RAWLOG(l, " \n"); \ + RAWLOG(l, " \n"); \ } static unsigned long long GetCurrentClockTimeMicroseconds(void) diff --git a/programs/windres/zstd32.res b/programs/windres/zstd32.res index d6caf985ed2a9469d7a06c8518fc8af5780e34d5..26800ab321c2fc6b0a0d57a28e2710f71c8732c7 100644 GIT binary patch delta 33 pcmbQjF@ `PF~5J4FH7X2oL}O delta 33 pcmbQjF@ `PF~5J4FH7X2oL}O delta 33 pcmbQjF@ Date: Tue, 19 Jun 2018 22:18:08 -0400 Subject: [PATCH 262/288] Remove Dead(!) HC3 DMS Lookup --- lib/compress/zstd_opt.c | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index d494982a3..5296d9603 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -580,7 +580,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( /* HC3 match finder */ if ((mls == 3) /*static*/ && (bestLength < mls)) { - U32 matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, ip); + U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, ip); if ((matchIndex3 > windowLow) & (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) { size_t mlen; @@ -608,34 +608,8 @@ U32 ZSTD_insertBtAndGetAllMatches ( return 1; } } - } else if (dictMode == ZSTD_dictMatchState) { - /* should we perform this search even if the working ctx search - * found a match? */ - U32 const hashLog3 = ms->hashLog3; - size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3); - U32 const dmsMatchIndex3 = dms->hashTable3[hash3]; - matchIndex3 = dmsMatchIndex3 + dmsIndexDelta; - if ((dmsMatchIndex3 >= dmsLowLimit) & (current - matchIndex3 < (1<<18))) { - const BYTE* const match = dmsBase + dmsMatchIndex3; - size_t mlen = ZSTD_count_2segments(ip, match, iLimit, dmsEnd, prefixStart); - if (mlen >= mls) { - matchIndex3 = dmsMatchIndex3 + dmsIndexDelta; - DEBUGLOG(8, "found small dms match with hlog3, of length %u", - (U32)mlen); - bestLength = mlen; - assert(current > matchIndex3); - assert(mnum==0); /* no prior solution */ - matches[0].off = (current - matchIndex3) + ZSTD_REP_MOVE; - matches[0].len = (U32)mlen; - mnum = 1; - if ( (mlen > sufficient_len) | - (ip+mlen == iLimit) ) { /* best possible length */ - ms->nextToUpdate = current+1; /* skip insertion */ - return 1; - } - } - } } + /* no dictMatchState lookup: dicts don't have a populated HC3 table */ } hashTable[h] = current; /* Update Hash Table */ From 0a6cf7cd1d02b42fcdff17d1a538f021ad6c8345 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 20 Jun 2018 15:27:23 -0400 Subject: [PATCH 263/288] Minor Changes --- lib/compress/zstd_opt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 5296d9603..0abdf3b90 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -359,7 +359,7 @@ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE* /** ZSTD_insertBt1() : add one or multiple positions to tree. * ip : assumed <= iend-8 . * @return : nb of positions added */ -static U32 ZSTD_insertBt1( +FORCE_INLINE_TEMPLATE U32 ZSTD_insertBt1( ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iend, U32 const mls, const ZSTD_dictMode_e dictMode) @@ -665,11 +665,10 @@ U32 ZSTD_insertBtAndGetAllMatches ( *smallerPtr = *largerPtr = 0; - commonLengthSmaller = commonLengthLarger = 0; - if (dictMode == ZSTD_dictMatchState && nbCompares) { U32 dictMatchIndex = dms->hashTable[h]; const U32* const dmsBt = dms->chainTable; + commonLengthSmaller = commonLengthLarger = 0; while (nbCompares-- && (dictMatchIndex > dmsLowLimit)) { const U32* const nextPtr = dmsBt + 2*(dictMatchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ From 9c14eafe3d679472382253b6de4ac5e40baac69e Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 20 Jun 2018 15:51:14 -0400 Subject: [PATCH 264/288] Also Use `matchLow` for HC3 Match --- lib/compress/zstd_opt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 0abdf3b90..90a262025 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -581,7 +581,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( /* HC3 match finder */ if ((mls == 3) /*static*/ && (bestLength < mls)) { U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, ip); - if ((matchIndex3 > windowLow) + if ((matchIndex3 >= matchLow) & (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) { size_t mlen; if ((dictMode == ZSTD_noDict) /*static*/ || (dictMode == ZSTD_dictMatchState) /*static*/ || (matchIndex3 >= dictLimit)) { From 5d81f71e8390ddce11e186854d407f5d9bfc7805 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 20 Jun 2018 16:54:53 -0400 Subject: [PATCH 265/288] Consistency in Guarding DMS-Only Variable Initializations --- lib/compress/zstd_opt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 90a262025..3a6e4c45d 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -526,7 +526,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( U32 mnum = 0; U32 nbCompares = 1U << cParams->searchLog; - const ZSTD_matchState_t* dms = ms->dictMatchState; + const ZSTD_matchState_t* dms = dictMode == ZSTD_dictMatchState ? ms->dictMatchState : NULL; const BYTE* const dmsBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; const BYTE* const dmsEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; U32 const dmsHighLimit = dictMode == ZSTD_dictMatchState ? (U32)(dmsEnd - dmsBase) : 0; From 6b48eb12c04faa34e6c950c0206b149edcc1f828 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 20 Jun 2018 14:35:39 -0700 Subject: [PATCH 266/288] change control of threadLimit now limits maximum nb of active threads even when queueSize > 1. --- lib/common/pool.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index 7f9219e36..ca5a38ee5 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -10,9 +10,10 @@ /* ====== Dependencies ======= */ -#include /* size_t */ -#include "pool.h" +#include /* size_t */ +#include "debug.h" /* assert */ #include "zstd_internal.h" /* ZSTD_malloc, ZSTD_free */ +#include "pool.h" /* ====== Compiler specifics ====== */ #if defined(_MSC_VER) @@ -70,14 +71,14 @@ static void* POOL_thread(void* opaque) { /* Lock the mutex and wait for a non-empty queue or until shutdown */ ZSTD_pthread_mutex_lock(&ctx->queueMutex); - while (ctx->queueEmpty && !ctx->shutdown) { + while ( ctx->queueEmpty + || (ctx->numThreadsBusy >= ctx->threadLimit) ) { + if (ctx->shutdown) { + ZSTD_pthread_mutex_unlock(&ctx->queueMutex); + return opaque; + } ZSTD_pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex); } - /* empty => shutting down: so stop */ - if (ctx->queueEmpty) { - ZSTD_pthread_mutex_unlock(&ctx->queueMutex); - return opaque; - } /* Pop a job off the queue */ { POOL_job const job = ctx->queue[ctx->queueHead]; ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize; @@ -97,7 +98,7 @@ static void* POOL_thread(void* opaque) { ZSTD_pthread_cond_signal(&ctx->queuePushCond); } } } /* for (;;) */ - /* Unreachable */ + assert(0); /* Unreachable */ } POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) { @@ -237,7 +238,7 @@ static int isQueueFull(POOL_ctx const* ctx) { if (ctx->queueSize > 1) { return ctx->queueHead == ((ctx->queueTail + 1) % ctx->queueSize); } else { - return ctx->numThreadsBusy == ctx->threadLimit || + return (ctx->numThreadsBusy == ctx->threadLimit) || !ctx->queueEmpty; } } From 6de249c1c61e19071af1b28d09f39b41484772d4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 20 Jun 2018 17:18:57 -0700 Subject: [PATCH 267/288] fixed: bug when counting nb of active threads when queueSize > 1 also : added a test in testpool.c verifying resizing is effective. --- lib/common/pool.c | 9 ++-- tests/poolTests.c | 130 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 115 insertions(+), 24 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index ca5a38ee5..d08b1de79 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -91,12 +91,13 @@ static void* POOL_thread(void* opaque) { job.function(job.opaque); /* If the intended queue size was 0, signal after finishing job */ + ZSTD_pthread_mutex_lock(&ctx->queueMutex); + ctx->numThreadsBusy--; if (ctx->queueSize == 1) { - ZSTD_pthread_mutex_lock(&ctx->queueMutex); - ctx->numThreadsBusy--; - ZSTD_pthread_mutex_unlock(&ctx->queueMutex); ZSTD_pthread_cond_signal(&ctx->queuePushCond); - } } + } + ZSTD_pthread_mutex_unlock(&ctx->queueMutex); + } } /* for (;;) */ assert(0); /* Unreachable */ } diff --git a/tests/poolTests.c b/tests/poolTests.c index 00ee83015..23061b568 100644 --- a/tests/poolTests.c +++ b/tests/poolTests.c @@ -15,11 +15,11 @@ #include #include -#define ASSERT_TRUE(p) \ - do { \ - if (!(p)) { \ - return 1; \ - } \ +#define ASSERT_TRUE(p) \ + do { \ + if (!(p)) { \ + return 1; \ + } \ } while (0) #define ASSERT_FALSE(p) ASSERT_TRUE(!(p)) #define ASSERT_EQ(lhs, rhs) ASSERT_TRUE((lhs) == (rhs)) @@ -32,10 +32,10 @@ struct data { void fn(void *opaque) { struct data *data = (struct data *)opaque; - pthread_mutex_lock(&data->mutex); + ZSTD_pthread_mutex_lock(&data->mutex); data->data[data->i] = data->i; ++data->i; - pthread_mutex_unlock(&data->mutex); + ZSTD_pthread_mutex_unlock(&data->mutex); } int testOrder(size_t numThreads, size_t queueSize) { @@ -43,25 +43,26 @@ int testOrder(size_t numThreads, size_t queueSize) { POOL_ctx *ctx = POOL_create(numThreads, queueSize); ASSERT_TRUE(ctx); data.i = 0; - pthread_mutex_init(&data.mutex, NULL); - { - size_t i; + ZSTD_pthread_mutex_init(&data.mutex, NULL); + { size_t i; for (i = 0; i < 16; ++i) { POOL_add(ctx, &fn, &data); } } POOL_free(ctx); ASSERT_EQ(16, data.i); - { - size_t i; + { size_t i; for (i = 0; i < data.i; ++i) { ASSERT_EQ(i, data.data[i]); } } - pthread_mutex_destroy(&data.mutex); + ZSTD_pthread_mutex_destroy(&data.mutex); return 0; } + +/* --- test deadlocks --- */ + void waitFn(void *opaque) { (void)opaque; UTIL_sleepMilli(1); @@ -72,8 +73,7 @@ int testWait(size_t numThreads, size_t queueSize) { struct data data; POOL_ctx *ctx = POOL_create(numThreads, queueSize); ASSERT_TRUE(ctx); - { - size_t i; + { size_t i; for (i = 0; i < 16; ++i) { POOL_add(ctx, &waitFn, &data); } @@ -82,25 +82,115 @@ int testWait(size_t numThreads, size_t queueSize) { return 0; } + +/* --- test POOL_resize() --- */ + +typedef struct { + ZSTD_pthread_mutex_t mut; + int val; + int max; + ZSTD_pthread_cond_t cond; +} test_t; + +void waitLongFn(void *opaque) { + test_t* test = (test_t*) opaque; + UTIL_sleepMilli(10); + ZSTD_pthread_mutex_lock(&test->mut); + test->val = test->val + 1; + if (test->val == test->max) + ZSTD_pthread_cond_signal(&test->cond); + ZSTD_pthread_mutex_unlock(&test->mut); +} + +static int testThreadReduction_internal(POOL_ctx* ctx, test_t test) +{ + int const nbWaits = 16; + UTIL_time_t startTime, time4threads, time2threads; + + test.val = 0; + test.max = nbWaits; + + startTime = UTIL_getTime(); + { int i; + for (i=0; i = time2threads) return 1; /* check 4 threads were effectively faster than 2 */ + return 0; +} + +static int testThreadReduction(void) { + int result; + test_t test; + POOL_ctx* ctx = POOL_create(4 /*nbThreads*/, 2 /*queueSize*/); + + ASSERT_TRUE(ctx); + + memset(&test, 0, sizeof(test)); + ASSERT_FALSE( ZSTD_pthread_mutex_init(&test.mut, NULL) ); + ASSERT_FALSE( ZSTD_pthread_cond_init(&test.cond, NULL) ); + + result = testThreadReduction_internal(ctx, test); + + ZSTD_pthread_mutex_destroy(&test.mut); + ZSTD_pthread_cond_destroy(&test.cond); + POOL_free(ctx); + return result; +} + + +/* --- test launcher --- */ + int main(int argc, const char **argv) { size_t numThreads; + (void)argc; + (void)argv; + + if (POOL_create(0, 1)) { /* should not be possible */ + printf("FAIL: should not create POOL with 0 threads\n"); + return 1; + } + for (numThreads = 1; numThreads <= 4; ++numThreads) { size_t queueSize; for (queueSize = 0; queueSize <= 2; ++queueSize) { + printf("queueSize==%u, numThreads=%u \n", + (unsigned)queueSize, (unsigned)numThreads); if (testOrder(numThreads, queueSize)) { printf("FAIL: testOrder\n"); return 1; } + printf("SUCCESS: testOrder\n"); if (testWait(numThreads, queueSize)) { printf("FAIL: testWait\n"); return 1; } + printf("SUCCESS: testWait\n"); } } - printf("PASS: testOrder\n"); - (void)argc; - (void)argv; - return (POOL_create(0, 1)) ? printf("FAIL: testInvalid\n"), 1 - : printf("PASS: testInvalid\n"), 0; + + if (testThreadReduction()) return 1; + printf("PASS: all POOL tests\n"); + return 0; } From 5da9bbc38ede591d6cf8464bfa28f1b7bba05e61 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 21 Jun 2018 11:20:01 -0400 Subject: [PATCH 268/288] Conceivably Dedup ZSTD_noDict and ZSTD_dictMatchState _insertBt1 Impls By reverting to the bool extDict flag, we call ZSTD_insertBt1 with the same const args in both non-extDict dictModes. --- lib/compress/zstd_opt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 3a6e4c45d..1d0ca9ca4 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -359,10 +359,10 @@ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE* /** ZSTD_insertBt1() : add one or multiple positions to tree. * ip : assumed <= iend-8 . * @return : nb of positions added */ -FORCE_INLINE_TEMPLATE U32 ZSTD_insertBt1( +static U32 ZSTD_insertBt1( ZSTD_matchState_t* ms, ZSTD_compressionParameters const* cParams, const BYTE* const ip, const BYTE* const iend, - U32 const mls, const ZSTD_dictMode_e dictMode) + U32 const mls, const int extDict) { U32* const hashTable = ms->hashTable; U32 const hashLog = cParams->hashLog; @@ -426,7 +426,7 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_insertBt1( } #endif - if ((dictMode == ZSTD_noDict) || (dictMode == ZSTD_dictMatchState) || (matchIndex+matchLength >= dictLimit)) { + if (!extDict || (matchIndex+matchLength >= dictLimit)) { assert(matchIndex+matchLength >= dictLimit); /* might be wrong if actually extDict */ match = base + matchIndex; matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend); @@ -482,7 +482,7 @@ void ZSTD_updateTree_internal( idx, target, dictMode); while(idx < target) - idx += ZSTD_insertBt1(ms, cParams, base+idx, iend, mls, dictMode); + idx += ZSTD_insertBt1(ms, cParams, base+idx, iend, mls, dictMode == ZSTD_extDict); ms->nextToUpdate = target; } From 7d80ada5caa31c1d964e960f86670238473041a5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 21 Jun 2018 12:24:36 -0700 Subject: [PATCH 269/288] added a test for POOL (multithreading) ensuring all jobs in queue are nonetheless completed when POOL is instructed to end abruptly (POOL_free()) --- tests/poolTests.c | 76 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/tests/poolTests.c b/tests/poolTests.c index 23061b568..d7ff70ac9 100644 --- a/tests/poolTests.c +++ b/tests/poolTests.c @@ -90,10 +90,10 @@ typedef struct { int val; int max; ZSTD_pthread_cond_t cond; -} test_t; +} poolTest_t; void waitLongFn(void *opaque) { - test_t* test = (test_t*) opaque; + poolTest_t* test = (poolTest_t*) opaque; UTIL_sleepMilli(10); ZSTD_pthread_mutex_lock(&test->mut); test->val = test->val + 1; @@ -102,7 +102,7 @@ void waitLongFn(void *opaque) { ZSTD_pthread_mutex_unlock(&test->mut); } -static int testThreadReduction_internal(POOL_ctx* ctx, test_t test) +static int testThreadReduction_internal(POOL_ctx* ctx, poolTest_t test) { int const nbWaits = 16; UTIL_time_t startTime, time4threads, time2threads; @@ -117,7 +117,7 @@ static int testThreadReduction_internal(POOL_ctx* ctx, test_t test) } ZSTD_pthread_mutex_lock(&test.mut); ZSTD_pthread_cond_wait(&test.cond, &test.mut); - ASSERT_TRUE(test.val == nbWaits); + ASSERT_EQ(test.val, nbWaits); ZSTD_pthread_mutex_unlock(&test.mut); time4threads = UTIL_clockSpanNano(startTime); @@ -131,7 +131,7 @@ static int testThreadReduction_internal(POOL_ctx* ctx, test_t test) } ZSTD_pthread_mutex_lock(&test.mut); ZSTD_pthread_cond_wait(&test.cond, &test.mut); - ASSERT_TRUE(test.val == nbWaits); + ASSERT_EQ(test.val, nbWaits); ZSTD_pthread_mutex_unlock(&test.mut); time2threads = UTIL_clockSpanNano(startTime); @@ -141,7 +141,7 @@ static int testThreadReduction_internal(POOL_ctx* ctx, test_t test) static int testThreadReduction(void) { int result; - test_t test; + poolTest_t test; POOL_ctx* ctx = POOL_create(4 /*nbThreads*/, 2 /*queueSize*/); ASSERT_TRUE(ctx); @@ -155,10 +155,59 @@ static int testThreadReduction(void) { ZSTD_pthread_mutex_destroy(&test.mut); ZSTD_pthread_cond_destroy(&test.cond); POOL_free(ctx); + return result; } +/* --- test abrupt ending --- */ + +typedef struct { + ZSTD_pthread_mutex_t mut; + int val; +} abruptEndCanary_t; + +void waitIncFn(void *opaque) { + abruptEndCanary_t* test = (abruptEndCanary_t*) opaque; + UTIL_sleepMilli(1); + ZSTD_pthread_mutex_lock(&test->mut); + test->val = test->val + 1; + ZSTD_pthread_mutex_unlock(&test->mut); +} + +static int testAbruptEnding_internal(abruptEndCanary_t test) +{ + int const nbWaits = 16; + + POOL_ctx* const ctx = POOL_create(2 /*numThreads*/, nbWaits /*queueSize*/); + ASSERT_TRUE(ctx); + test.val = 0; + + { int i; + for (i=0; i Date: Thu, 21 Jun 2018 15:24:08 -0400 Subject: [PATCH 270/288] Fix `dmsBtLow` Test --- lib/compress/zstd_lazy.c | 2 +- lib/compress/zstd_opt.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index 078d20360..bfe944928 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -166,7 +166,7 @@ static size_t ZSTD_DUBT_findBetterDictMatch ( U32* const dictBt = dms->chainTable; U32 const btLog = cParams->chainLog - 1; U32 const btMask = (1 << btLog) - 1; - U32 const btLow = (btMask >= dictHighLimit - dictLowLimit) ? 0 : dictHighLimit - btMask; + U32 const btLow = (btMask >= dictHighLimit - dictLowLimit) ? dictLowLimit : dictHighLimit - btMask; size_t commonLengthSmaller=0, commonLengthLarger=0; U32 matchEndIdx = current+8+1; diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 1d0ca9ca4..3f7023f78 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -532,7 +532,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( U32 const dmsHighLimit = dictMode == ZSTD_dictMatchState ? (U32)(dmsEnd - dmsBase) : 0; U32 const dmsLowLimit = dictMode == ZSTD_dictMatchState ? dms->window.lowLimit : 0; U32 const dmsIndexDelta = dictMode == ZSTD_dictMatchState ? windowLow - dmsHighLimit : 0; - U32 const dmsBtLow = dictMode == ZSTD_dictMatchState || btMask >= dmsHighLimit ? 0 : dmsHighLimit - btMask; + U32 const dmsBtLow = dictMode == ZSTD_dictMatchState && btMask < dmsHighLimit - dmsLowLimit ? dmsHighLimit - btMask : dmsLowLimit; size_t bestLength = lengthToBeat-1; DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches: current=%u", current); From 5bd3d4b7d2dc660a25fe14e4d966ac947ac7b5c5 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 21 Jun 2018 15:25:44 -0400 Subject: [PATCH 271/288] Add Debug Log Statement --- lib/compress/zstd_opt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 3f7023f78..7bd7941d8 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -679,6 +679,8 @@ U32 ZSTD_insertBtAndGetAllMatches ( if (matchLength > bestLength) { matchIndex = dictMatchIndex + dmsIndexDelta; + DEBUGLOG(8, "found dms match of length %u at distance %u (offCode=%u)", + (U32)matchLength, current - matchIndex, current - matchIndex + ZSTD_REP_MOVE); if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; bestLength = matchLength; From 3e91dc4d6a96b6122807c2e411851c5220212f91 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 21 Jun 2018 15:25:32 -0400 Subject: [PATCH 272/288] Add Repcode Bounds Check --- lib/compress/zstd_opt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 7bd7941d8..476cdc148 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -561,7 +561,8 @@ U32 ZSTD_insertBtAndGetAllMatches ( repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dictEnd, prefixStart) + minMatch; } if (dictMode == ZSTD_dictMatchState - && ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow : do not test positions overlapping 2 memory segments */ + && ( ((repOffset-1) /*intentional overflow*/ < current - (dmsLowLimit + dmsIndexDelta)) /* equivalent to `current > repIndex >= dmsLowLimit` */ + & ((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */ && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) { repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dmsEnd, prefixStart) + minMatch; } } From 01bb1c1016e5bf9bd0ae7501333d70c5978caf48 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 21 Jun 2018 17:02:50 -0400 Subject: [PATCH 273/288] Add CCtx Param Controlling Dict Attachment Behavior --- lib/compress/zstd_compress.c | 17 ++++++++++++++++- lib/compress/zstd_compress_internal.h | 8 ++++++++ lib/zstd.h | 22 ++++++++++++++++++++++ tests/fuzz/zstd_helpers.c | 1 + 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c071c5738..d7bcffdf2 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -275,6 +275,7 @@ static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param) case ZSTD_p_ldmMinMatch: case ZSTD_p_ldmBucketSizeLog: case ZSTD_p_ldmHashEveryLog: + case ZSTD_p_forceAttachDict: default: return 0; } @@ -319,6 +320,9 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v * default : 0 when using a CDict, 1 when using a Prefix */ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); + case ZSTD_p_forceAttachDict: + return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); + case ZSTD_p_nbWorkers: if ((value>0) && cctx->staticSize) { return ERROR(parameter_unsupported); /* MT not compatible with static alloc */ @@ -424,6 +428,12 @@ size_t ZSTD_CCtxParam_setParameter( CCtxParams->forceWindow = (value > 0); return CCtxParams->forceWindow; + case ZSTD_p_forceAttachDict : + CCtxParams->attachDictPref = value ? + (value > 0 ? ZSTD_dictForceAttach : ZSTD_dictForceCopy) : + ZSTD_dictDefaultAttach; + return CCtxParams->attachDictPref; + case ZSTD_p_nbWorkers : #ifndef ZSTD_MULTITHREAD if (value>0) return ERROR(parameter_unsupported); @@ -527,6 +537,9 @@ size_t ZSTD_CCtxParam_getParameter( case ZSTD_p_forceMaxWindow : *value = CCtxParams->forceWindow; break; + case ZSTD_p_forceAttachDict : + *value = CCtxParams->attachDictPref; + break; case ZSTD_p_nbWorkers : #ifndef ZSTD_MULTITHREAD assert(CCtxParams->nbWorkers == 0); @@ -1242,7 +1255,9 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, 256 KB /* ZSTD_btultra */ }; const int attachDict = ( pledgedSrcSize <= attachDictSizeCutoffs[cdict->cParams.strategy] - || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) + || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN + || params.attachDictPref == ZSTD_dictForceAttach ) + && params.attachDictPref != ZSTD_dictForceCopy && !params.forceWindow /* dictMatchState isn't correctly * handled in _enforceMaxDist */ && cdict->cParams.strategy <= ZSTD_btlazy2 diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index ec9d01ccc..d31542c69 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -48,6 +48,12 @@ extern "C" { typedef enum { ZSTDcs_created=0, ZSTDcs_init, ZSTDcs_ongoing, ZSTDcs_ending } ZSTD_compressionStage_e; typedef enum { zcss_init=0, zcss_load, zcss_flush } ZSTD_cStreamStage; +typedef enum { + ZSTD_dictDefaultAttach = 0, + ZSTD_dictForceAttach = 1, + ZSTD_dictForceCopy = -1, +} ZSTD_dictAttachPref_e; + typedef struct ZSTD_prefixDict_s { const void* dict; size_t dictSize; @@ -186,6 +192,8 @@ struct ZSTD_CCtx_params_s { int forceWindow; /* force back-references to respect limit of * 1< Date: Thu, 21 Jun 2018 14:58:59 -0700 Subject: [PATCH 274/288] added extended POOL test abrupt end + downsizing with running jobs remaining in queue. also : POOL_resize() requires numThreads >= 1 --- lib/common/pool.c | 17 +++++++++++------ lib/common/pool.h | 1 + tests/poolTests.c | 7 ++++--- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index d08b1de79..bd31f032f 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -73,10 +73,13 @@ static void* POOL_thread(void* opaque) { while ( ctx->queueEmpty || (ctx->numThreadsBusy >= ctx->threadLimit) ) { - if (ctx->shutdown) { - ZSTD_pthread_mutex_unlock(&ctx->queueMutex); - return opaque; - } + if (ctx->shutdown) { + /* even if !queueEmpty, (possible if numThreadsBusy >= threadLimit), + * a few threads will be shutdown while !queueEmpty, + * but enough threads will remain active to finish the queue */ + ZSTD_pthread_mutex_unlock(&ctx->queueMutex); + return opaque; + } ZSTD_pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex); } /* Pop a job off the queue */ @@ -99,7 +102,7 @@ static void* POOL_thread(void* opaque) { ZSTD_pthread_mutex_unlock(&ctx->queueMutex); } } /* for (;;) */ - assert(0); /* Unreachable */ + assert(0); /* Unreachable */ } POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) { @@ -187,10 +190,12 @@ size_t POOL_sizeof(POOL_ctx *ctx) { } -/* note : only works if no job is running ! */ +/* @return : a working pool on success, NULL on failure + * note : starting context is considered consumed. */ static POOL_ctx* POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) { if (numThreads <= ctx->threadCapacity) { + if (!numThreads) return NULL; ctx->threadLimit = numThreads; return ctx; } diff --git a/lib/common/pool.h b/lib/common/pool.h index 00ea76631..caf514907 100644 --- a/lib/common/pool.h +++ b/lib/common/pool.h @@ -45,6 +45,7 @@ void POOL_free(POOL_ctx* ctx); * note : new pool context might have same address as original one, but it's not guaranteed. * consider starting context as consumed, only rely on returned one. * note 2 : only numThreads can be resized, queueSize is unchanged. + * note 3 : `numThreads` must be at least 1 */ POOL_ctx* POOL_resize(POOL_ctx* ctx, size_t numThreads); diff --git a/tests/poolTests.c b/tests/poolTests.c index d7ff70ac9..d5768967a 100644 --- a/tests/poolTests.c +++ b/tests/poolTests.c @@ -169,7 +169,7 @@ typedef struct { void waitIncFn(void *opaque) { abruptEndCanary_t* test = (abruptEndCanary_t*) opaque; - UTIL_sleepMilli(1); + UTIL_sleepMilli(10); ZSTD_pthread_mutex_lock(&test->mut); test->val = test->val + 1; ZSTD_pthread_mutex_unlock(&test->mut); @@ -179,14 +179,15 @@ static int testAbruptEnding_internal(abruptEndCanary_t test) { int const nbWaits = 16; - POOL_ctx* const ctx = POOL_create(2 /*numThreads*/, nbWaits /*queueSize*/); + POOL_ctx* ctx = POOL_create(3 /*numThreads*/, nbWaits /*queueSize*/); ASSERT_TRUE(ctx); test.val = 0; { int i; for (i=0; i Date: Thu, 21 Jun 2018 17:48:34 -0700 Subject: [PATCH 275/288] updated Zstandard frame format adding clarifications from IETF RFC DISCUSS. --- doc/zstd_compression_format.md | 181 +++++++++++++++++++++------------ 1 file changed, 116 insertions(+), 65 deletions(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index 7fcc1f629..91838f757 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -57,11 +57,7 @@ explaining which parameter is unsupported. This specification is intended for use by implementers of software to compress data into Zstandard format and/or decompress data from Zstandard format. The Zstandard format is supported by an open source reference implementation, -which also contains some useful validation tool, -such as `decodeCorpus`, which generate random valid frames, -that a compliant decoder should be able to decode, -or provide a meaningful error code explaining why it cannot. - +written in portable C, and available at : https://github.com/facebook/zstd . ### Overall conventions @@ -208,10 +204,10 @@ depending on local limitations. __`Unused_bit`__ -The value of this bit should be set to zero. -A decoder compliant with this specification version shall not interpret it. -It might be used in a future version, -to signal a property which is not mandatory to properly decode the frame. +A decoder compliant with this specification version shall not interpret this bit. +It might be used in any future version, +to signal a property which is transparent to properly decode the frame. +An encoder compliant with this specification version must set this bit to zero. __`Reserved_bit`__ @@ -261,6 +257,9 @@ Window_Size = windowBase + windowAdd; The minimum `Window_Size` is 1 KB. The maximum `Window_Size` is `(1<<41) + 7*(1<<38)` bytes, which is 3.75 TB. +In general, larger `Window_Size` tend to improve compression ratio, +but at the cost of memory usage. + To properly decode compressed data, a decoder will need to allocate a buffer of at least `Window_Size` bytes. @@ -269,8 +268,8 @@ a decoder is allowed to reject a compressed frame which requests a memory size beyond decoder's authorized range. For improved interoperability, -decoders are recommended to be compatible with `Window_Size <= 8 MB`, -and encoders are recommended to not request more than 8 MB. +it's recommended for decoders to support `Window_Size` of up to 8 MB, +and it's recommended for encoders to not generate frame requiring `Window_Size` larger than 8 MB. It's merely a recommendation though, decoders are free to support larger or lower limits, depending on local limitations. @@ -280,7 +279,7 @@ depending on local limitations. This is a variable size field, which contains the ID of the dictionary required to properly decode the frame. `Dictionary_ID` field is optional. When it's not present, -it's up to the decoder to make sure it uses the correct dictionary. +it's up to the decoder to know which dictionary to use. `Dictionary_ID` field size is provided by `DID_Field_Size`. `DID_Field_Size` is directly derived from value of `Dictionary_ID_flag`. @@ -293,13 +292,21 @@ It's allowed to represent a small ID (for example `13`) with a large 4-bytes dictionary ID, even if it is less efficient. _Reserved ranges :_ -If the frame is going to be distributed in a private environment, -any dictionary ID can be used. -However, for public distribution of compressed frames using a dictionary, -the following ranges are reserved and shall not be used : +Within private environments, any `Dictionary_ID` can be used. + +However, for frames and dictionaries distributed in public space, +`Dictionary_ID` must be attributed carefully. +Rules for public environment are not yet decided, +but the following ranges are reserved for some future registrar : - low range : `<= 32767` - high range : `>= (1 << 31)` +Outside of these ranges, any value of `Dictionary_ID` +which is both `>= 32768` and `< (1<<31)` can be used freely, +even in public environment. + + + #### `Frame_Content_Size` This is the original (uncompressed) size. This information is optional. @@ -372,6 +379,7 @@ There are 4 block types : - `Reserved` - this is not a block. This value cannot be used with current version of this specification. + If such a value is present, it is considered corrupted data. __`Block_Size`__ @@ -384,6 +392,8 @@ A block can contain any number of bytes (even zero), up to A `Compressed_Block` has the extra restriction that `Block_Size` is always strictly less than the decompressed size. +If this condition cannot be respected, +the block must be sent uncompressed instead (`Raw_Block`). Compressed Blocks @@ -401,7 +411,7 @@ data in [Sequence Execution](#sequence-execution) #### Prerequisites To decode a compressed block, the following elements are necessary : - Previous decoded data, up to a distance of `Window_Size`, - or all previously decoded data when `Single_Segment_flag` is set. + or beginning of the Frame, whichever is smaller. - List of "recent offsets" from previous `Compressed_Block`. - The previous Huffman tree, required by `Treeless_Literals_Block` type - Previous FSE decoding tables, required by `Repeat_Mode` @@ -422,11 +432,11 @@ Literals can be stored uncompressed or compressed using Huffman prefix codes. When compressed, an optional tree description can be present, followed by 1 or 4 streams. -| `Literals_Section_Header` | [`Huffman_Tree_Description`] | Stream1 | [Stream2] | [Stream3] | [Stream4] | -| ------------------------- | ---------------------------- | ------- | --------- | --------- | --------- | +| `Literals_Section_Header` | [`Huffman_Tree_Description`] | [jumpTable] | Stream1 | [Stream2] | [Stream3] | [Stream4] | +| ------------------------- | ---------------------------- | ----------- | ------- | --------- | --------- | --------- | -#### `Literals_Section_Header` +### `Literals_Section_Header` Header is in charge of describing how literals are packed. It's a byte-aligned variable-size bitfield, ranging from 1 to 5 bytes, @@ -518,50 +528,55 @@ Both `Compressed_Size` and `Regenerated_Size` fields follow __little-endian__ co Note: `Compressed_Size` __includes__ the size of the Huffman Tree description _when_ it is present. -### Raw Literals Block +#### Raw Literals Block The data in Stream1 is `Regenerated_Size` bytes long, it contains the raw literals data to be used during [Sequence Execution]. -### RLE Literals Block +#### RLE Literals Block Stream1 consists of a single byte which should be repeated `Regenerated_Size` times to generate the decoded literals. -### Compressed Literals Block and Treeless Literals Block +#### Compressed Literals Block and Treeless Literals Block Both of these modes contain Huffman encoded data. -`Treeless_Literals_Block` does not have a `Huffman_Tree_Description`. -#### `Huffman_Tree_Description` +For `Treeless_Literals_Block`, +the Huffman table comes from previously compressed literals block, +or from a dictionary. + + +### `Huffman_Tree_Description` This section is only present when `Literals_Block_Type` type is `Compressed_Literals_Block` (`2`). The format of the Huffman tree description can be found at [Huffman Tree description](#huffman-tree-description). The size of `Huffman_Tree_Description` is determined during decoding process, it must be used to determine where streams begin. `Total_Streams_Size = Compressed_Size - Huffman_Tree_Description_Size`. -For `Treeless_Literals_Block`, -the Huffman table comes from previously compressed literals block, -or from a dictionary. -Huffman compressed data consists of either 1 or 4 Huffman-coded streams. +### Jump Table +The Jump Table is only present when there are 4 Huffman-coded streams. + +Reminder : Huffman compressed data consists of either 1 or 4 Huffman-coded streams. If only one stream is present, it is a single bitstream occupying the entire remaining portion of the literals block, encoded as described within [Huffman-Coded Streams](#huffman-coded-streams). -If there are four streams, the literals section header only provides enough -information to know the decompressed and compressed sizes of all four streams _combined_. -The decompressed size of each stream is equal to `(Regenerated_Size+3)/4`, +If there are four streams, `Literals_Section_Header` only provided +enough information to know the decompressed and compressed sizes +of all four streams _combined_. +The decompressed size of _each_ stream is equal to `(Regenerated_Size+3)/4`, except for the last stream which may be up to 3 bytes smaller, to reach a total decompressed size as specified in `Regenerated_Size`. -The compressed size of each stream is provided explicitly: -the first 6 bytes of the compressed data consist of three 2-byte __little-endian__ fields, +The compressed size of each stream is provided explicitly in the Jump Table. +Jump Table is 6 bytes long, and consist of three 2-byte __little-endian__ fields, describing the compressed sizes of the first three streams. `Stream4_Size` is computed from total `Total_Streams_Size` minus sizes of other streams. `Stream4_Size = Total_Streams_Size - 6 - Stream1_Size - Stream2_Size - Stream3_Size`. -Note: remember that `Total_Streams_Size` can be smaller than `Compressed_Size` in header, -because `Compressed_Size` also contains `Huffman_Tree_Description_Size` when it is present. +Note: if `Stream1_Size + Stream2_Size + Stream3_Size > Total_Streams_Size`, +data is considered corrupted. Each of these 4 bitstreams is then decoded independently as a Huffman-Coded stream, as described at [Huffman-Coded Streams](#huffman-coded-streams) @@ -579,7 +594,7 @@ When all _sequences_ are decoded, if there are literals left in the _literal section_, these bytes are added at the end of the block. -This is described in more detail in [Sequence Execution](#sequence-execution) +This is described in more detail in [Sequence Execution](#sequence-execution). The `Sequences_Section` regroup all symbols required to decode commands. There are 3 symbol types : literals lengths, offsets and match lengths. @@ -725,7 +740,7 @@ Offset codes are values ranging from `0` to `N`. A decoder is free to limit its maximum `N` supported. Recommendation is to support at least up to `22`. For information, at the time of this writing. -the reference decoder supports a maximum `N` value of `31` in 64-bits mode. +the reference decoder supports a maximum `N` value of `31`. An offset code is also the number of additional bits to read in __little-endian__ fashion, and can be translated into an `Offset_Value` using the following formulas : @@ -734,11 +749,12 @@ and can be translated into an `Offset_Value` using the following formulas : Offset_Value = (1 << offsetCode) + readNBits(offsetCode); if (Offset_Value > 3) offset = Offset_Value - 3; ``` -It means that maximum `Offset_Value` is `(2^(N+1))-1` and it supports back-reference distance up to `(2^(N+1))-4` +It means that maximum `Offset_Value` is `(2^(N+1))-1` +supporting back-reference distances up to `(2^(N+1))-4`, but is limited by [maximum back-reference distance](#window_descriptor). `Offset_Value` from 1 to 3 are special : they define "repeat codes". -This is described in more detail in [Repeat Offsets](#repeat-offsets). +This is described in more details in [Repeat Offsets](#repeat-offsets). #### Decoding Sequences FSE bitstreams are read in reverse direction than written. In zstd, @@ -885,7 +901,8 @@ so an `offset_value` of 1 means `Repeated_Offset2`, an `offset_value` of 2 means `Repeated_Offset3`, and an `offset_value` of 3 means `Repeated_Offset1 - 1_byte`. -For the first block, the starting offset history is populated with the following values : 1, 4 and 8 (in order), +For the first block, the starting offset history is populated with following values : +`Repeated_Offset1`=1, `Repeated_Offset2`=4, `Repeated_Offset3`=8, unless a dictionary is used, in which case they come from the dictionary. Then each block gets its starting offset history from the ending values of the most recent `Compressed_Block`. @@ -923,7 +940,7 @@ and their content ignored, resuming decoding after the skippable frame. It can be noted that a skippable frame can be used to watermark a stream of concatenated frames embedding any kind of tracking information (even just an UUID). -User wary of such usage should scan the stream of concatenated frames +Users wary of such possibility should scan the stream of concatenated frames in an attempt to detect such frame for analysis or removal. __`Magic_Number`__ @@ -931,6 +948,7 @@ __`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. +This specification doesn't detail any specific tagging for skippable frames. __`Frame_Size`__ @@ -944,10 +962,16 @@ __`User_Data`__ The `User_Data` can be anything. Data will just be skipped by the decoder. + Entropy Encoding ---------------- Two types of entropy encoding are used by the Zstandard format: FSE, and Huffman coding. +Huffman is used to compress literals, +while FSE is used for all other symbols +(`Literals_Length_Code`, `Match_Length_Code`, offset codes) +and to compress Huffman headers. + FSE --- @@ -965,7 +989,7 @@ For additional details on FSE, see [Finite State Entropy]. FSE decoding involves a decoding table which has a power of 2 size, and contain three elements: `Symbol`, `Num_Bits`, and `Baseline`. The `log2` of the table size is its `Accuracy_Log`. -The FSE state represents an index in this table. +An FSE state value represents an index in this table. To obtain the initial state value, consume `Accuracy_Log` bits from the stream as a __little-endian__ value. The next symbol in the stream is the `Symbol` indicated in the table for that state. @@ -984,10 +1008,11 @@ on a normalized scale of `1 << Accuracy_Log` . Note that there must be two or more symbols with nonzero probability. It's a bitstream which is read forward, in __little-endian__ fashion. -It's not necessary to know its exact size, -since it will be discovered and reported by the decoding process. +It's not necessary to know bitstream exact size, +it will be discovered and reported by the decoding process. The bitstream starts by reporting on which scale it operates. +Let's `low4Bits` designate the lowest 4 bits of the first byte : `Accuracy_Log = low4bits + 5`. Then follows each symbol value, from `0` to last present one. @@ -1045,7 +1070,7 @@ and how many symbols are present. The bitstream consumes a round number of bytes. Any remaining bit within the last byte is just unused. -##### From normalized distribution to decoding tables +#### From normalized distribution to decoding tables The distribution of normalized probabilities is enough to create a unique decoding table. @@ -1156,7 +1181,7 @@ More bits improve accuracy but cost more header size, and require more memory or more complex decoding operations. This specification limits maximum code length to 11 bits. -##### Representation +#### Representation All literal values from zero (included) to last present one (excluded) are represented by `Weight` with values from `0` to `Max_Number_of_Bits`. @@ -1171,12 +1196,13 @@ This power of 2 gives `Max_Number_of_Bits`, the depth of the current tree. __Example__ : Let's presume the following Huffman tree must be described : -| literal | 0 | 1 | 2 | 3 | 4 | 5 | +| literal value | 0 | 1 | 2 | 3 | 4 | 5 | | ---------------- | --- | --- | --- | --- | --- | --- | | `Number_of_Bits` | 1 | 2 | 3 | 0 | 4 | 4 | -The tree depth is 4, since its smallest element uses 4 bits. -Value `5` will not be listed as it can be determined from the values for 0-4, +The tree depth is 4, since its longest elements uses 4 bits +(longest elements are the one with smallest frequency). +Value `5` will not be listed, as it can be determined from values for 0-4, nor will values above `5` as they are all 0. Values from `0` to `4` will be listed using `Weight` instead of `Number_of_Bits`. Weight formula is : @@ -1185,9 +1211,9 @@ Weight = Number_of_Bits ? (Max_Number_of_Bits + 1 - Number_of_Bits) : 0 ``` It gives the following series of weights : -| literal | 0 | 1 | 2 | 3 | 4 | -| -------- | --- | --- | --- | --- | --- | -| `Weight` | 4 | 3 | 2 | 0 | 1 | +| literal value | 0 | 1 | 2 | 3 | 4 | +| ------------- | --- | --- | --- | --- | --- | +| `Weight` | 4 | 3 | 2 | 0 | 1 | The decoder will do the inverse operation : having collected weights of literals from `0` to `4`, @@ -1196,12 +1222,16 @@ The weight of `5` can be determined by advancing to the next power of 2. The sum of `2^(Weight-1)` (excluding 0's) is : `8 + 4 + 2 + 0 + 1 = 15`. Nearest power of 2 is 16. -Therefore, `Max_Number_of_Bits = 4` and `Weight[5] = 1`. +Therefore, `Max_Number_of_Bits = 4` and `Weight[5] = 16-15 = 1`. -##### Huffman Tree header +#### Huffman Tree header This is a single byte value (0-255), -which describes how to decode the list of weights. +which describes how the series of weights is encoded. + +- if `headerByte` < 128 : + the series of weights is compressed using FSE (see below). + The length of the FSE-compressed series is equal to `headerByte` (0-127). - if `headerByte` >= 128 : this is a direct representation, where each `Weight` is written directly as a 4 bits field (0-15). @@ -1213,14 +1243,11 @@ which describes how to decode the list of weights. meaning it uses only full bytes even if `Number_of_Symbols` is odd. `Number_of_Symbols = headerByte - 127`. Note that maximum `Number_of_Symbols` is 255-127 = 128. - If any present literal has a value > 128, raw header mode is not possible. - It's necessary to use FSE compression. + If any literal has a value > 128, raw header mode is not possible. + In such case, it's necessary to use FSE compression. -- if `headerByte` < 128 : - the series of weights is compressed using FSE. - The length of the FSE-compressed series is equal to `headerByte` (0-127). -##### Finite State Entropy (FSE) compression of Huffman weights +#### Finite State Entropy (FSE) compression of Huffman weights In this case, the series of Huffman weights is compressed using FSE compression. It's a single bitstream with 2 interleaved states, @@ -1251,12 +1278,12 @@ If updating state after decoding a symbol would require more bits than remain in the stream, it is assumed that extra bits are 0. Then, symbols for each of the final states are decoded and the process is complete. -##### Conversion from weights to Huffman prefix codes +#### Conversion from weights to Huffman prefix codes All present symbols shall now have a `Weight` value. It is possible to transform weights into` Number_of_Bits`, using this formula: ``` -Number_of_Bits = Weight ? Max_Number_of_Bits + 1 - Weight : 0 +Number_of_Bits = (Weight>0) ? Max_Number_of_Bits + 1 - Weight : 0 ``` Symbols are sorted by `Weight`. Within same `Weight`, symbols keep natural sequential order. @@ -1358,7 +1385,7 @@ _Reserved ranges :_ - low range : <= 32767 - high range : >= (2^31) -__`Entropy_Tables`__ : following the same format as the tables in compressed blocks. +__`Entropy_Tables`__ : follow the same format as tables in [compressed blocks]. See the relevant [FSE](#fse-table-description) and [Huffman](#huffman-tree-description) sections for how to decode these tables. They are stored in following order : @@ -1382,6 +1409,10 @@ __`Content`__ : The rest of the dictionary is its content. [compressed blocks]: #the-format-of-compressed_block +If a dictionary is provided by an external source, +it should be loaded with great care, its content considered untrusted. + + Appendix A - Decoding tables for predefined codes ------------------------------------------------- @@ -1568,6 +1599,26 @@ to crosscheck that an implementation build its decoding tables correctly. | 30 | 25 | 5 | 0 | | 31 | 24 | 5 | 0 | + + +Appendix B - Resources for implementers +------------------------------------------------- + +An open source reference implementation is available on : +https://github.com/facebook/zstd + +The project contains a frame generator, called [decodeCorpus], +which can be used by any 3rd-party implementation +to verify that a tested decoder is compliant with the specification. + +[decodeCorpus]: https://github.com/facebook/zstd/tree/v1.3.4/tests#decodecorpus---tool-to-generate-zstandard-frames-for-decoder-testing + +`decodeCorpus` generates random valid frames. +A compliant decoder should be able to decode them all, +or at least provide a meaningful error code explaining for which reason it cannot +(memory limit restrictions for example). + + Version changes --------------- - 0.2.8 : clarifications for IETF RFC discuss From 243cd9d8bbc4cc907b661512c907dc85203a8d87 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 21 Jun 2018 18:04:58 -0700 Subject: [PATCH 276/288] add a cond_broadcast after resize to make sure all threads (notably newly available threads) get awaken to immediately process potential items in the queue. --- lib/common/pool.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index bd31f032f..41a216f16 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -225,13 +225,16 @@ static POOL_ctx* POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) * note : starting context is considered consumed. */ POOL_ctx* POOL_resize(POOL_ctx* ctx, size_t numThreads) { - POOL_ctx* newCtx; if (ctx==NULL) return NULL; ZSTD_pthread_mutex_lock(&ctx->queueMutex); - newCtx = POOL_resize_internal(ctx, numThreads); + { POOL_ctx* const newCtx = POOL_resize_internal(ctx, numThreads); + if (newCtx!=ctx) { + POOL_free(ctx); + return newCtx; + } } + ZSTD_pthread_cond_broadcast(&ctx->queuePopCond); ZSTD_pthread_mutex_unlock(&ctx->queueMutex); - if (newCtx!=ctx) POOL_free(ctx); - return newCtx; + return ctx; } /** From c1e63477174798e3e04b4c7cfda6c8358e462d18 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 21 Jun 2018 18:08:11 -0700 Subject: [PATCH 277/288] fixed minor typos, detected by @terrelln --- doc/zstd_compression_format.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index 91838f757..62430e48f 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -652,7 +652,7 @@ They follow the same enumeration : - `Predefined_Mode` : A predefined FSE distribution table is used, defined in [default distributions](#default-distributions). No distribution table will be present. -- `RLE_Mode` : The table description consists of a single byte, which contain symbol's value. +- `RLE_Mode` : The table description consists of a single byte, which contains the symbol's value. This symbol will be used for all sequences. - `FSE_Compressed_Mode` : standard FSE compression. A distribution table will be present. @@ -754,7 +754,7 @@ supporting back-reference distances up to `(2^(N+1))-4`, but is limited by [maximum back-reference distance](#window_descriptor). `Offset_Value` from 1 to 3 are special : they define "repeat codes". -This is described in more details in [Repeat Offsets](#repeat-offsets). +This is described in more detail in [Repeat Offsets](#repeat-offsets). #### Decoding Sequences FSE bitstreams are read in reverse direction than written. In zstd, @@ -1281,7 +1281,7 @@ symbols for each of the final states are decoded and the process is complete. #### Conversion from weights to Huffman prefix codes All present symbols shall now have a `Weight` value. -It is possible to transform weights into` Number_of_Bits`, using this formula: +It is possible to transform weights into `Number_of_Bits`, using this formula: ``` Number_of_Bits = (Weight>0) ? Max_Number_of_Bits + 1 - Weight : 0 ``` From 698fd00afbbda554409ecfc74a250f7b3a760ce1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 21 Jun 2018 18:32:38 -0700 Subject: [PATCH 278/288] huf: increase threshold detection of poorly compressible data --- lib/compress/huf_compress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 2e5319187..9cdaa5d79 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -670,7 +670,7 @@ static size_t HUF_compress_internal ( /* Scan input and build symbol stats */ { CHECK_V_F(largest, HIST_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, table->count) ); if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */ - if (largest <= (srcSize >> 7)+1) return 0; /* heuristic : probably not compressible enough */ + if (largest <= (srcSize >> 7)+4) return 0; /* heuristic : probably not compressible enough */ } /* Check validity of previous table */ From fbd5dfc1b163b462e2aa75a8b224d24938da81f3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 22 Jun 2018 12:14:59 -0700 Subject: [PATCH 279/288] changed POOL_resize() return type to int return is now just en error code. This guarantee that `ctx` remains valid after POOL_resize(). Gets rid of internal POOL_free() operation. --- lib/common/pool.c | 40 +++++++++++++++------------------- lib/common/pool.h | 14 ++++++------ lib/compress/zstdmt_compress.c | 3 +-- tests/poolTests.c | 9 ++++---- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index 41a216f16..281b3824a 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -190,20 +190,19 @@ size_t POOL_sizeof(POOL_ctx *ctx) { } -/* @return : a working pool on success, NULL on failure - * note : starting context is considered consumed. */ -static POOL_ctx* POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) +/* @return : 0 on success, 1 on error */ +static int POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) { if (numThreads <= ctx->threadCapacity) { - if (!numThreads) return NULL; + if (!numThreads) return 1; ctx->threadLimit = numThreads; - return ctx; + return 0; } /* numThreads > threadCapacity */ { ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem); - if (!threadPool) return NULL; + if (!threadPool) return 1; /* replace existing thread pool */ - memcpy(threadPool, ctx->threads, ctx->threadCapacity * sizeof(ctx->threads[0])); + memcpy(threadPool, ctx->threads, ctx->threadCapacity * sizeof(*threadPool)); ZSTD_free(ctx->threads, ctx->customMem); ctx->threads = threadPool; /* Initialize additional threads */ @@ -211,30 +210,25 @@ static POOL_ctx* POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) for (threadId = ctx->threadCapacity; threadId < numThreads; ++threadId) { if (ZSTD_pthread_create(&threadPool[threadId], NULL, &POOL_thread, ctx)) { ctx->threadCapacity = threadId; - ctx->threadLimit = threadId; - return NULL; /* will release the pool */ + return 1; } } } } /* successfully expanded */ ctx->threadCapacity = numThreads; ctx->threadLimit = numThreads; - return ctx; + return 0; } -/* @return : a working pool on success, NULL on failure - * note : starting context is considered consumed. */ -POOL_ctx* POOL_resize(POOL_ctx* ctx, size_t numThreads) +/* @return : 0 on success, 1 on error */ +int POOL_resize(POOL_ctx* ctx, size_t numThreads) { - if (ctx==NULL) return NULL; + int result; + if (ctx==NULL) return 1; ZSTD_pthread_mutex_lock(&ctx->queueMutex); - { POOL_ctx* const newCtx = POOL_resize_internal(ctx, numThreads); - if (newCtx!=ctx) { - POOL_free(ctx); - return newCtx; - } } + result = POOL_resize_internal(ctx, numThreads); ZSTD_pthread_cond_broadcast(&ctx->queuePopCond); ZSTD_pthread_mutex_unlock(&ctx->queueMutex); - return ctx; + return result; } /** @@ -321,9 +315,9 @@ void POOL_free(POOL_ctx* ctx) { (void)ctx; } -POOL_ctx* POOL_resize(POOL_ctx* ctx, size_t numThreads) { - (void)numThreads; - return ctx; +int POOL_resize(POOL_ctx* ctx, size_t numThreads) { + (void)ctx; (void)numThreads; + return 0; } void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque) { diff --git a/lib/common/pool.h b/lib/common/pool.h index caf514907..458d37f13 100644 --- a/lib/common/pool.h +++ b/lib/common/pool.h @@ -40,14 +40,14 @@ void POOL_free(POOL_ctx* ctx); /*! POOL_resize() : * Expands or shrinks pool's number of threads. - * This is more efficient than releasing and creating a new context. - * @return : a new pool context on success, NULL on failure - * note : new pool context might have same address as original one, but it's not guaranteed. - * consider starting context as consumed, only rely on returned one. - * note 2 : only numThreads can be resized, queueSize is unchanged. - * note 3 : `numThreads` must be at least 1 + * This is more efficient than releasing + creating a new context, + * since it tries to preserve and re-use existing threads. + * `numThreads` must be at least 1. + * @return : 0 when resize was successful, + * !0 (typically 1) if there is an error. + * note : only numThreads can be resized, queueSize remains unchanged. */ -POOL_ctx* POOL_resize(POOL_ctx* ctx, size_t numThreads); +int POOL_resize(POOL_ctx* ctx, size_t numThreads); /*! POOL_sizeof() : * @return threadpool memory usage diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index bbbdc5cd4..dc025e2a5 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1018,8 +1018,7 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) * @return : error code if fails, 0 on success */ static size_t ZSTDMT_resize(ZSTDMT_CCtx* mtctx, unsigned nbWorkers) { - mtctx->factory = POOL_resize(mtctx->factory, nbWorkers); - if (mtctx->factory == NULL) return ERROR(memory_allocation); + if (POOL_resize(mtctx->factory, nbWorkers)) return ERROR(memory_allocation); CHECK_F( ZSTDMT_expandJobsTable(mtctx, nbWorkers) ); mtctx->bufPool = ZSTDMT_expandBufferPool(mtctx->bufPool, nbWorkers); if (mtctx->bufPool == NULL) return ERROR(memory_allocation); diff --git a/tests/poolTests.c b/tests/poolTests.c index d5768967a..6a058a5a3 100644 --- a/tests/poolTests.c +++ b/tests/poolTests.c @@ -121,8 +121,7 @@ static int testThreadReduction_internal(POOL_ctx* ctx, poolTest_t test) ZSTD_pthread_mutex_unlock(&test.mut); time4threads = UTIL_clockSpanNano(startTime); - ctx = POOL_resize(ctx, 2/*nbThreads*/); - ASSERT_TRUE(ctx); + ASSERT_EQ( POOL_resize(ctx, 2/*nbThreads*/) , 0 ); test.val = 0; startTime = UTIL_getTime(); { int i; @@ -142,7 +141,7 @@ static int testThreadReduction_internal(POOL_ctx* ctx, poolTest_t test) static int testThreadReduction(void) { int result; poolTest_t test; - POOL_ctx* ctx = POOL_create(4 /*nbThreads*/, 2 /*queueSize*/); + POOL_ctx* const ctx = POOL_create(4 /*nbThreads*/, 2 /*queueSize*/); ASSERT_TRUE(ctx); @@ -179,7 +178,7 @@ static int testAbruptEnding_internal(abruptEndCanary_t test) { int const nbWaits = 16; - POOL_ctx* ctx = POOL_create(3 /*numThreads*/, nbWaits /*queueSize*/); + POOL_ctx* const ctx = POOL_create(3 /*numThreads*/, nbWaits /*queueSize*/); ASSERT_TRUE(ctx); test.val = 0; @@ -187,7 +186,7 @@ static int testAbruptEnding_internal(abruptEndCanary_t test) for (i=0; i Date: Fri, 22 Jun 2018 17:58:21 -0700 Subject: [PATCH 280/288] error on no forward progress streaming decoders, such as ZSTD_decompressStream() or ZSTD_decompress_generic(), may end up making no forward progress, (aka no byte read from input __and__ no byte written to output), due to unusual parameters conditions, such as providing an output buffer already full. In such case, the caller may be caught in an infinite loop, calling the streaming decompression function again and again, without making any progress. This version detects such situation, and generates an error instead : ZSTD_error_dstSize_tooSmall when output buffer is full, ZSTD_error_srcSize_wrong when input buffer is empty. The detection tolerates a number of attempts before triggering an error, controlled by ZSTD_NO_FORWARD_PROGRESS_MAX macro constant, which is set to 16 by default, and can be re-defined at compilation time. This behavior tolerates potentially existing implementations where such cases happen sporadically, like once or twice, which is not dangerous (only infinite loops are), without generating an error, hence without breaking these implementations. --- lib/decompress/zstd_decompress.c | 28 ++++++++++++++++++++++++-- tests/fuzzer.c | 4 ++++ tests/zstreamtest.c | 34 +++++++++++++++++++++++++++++--- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 4de98a8e6..8f4589d13 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -41,6 +41,17 @@ #endif +/*! + * NO_FORWARD_PROGRESS_MAX : + * maximum allowed nb of calls to ZSTD_decompressStream() and ZSTD_decompress_generic() + * without any forward progress + * (defined as: no byte read from input, and no byte flushed to output) + * before triggering an error. + */ +#ifndef ZSTD_NO_FORWARD_PROGRESS_MAX +# define ZSTD_NO_FORWARD_PROGRESS_MAX 16 +#endif + /*-******************************************************* * Dependencies *********************************************************/ @@ -153,6 +164,7 @@ struct ZSTD_DCtx_s U32 previousLegacyVersion; U32 legacyVersion; U32 hostageByte; + int noForwardProgress; /* workspace */ BYTE litBuffer[ZSTD_BLOCKSIZE_MAX + WILDCOPY_OVERLENGTH]; @@ -194,6 +206,7 @@ static void ZSTD_initDCtx_internal(ZSTD_DCtx* dctx) dctx->streamStage = zdss_init; dctx->legacyContext = NULL; dctx->previousLegacyVersion = 0; + dctx->noForwardProgress = 0; dctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); } @@ -2620,6 +2633,7 @@ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t di { DEBUGLOG(4, "ZSTD_initDStream_usingDict"); zds->streamStage = zdss_init; + zds->noForwardProgress = 0; CHECK_F( ZSTD_DCtx_loadDictionary(zds, dict, dictSize) ); return ZSTD_frameHeaderSize_prefix; } @@ -2960,8 +2974,18 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB } } /* result */ - input->pos += (size_t)(ip-istart); - output->pos += (size_t)(op-ostart); + input->pos = (size_t)(ip - (const char*)(input->src)); + output->pos = (size_t)(op - (char*)(output->dst)); + if ((ip==istart) && (op==ostart)) { /* no forward progress */ + zds->noForwardProgress ++; + if (zds->noForwardProgress >= ZSTD_NO_FORWARD_PROGRESS_MAX) { + if (op==oend) return ERROR(dstSize_tooSmall); + if (ip==iend) return ERROR(srcSize_wrong); + assert(0); + } + } else { + zds->noForwardProgress = 0; + } { size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zds); if (!nextSrcSizeHint) { /* frame fully decoded */ if (zds->outEnd == zds->outStart) { /* output fully flushed */ diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 9b4cd58d5..f7bacd5ca 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -66,6 +66,9 @@ static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; if (g_displayLevel>=4) fflush(stderr); } } +/*-******************************************************* +* Compile time test +*********************************************************/ #undef MIN #undef MAX void FUZ_bug976(void) @@ -74,6 +77,7 @@ void FUZ_bug976(void) assert(ZSTD_CHAINLOG_MAX < 31); } + /*-******************************************************* * Internal functions *********************************************************/ diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index c76622701..70da29728 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -433,11 +433,12 @@ static int basicUnitTests(U32 seed, double compressibility) inBuff.pos = 0; outBuff.pos = 0; while (r) { /* skippable frame */ - size_t const inSize = FUZ_rand(&coreSeed) & 15; - size_t const outSize = FUZ_rand(&coreSeed) & 15; + size_t const inSize = (FUZ_rand(&coreSeed) & 15) + 1; + size_t const outSize = (FUZ_rand(&coreSeed) & 15) + 1; inBuff.size = inBuff.pos + inSize; outBuff.size = outBuff.pos + outSize; r = ZSTD_decompressStream(zd, &outBuff, &inBuff); + if (ZSTD_isError(r)) DISPLAYLEVEL(4, "ZSTD_decompressStream on skippable frame error : %s \n", ZSTD_getErrorName(r)); if (ZSTD_isError(r)) goto _output_error; } /* normal frame */ @@ -445,14 +446,17 @@ static int basicUnitTests(U32 seed, double compressibility) r=1; while (r) { size_t const inSize = FUZ_rand(&coreSeed) & 15; - size_t const outSize = FUZ_rand(&coreSeed) & 15; + size_t const outSize = (FUZ_rand(&coreSeed) & 15) + (!inSize); /* avoid having both sizes at 0 => would trigger a no_forward_progress error */ inBuff.size = inBuff.pos + inSize; outBuff.size = outBuff.pos + outSize; r = ZSTD_decompressStream(zd, &outBuff, &inBuff); + if (ZSTD_isError(r)) DISPLAYLEVEL(4, "ZSTD_decompressStream error : %s \n", ZSTD_getErrorName(r)); if (ZSTD_isError(r)) goto _output_error; } } + if (outBuff.pos != CNBufferSize) DISPLAYLEVEL(4, "outBuff.pos != CNBufferSize : should have regenerated same amount ! \n"); if (outBuff.pos != CNBufferSize) goto _output_error; /* should regenerate the same amount */ + if (inBuff.pos != cSize) DISPLAYLEVEL(4, "inBuff.pos != cSize : should have real all input ! \n"); if (inBuff.pos != cSize) goto _output_error; /* should have read the entire frame */ DISPLAYLEVEL(3, "OK \n"); @@ -464,6 +468,30 @@ static int basicUnitTests(U32 seed, double compressibility) } } DISPLAYLEVEL(3, "OK \n"); + /* Decompression forward progress */ + DISPLAYLEVEL(3, "test%3i : generate error when ZSTD_decompressStream() doesn't progress : ", testNb++); + { /* skippable frame */ + size_t r = 0; + int decNb = 0; + int const maxDec = 100; + inBuff.src = compressedBuffer; + inBuff.size = cSize; + inBuff.pos = 0; + + outBuff.dst = decodedBuffer; + outBuff.pos = 0; + outBuff.size = CNBufferSize-1; /* 1 byte missing */ + + for (decNb=0; decNb Date: Mon, 25 Jun 2018 15:21:08 -0700 Subject: [PATCH 281/288] [zstdmt] Fix jobsize bugs (#1205) [zstdmt] Fix jobsize bugs * `ZSTDMT_serialState_reset()` should use `targetSectionSize`, not `jobSize` when sizing the seqstore. Add an assert that checks that we sized the seqstore using the right job size. * `ZSTDMT_compressionJob()` should check if `rawSeqStore.seq == NULL`. * `ZSTDMT_initCStream_internal()` should not adjust `mtctx->params.jobSize` (clamping to MIN/MAX is okay). --- lib/compress/zstdmt_compress.c | 54 ++++++++++++++++++++++++---------- lib/compress/zstdmt_compress.h | 5 ++++ tests/zstreamtest.c | 5 ++++ 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index b180b1b7d..6daedca8b 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -459,7 +459,7 @@ typedef struct { ZSTD_window_t ldmWindow; /* A thread-safe copy of ldmState.window */ } serialState_t; -static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* seqPool, ZSTD_CCtx_params params) +static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* seqPool, ZSTD_CCtx_params params, size_t jobSize) { /* Adjust parameters */ if (params.ldmParams.enableLdm) { @@ -486,7 +486,7 @@ static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* serialState->params.ldmParams.hashLog - serialState->params.ldmParams.bucketSizeLog; /* Size the seq pool tables */ - ZSTDMT_setNbSeq(seqPool, ZSTD_ldm_getMaxNbSeq(params.ldmParams, params.jobSize)); + ZSTDMT_setNbSeq(seqPool, ZSTD_ldm_getMaxNbSeq(params.ldmParams, jobSize)); /* Reset the window */ ZSTD_window_clear(&serialState->ldmState.window); serialState->ldmWindow = serialState->ldmState.window; @@ -506,6 +506,7 @@ static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* memset(serialState->ldmState.bucketOffsets, 0, bucketSize); } serialState->params = params; + serialState->params.jobSize = (U32)jobSize; return 0; } @@ -547,6 +548,7 @@ static void ZSTDMT_serialState_update(serialState_t* serialState, size_t error; assert(seqStore.seq != NULL && seqStore.pos == 0 && seqStore.size == 0 && seqStore.capacity > 0); + assert(src.size <= serialState->params.jobSize); ZSTD_window_update(&serialState->ldmState.window, src.start, src.size); error = ZSTD_ldm_generateSequences( &serialState->ldmState, &seqStore, @@ -635,13 +637,6 @@ void ZSTDMT_compressionJob(void* jobDescription) rawSeqStore_t rawSeqStore = ZSTDMT_getSeq(job->seqPool); buffer_t dstBuff = job->dstBuff; - /* Don't compute the checksum for chunks, since we compute it externally, - * but write it in the header. - */ - if (job->jobID != 0) jobParams.fParams.checksumFlag = 0; - /* Don't run LDM for the chunks, since we handle it externally */ - jobParams.ldmParams.enableLdm = 0; - /* ressources */ if (cctx==NULL) { job->cSize = ERROR(memory_allocation); @@ -655,6 +650,18 @@ void ZSTDMT_compressionJob(void* jobDescription) } job->dstBuff = dstBuff; /* this value can be read in ZSTDMT_flush, when it copies the whole job */ } + if (jobParams.ldmParams.enableLdm && rawSeqStore.seq == NULL) { + job->cSize = ERROR(memory_allocation); + goto _endJob; + } + + /* Don't compute the checksum for chunks, since we compute it externally, + * but write it in the header. + */ + if (job->jobID != 0) jobParams.fParams.checksumFlag = 0; + /* Don't run LDM for the chunks, since we handle it externally */ + jobParams.ldmParams.enableLdm = 0; + /* init */ if (job->cdict) { @@ -972,6 +979,8 @@ size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, if ( (value > 0) /* value==0 => automatic job size */ & (value < ZSTDMT_JOBSIZE_MIN) ) value = ZSTDMT_JOBSIZE_MIN; + if (value > ZSTDMT_JOBSIZE_MAX) + value = ZSTDMT_JOBSIZE_MAX; params->jobSize = value; return value; case ZSTDMT_p_overlapSectionLog : @@ -998,6 +1007,21 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, } } +size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, unsigned* value) +{ + switch (parameter) { + case ZSTDMT_p_jobSize: + *value = mtctx->params.jobSize; + break; + case ZSTDMT_p_overlapSectionLog: + *value = mtctx->params.overlapSizeLog; + break; + default: + return ERROR(parameter_unsupported); + } + return 0; +} + /* Sets parameters relevant to the compression job, * initializing others to default values. */ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) @@ -1143,7 +1167,7 @@ static size_t ZSTDMT_compress_advanced_internal( assert(avgJobSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), required to compress directly into Dst (no additional buffer) */ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgJobSize) ); - if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params)) + if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, avgJobSize)) return ERROR(memory_allocation); CHECK_F( ZSTDMT_expandJobsTable(mtctx, nbJobs) ); /* only expands if necessary */ @@ -1280,9 +1304,7 @@ size_t ZSTDMT_initCStream_internal( if (params.nbWorkers != mtctx->params.nbWorkers) CHECK_F( ZSTDMT_resize(mtctx, params.nbWorkers) ); - if (params.jobSize == 0) { - params.jobSize = 1U << ZSTDMT_computeTargetJobLog(params); - } + if (params.jobSize > 0 && params.jobSize < ZSTDMT_JOBSIZE_MIN) params.jobSize = ZSTDMT_JOBSIZE_MIN; if (params.jobSize > ZSTDMT_JOBSIZE_MAX) params.jobSize = ZSTDMT_JOBSIZE_MAX; mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */ @@ -1321,7 +1343,9 @@ size_t ZSTDMT_initCStream_internal( mtctx->targetPrefixSize = (size_t)1 << ZSTDMT_computeOverlapLog(params); DEBUGLOG(4, "overlapLog=%u => %u KB", params.overlapSizeLog, (U32)(mtctx->targetPrefixSize>>10)); mtctx->targetSectionSize = params.jobSize; - if (mtctx->targetSectionSize < ZSTDMT_JOBSIZE_MIN) mtctx->targetSectionSize = ZSTDMT_JOBSIZE_MIN; + if (mtctx->targetSectionSize == 0) { + mtctx->targetSectionSize = 1ULL << ZSTDMT_computeTargetJobLog(params); + } if (mtctx->targetSectionSize < mtctx->targetPrefixSize) mtctx->targetSectionSize = mtctx->targetPrefixSize; /* job size must be >= overlap size */ DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(mtctx->targetSectionSize>>10), params.jobSize); DEBUGLOG(4, "inBuff Size : %u KB", (U32)(mtctx->targetSectionSize>>10)); @@ -1363,7 +1387,7 @@ size_t ZSTDMT_initCStream_internal( mtctx->allJobsCompleted = 0; mtctx->consumed = 0; mtctx->produced = 0; - if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params)) + if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, mtctx->targetSectionSize)) return ERROR(memory_allocation); return 0; } diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 4249a82de..34a475a42 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -95,6 +95,11 @@ typedef enum { * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ ZSTDLIB_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, unsigned value); +/* ZSTDMT_getMTCtxParameter() : + * Query the ZSTDMT_CCtx for a parameter value. + * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ +ZSTDLIB_API size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, unsigned* value); + /*! ZSTDMT_compressStream_generic() : * Combines ZSTDMT_compressStream() with optional ZSTDMT_flushStream() or ZSTDMT_endStream() diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 70da29728..22c49cb35 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -844,7 +844,12 @@ static int basicUnitTests(U32 seed, double compressibility) /* Basic multithreading compression test */ DISPLAYLEVEL(3, "test%3i : compress %u bytes with multiple threads : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); { ZSTD_parameters const params = ZSTD_getParams(1, 0, 0); + unsigned jobSize; + CHECK_Z( ZSTDMT_getMTCtxParameter(mtctx, ZSTDMT_p_jobSize, &jobSize)); + CHECK(jobSize != 0, "job size non-zero"); CHECK_Z( ZSTDMT_initCStream_advanced(mtctx, CNBuffer, dictSize, params, CNBufferSize) ); + CHECK_Z( ZSTDMT_getMTCtxParameter(mtctx, ZSTDMT_p_jobSize, &jobSize)); + CHECK(jobSize != 0, "job size non-zero"); } outBuff.dst = compressedBuffer; outBuff.size = compressedBufferSize; From 4e196b2ac3a21116b8b24597b294315e33825269 Mon Sep 17 00:00:00 2001 From: oleid Date: Tue, 26 Jun 2018 08:36:41 +0200 Subject: [PATCH 282/288] Correct multithread logic, fixing 'unsupported parameter' error The original conditions only worked, when both, static and shared variants where built, resulting in an inconsistency between programs and library. The program was built with MT support enabled, the library not. That lead to error 11 'unsupported parameter' when compressing anything with the command line tool. When changing the AND condition to `ZSTD_MULTITHREAD_SUPPORT AND (ZSTD_BUILD_SHARED OR ZSTD_BUILD_SHARED)`, cmake stopps complaining one of the targets wasn't built. This commit works for any case. --- build/cmake/lib/CMakeLists.txt | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index ade946daf..c4c2f81e6 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -108,9 +108,21 @@ ENDIF (MSVC) # Split project to static and shared libraries build IF (ZSTD_BUILD_SHARED) ADD_LIBRARY(libzstd_shared SHARED ${Sources} ${Headers} ${PlatformDependResources}) + IF (ZSTD_MULTITHREAD_SUPPORT) + SET_PROPERTY(TARGET libzstd_shared APPEND PROPERTY COMPILE_DEFINITIONS "ZSTD_MULTITHREAD") + IF (UNIX) + TARGET_LINK_LIBRARIES(libzstd_shared ${THREADS_LIBS}) + ENDIF () + ENDIF() ENDIF (ZSTD_BUILD_SHARED) IF (ZSTD_BUILD_STATIC) ADD_LIBRARY(libzstd_static STATIC ${Sources} ${Headers}) + IF (ZSTD_MULTITHREAD_SUPPORT) + SET_PROPERTY(TARGET libzstd_static APPEND PROPERTY COMPILE_DEFINITIONS "ZSTD_MULTITHREAD") + IF (UNIX) + TARGET_LINK_LIBRARIES(libzstd_static ${THREADS_LIBS}) + ENDIF () + ENDIF () ENDIF (ZSTD_BUILD_STATIC) # Add specific compile definitions for MSVC project @@ -123,17 +135,6 @@ IF (MSVC) ENDIF (ZSTD_BUILD_STATIC) ENDIF (MSVC) -# Add multi-threading support definitions - -IF (ZSTD_MULTITHREAD_SUPPORT AND ZSTD_BUILD_SHARED AND ZSTD_BUILD_STATIC) - SET_PROPERTY(TARGET libzstd_shared libzstd_static APPEND PROPERTY COMPILE_DEFINITIONS "ZSTD_MULTITHREAD") - - IF (UNIX) - TARGET_LINK_LIBRARIES(libzstd_shared ${THREADS_LIBS}) - TARGET_LINK_LIBRARIES(libzstd_static ${THREADS_LIBS}) - ENDIF () -ENDIF (ZSTD_MULTITHREAD_SUPPORT AND ZSTD_BUILD_SHARED AND ZSTD_BUILD_STATIC) - # With MSVC static library needs to be renamed to avoid conflict with import library IF (MSVC) SET(STATIC_LIBRARY_BASE_NAME zstd_static) From f741fb8fcd5c5fdd0a64936cc606f041f530eabf Mon Sep 17 00:00:00 2001 From: cyan4973 Date: Tue, 26 Jun 2018 01:22:45 -0700 Subject: [PATCH 283/288] minor fixes for MSYS2 compilation --- programs/platform.h | 5 ++++- programs/util.h | 2 +- programs/zstdcli.c | 6 +++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/programs/platform.h b/programs/platform.h index c86d289f5..a550eb1a3 100644 --- a/programs/platform.h +++ b/programs/platform.h @@ -96,7 +96,10 @@ extern "C" { /*-********************************************* * Detect if isatty() and fileno() are available ************************************************/ -#if (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) || (PLATFORM_POSIX_VERSION >= 200112L) || defined(__DJGPP__) +#if (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) \ + || (PLATFORM_POSIX_VERSION >= 200112L) \ + || defined(__DJGPP__) \ + || defined(__MSYS__) # include /* isatty */ # define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) #elif defined(MSDOS) || defined(OS2) || defined(__CYGWIN__) diff --git a/programs/util.h b/programs/util.h index e88d20146..4392a5bd0 100644 --- a/programs/util.h +++ b/programs/util.h @@ -513,7 +513,7 @@ UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_ UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_t* pos, char** bufEnd, int followLinks) { - (void)bufStart; (void)bufEnd; (void)pos; + (void)bufStart; (void)bufEnd; (void)pos; (void)followLinks; UTIL_DISPLAYLEVEL(1, "Directory %s ignored (compiled without _WIN32 or _POSIX_C_SOURCE)\n", dirName); return 0; } diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 6b6a93528..1af5f26fa 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -761,8 +761,11 @@ int main(int argCount, const char* argv[]) nbWorkers = UTIL_countPhysicalCores(); DISPLAYLEVEL(3, "Note: %d physical core(s) detected \n", nbWorkers); } +#else + (void)singleThread; #endif +#ifdef UTIL_HAS_CREATEFILELIST g_utilDisplayLevel = g_displayLevel; if (!followLinks) { unsigned u; @@ -775,7 +778,6 @@ int main(int argCount, const char* argv[]) } filenameIdx = fileNamesNb; } -#ifdef UTIL_HAS_CREATEFILELIST if (recursive) { /* at this stage, filenameTable is a list of paths, which can contain both files and directories */ extendedFileList = UTIL_createFileList(filenameTable, filenameIdx, &fileNamesBuf, &fileNamesNb, followLinks); if (extendedFileList) { @@ -786,6 +788,8 @@ int main(int argCount, const char* argv[]) filenameIdx = fileNamesNb; } } +#else + (void)followLinks; #endif if (operation == zom_list) { From f98ec469792ed1ca5c6e9050bad63365dd49741f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 26 Jun 2018 12:04:59 -0700 Subject: [PATCH 284/288] updated DEBUG_STATIC_ASSERT() following suggestion from #1209 --- doc/zstd_manual.html | 22 ++++++++++++++++++++++ lib/common/debug.h | 9 +++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 6bf731dd9..bd792008b 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -872,6 +872,28 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize, * even when referencing into Dictionary content (default:0) */ + ZSTD_p_forceAttachDict, /* ZSTD supports usage of a CDict in-place + * (avoiding having to copy the compression tables + * from the CDict into the working context). Using + * a CDict in this way saves an initial setup step, + * but comes at the cost of more work per byte of + * input. ZSTD has a simple internal heuristic that + * guesses which strategy will be faster. You can + * use this flag to override that guess. + * + * Note that the by-reference, in-place strategy is + * only used when reusing a compression context + * with compatible compression parameters. (If + * incompatible / uninitialized, the working + * context needs to be cleared anyways, which is + * about as expensive as overwriting it with the + * dictionary context, so there's no savings in + * using the CDict by-ref.) + * + * Values greater than 0 force attaching the dict. + * Values less than 0 force copying the dict. + * 0 selects the default heuristic-guided behavior. + */ } ZSTD_cParameter;
diff --git a/lib/common/debug.h b/lib/common/debug.h index 95a3fcabe..9b5878cfc 100644 --- a/lib/common/debug.h +++ b/lib/common/debug.h @@ -35,9 +35,10 @@ /* * The purpose of this header is to enable debug functions. - * They regroup assert(), DEBUGLOG() and RAWLOG(). + * They regroup assert(), DEBUGLOG() and RAWLOG() for run-time, + * and DEBUG_STATIC_ASSERT() for compile-time. * - * By default, DEBUGLEVEL==0, which means debug is disabled. + * By default, DEBUGLEVEL==0, which means run-time debug is disabled. * * Level 1 enables assert() only. * Starting level 2, traces can be generated and pushed to stderr. @@ -58,7 +59,7 @@ extern "C" { /* static assert is triggered at compile time, leaving no runtime artefact, * but can only work with compile-time constants */ -#define DEBUG_STATIC_ASSERT(c) { enum { DEBUG_static_assert = 1/(int)(!!(c)) }; } +#define DEBUG_STATIC_ASSERT(c) { extern char DEBUG_static_assert[(c) ? 1 : -1]; } /* DEBUGLEVEL is expected to be defined externally, @@ -69,7 +70,7 @@ extern "C" { #endif /* recommended values for DEBUGLEVEL : - * 0 : no debug, all functions disabled (except DEBUG_STATIC_ASSERT) + * 0 : no debug, all run-time functions disabled * 1 : no display, enables assert() only * 2 : reserved, for currently active debug path * 3 : events once per object lifetime (CCtx, CDict, etc.) From 7b9bbf77c9341c43bd0111e0dcdfefc2e786fcb6 Mon Sep 17 00:00:00 2001 From: Yann ColletDate: Tue, 26 Jun 2018 14:08:35 -0700 Subject: [PATCH 285/288] switched to a sizeof() version avoid -Werror=unused-variable issue --- lib/common/debug.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/common/debug.h b/lib/common/debug.h index 9b5878cfc..0c04ad2cc 100644 --- a/lib/common/debug.h +++ b/lib/common/debug.h @@ -58,8 +58,9 @@ extern "C" { /* static assert is triggered at compile time, leaving no runtime artefact, - * but can only work with compile-time constants */ -#define DEBUG_STATIC_ASSERT(c) { extern char DEBUG_static_assert[(c) ? 1 : -1]; } + * but can only work with compile-time constants. + * This variant can only be used inside a function. */ +#define DEBUG_STATIC_ASSERT(c) (void)sizeof(char[(c) ? 1 : -1]) /* DEBUGLEVEL is expected to be defined externally, From ff773bfcdebb75730eb0546c425030396168b6fb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 26 Jun 2018 17:24:41 -0700 Subject: [PATCH 286/288] zeroise freq table with memset() improves decoding speed by ~5% in github_users sample set --- lib/common/entropy_common.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/common/entropy_common.c b/lib/common/entropy_common.c index 33fd04bd6..b12944e1d 100644 --- a/lib/common/entropy_common.c +++ b/lib/common/entropy_common.c @@ -85,6 +85,8 @@ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* t } } assert(hbSize >= 4); + /* init */ + memset(normalizedCounter, 0, (*maxSVPtr+1) * sizeof(normalizedCounter[0])); /* all symbols not present in NCount have a frequency of 0 */ bitStream = MEM_readLE32(ip); nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */ if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) return ERROR(tableLog_tooLarge); @@ -156,11 +158,6 @@ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* t } } /* while ((remaining>1) & (charnum<=*maxSVPtr)) */ if (remaining != 1) return ERROR(corruption_detected); if (bitCount > 32) return ERROR(corruption_detected); - /* zeroise the rest */ - { unsigned symbNb = charnum; - for (symbNb=charnum; symbNb <= *maxSVPtr; symbNb++) - normalizedCounter[symbNb] = 0; - } *maxSVPtr = charnum-1; ip += (bitCount+7)>>3; From 4489daec09f5fe058f107ed4828f4fff0c563004 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 26 Jun 2018 20:10:45 -0700 Subject: [PATCH 287/288] slightly adjusted default-distribution threshold depending on strategy. fast favors faster compression and decompression speeds. --- doc/zstd_manual.html | 22 ++++++++++++++++++++++ lib/compress/zstd_compress.c | 5 ++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 6bf731dd9..bd792008b 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -872,6 +872,28 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long ZSTD_p_forceMaxWindow=1100, /* Force back-reference distances to remain < windowSize, * even when referencing into Dictionary content (default:0) */ + ZSTD_p_forceAttachDict, /* ZSTD supports usage of a CDict in-place + * (avoiding having to copy the compression tables + * from the CDict into the working context). Using + * a CDict in this way saves an initial setup step, + * but comes at the cost of more work per byte of + * input. ZSTD has a simple internal heuristic that + * guesses which strategy will be faster. You can + * use this flag to override that guess. + * + * Note that the by-reference, in-place strategy is + * only used when reusing a compression context + * with compatible compression parameters. (If + * incompatible / uninitialized, the working + * context needs to be cleared anyways, which is + * about as expensive as overwriting it with the + * dictionary context, so there's no savings in + * using the CDict by-ref.) + * + * Values greater than 0 force attaching the dict. + * Values less than 0 force copying the dict. + * 0 selects the default heuristic-guided behavior. + */ } ZSTD_cParameter;
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 96ffe244a..c66862524 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1819,8 +1819,11 @@ ZSTD_selectEncodingType( if (strategy < ZSTD_lazy) { if (isDefaultAllowed) { size_t const staticFse_nbSeq_max = 1000; - size_t const dynamicFse_nbSeq_min = (size_t)1 << defaultNormLog; /* 32 for offset, 64 for lengths */ + size_t const mult = 10 - strategy; + size_t const baseLog = 3; + size_t const dynamicFse_nbSeq_min = (((size_t)1 << defaultNormLog) * mult) >> baseLog; /* 28-36 for offset, 56-72 for lengths */ assert(defaultNormLog >= 5 && defaultNormLog <= 6); /* xx_DEFAULTNORMLOG */ + assert(mult <= 9 && mult >= 7); if ( (*repeatMode == FSE_repeat_valid) && (nbSeq < staticFse_nbSeq_max) ) { DEBUGLOG(5, "Selected set_repeat"); From 9c277f137cbcaa385ff5b95ec4cbdce50675541d Mon Sep 17 00:00:00 2001 From: Yann ColletDate: Wed, 27 Jun 2018 13:19:14 -0700 Subject: [PATCH 288/288] attempt to re-enable arm64 tests --- .travis.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c5eb70f2..081af4aad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,12 +25,7 @@ matrix: - env: Cmd='make valgrindinstall && make -C tests clean valgrindTest' - env: Cmd='make arminstall && make armfuzz' - -# Following test is disabled, as there is a bug in Travis' ld -# preventing aarch64 compilation to complete. -# > collect2: error: ld terminated with signal 11 [Segmentation fault], core dumped -# to be re-enabled in a few commit, as it's possible that a random code change circumvent the ld bug -# - env: Cmd='make arminstall && make aarch64fuzz' + - env: Cmd='make arminstall && make aarch64fuzz' - env: Cmd='make ppcinstall && make ppcfuzz' - env: Cmd='make ppcinstall && make ppc64fuzz'