diff --git a/lib/common/fse.h b/lib/common/fse.h index 714bfd3e7f2..1bac6bca5ef 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 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); + /*! 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..23c31ee0944 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -46,6 +46,7 @@ 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 !!! @@ -79,23 +80,30 @@ 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 . - * `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); + HUF_depthStrategy depthStrategy, void* workSpace, size_t wkspSize); #endif /* HUF_H_298734234 */ @@ -185,8 +193,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, 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_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); @@ -204,12 +214,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. @@ -301,8 +312,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, 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() : @@ -315,7 +326,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 5547b4ac099..6805ddb221a 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -337,6 +337,48 @@ 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) +{ + 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 */ + bitCount += ((symbol - start) / 24) * 16; + bitCount += (((symbol - start) % 24 + 2) / 3) * 2; + } + { 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; } + } + } + + return (bitCount + 7) / 8; +} /*-************************************************************** * FSE Compression Code @@ -353,7 +395,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 +631,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; + U32 s; + for (s = 0; s <= maxSymbolValue; ++s) { + nbBits += FSE_bitCost(symbolTT, tableLog, s, 8 /* accuracyLog */) * count[s]; + } + return nbBits >> (8 + 3); /* accuracyLog + bit->byte */ +} + 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 d1b98afdb42..1fe7153691a 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -143,6 +143,32 @@ 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))); + 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, @@ -226,6 +252,34 @@ typedef struct { BYTE huffWeight[HUF_SYMBOLVALUE_MAX]; } HUF_WriteCTableWksp; +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; + 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) @@ -264,6 +318,15 @@ size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, return ((maxSymbolValue+1)/2) + 1; } +/*! HUF_getEncodedCTableSize() : + `CTable` : Huffman tree to size, using huf representation. + @return : size of CTable */ +size_t HUF_getEncodedCTableSize(const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog) +{ + HUF_WriteCTableWksp wksp; + return HUF_getEncodedCTableSize_wksp(CTable, maxSymbolValue, huffLog, &wksp, sizeof(wksp)); +} + /*! HUF_writeCTable() : `CTable` : Huffman tree to save, using huf representation. @return : size of saved CTable */ @@ -507,6 +570,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. */ @@ -1221,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)]; @@ -1250,7 +1362,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; @@ -1314,7 +1426,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, depthStrategy); { size_t const maxBits = HUF_buildCTable_wksp(table->CTable, table->count, maxSymbolValue, huffLog, &table->wksps.buildCTable_wksp, sizeof(table->wksps.buildCTable_wksp)); @@ -1358,12 +1470,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) + HUF_depthStrategy depthStrategy, void* workSpace, size_t wkspSize) { 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, @@ -1371,13 +1483,13 @@ 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) { DEBUGLOG(5, "HUF_compress1X_repeat (srcSize = %zu)", srcSize); 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(): @@ -1386,13 +1498,13 @@ 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, void* workSpace, size_t wkspSize) { DEBUGLOG(5, "HUF_compress4X_wksp (srcSize = %zu)", srcSize); 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(): @@ -1403,44 +1515,44 @@ 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) { DEBUGLOG(5, "HUF_compress4X_repeat (srcSize = %zu)", srcSize); 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); } -#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) + 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, depthStrategy, workSpace, sizeof(workSpace)); } 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, depthStrategy, workSpace, sizeof(workSpace)); } 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); + 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 ed10d855591..5d9fbb185a6 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3055,7 +3055,7 @@ static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSi ZSTD_hufCTables_t* nextHuf, ZSTD_hufCTablesMetadata_t* hufMetadata, const int literalsCompressionIsDisabled, - void* workspace, size_t wkspSize) + ZSTD_strategy strategy, void* workspace, size_t wkspSize) { BYTE* const wkspStart = (BYTE*)workspace; BYTE* const wkspEnd = wkspStart + wkspSize; @@ -3112,7 +3112,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, 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); @@ -3220,7 +3221,7 @@ size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr, &prevEntropy->huf, &nextEntropy->huf, &entropyMetadata->hufMetadata, ZSTD_literalsCompressionIsDisabled(cctxParams), - workspace, wkspSize); + 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 83f9116487f..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; @@ -136,7 +136,8 @@ size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, entropyWorkspace, entropyWorkspaceSize, (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, - bmi2, suspectUncompressible); + 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/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); diff --git a/tests/fuzz/huf_round_trip.c b/tests/fuzz/huf_round_trip.c index 0e26ca9b51c..67d527a5ba8 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, HUF_seek_neighbors); 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 */