feat(compress): move no-delimiter sequence transfer to Rust
Port the no-delimiter external-sequence block scanner, match splitting, repcode updates, validation, and sequence/literal storage behind a narrow C shim. Preserve trailing bytes as literals when the sequence buffer ends before the requested block, and remove the obsolete C validation wrapper.
Test Plan:
- cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression (242 passed)
- cargo clippy --manifest-path rust/Cargo.toml --no-default-features --features compression [--benches|--tests] (clean)
- cargo +nightly fmt --manifest-path rust/Cargo.toml (clean)
- make -B -C lib -j2 lib (passed)
- make -C tests -j2 test-zstream (84 deterministic; 5960 and 8307 randomized passed)
- make -C tests -j2 test-fuzzer (existing test 56 CCtx-reuse mismatch; also fails at 37eb75758)
- make -C tests -j2 test-zstd (existing Rust CLI --patch-from gap)
This commit is contained in:
+18
-124
@@ -300,9 +300,6 @@ size_t ZSTD_rust_blockSizeExplicitDelimiter(const ZSTD_Sequence* inSeqs,
|
||||
size_t ZSTD_rust_determineBlockSize(int mode, size_t blockSize, size_t remaining,
|
||||
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
|
||||
U32 seqIdx);
|
||||
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(
|
||||
@@ -313,6 +310,14 @@ size_t ZSTD_rust_transferSequencesWBlockDelim(
|
||||
U32 nextRepcodes[ZSTD_REP_NUM], U32 dictSize,
|
||||
int validateSequences, U32 minMatch, U32 windowLog,
|
||||
int useSequenceProducer);
|
||||
size_t ZSTD_rust_transferSequencesNoDelim(
|
||||
SeqStore_t* seqStore, ZSTD_SequencePosition* seqPos,
|
||||
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
|
||||
const BYTE* src, size_t blockSize,
|
||||
const U32 prevRepcodes[ZSTD_REP_NUM],
|
||||
U32 nextRepcodes[ZSTD_REP_NUM], U32 dictSize,
|
||||
int validateSequences, U32 minMatch, U32 windowLog,
|
||||
int useSequenceProducer);
|
||||
size_t ZSTD_rust_optimalBlockSize(const void* src, size_t srcSize,
|
||||
size_t blockSizeMax, int splitLevel,
|
||||
int strategy, S64 savings,
|
||||
@@ -5124,19 +5129,6 @@ size_t ZSTD_compress2_c(ZSTD_CCtx* cctx,
|
||||
}
|
||||
}
|
||||
|
||||
/* ZSTD_validateSequence() :
|
||||
* @offBase : must use the format required by ZSTD_storeSeq()
|
||||
* @returns a ZSTD error code if sequence is not valid
|
||||
*/
|
||||
static size_t
|
||||
ZSTD_validateSequence(U32 offBase, U32 matchLength, U32 minMatch,
|
||||
size_t posInSrc, U32 windowLog, size_t dictSize, int useSequenceProducer)
|
||||
{
|
||||
return ZSTD_rust_validateSequence(offBase, matchLength, minMatch,
|
||||
posInSrc, windowLog, dictSize,
|
||||
useSequenceProducer);
|
||||
}
|
||||
|
||||
/* Returns an offset code, given a sequence's raw offset, the ongoing repcode array, and whether litLength == 0 */
|
||||
static U32 ZSTD_finalizeOffBase(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0)
|
||||
{
|
||||
@@ -5194,19 +5186,7 @@ ZSTD_transferSequences_noDelim(ZSTD_CCtx* cctx,
|
||||
const void* src, size_t blockSize,
|
||||
ZSTD_ParamSwitch_e externalRepSearch)
|
||||
{
|
||||
U32 idx = seqPos->idx;
|
||||
U32 startPosInSequence = seqPos->posInSequence;
|
||||
U32 endPosInSequence = seqPos->posInSequence + (U32)blockSize;
|
||||
size_t dictSize;
|
||||
const BYTE* const istart = (const BYTE*)(src);
|
||||
const BYTE* ip = istart;
|
||||
const BYTE* iend = istart + blockSize; /* May be adjusted if we decide to process fewer than blockSize bytes */
|
||||
Repcodes_t updatedRepcodes;
|
||||
U32 bytesAdjustment = 0;
|
||||
U32 finalMatchSplit = 0;
|
||||
|
||||
/* TODO(embg) support fast parsing mode in noBlockDelim mode */
|
||||
(void)externalRepSearch;
|
||||
|
||||
if (cctx->cdict) {
|
||||
dictSize = cctx->cdict->dictContentSize;
|
||||
@@ -5215,102 +5195,16 @@ ZSTD_transferSequences_noDelim(ZSTD_CCtx* cctx,
|
||||
} else {
|
||||
dictSize = 0;
|
||||
}
|
||||
DEBUGLOG(5, "ZSTD_transferSequences_noDelim: idx: %u PIS: %u blockSize: %zu", idx, startPosInSequence, blockSize);
|
||||
DEBUGLOG(5, "Start seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength);
|
||||
ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(Repcodes_t));
|
||||
while (endPosInSequence && idx < inSeqsSize && !finalMatchSplit) {
|
||||
const ZSTD_Sequence currSeq = inSeqs[idx];
|
||||
U32 litLength = currSeq.litLength;
|
||||
U32 matchLength = currSeq.matchLength;
|
||||
U32 const rawOffset = currSeq.offset;
|
||||
U32 offBase;
|
||||
|
||||
/* Modify the sequence depending on where endPosInSequence lies */
|
||||
if (endPosInSequence >= currSeq.litLength + currSeq.matchLength) {
|
||||
if (startPosInSequence >= litLength) {
|
||||
startPosInSequence -= litLength;
|
||||
litLength = 0;
|
||||
matchLength -= startPosInSequence;
|
||||
} else {
|
||||
litLength -= startPosInSequence;
|
||||
}
|
||||
/* Move to the next sequence */
|
||||
endPosInSequence -= currSeq.litLength + currSeq.matchLength;
|
||||
startPosInSequence = 0;
|
||||
} else {
|
||||
/* This is the final (partial) sequence we're adding from inSeqs, and endPosInSequence
|
||||
does not reach the end of the match. So, we have to split the sequence */
|
||||
DEBUGLOG(6, "Require a split: diff: %u, idx: %u PIS: %u",
|
||||
currSeq.litLength + currSeq.matchLength - endPosInSequence, idx, endPosInSequence);
|
||||
if (endPosInSequence > litLength) {
|
||||
U32 firstHalfMatchLength;
|
||||
litLength = startPosInSequence >= litLength ? 0 : litLength - startPosInSequence;
|
||||
firstHalfMatchLength = endPosInSequence - startPosInSequence - litLength;
|
||||
if (matchLength > blockSize && firstHalfMatchLength >= cctx->appliedParams.cParams.minMatch) {
|
||||
/* Only ever split the match if it is larger than the block size */
|
||||
U32 secondHalfMatchLength = currSeq.matchLength + currSeq.litLength - endPosInSequence;
|
||||
if (secondHalfMatchLength < cctx->appliedParams.cParams.minMatch) {
|
||||
/* Move the endPosInSequence backward so that it creates match of minMatch length */
|
||||
endPosInSequence -= cctx->appliedParams.cParams.minMatch - secondHalfMatchLength;
|
||||
bytesAdjustment = cctx->appliedParams.cParams.minMatch - secondHalfMatchLength;
|
||||
firstHalfMatchLength -= bytesAdjustment;
|
||||
}
|
||||
matchLength = firstHalfMatchLength;
|
||||
/* Flag that we split the last match - after storing the sequence, exit the loop,
|
||||
but keep the value of endPosInSequence */
|
||||
finalMatchSplit = 1;
|
||||
} else {
|
||||
/* Move the position in sequence backwards so that we don't split match, and break to store
|
||||
* the last literals. We use the original currSeq.litLength as a marker for where endPosInSequence
|
||||
* should go. We prefer to do this whenever it is not necessary to split the match, or if doing so
|
||||
* would cause the first half of the match to be too small
|
||||
*/
|
||||
bytesAdjustment = endPosInSequence - currSeq.litLength;
|
||||
endPosInSequence = currSeq.litLength;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* This sequence ends inside the literals, break to store the last literals */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Check if this offset can be represented with a repcode */
|
||||
{ U32 const ll0 = (litLength == 0);
|
||||
offBase = ZSTD_finalizeOffBase(rawOffset, updatedRepcodes.rep, ll0);
|
||||
ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0);
|
||||
}
|
||||
|
||||
if (cctx->appliedParams.validateSequences) {
|
||||
seqPos->posInSrc += litLength + matchLength;
|
||||
FORWARD_IF_ERROR(ZSTD_validateSequence(offBase, matchLength, cctx->appliedParams.cParams.minMatch, seqPos->posInSrc,
|
||||
cctx->appliedParams.cParams.windowLog, dictSize, ZSTD_hasExtSeqProd(&cctx->appliedParams)),
|
||||
"Sequence validation failed");
|
||||
}
|
||||
DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offBase, matchLength, litLength);
|
||||
RETURN_ERROR_IF(idx - seqPos->idx >= cctx->seqStore.maxNbSeq, externalSequences_invalid,
|
||||
"Not enough memory allocated. Try adjusting ZSTD_c_minMatch.");
|
||||
ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offBase, matchLength);
|
||||
ip += matchLength + litLength;
|
||||
if (!finalMatchSplit)
|
||||
idx++; /* Next Sequence */
|
||||
}
|
||||
DEBUGLOG(5, "Ending seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength);
|
||||
assert(idx == inSeqsSize || endPosInSequence <= inSeqs[idx].litLength + inSeqs[idx].matchLength);
|
||||
seqPos->idx = idx;
|
||||
seqPos->posInSequence = endPosInSequence;
|
||||
ZSTD_memcpy(cctx->blockState.nextCBlock->rep, updatedRepcodes.rep, sizeof(Repcodes_t));
|
||||
|
||||
iend -= bytesAdjustment;
|
||||
if (ip != iend) {
|
||||
/* Store any last literals */
|
||||
U32 const lastLLSize = (U32)(iend - ip);
|
||||
assert(ip <= iend);
|
||||
DEBUGLOG(6, "Storing last literals of size: %u", lastLLSize);
|
||||
ZSTD_storeLastLiterals(&cctx->seqStore, ip, lastLLSize);
|
||||
seqPos->posInSrc += lastLLSize;
|
||||
}
|
||||
|
||||
return (size_t)(iend-istart);
|
||||
(void)externalRepSearch;
|
||||
return ZSTD_rust_transferSequencesNoDelim(
|
||||
&cctx->seqStore, seqPos, inSeqs, inSeqsSize,
|
||||
(const BYTE*)src, blockSize,
|
||||
cctx->blockState.prevCBlock->rep,
|
||||
cctx->blockState.nextCBlock->rep, (U32)dictSize,
|
||||
cctx->appliedParams.validateSequences,
|
||||
cctx->appliedParams.cParams.minMatch,
|
||||
cctx->appliedParams.cParams.windowLog,
|
||||
ZSTD_hasExtSeqProd(&cctx->appliedParams));
|
||||
}
|
||||
|
||||
/* @seqPos represents a position within @inSeqs,
|
||||
|
||||
Reference in New Issue
Block a user