feat(compress): move target block emission into Rust

Move the target-compressed-block body out of zstd_compress.c while keeping
sequence-store construction, matchfinding, context lifetime, and the outer
offcode repeat-mode cleanup in C. Rust now owns the target policy, repeated
block decision, Rust superblock call, compressed-state confirmation, and raw
fallback. The narrow C projection asserts the cross-language layout instead
of exposing ZSTD_CCtx.

Add focused tests for first-block RLE suppression, RLE output, superblock
errors, raw fallback, and state-slot swapping. Remove the C-only maybeRLE
wrapper that became unreachable after the extraction.

Test Plan:
- cargo test --manifest-path rust/Cargo.toml --lib -- --test-threads=1
- cargo clippy --manifest-path rust/Cargo.toml --lib -- -D warnings
- cargo +nightly fmt --manifest-path rust/Cargo.toml -- --check
- make -B -C lib -j2 lib
- make -B -C tests -j2 test-cli-tests
- ZSTREAM_TESTTIME=-T2s make -B -C tests -j2 test-zstream
- FUZZERTEST=-T5s make -B -C tests -j2 test-fuzzer
This commit is contained in:
2026-07-18 19:47:42 +02:00
parent b0488f285a
commit 23f3c6dea7
2 changed files with 532 additions and 85 deletions
+56 -85
View File
@@ -79,16 +79,47 @@ void ZSTD_rust_copyCDictTableIntoCCtx(U32* dst, U32 const* src,
U64 ZSTD_rust_advanceHashSalt(U64 hashSalt, U64 hashSaltEntropy);
int ZSTD_rust_indexTooCloseToMax(size_t nextSrcBaseOffset);
int ZSTD_rust_dictTooBig(size_t loadedDictSize);
enum {
ZSTD_RUST_TARGET_CBLOCK_ACTION_RAW = 0,
ZSTD_RUST_TARGET_CBLOCK_ACTION_RLE = 1,
ZSTD_RUST_TARGET_CBLOCK_ACTION_COMPRESSED = 2,
ZSTD_RUST_TARGET_CBLOCK_ACTION_ERROR = 3
};
int ZSTD_rust_targetCBlockSizeAction(int bss, int isFirstBlock,
int maybeRLE, int isRLE,
size_t cSize, size_t srcSize,
int strategy);
/* The target-sized block body only needs this narrow projection of ZSTD_CCtx.
* Matchfinder/window state, sequence-store construction, and outer repeat-mode
* cleanup remain in C. */
typedef struct {
SeqStore_t* seqStore;
ZSTD_compressedBlockState_t** prevCBlock;
ZSTD_compressedBlockState_t** nextCBlock;
void* tmpWorkspace;
size_t tmpWkspSize;
int strategy;
int disableLiteralCompression;
int bmi2;
U32 windowLog;
size_t targetCBlockSize;
int isFirstBlock;
} ZSTD_rust_targetCBlockSizeState;
size_t ZSTD_rust_compressBlockTargetCBlockSize(
const ZSTD_rust_targetCBlockSizeState* state,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
int bss, U32 lastBlock);
typedef char ZSTD_rust_target_cblock_state_layout[
(offsetof(ZSTD_rust_targetCBlockSizeState, seqStore) == 0
&& offsetof(ZSTD_rust_targetCBlockSizeState, prevCBlock) == sizeof(void*)
&& offsetof(ZSTD_rust_targetCBlockSizeState, nextCBlock) == 2 * sizeof(void*)
&& offsetof(ZSTD_rust_targetCBlockSizeState, tmpWorkspace) == 3 * sizeof(void*)
&& offsetof(ZSTD_rust_targetCBlockSizeState, tmpWkspSize) == 4 * sizeof(void*)
&& offsetof(ZSTD_rust_targetCBlockSizeState, strategy) == 5 * sizeof(void*)
&& offsetof(ZSTD_rust_targetCBlockSizeState, disableLiteralCompression)
== 5 * sizeof(void*) + sizeof(int)
&& offsetof(ZSTD_rust_targetCBlockSizeState, bmi2)
== 5 * sizeof(void*) + 2 * sizeof(int)
&& offsetof(ZSTD_rust_targetCBlockSizeState, windowLog)
== 5 * sizeof(void*) + 3 * sizeof(int)
&& offsetof(ZSTD_rust_targetCBlockSizeState, targetCBlockSize)
== 5 * sizeof(void*) + 4 * sizeof(int)
&& offsetof(ZSTD_rust_targetCBlockSizeState, isFirstBlock)
== 5 * sizeof(void*) + 4 * sizeof(int) + sizeof(size_t)
&& sizeof(ZSTD_rust_targetCBlockSizeState)
== (sizeof(void*) == 8 ? 72 : 44))
? 1 : -1];
size_t ZSTD_rust_nextInputSizeHint(int inBufferMode,
size_t blockSizeMax,
size_t stableInNotConsumed,
@@ -373,7 +404,6 @@ size_t ZSTD_rust_convertSequencesNoRepcodes(
BlockSummary ZSTD_rust_get1BlockSummary(const ZSTD_Sequence* seqs,
size_t nbSeqs);
int ZSTD_rust_isRLE(const BYTE* src, size_t length);
int ZSTD_rust_maybeRLE(const SeqStore_t* seqStore);
size_t ZSTD_rust_postProcessSequenceProducerResult(
ZSTD_Sequence* outSeqs, size_t nbExternalSeqs,
size_t outSeqsCapacity, size_t srcSize);
@@ -2634,15 +2664,6 @@ static int ZSTD_isRLE(const BYTE* src, size_t length) {
return ZSTD_rust_isRLE(src, length);
}
/* Returns true if the given block may be RLE.
* This is just a heuristic based on the compressibility.
* It may return both false positives and false negatives.
*/
static int ZSTD_maybeRLE(SeqStore_t const* seqStore)
{
return ZSTD_rust_maybeRLE(seqStore);
}
static void
ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs)
{
@@ -3065,70 +3086,6 @@ out:
return cSize;
}
static size_t ZSTD_compressBlock_targetCBlockSize_body(ZSTD_CCtx* zc,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
const size_t bss, U32 lastBlock)
{
int const isCompress = bss == ZSTDbss_compress;
int maybeRLE = 0;
int isRLE = 0;
int action;
DEBUGLOG(6, "Attempting ZSTD_compressSuperBlock()");
if (isCompress) {
maybeRLE = ZSTD_maybeRLE(&zc->seqStore);
isRLE = ZSTD_isRLE((BYTE const*)src, srcSize);
}
action = ZSTD_rust_targetCBlockSizeAction(
(int)bss, (int)zc->isFirstBlock, maybeRLE, isRLE,
0, srcSize, (int)zc->appliedParams.cParams.strategy);
if (action == ZSTD_RUST_TARGET_CBLOCK_ACTION_RLE) {
return ZSTD_rust_rleCompressBlock(dst, dstCapacity, *(BYTE const*)src, srcSize, lastBlock);
}
if (isCompress) {
/* Attempt superblock compression.
*
* Note that compressed size of ZSTD_compressSuperBlock() is not bound by the
* standard ZSTD_compressBound(). This is a problem, because even if we have
* space now, taking an extra byte now could cause us to run out of space later
* and violate ZSTD_compressBound().
*
* Define blockBound(blockSize) = blockSize + ZSTD_blockHeaderSize.
*
* In order to respect ZSTD_compressBound() we must attempt to emit a raw
* uncompressed block in these cases:
* * cSize == 0: Return code for an uncompressed block.
* * cSize == dstSize_tooSmall: We may have expanded beyond blockBound(srcSize).
* ZSTD_noCompressBlock() will return dstSize_tooSmall if we are really out of
* output space.
* * cSize >= blockBound(srcSize): We have expanded the block too much so
* emit an uncompressed block.
*/
{ size_t const cSize =
ZSTD_compressSuperBlock(zc, dst, dstCapacity, src, srcSize, lastBlock);
action = ZSTD_rust_targetCBlockSizeAction(
(int)bss, (int)zc->isFirstBlock, 0, 0,
cSize, srcSize, (int)zc->appliedParams.cParams.strategy);
if (action == ZSTD_RUST_TARGET_CBLOCK_ACTION_ERROR) {
FORWARD_IF_ERROR(cSize, "ZSTD_compressSuperBlock failed");
}
if (action == ZSTD_RUST_TARGET_CBLOCK_ACTION_COMPRESSED) {
ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState);
return cSize;
}
}
} /* if (bss == ZSTDbss_compress)*/
DEBUGLOG(6, "Resorting to ZSTD_noCompressBlock()");
/* Superblock compression failed, attempt to emit a single no compress block.
* The decoder will be able to stream this block since it is uncompressed.
*/
return ZSTD_rust_noCompressBlock(dst, dstCapacity, src, srcSize, lastBlock);
}
static size_t ZSTD_compressBlock_targetCBlockSize(ZSTD_CCtx* zc,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
@@ -3140,7 +3097,21 @@ static size_t ZSTD_compressBlock_targetCBlockSize(ZSTD_CCtx* zc,
(unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit, (unsigned)zc->blockState.matchState.nextToUpdate, srcSize);
FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");
cSize = ZSTD_compressBlock_targetCBlockSize_body(zc, dst, dstCapacity, src, srcSize, bss, lastBlock);
{ ZSTD_rust_targetCBlockSizeState state;
state.seqStore = &zc->seqStore;
state.prevCBlock = &zc->blockState.prevCBlock;
state.nextCBlock = &zc->blockState.nextCBlock;
state.tmpWorkspace = zc->tmpWorkspace;
state.tmpWkspSize = zc->tmpWkspSize;
state.strategy = (int)zc->appliedParams.cParams.strategy;
state.disableLiteralCompression = ZSTD_literalsCompressionIsDisabled(&zc->appliedParams);
state.bmi2 = zc->bmi2;
state.windowLog = zc->appliedParams.cParams.windowLog;
state.targetCBlockSize = zc->appliedParams.targetCBlockSize;
state.isFirstBlock = zc->isFirstBlock;
cSize = ZSTD_rust_compressBlockTargetCBlockSize(
&state, dst, dstCapacity, src, srcSize, (int)bss, lastBlock);
}
FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_targetCBlockSize_body failed");
if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)