From c7ff194d4a9cf3b6c5193319ed8e42147796df90 Mon Sep 17 00:00:00 2001 From: Binh Vo Date: Thu, 30 Dec 2021 15:49:16 -0500 Subject: [PATCH 1/5] Improved huffman depth optimization --- lib/common/fse.h | 12 ++++ lib/common/huf.h | 4 +- lib/compress/fse_compress.c | 78 +++++++++++++++++++++++- lib/compress/huf_compress.c | 112 ++++++++++++++++++++++++++++++++++- lib/compress/zstd_compress.c | 2 +- 5 files changed, 202 insertions(+), 6 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 714bfd3e7f2..f22061ed3f0 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -161,6 +161,12 @@ FSE_PUBLIC_API size_t FSE_writeNCount (void* buffer, size_t bufferSize, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); +/*! FSE_estimateNCountSize(): + Estimate space requirement to save 'normalizedCounter'. + @return : size of the compressed table, + or an errorCode, which can be tested using FSE_isError(). */ +FSE_PUBLIC_API size_t FSE_estimateNCountSize(const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); + /*! Constructor and Destructor of FSE_CTable. Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */ typedef unsigned FSE_CTable; /* don't allocate that. It's only meant to be more restrictive than void* */ @@ -179,6 +185,11 @@ FSE_PUBLIC_API size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCou or an errorCode, which can be tested using FSE_isError() */ FSE_PUBLIC_API size_t FSE_compress_usingCTable (void* dst, size_t dstCapacity, const void* src, size_t srcSize, const FSE_CTable* ct); +/*! FSE_estimateCompressedSize(): + Estimate compressed size of data. + @return : estimated size of compressed data */ +FSE_PUBLIC_API size_t FSE_estimateCompressedSize (const FSE_CTable* ct, const unsigned* count, unsigned maxSymbolValue, unsigned tableLog); + /*! Tutorial : ---------- @@ -317,6 +328,7 @@ If there is an error, the function will return an error code, which can be teste * FSE advanced API ***************************************** */ +unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue); 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/common/huf.h b/lib/common/huf.h index 85518481ec6..25fd3bdd377 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -185,8 +185,10 @@ size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, * For example, it's possible to compress several blocks using the same 'CTable', * or to save and regenerate 'CTable' using external methods. */ -unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); +unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, const unsigned* count, unsigned maxSymbolValue); size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits); /* @return : maxNbBits; CTable and count can overlap. In which case, CTable will overwrite count content */ +size_t HUF_getCTableSize (const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); +size_t HUF_getCTableSize_wksp (const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize); size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize); size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index 5547b4ac099..d78f544a622 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -337,6 +337,67 @@ size_t FSE_writeNCount (void* buffer, size_t bufferSize, return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 1 /* write in buffer is safe */); } +size_t FSE_estimateNCountSize(const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) +{ + size_t result = 0; + int nbBits; + const int tableSize = 1 << tableLog; + int remaining; + int threshold; + int bitCount = 0; + unsigned symbol = 0; + unsigned const alphabetSize = maxSymbolValue + 1; + int previousIs0 = 0; + + /* Table Size */ + bitCount += 4; + + /* Init */ + remaining = tableSize + 1; /* +1 for extra accuracy */ + threshold = tableSize; + nbBits = tableLog + 1; + + while ((symbol < alphabetSize) && (remaining > 1)) { /* stops at 1 */ + if (previousIs0) { + unsigned start = symbol; + while ((symbol < alphabetSize) && !normalizedCounter[symbol]) symbol++; + if (symbol == alphabetSize) break; /* incorrect distribution */ + while (symbol >= start + 24) { + start += 24; + result += 2; + } + while (symbol >= start + 3) { + start += 3; + bitCount += 2; + } + bitCount += 2; + if (bitCount > 16) { + result += 2; + bitCount -= 16; + } + } + { int count = normalizedCounter[symbol++]; + int const max = (2 * threshold - 1) - remaining; + remaining -= count < 0 ? -count : count; + count++; /* +1 for extra accuracy */ + if (count >= threshold) + count += max; /* [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ */ + bitCount += nbBits; + bitCount -= (count < max); + previousIs0 = (count == 1); + while (remaining < threshold) { nbBits--; threshold >>= 1; } + } + if (bitCount > 16) { + result += 2; + bitCount -= 16; + } + } + + /* flush remaining bitStream */ + result += (bitCount + 7) / 8; + + return result; +} /*-************************************************************** * FSE Compression Code @@ -353,7 +414,7 @@ FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog) void FSE_freeCTable (FSE_CTable* ct) { ZSTD_free(ct); } /* provides the minimum logSize to safely represent a distribution */ -static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue) +unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue) { U32 minBitsSrc = BIT_highbit32((U32)(srcSize)) + 1; U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2; @@ -589,6 +650,21 @@ size_t FSE_buildCTable_rle (FSE_CTable* ct, BYTE symbolValue) return 0; } +size_t FSE_estimateCompressedSize(const FSE_CTable* ct, const unsigned* count, unsigned maxSymbolValue, unsigned tableLog) +{ + U32 const tableSize = 1 << tableLog; + const void* const ptr = ct; + const void* const FSCT = ((const U32*)ptr) + 1 /* header */ + (tableLog ? tableSize >> 1 : 1); + const FSE_symbolCompressionTransform* const symbolTT = (const FSE_symbolCompressionTransform*)(FSCT); + + size_t nbBits = 0; + int s; + for (s = 0; s <= (int)maxSymbolValue; ++s) { + nbBits += symbolTT[s].deltaNbBits * count[s]; + } + return nbBits >> 3; +} + static size_t FSE_compress_usingCTable_generic (void* dst, size_t dstSize, const void* src, size_t srcSize, diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 2b3d6adc2a2..3fd10d4e689 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -44,9 +44,50 @@ /* ************************************************************** * Utils ****************************************************************/ -unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue) +size_t HUF_getCTableSize(const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); + +#define IMPROVEMENT_THRESHOLD 5 /* estimation can be off by 4 bytes, only shift if we're sure we're winning */ +#define ESTIMATE_SIZE(c, result) CHECK_F(HUF_buildCTable(ct, count, maxSymbolValue, c)); \ + result = HUF_estimateCompressedSize(ct, count, maxSymbolValue) + HUF_getCTableSize(ct, maxSymbolValue, c); +unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, const unsigned* count, unsigned maxSymbolValue) { - return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1); + U32 maxBits = BIT_highbit32((U32)(srcSize - 1)) - 1; + U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue); + U32 tableLog = maxTableLog; + size_t estimatedSize = 0; + size_t proposedSize = 0; + HUF_CElt ct[HUF_SYMBOLVALUE_MAX+1]; + unsigned movedUp = 0; + + /* initial bounds */ + if (maxBits > HUF_TABLELOG_MAX) maxBits = HUF_TABLELOG_MAX; + if (tableLog == 0) tableLog = HUF_TABLELOG_DEFAULT; + if (tableLog > maxBits) tableLog = maxBits; + if (tableLog < minBits) tableLog = minBits; + + + /* base size estimation */ + ESTIMATE_SIZE(tableLog, estimatedSize); + + /* incrementally check neighboring depths */ + for (; tableLog < maxBits; tableLog++) { + ESTIMATE_SIZE(tableLog + 1, proposedSize); + if (proposedSize >= estimatedSize - IMPROVEMENT_THRESHOLD) + break; + estimatedSize = proposedSize; + movedUp = 1; + } + if (movedUp) // no point in scanning back down since we just came from that value + return tableLog; + + for (; tableLog > minBits; tableLog--) { + ESTIMATE_SIZE(tableLog, proposedSize); + if (proposedSize >= estimatedSize - IMPROVEMENT_THRESHOLD) + break; + estimatedSize = proposedSize; + } + + return tableLog; } @@ -89,6 +130,34 @@ typedef struct { S16 norm[HUF_TABLELOG_MAX+1]; } HUF_CompressWeightsWksp; +static size_t HUF_getWeightsSize(const void* weightTable, size_t wtSize, void* workspace, size_t workspaceSize) +{ + unsigned maxSymbolValue = HUF_TABLELOG_MAX; + U32 tableLog = MAX_FSE_TABLELOG_FOR_HUFF_HEADER; + size_t result = 0; + HUF_CompressWeightsWksp* wksp = (HUF_CompressWeightsWksp*)HUF_alignUpWorkspace(workspace, &workspaceSize, ZSTD_ALIGNOF(U32)); + + if (workspaceSize < sizeof(HUF_CompressWeightsWksp)) return ERROR(GENERIC); + + /* init conditions */ + if (wtSize <= 1) return 0; /* Not compressible */ + + /* Scan input and build symbol stats */ + { unsigned const maxCount = HIST_count_simple(wksp->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 */ + } + + tableLog = FSE_optimalTableLog(tableLog, wtSize, maxSymbolValue); + CHECK_F(FSE_normalizeCount(wksp->norm, tableLog, wksp->count, wtSize, maxSymbolValue, /* useLowProbCount */ 0)); + CHECK_F(FSE_buildCTable_wksp(wksp->CTable, wksp->norm, maxSymbolValue, tableLog, wksp->scratchBuffer, sizeof(wksp->scratchBuffer))); + /* table header estimation can be improved */ + result += FSE_estimateNCountSize(wksp->norm, maxSymbolValue, tableLog) + FSE_estimateCompressedSize(wksp->CTable, wksp->count, maxSymbolValue, tableLog); + + return result; +} + + static size_t HUF_compressWeights(void* dst, size_t dstSize, const void* weightTable, size_t wtSize, void* workspace, size_t workspaceSize) { BYTE* const ostart = (BYTE*) dst; @@ -169,6 +238,34 @@ typedef struct { BYTE huffWeight[HUF_SYMBOLVALUE_MAX]; } HUF_WriteCTableWksp; +size_t HUF_getCTableSize_wksp(const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, + void* workspace, size_t workspaceSize) +{ + HUF_CElt const* const ct = CTable + 1; + U32 n; + HUF_WriteCTableWksp* wksp = (HUF_WriteCTableWksp*)HUF_alignUpWorkspace(workspace, &workspaceSize, ZSTD_ALIGNOF(U32)); + + /* check conditions */ + if (workspaceSize < sizeof(HUF_WriteCTableWksp)) return ERROR(GENERIC); + if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge); + + /* convert to weight */ + wksp->bitsToWeight[0] = 0; + for (n = 1; n < huffLog + 1; n++) + wksp->bitsToWeight[n] = (BYTE)(huffLog + 1 - n); + for (n = 0; n < maxSymbolValue; n++) + wksp->huffWeight[n] = wksp->bitsToWeight[HUF_getNbBits(ct[n])]; + + /* check weights compression by FSE */ + {size_t hSize = HUF_getWeightsSize(wksp->huffWeight, maxSymbolValue, &wksp->wksp, sizeof(wksp->wksp)); + if ((hSize > 1) & (hSize < maxSymbolValue / 2)) { /* FSE compressed */ + return hSize + 1; + } } + + /* size based on writing raw values as 4-bits (max : 15) */ + return ((maxSymbolValue + 1) / 2) + 1; +} + size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize) @@ -207,6 +304,15 @@ size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, return ((maxSymbolValue+1)/2) + 1; } +/*! HUF_getCTableSize() : + `CTable` : Huffman tree to size, using huf representation. + @return : size of CTable */ +size_t HUF_getCTableSize(const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog) +{ + HUF_WriteCTableWksp wksp; + return HUF_getCTableSize_wksp(CTable, maxSymbolValue, huffLog, &wksp, sizeof(wksp)); +} + /*! HUF_writeCTable() : `CTable` : Huffman tree to save, using huf representation. @return : size of saved CTable */ @@ -1242,7 +1348,7 @@ HUF_compress_internal (void* dst, size_t dstSize, } /* Build Huffman Tree */ - huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); + huffLog = HUF_optimalTableLog(huffLog, srcSize, table->count, maxSymbolValue); { size_t const maxBits = HUF_buildCTable_wksp(table->CTable, table->count, maxSymbolValue, huffLog, &table->wksps.buildCTable_wksp, sizeof(table->wksps.buildCTable_wksp)); diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 34f8e970f1a..09f61238914 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3113,7 +3113,7 @@ static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSi /* Build Huffman Tree */ ZSTD_memset(nextHuf->CTable, 0, sizeof(nextHuf->CTable)); - huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); + huffLog = HUF_optimalTableLog(huffLog, srcSize, countWksp, maxSymbolValue); { size_t const maxBits = HUF_buildCTable_wksp((HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue, huffLog, nodeWksp, nodeWkspSize); From a74f7e884d0b8c7db1be7a7516786fbc6fc9dbd5 Mon Sep 17 00:00:00 2001 From: Binh Vo Date: Fri, 14 Jan 2022 12:59:17 -0500 Subject: [PATCH 2/5] Fix CI failures --- lib/compress/fse_compress.c | 8 ++++---- lib/compress/huf_compress.c | 9 ++++----- tests/fuzz/huf_round_trip.c | 8 ++++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index d78f544a622..af7c3176950 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -658,11 +658,11 @@ size_t FSE_estimateCompressedSize(const FSE_CTable* ct, const unsigned* count, u const FSE_symbolCompressionTransform* const symbolTT = (const FSE_symbolCompressionTransform*)(FSCT); size_t nbBits = 0; - int s; - for (s = 0; s <= (int)maxSymbolValue; ++s) { - nbBits += symbolTT[s].deltaNbBits * count[s]; + U32 s; + for (s = 0; s <= maxSymbolValue; ++s) { + nbBits += FSE_bitCost(symbolTT, tableLog, s, 8 /* accuracyLog */) * count[s]; } - return nbBits >> 3; + return nbBits >> (8 + 3); /* accuracyLog + bit->byte */ } diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 5b10433ca5d..08e3d58f13b 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -46,7 +46,7 @@ ****************************************************************/ #define IMPROVEMENT_THRESHOLD 5 /* estimation can be off by 4 bytes, only shift if we're sure we're winning */ -#define ESTIMATE_SIZE(c, result) CHECK_F(HUF_buildCTable(ct, count, maxSymbolValue, c)); \ +#define ESTIMATE_SIZE(c, result) HUF_buildCTable(ct, count, maxSymbolValue, c); \ result = HUF_estimateCompressedSize(ct, count, maxSymbolValue) + HUF_getCTableSize(ct, maxSymbolValue, c); unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, const unsigned* count, unsigned maxSymbolValue) { @@ -55,7 +55,7 @@ unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, const unsigne U32 tableLog = maxTableLog; size_t estimatedSize = 0; size_t proposedSize = 0; - HUF_CElt ct[HUF_SYMBOLVALUE_MAX+1]; + HUF_CElt ct[HUF_CTABLE_SIZE(HUF_SYMBOLVALUE_MAX)]; unsigned movedUp = 0; /* initial bounds */ @@ -150,7 +150,6 @@ static size_t HUF_getWeightsSize(const void* weightTable, size_t wtSize, void* w tableLog = FSE_optimalTableLog(tableLog, wtSize, maxSymbolValue); CHECK_F(FSE_normalizeCount(wksp->norm, tableLog, wksp->count, wtSize, maxSymbolValue, /* useLowProbCount */ 0)); CHECK_F(FSE_buildCTable_wksp(wksp->CTable, wksp->norm, maxSymbolValue, tableLog, wksp->scratchBuffer, sizeof(wksp->scratchBuffer))); - /* table header estimation can be improved */ result += FSE_estimateNCountSize(wksp->norm, maxSymbolValue, tableLog) + FSE_estimateCompressedSize(wksp->CTable, wksp->count, maxSymbolValue, tableLog); return result; @@ -1441,17 +1440,17 @@ size_t HUF_compress4X_repeat (void* dst, size_t dstSize, hufTable, repeat, preferRepeat, bmi2, suspectUncompressible); } -#ifndef ZSTD_NO_UNUSED_FUNCTIONS /** HUF_buildCTable() : * @return : maxNbBits * Note : count is used before tree is written, so they can safely overlap */ -size_t HUF_buildCTable (HUF_CElt* tree, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits) +size_t HUF_buildCTable(HUF_CElt* tree, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits) { HUF_buildCTable_wksp_tables workspace; return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, &workspace, sizeof(workspace)); } +#ifndef ZSTD_NO_UNUSED_FUNCTIONS size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog) diff --git a/tests/fuzz/huf_round_trip.c b/tests/fuzz/huf_round_trip.c index 0e26ca9b51c..ad3f3fc640a 100644 --- a/tests/fuzz/huf_round_trip.c +++ b/tests/fuzz/huf_round_trip.c @@ -79,13 +79,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) void* rBuf = FUZZ_malloc(size); void* cBuf = FUZZ_malloc(cBufSize); HUF_CElt* ct = (HUF_CElt*)FUZZ_malloc(HUF_CTABLE_SIZE(maxSymbol)); - HUF_DTable* dt = (HUF_DTable*)FUZZ_malloc(HUF_DTABLE_SIZE(tableLog) * sizeof(HUF_DTable)); - dt[0] = tableLog * 0x01000001; - - tableLog = HUF_optimalTableLog(tableLog, size, maxSymbol); + tableLog = HUF_optimalTableLog(tableLog, size, count, maxSymbol); FUZZ_ASSERT(tableLog <= 12); tableLog = HUF_buildCTable_wksp(ct, count, maxSymbol, tableLog, wksp, wkspSize); FUZZ_ZASSERT(tableLog); + HUF_DTable* dt = (HUF_DTable*)FUZZ_malloc(HUF_DTABLE_SIZE(tableLog) * sizeof(HUF_DTable)); + dt[0] = tableLog * 0x01000001; + size_t const tableSize = HUF_writeCTable_wksp(cBuf, cBufSize, ct, maxSymbol, tableLog, wksp, wkspSize); if (ERR_isError(tableSize)) { /* Errors on uncompressible data or cBufSize too small */ From 2c35da8567430a914a3f6fb49badfc81f97d2681 Mon Sep 17 00:00:00 2001 From: Binh Vo Date: Sat, 22 Jan 2022 14:11:49 -0500 Subject: [PATCH 3/5] Refactor for api control --- lib/common/fse.h | 2 +- lib/common/huf.h | 34 +++--- lib/compress/fse_compress.c | 25 +---- lib/compress/huf_compress.c | 144 ++++++++++++++------------ lib/compress/zstd_compress.c | 8 +- lib/compress/zstd_compress_literals.c | 7 +- tests/fuzz/huf_round_trip.c | 2 +- 7 files changed, 111 insertions(+), 111 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index f22061ed3f0..1bac6bca5ef 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -186,7 +186,7 @@ FSE_PUBLIC_API size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCou FSE_PUBLIC_API size_t FSE_compress_usingCTable (void* dst, size_t dstCapacity, const void* src, size_t srcSize, const FSE_CTable* ct); /*! FSE_estimateCompressedSize(): - Estimate compressed size of data. + Estimate compressed size of data based on sum of fractional bit counts, will be >= actual cost. @return : estimated size of compressed data */ FSE_PUBLIC_API size_t FSE_estimateCompressedSize (const FSE_CTable* ct, const unsigned* count, unsigned maxSymbolValue, unsigned tableLog); diff --git a/lib/common/huf.h b/lib/common/huf.h index 25fd3bdd377..49d58178c41 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -46,13 +46,18 @@ extern "C" { * Compress content from buffer 'src', of size 'srcSize', into buffer 'dst'. * 'dst' buffer must be already allocated. * Compression runs faster if `dstCapacity` >= HUF_compressBound(srcSize). + * If depthStrategy = HUF_seek_neighbor additional huffman depths will be considered * `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB. * @return : size of compressed data (<= `dstCapacity`). * Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!! * if HUF_isError(return), compression failed (more details using HUF_getErrorName()) */ +typedef enum { + HUF_default, + HUF_seek_neighbors +} HUF_depthStrategy; HUF_PUBLIC_API size_t HUF_compress(void* dst, size_t dstCapacity, - const void* src, size_t srcSize); + const void* src, size_t srcSize, HUF_depthStrategy depthStrategy); /** HUF_decompress() : * Decompress HUF data from buffer 'cSrc', of size 'cSrcSize', @@ -82,20 +87,22 @@ HUF_PUBLIC_API const char* HUF_getErrorName(size_t code); /**< provides error c /** HUF_compress2() : * Same as HUF_compress(), but offers control over `maxSymbolValue` and `tableLog`. * `maxSymbolValue` must be <= HUF_SYMBOLVALUE_MAX . - * `tableLog` must be `<= HUF_TABLELOG_MAX` . */ + * `tableLog` must be `<= HUF_TABLELOG_MAX` . + * If depthStrategy = HUF_seek_neighbor additional huffman depths will be considered */ HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned tableLog); + unsigned maxSymbolValue, unsigned tableLog, HUF_depthStrategy depthStrategy); /** HUF_compress4X_wksp() : * Same as HUF_compress2(), but uses externally allocated `workSpace`. - * `workspace` must be at least as large as HUF_WORKSPACE_SIZE */ + * `workspace` must be at least as large as HUF_WORKSPACE_SIZE + * If depthStrategy = HUF_seek_neighbor additional huffman depths will be considered */ #define HUF_WORKSPACE_SIZE ((8 << 10) + 512 /* sorting scratch space */) #define HUF_WORKSPACE_SIZE_U64 (HUF_WORKSPACE_SIZE / sizeof(U64)) HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, - void* workSpace, size_t wkspSize); + void* workSpace, size_t wkspSize, HUF_depthStrategy depthStrategy); #endif /* HUF_H_298734234 */ @@ -185,10 +192,10 @@ size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, * For example, it's possible to compress several blocks using the same 'CTable', * or to save and regenerate 'CTable' using external methods. */ -unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, const unsigned* count, unsigned maxSymbolValue); +unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, const unsigned* count, unsigned maxSymbolValue, HUF_depthStrategy strategy); size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits); /* @return : maxNbBits; CTable and count can overlap. In which case, CTable will overwrite count content */ -size_t HUF_getCTableSize (const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); -size_t HUF_getCTableSize_wksp (const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize); +size_t HUF_getEncodedCTableSize (const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); +size_t HUF_getEncodedCTableSize_wksp (const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize); size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize); size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); @@ -206,12 +213,13 @@ typedef enum { * If it uses hufTable it does not modify hufTable or repeat. * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. * If preferRepeat then the old table will always be used if valid. - * If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding */ + * If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding + * If depthStrategy = HUF_seek_neighbor additional huffman depths will be considered */ size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */ - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2, unsigned suspectUncompressible); + HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2, unsigned suspectUncompressible, HUF_depthStrategy depthStrategy); /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. @@ -303,8 +311,8 @@ size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* c /* single stream variants */ /* ====================== */ -size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); -size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U64 U64 */ +size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, HUF_depthStrategy depthStrategy); +size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_depthStrategy depthStrategy); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U64 U64 */ size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); size_t HUF_compress1X_usingCTable_bmi2(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int bmi2); /** HUF_compress1X_repeat() : @@ -317,7 +325,7 @@ size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */ - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2, unsigned suspectUncompressible); + HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2, unsigned suspectUncompressible, HUF_depthStrategy depthStrategy); size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ #ifndef HUF_FORCE_DECOMPRESS_X1 diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index af7c3176950..6805ddb221a 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -339,7 +339,6 @@ size_t FSE_writeNCount (void* buffer, size_t bufferSize, size_t FSE_estimateNCountSize(const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) { - size_t result = 0; int nbBits; const int tableSize = 1 << tableLog; int remaining; @@ -362,19 +361,8 @@ size_t FSE_estimateNCountSize(const short* normalizedCounter, unsigned maxSymbol unsigned start = symbol; while ((symbol < alphabetSize) && !normalizedCounter[symbol]) symbol++; if (symbol == alphabetSize) break; /* incorrect distribution */ - while (symbol >= start + 24) { - start += 24; - result += 2; - } - while (symbol >= start + 3) { - start += 3; - bitCount += 2; - } - bitCount += 2; - if (bitCount > 16) { - result += 2; - bitCount -= 16; - } + bitCount += ((symbol - start) / 24) * 16; + bitCount += (((symbol - start) % 24 + 2) / 3) * 2; } { int count = normalizedCounter[symbol++]; int const max = (2 * threshold - 1) - remaining; @@ -387,16 +375,9 @@ size_t FSE_estimateNCountSize(const short* normalizedCounter, unsigned maxSymbol previousIs0 = (count == 1); while (remaining < threshold) { nbBits--; threshold >>= 1; } } - if (bitCount > 16) { - result += 2; - bitCount -= 16; - } } - /* flush remaining bitStream */ - result += (bitCount + 7) / 8; - - return result; + return (bitCount + 7) / 8; } /*-************************************************************** diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 08e3d58f13b..483fd9b9d41 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -41,55 +41,6 @@ #define HUF_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c) /* use only *after* variable declarations */ -/* ************************************************************** -* Utils -****************************************************************/ - -#define IMPROVEMENT_THRESHOLD 5 /* estimation can be off by 4 bytes, only shift if we're sure we're winning */ -#define ESTIMATE_SIZE(c, result) HUF_buildCTable(ct, count, maxSymbolValue, c); \ - result = HUF_estimateCompressedSize(ct, count, maxSymbolValue) + HUF_getCTableSize(ct, maxSymbolValue, c); -unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, const unsigned* count, unsigned maxSymbolValue) -{ - U32 maxBits = BIT_highbit32((U32)(srcSize - 1)) - 1; - U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue); - U32 tableLog = maxTableLog; - size_t estimatedSize = 0; - size_t proposedSize = 0; - HUF_CElt ct[HUF_CTABLE_SIZE(HUF_SYMBOLVALUE_MAX)]; - unsigned movedUp = 0; - - /* initial bounds */ - if (maxBits > HUF_TABLELOG_MAX) maxBits = HUF_TABLELOG_MAX; - if (tableLog == 0) tableLog = HUF_TABLELOG_DEFAULT; - if (tableLog > maxBits) tableLog = maxBits; - if (tableLog < minBits) tableLog = minBits; - - - /* base size estimation */ - ESTIMATE_SIZE(tableLog, estimatedSize); - - /* incrementally check neighboring depths */ - for (; tableLog < maxBits; tableLog++) { - ESTIMATE_SIZE(tableLog + 1, proposedSize); - if (proposedSize >= estimatedSize - IMPROVEMENT_THRESHOLD) - break; - estimatedSize = proposedSize; - movedUp = 1; - } - if (movedUp) // no point in scanning back down since we just came from that value - return tableLog; - - for (; tableLog > minBits; tableLog--) { - ESTIMATE_SIZE(tableLog, proposedSize); - if (proposedSize >= estimatedSize - IMPROVEMENT_THRESHOLD) - break; - estimatedSize = proposedSize; - } - - return tableLog; -} - - /* ******************************************************* * HUF : Huffman block compression *********************************************************/ @@ -236,7 +187,7 @@ typedef struct { BYTE huffWeight[HUF_SYMBOLVALUE_MAX]; } HUF_WriteCTableWksp; -size_t HUF_getCTableSize_wksp(const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, +size_t HUF_getEncodedCTableSize_wksp(const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize) { HUF_CElt const* const ct = CTable + 1; @@ -302,13 +253,13 @@ size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, return ((maxSymbolValue+1)/2) + 1; } -/*! HUF_getCTableSize() : +/*! HUF_getEncodedCTableSize() : `CTable` : Huffman tree to size, using huf representation. @return : size of CTable */ -size_t HUF_getCTableSize(const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog) +size_t HUF_getEncodedCTableSize(const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog) { HUF_WriteCTableWksp wksp; - return HUF_getCTableSize_wksp(CTable, maxSymbolValue, huffLog, &wksp, sizeof(wksp)); + return HUF_getEncodedCTableSize_wksp(CTable, maxSymbolValue, huffLog, &wksp, sizeof(wksp)); } /*! HUF_writeCTable() : @@ -558,6 +509,61 @@ typedef struct { #define RANK_POSITION_LOG_BUCKETS_BEGIN (RANK_POSITION_TABLE_SIZE - 1) - RANK_POSITION_MAX_COUNT_LOG - 1 /* == 158 */ #define RANK_POSITION_DISTINCT_COUNT_CUTOFF RANK_POSITION_LOG_BUCKETS_BEGIN + BIT_highbit32(RANK_POSITION_LOG_BUCKETS_BEGIN) /* == 166 */ + /* ************************************************************** + * Utils + ****************************************************************/ + +#define IMPROVEMENT_THRESHOLD 5 /* estimation can be off by 4 bytes, only shift if we're sure we're winning */ +#define ESTIMATE_SIZE(c, result) HUF_buildCTable_wksp(ct, count, maxSymbolValue, c, &buildTableWorkspace, sizeof(buildTableWorkspace)); \ + result = HUF_estimateCompressedSize(ct, count, maxSymbolValue) + HUF_getEncodedCTableSize_wksp(ct, maxSymbolValue, c, &writeTableWorkspace, sizeof(writeTableWorkspace)); +unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, const unsigned* count, unsigned maxSymbolValue, HUF_depthStrategy strategy) +{ + if (strategy == HUF_seek_neighbors) { + U32 maxBits = BIT_highbit32((U32)(srcSize - 1)) - 1; + U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue); + U32 tableLog = maxTableLog; + size_t estimatedSize = 0; + size_t proposedSize = 0; + HUF_CElt ct[HUF_CTABLE_SIZE(HUF_SYMBOLVALUE_MAX)]; + unsigned movedUp = 0; + HUF_buildCTable_wksp_tables buildTableWorkspace; + HUF_WriteCTableWksp writeTableWorkspace; + + /* initial bounds */ + if (maxBits > HUF_TABLELOG_MAX) maxBits = HUF_TABLELOG_MAX; + if (tableLog == 0) tableLog = HUF_TABLELOG_DEFAULT; + if (tableLog > maxBits) tableLog = maxBits; + if (tableLog < minBits) tableLog = minBits; + + + /* base size estimation */ + ESTIMATE_SIZE(tableLog, estimatedSize); + + /* incrementally check neighboring depths */ + for (; tableLog < maxBits; tableLog++) { + ESTIMATE_SIZE(tableLog + 1, proposedSize); + if (proposedSize >= estimatedSize - IMPROVEMENT_THRESHOLD) + break; + estimatedSize = proposedSize; + movedUp = 1; + } + if (movedUp) // no point in scanning back down since we just came from that value + return tableLog; + + for (; tableLog > minBits; tableLog--) { + ESTIMATE_SIZE(tableLog, proposedSize); + if (proposedSize >= estimatedSize - IMPROVEMENT_THRESHOLD) + break; + estimatedSize = proposedSize; + } + + return tableLog; + } + else { + return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1); + } +} + /* Return the appropriate bucket index for a given count. See definition of * RANK_POSITION_DISTINCT_COUNT_CUTOFF for explanation of bucketing strategy. */ @@ -1285,7 +1291,7 @@ HUF_compress_internal (void* dst, size_t dstSize, HUF_nbStreams_e nbStreams, void* workSpace, size_t wkspSize, HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat, - const int bmi2, unsigned suspectUncompressible) + const int bmi2, unsigned suspectUncompressible, HUF_depthStrategy depthStrategy) { HUF_compress_tables_t* const table = (HUF_compress_tables_t*)HUF_alignUpWorkspace(workSpace, &wkspSize, ZSTD_ALIGNOF(size_t)); BYTE* const ostart = (BYTE*)dst; @@ -1346,7 +1352,7 @@ HUF_compress_internal (void* dst, size_t dstSize, } /* Build Huffman Tree */ - huffLog = HUF_optimalTableLog(huffLog, srcSize, table->count, maxSymbolValue); + huffLog = HUF_optimalTableLog(huffLog, srcSize, table->count, maxSymbolValue, depthStrategy); { size_t const maxBits = HUF_buildCTable_wksp(table->CTable, table->count, maxSymbolValue, huffLog, &table->wksps.buildCTable_wksp, sizeof(table->wksps.buildCTable_wksp)); @@ -1389,12 +1395,12 @@ HUF_compress_internal (void* dst, size_t dstSize, size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, - void* workSpace, size_t wkspSize) + void* workSpace, size_t wkspSize, HUF_depthStrategy depthStrategy) { return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_singleStream, workSpace, wkspSize, - NULL, NULL, 0, 0 /*bmi2*/, 0); + NULL, NULL, 0, 0 /*bmi2*/, 0, depthStrategy); } size_t HUF_compress1X_repeat (void* dst, size_t dstSize, @@ -1402,12 +1408,12 @@ size_t HUF_compress1X_repeat (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, - int bmi2, unsigned suspectUncompressible) + int bmi2, unsigned suspectUncompressible, HUF_depthStrategy depthStrategy) { return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_singleStream, workSpace, wkspSize, hufTable, - repeat, preferRepeat, bmi2, suspectUncompressible); + repeat, preferRepeat, bmi2, suspectUncompressible, depthStrategy); } /* HUF_compress4X_repeat(): @@ -1416,12 +1422,12 @@ size_t HUF_compress1X_repeat (void* dst, size_t dstSize, size_t HUF_compress4X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, - void* workSpace, size_t wkspSize) + void* workSpace, size_t wkspSize, HUF_depthStrategy depthStrategy) { return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_fourStreams, workSpace, wkspSize, - NULL, NULL, 0, 0 /*bmi2*/, 0); + NULL, NULL, 0, 0 /*bmi2*/, 0, depthStrategy); } /* HUF_compress4X_repeat(): @@ -1432,12 +1438,12 @@ size_t HUF_compress4X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2, unsigned suspectUncompressible) + HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2, unsigned suspectUncompressible, HUF_depthStrategy depthStrategy) { return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_fourStreams, workSpace, wkspSize, - hufTable, repeat, preferRepeat, bmi2, suspectUncompressible); + hufTable, repeat, preferRepeat, bmi2, suspectUncompressible, depthStrategy); } /** HUF_buildCTable() : @@ -1453,22 +1459,22 @@ size_t HUF_buildCTable(HUF_CElt* tree, const unsigned* count, unsigned maxSymbol #ifndef ZSTD_NO_UNUSED_FUNCTIONS size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned huffLog) + unsigned maxSymbolValue, unsigned huffLog, HUF_depthStrategy depthStrategy) { U64 workSpace[HUF_WORKSPACE_SIZE_U64]; - return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); + return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace), depthStrategy); } size_t HUF_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned huffLog) + unsigned maxSymbolValue, unsigned huffLog, HUF_depthStrategy depthStrategy) { U64 workSpace[HUF_WORKSPACE_SIZE_U64]; - return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); + return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace), depthStrategy); } -size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize) +size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize, HUF_depthStrategy depthStrategy) { - return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT); + return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT, depthStrategy); } #endif diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e709123aee7..bb398f46c4e 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3051,12 +3051,13 @@ static void writeBlockHeader(void* op, size_t cSize, size_t blockSize, U32 lastB * huffman description table to hufMetadata. * Requires ENTROPY_WORKSPACE_SIZE workspace * @return : size of huffman description table or error code */ +#define HUF_DEPTH_STRATEGY_CUTOFF ZSTD_btopt static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSize, const ZSTD_hufCTables_t* prevHuf, ZSTD_hufCTables_t* nextHuf, ZSTD_hufCTablesMetadata_t* hufMetadata, const int literalsCompressionIsDisabled, - void* workspace, size_t wkspSize) + void* workspace, size_t wkspSize, ZSTD_strategy strategy) { BYTE* const wkspStart = (BYTE*)workspace; BYTE* const wkspEnd = wkspStart + wkspSize; @@ -3113,7 +3114,8 @@ static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSi /* Build Huffman Tree */ ZSTD_memset(nextHuf->CTable, 0, sizeof(nextHuf->CTable)); - huffLog = HUF_optimalTableLog(huffLog, srcSize, countWksp, maxSymbolValue); + huffLog = HUF_optimalTableLog(huffLog, srcSize, countWksp, maxSymbolValue, + strategy >= HUF_DEPTH_STRATEGY_CUTOFF ? HUF_seek_neighbors : HUF_default); { size_t const maxBits = HUF_buildCTable_wksp((HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue, huffLog, nodeWksp, nodeWkspSize); @@ -3221,7 +3223,7 @@ size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr, &prevEntropy->huf, &nextEntropy->huf, &entropyMetadata->hufMetadata, ZSTD_literalsCompressionIsDisabled(cctxParams), - workspace, wkspSize); + workspace, wkspSize, cctxParams->cParams.strategy); FORWARD_IF_ERROR(entropyMetadata->hufMetadata.hufDesSize, "ZSTD_buildBlockEntropyStats_literals failed"); entropyMetadata->fseMetadata.fseTablesSize = ZSTD_buildBlockEntropyStats_sequences(seqStorePtr, diff --git a/lib/compress/zstd_compress_literals.c b/lib/compress/zstd_compress_literals.c index 52b0a8059ab..3293fc45aed 100644 --- a/lib/compress/zstd_compress_literals.c +++ b/lib/compress/zstd_compress_literals.c @@ -67,6 +67,7 @@ size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* return flSize+1; } +#define HUF_DEPTH_STRATEGY_CUTOFF ZSTD_btopt size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, ZSTD_hufCTables_t* nextHuf, ZSTD_strategy strategy, int disableLiteralCompression, @@ -106,11 +107,13 @@ size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, HUF_compress1X_repeat( ostart+lhSize, dstCapacity-lhSize, src, srcSize, HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize, - (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2, suspectUncompressible) : + (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2, suspectUncompressible, + strategy >= HUF_DEPTH_STRATEGY_CUTOFF ? HUF_seek_neighbors : HUF_default) : HUF_compress4X_repeat( ostart+lhSize, dstCapacity-lhSize, src, srcSize, HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize, - (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2, suspectUncompressible); + (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2, suspectUncompressible, + strategy >= HUF_DEPTH_STRATEGY_CUTOFF ? HUF_seek_neighbors : HUF_default); if (repeat != HUF_repeat_none) { /* reused the existing table */ DEBUGLOG(5, "Reusing previous huffman table"); diff --git a/tests/fuzz/huf_round_trip.c b/tests/fuzz/huf_round_trip.c index ad3f3fc640a..67d527a5ba8 100644 --- a/tests/fuzz/huf_round_trip.c +++ b/tests/fuzz/huf_round_trip.c @@ -79,7 +79,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) void* rBuf = FUZZ_malloc(size); void* cBuf = FUZZ_malloc(cBufSize); HUF_CElt* ct = (HUF_CElt*)FUZZ_malloc(HUF_CTABLE_SIZE(maxSymbol)); - tableLog = HUF_optimalTableLog(tableLog, size, count, maxSymbol); + tableLog = HUF_optimalTableLog(tableLog, size, count, maxSymbol, HUF_seek_neighbors); FUZZ_ASSERT(tableLog <= 12); tableLog = HUF_buildCTable_wksp(ct, count, maxSymbol, tableLog, wksp, wkspSize); FUZZ_ZASSERT(tableLog); From 57149a000125a094c834c246a28f5b42f80bcea0 Mon Sep 17 00:00:00 2001 From: Binh Vo Date: Mon, 24 Jan 2022 15:55:29 -0500 Subject: [PATCH 4/5] API and consistency changes --- lib/common/huf.h | 15 ++++++++------- lib/compress/huf_compress.c | 12 ++++++------ lib/compress/zstd_compress.c | 5 ++--- lib/compress/zstd_compress_literals.c | 1 - lib/compress/zstd_compress_literals.h | 1 + 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/common/huf.h b/lib/common/huf.h index 49d58178c41..23c31ee0944 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -52,12 +52,8 @@ extern "C" { * Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!! * if HUF_isError(return), compression failed (more details using HUF_getErrorName()) */ -typedef enum { - HUF_default, - HUF_seek_neighbors -} HUF_depthStrategy; HUF_PUBLIC_API size_t HUF_compress(void* dst, size_t dstCapacity, - const void* src, size_t srcSize, HUF_depthStrategy depthStrategy); + const void* src, size_t srcSize); /** HUF_decompress() : * Decompress HUF data from buffer 'cSrc', of size 'cSrcSize', @@ -84,6 +80,11 @@ HUF_PUBLIC_API const char* HUF_getErrorName(size_t code); /**< provides error c /* *** Advanced function *** */ +typedef enum { + HUF_default, + HUF_seek_neighbors +} HUF_depthStrategy; + /** HUF_compress2() : * Same as HUF_compress(), but offers control over `maxSymbolValue` and `tableLog`. * `maxSymbolValue` must be <= HUF_SYMBOLVALUE_MAX . @@ -102,7 +103,7 @@ HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity, HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, - void* workSpace, size_t wkspSize, HUF_depthStrategy depthStrategy); + HUF_depthStrategy depthStrategy, void* workSpace, size_t wkspSize); #endif /* HUF_H_298734234 */ @@ -312,7 +313,7 @@ size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* c /* ====================== */ size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, HUF_depthStrategy depthStrategy); -size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_depthStrategy depthStrategy); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U64 U64 */ +size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, HUF_depthStrategy depthStrategy,void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U64 U64 */ size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); size_t HUF_compress1X_usingCTable_bmi2(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int bmi2); /** HUF_compress1X_repeat() : diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 483fd9b9d41..1f1ad7dae90 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -1395,7 +1395,7 @@ HUF_compress_internal (void* dst, size_t dstSize, size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, - void* workSpace, size_t wkspSize, HUF_depthStrategy depthStrategy) + HUF_depthStrategy depthStrategy, void* workSpace, size_t wkspSize) { return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_singleStream, @@ -1422,7 +1422,7 @@ size_t HUF_compress1X_repeat (void* dst, size_t dstSize, size_t HUF_compress4X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, - void* workSpace, size_t wkspSize, HUF_depthStrategy depthStrategy) + HUF_depthStrategy depthStrategy, void* workSpace, size_t wkspSize) { return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_fourStreams, @@ -1462,7 +1462,7 @@ size_t HUF_compress1X (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, HUF_depthStrategy depthStrategy) { U64 workSpace[HUF_WORKSPACE_SIZE_U64]; - return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace), depthStrategy); + return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, depthStrategy, workSpace, sizeof(workSpace)); } size_t HUF_compress2 (void* dst, size_t dstSize, @@ -1470,11 +1470,11 @@ size_t HUF_compress2 (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, HUF_depthStrategy depthStrategy) { U64 workSpace[HUF_WORKSPACE_SIZE_U64]; - return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace), depthStrategy); + return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, depthStrategy, workSpace, sizeof(workSpace)); } -size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize, HUF_depthStrategy depthStrategy) +size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize) { - return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT, depthStrategy); + return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT, HUF_default); } #endif diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index bb398f46c4e..55e7df9ae15 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3051,13 +3051,12 @@ static void writeBlockHeader(void* op, size_t cSize, size_t blockSize, U32 lastB * huffman description table to hufMetadata. * Requires ENTROPY_WORKSPACE_SIZE workspace * @return : size of huffman description table or error code */ -#define HUF_DEPTH_STRATEGY_CUTOFF ZSTD_btopt static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSize, const ZSTD_hufCTables_t* prevHuf, ZSTD_hufCTables_t* nextHuf, ZSTD_hufCTablesMetadata_t* hufMetadata, const int literalsCompressionIsDisabled, - void* workspace, size_t wkspSize, ZSTD_strategy strategy) + ZSTD_strategy strategy, void* workspace, size_t wkspSize) { BYTE* const wkspStart = (BYTE*)workspace; BYTE* const wkspEnd = wkspStart + wkspSize; @@ -3223,7 +3222,7 @@ size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr, &prevEntropy->huf, &nextEntropy->huf, &entropyMetadata->hufMetadata, ZSTD_literalsCompressionIsDisabled(cctxParams), - workspace, wkspSize, cctxParams->cParams.strategy); + cctxParams->cParams.strategy, workspace, wkspSize); FORWARD_IF_ERROR(entropyMetadata->hufMetadata.hufDesSize, "ZSTD_buildBlockEntropyStats_literals failed"); entropyMetadata->fseMetadata.fseTablesSize = ZSTD_buildBlockEntropyStats_sequences(seqStorePtr, diff --git a/lib/compress/zstd_compress_literals.c b/lib/compress/zstd_compress_literals.c index 3293fc45aed..22b41fcda34 100644 --- a/lib/compress/zstd_compress_literals.c +++ b/lib/compress/zstd_compress_literals.c @@ -67,7 +67,6 @@ size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* return flSize+1; } -#define HUF_DEPTH_STRATEGY_CUTOFF ZSTD_btopt size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, ZSTD_hufCTables_t* nextHuf, ZSTD_strategy strategy, int disableLiteralCompression, diff --git a/lib/compress/zstd_compress_literals.h b/lib/compress/zstd_compress_literals.h index 9775fb97cb7..0368213c6f0 100644 --- a/lib/compress/zstd_compress_literals.h +++ b/lib/compress/zstd_compress_literals.h @@ -13,6 +13,7 @@ #include "zstd_compress_internal.h" /* ZSTD_hufCTables_t, ZSTD_minGain() */ +#define HUF_DEPTH_STRATEGY_CUTOFF ZSTD_btopt size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize); From 699b08c3abc4a0a8aff82df4a1193e2d45b6bc71 Mon Sep 17 00:00:00 2001 From: binhvo Date: Wed, 26 Jan 2022 20:07:29 -0800 Subject: [PATCH 5/5] Fix merge failures --- lib/compress/huf_compress.c | 6 ------ lib/compress/zstd_compress_literals.c | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 24d573ce25f..1fe7153691a 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -1339,12 +1339,6 @@ static size_t HUF_compressCTable_internal( return (size_t)(op-ostart); } - -unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue) -{ - return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1); -} - typedef struct { unsigned count[HUF_SYMBOLVALUE_MAX + 1]; HUF_CElt CTable[HUF_CTABLE_SIZE_ST(HUF_SYMBOLVALUE_MAX)]; diff --git a/lib/compress/zstd_compress_literals.c b/lib/compress/zstd_compress_literals.c index a9a9c1a01ee..8c7ea54d917 100644 --- a/lib/compress/zstd_compress_literals.c +++ b/lib/compress/zstd_compress_literals.c @@ -126,7 +126,7 @@ size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, RETURN_ERROR_IF(dstCapacity < lhSize+1, dstSize_tooSmall, "not enough space for compression"); { HUF_repeat repeat = prevHuf->repeatMode; int const preferRepeat = (strategy < ZSTD_lazy) ? srcSize <= 1024 : 0; - typedef size_t (*huf_compress_f)(void*, size_t, const void*, size_t, unsigned, unsigned, void*, size_t, HUF_CElt*, HUF_repeat*, int, int, unsigned); + typedef size_t (*huf_compress_f)(void*, size_t, const void*, size_t, unsigned, unsigned, void*, size_t, HUF_CElt*, HUF_repeat*, int, int, unsigned, HUF_depthStrategy); huf_compress_f huf_compress; if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1; huf_compress = singleStream ? HUF_compress1X_repeat : HUF_compress4X_repeat;