feat(compress): move dictionary entropy loading to Rust
Port ZSTD_dictNCountRepeat and ZSTD_loadCEntropy to a dedicated Rust compression module. Preserve the existing C-facing ZSTD_loadCEntropy symbol through a narrow shim while keeping dictionary content and compression-context ownership in C. Test Plan: - cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression - cargo clippy --manifest-path rust/Cargo.toml --no-default-features --features compression --benches --tests - make -B -C lib -j2 lib - make -C tests -j2 test-invalidDictionaries - make -C tests -j2 test-zstream
This commit is contained in:
@@ -303,6 +303,8 @@ size_t ZSTD_rust_determineBlockSize(int mode, size_t blockSize, size_t remaining
|
||||
size_t ZSTD_rust_validateSequence(U32 offBase, U32 matchLength, U32 minMatch,
|
||||
size_t posInSrc, U32 windowLog, size_t dictSize,
|
||||
int useSequenceProducer);
|
||||
size_t ZSTD_rust_loadCEntropy(ZSTD_compressedBlockState_t* bs, void* workspace,
|
||||
const void* dict, size_t dictSize);
|
||||
size_t ZSTD_rust_transferSequencesWBlockDelim(
|
||||
SeqStore_t* seqStore, ZSTD_SequencePosition* seqPos,
|
||||
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
|
||||
@@ -3566,113 +3568,12 @@ ZSTD_loadDictionaryContent(ZSTD_MatchState_t* ms,
|
||||
}
|
||||
|
||||
|
||||
/* Dictionaries that assign zero probability to symbols that show up causes problems
|
||||
* when FSE encoding. Mark dictionaries with zero probability symbols as FSE_repeat_check
|
||||
* and only dictionaries with 100% valid symbols can be assumed valid.
|
||||
*/
|
||||
static FSE_repeat ZSTD_dictNCountRepeat(short* normalizedCounter, unsigned dictMaxSymbolValue, unsigned maxSymbolValue)
|
||||
{
|
||||
U32 s;
|
||||
if (dictMaxSymbolValue < maxSymbolValue) {
|
||||
return FSE_repeat_check;
|
||||
}
|
||||
for (s = 0; s <= maxSymbolValue; ++s) {
|
||||
if (normalizedCounter[s] == 0) {
|
||||
return FSE_repeat_check;
|
||||
}
|
||||
}
|
||||
return FSE_repeat_valid;
|
||||
}
|
||||
|
||||
/* Dictionary entropy-header loading lives in Rust; C keeps the public internal
|
||||
* symbol and the dictionary-content orchestration around it. */
|
||||
size_t ZSTD_loadCEntropy(ZSTD_compressedBlockState_t* bs, void* workspace,
|
||||
const void* const dict, size_t dictSize)
|
||||
{
|
||||
short offcodeNCount[MaxOff+1];
|
||||
unsigned offcodeMaxValue = MaxOff;
|
||||
const BYTE* dictPtr = (const BYTE*)dict; /* skip magic num and dict ID */
|
||||
const BYTE* const dictEnd = dictPtr + dictSize;
|
||||
dictPtr += 8;
|
||||
bs->entropy.huf.repeatMode = HUF_repeat_check;
|
||||
|
||||
{ unsigned maxSymbolValue = 255;
|
||||
unsigned hasZeroWeights = 1;
|
||||
size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.huf.CTable, &maxSymbolValue, dictPtr,
|
||||
(size_t)(dictEnd-dictPtr), &hasZeroWeights);
|
||||
|
||||
/* We only set the loaded table as valid if it contains all non-zero
|
||||
* weights. Otherwise, we set it to check */
|
||||
if (!hasZeroWeights && maxSymbolValue == 255)
|
||||
bs->entropy.huf.repeatMode = HUF_repeat_valid;
|
||||
|
||||
RETURN_ERROR_IF(HUF_isError(hufHeaderSize), dictionary_corrupted, "");
|
||||
dictPtr += hufHeaderSize;
|
||||
}
|
||||
|
||||
{ unsigned offcodeLog;
|
||||
size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, (size_t)(dictEnd-dictPtr));
|
||||
RETURN_ERROR_IF(FSE_isError(offcodeHeaderSize), dictionary_corrupted, "");
|
||||
RETURN_ERROR_IF(offcodeLog > OffFSELog, dictionary_corrupted, "");
|
||||
/* fill all offset symbols to avoid garbage at end of table */
|
||||
RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(
|
||||
bs->entropy.fse.offcodeCTable,
|
||||
offcodeNCount, MaxOff, offcodeLog,
|
||||
workspace, HUF_WORKSPACE_SIZE)),
|
||||
dictionary_corrupted, "");
|
||||
/* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */
|
||||
dictPtr += offcodeHeaderSize;
|
||||
}
|
||||
|
||||
{ short matchlengthNCount[MaxML+1];
|
||||
unsigned matchlengthMaxValue = MaxML, matchlengthLog;
|
||||
size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, (size_t)(dictEnd-dictPtr));
|
||||
RETURN_ERROR_IF(FSE_isError(matchlengthHeaderSize), dictionary_corrupted, "");
|
||||
RETURN_ERROR_IF(matchlengthLog > MLFSELog, dictionary_corrupted, "");
|
||||
RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(
|
||||
bs->entropy.fse.matchlengthCTable,
|
||||
matchlengthNCount, matchlengthMaxValue, matchlengthLog,
|
||||
workspace, HUF_WORKSPACE_SIZE)),
|
||||
dictionary_corrupted, "");
|
||||
bs->entropy.fse.matchlength_repeatMode = ZSTD_dictNCountRepeat(matchlengthNCount, matchlengthMaxValue, MaxML);
|
||||
dictPtr += matchlengthHeaderSize;
|
||||
}
|
||||
|
||||
{ short litlengthNCount[MaxLL+1];
|
||||
unsigned litlengthMaxValue = MaxLL, litlengthLog;
|
||||
size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, (size_t)(dictEnd-dictPtr));
|
||||
RETURN_ERROR_IF(FSE_isError(litlengthHeaderSize), dictionary_corrupted, "");
|
||||
RETURN_ERROR_IF(litlengthLog > LLFSELog, dictionary_corrupted, "");
|
||||
RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(
|
||||
bs->entropy.fse.litlengthCTable,
|
||||
litlengthNCount, litlengthMaxValue, litlengthLog,
|
||||
workspace, HUF_WORKSPACE_SIZE)),
|
||||
dictionary_corrupted, "");
|
||||
bs->entropy.fse.litlength_repeatMode = ZSTD_dictNCountRepeat(litlengthNCount, litlengthMaxValue, MaxLL);
|
||||
dictPtr += litlengthHeaderSize;
|
||||
}
|
||||
|
||||
RETURN_ERROR_IF(dictPtr+12 > dictEnd, dictionary_corrupted, "");
|
||||
bs->rep[0] = MEM_readLE32(dictPtr+0);
|
||||
bs->rep[1] = MEM_readLE32(dictPtr+4);
|
||||
bs->rep[2] = MEM_readLE32(dictPtr+8);
|
||||
dictPtr += 12;
|
||||
|
||||
{ size_t const dictContentSize = (size_t)(dictEnd - dictPtr);
|
||||
U32 offcodeMax = MaxOff;
|
||||
if (dictContentSize <= ((U32)-1) - 128 KB) {
|
||||
U32 const maxOffset = (U32)dictContentSize + 128 KB; /* The maximum offset that must be supported */
|
||||
offcodeMax = ZSTD_highbit32(maxOffset); /* Calculate minimum offset code required to represent maxOffset */
|
||||
}
|
||||
/* All offset values <= dictContentSize + 128 KB must be representable for a valid table */
|
||||
bs->entropy.fse.offcode_repeatMode = ZSTD_dictNCountRepeat(offcodeNCount, offcodeMaxValue, MIN(offcodeMax, MaxOff));
|
||||
|
||||
/* All repCodes must be <= dictContentSize and != 0 */
|
||||
{ U32 u;
|
||||
for (u=0; u<3; u++) {
|
||||
RETURN_ERROR_IF(bs->rep[u] == 0, dictionary_corrupted, "");
|
||||
RETURN_ERROR_IF(bs->rep[u] > dictContentSize, dictionary_corrupted, "");
|
||||
} } }
|
||||
|
||||
return (size_t)(dictPtr - (const BYTE*)dict);
|
||||
return ZSTD_rust_loadCEntropy(bs, workspace, dict, dictSize);
|
||||
}
|
||||
|
||||
/* Dictionary format :
|
||||
|
||||
Reference in New Issue
Block a user