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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user