feat(compress): move sequence-store branch policy into Rust
Move buildSeqStore's preloaded external-sequence, LDM, external-producer, fallback, and ordinary matchfinder branch ordering into Rust. Keep the private CCtx, matchfinder, LDM, and producer operations behind narrow C callbacks, add ABI layout assertions, update the Rust boundary documentation, and cover each policy branch with focused tests. Test Plan: - cargo fmt --manifest-path rust/Cargo.toml -- --check - ulimit -v 41943040 && CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml --lib (690 passed) - ulimit -v 41943040 && CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings - ulimit -v 41943040 && MAKEFLAGS=-j1 make -B -C programs -j1 zstd - ulimit -v 41943040 && MAKEFLAGS=-j1 make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s (84 tests and both short fuzz rounds passed)
This commit is contained in:
+125
-98
@@ -1862,9 +1862,13 @@ typedef void (*ZSTD_rust_buildSeqStoreSkip_f)(void* context, size_t srcSize);
|
|||||||
typedef void (*ZSTD_rust_buildSeqStorePrepare_f)(void* context,
|
typedef void (*ZSTD_rust_buildSeqStorePrepare_f)(void* context,
|
||||||
const void* src,
|
const void* src,
|
||||||
size_t srcSize);
|
size_t srcSize);
|
||||||
typedef size_t (*ZSTD_rust_buildSeqStoreSelect_f)(
|
typedef size_t (*ZSTD_rust_buildSeqStoreCompress_f)(
|
||||||
void* context, SeqStore_t* seqStore, U32 nextRep[ZSTD_REP_NUM],
|
void* context, SeqStore_t* seqStore, U32 nextRep[ZSTD_REP_NUM],
|
||||||
const void* src, size_t srcSize, int* seqStoreComplete);
|
const void* src, size_t srcSize);
|
||||||
|
typedef size_t (*ZSTD_rust_buildSeqStoreTryExternalProducer_f)(
|
||||||
|
void* context, const void* src, size_t srcSize,
|
||||||
|
int* seqStoreComplete, int* allowFallback);
|
||||||
|
typedef void (*ZSTD_rust_buildSeqStoreClearLdm_f)(void* context);
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SeqStore_t* seqStore;
|
SeqStore_t* seqStore;
|
||||||
ZSTD_compressedBlockState_t** prevCBlock;
|
ZSTD_compressedBlockState_t** prevCBlock;
|
||||||
@@ -1874,7 +1878,15 @@ typedef struct {
|
|||||||
int validateSeqStore;
|
int validateSeqStore;
|
||||||
ZSTD_rust_buildSeqStoreSkip_f skipSmallBlock;
|
ZSTD_rust_buildSeqStoreSkip_f skipSmallBlock;
|
||||||
ZSTD_rust_buildSeqStorePrepare_f prepareMatchState;
|
ZSTD_rust_buildSeqStorePrepare_f prepareMatchState;
|
||||||
ZSTD_rust_buildSeqStoreSelect_f selectSequences;
|
int hasExternalSequences;
|
||||||
|
int ldmEnabled;
|
||||||
|
int hasExternalSequenceProducer;
|
||||||
|
int enableMatchFinderFallback;
|
||||||
|
ZSTD_rust_buildSeqStoreCompress_f compressExternalSequences;
|
||||||
|
ZSTD_rust_buildSeqStoreCompress_f compressLdm;
|
||||||
|
ZSTD_rust_buildSeqStoreTryExternalProducer_f tryExternalSequenceProducer;
|
||||||
|
ZSTD_rust_buildSeqStoreCompress_f compressInternal;
|
||||||
|
ZSTD_rust_buildSeqStoreClearLdm_f clearLdmSeqStore;
|
||||||
} ZSTD_rust_buildSeqStoreState;
|
} ZSTD_rust_buildSeqStoreState;
|
||||||
size_t ZSTD_rust_buildSeqStore(const ZSTD_rust_buildSeqStoreState* state,
|
size_t ZSTD_rust_buildSeqStore(const ZSTD_rust_buildSeqStoreState* state,
|
||||||
const void* src, size_t srcSize);
|
const void* src, size_t srcSize);
|
||||||
@@ -1890,10 +1902,26 @@ typedef char ZSTD_rust_build_seq_store_state_layout[
|
|||||||
== 4 * sizeof(void*) + sizeof(U32) + sizeof(int)
|
== 4 * sizeof(void*) + sizeof(U32) + sizeof(int)
|
||||||
&& offsetof(ZSTD_rust_buildSeqStoreState, prepareMatchState)
|
&& offsetof(ZSTD_rust_buildSeqStoreState, prepareMatchState)
|
||||||
== 5 * sizeof(void*) + sizeof(U32) + sizeof(int)
|
== 5 * sizeof(void*) + sizeof(U32) + sizeof(int)
|
||||||
&& offsetof(ZSTD_rust_buildSeqStoreState, selectSequences)
|
&& offsetof(ZSTD_rust_buildSeqStoreState, hasExternalSequences)
|
||||||
== 6 * sizeof(void*) + sizeof(U32) + sizeof(int)
|
== 6 * sizeof(void*) + sizeof(U32) + sizeof(int)
|
||||||
|
&& offsetof(ZSTD_rust_buildSeqStoreState, ldmEnabled)
|
||||||
|
== 6 * sizeof(void*) + sizeof(U32) + 2 * sizeof(int)
|
||||||
|
&& offsetof(ZSTD_rust_buildSeqStoreState, hasExternalSequenceProducer)
|
||||||
|
== 6 * sizeof(void*) + sizeof(U32) + 3 * sizeof(int)
|
||||||
|
&& offsetof(ZSTD_rust_buildSeqStoreState, enableMatchFinderFallback)
|
||||||
|
== 6 * sizeof(void*) + sizeof(U32) + 4 * sizeof(int)
|
||||||
|
&& offsetof(ZSTD_rust_buildSeqStoreState, compressExternalSequences)
|
||||||
|
== 6 * sizeof(void*) + sizeof(U32) + 5 * sizeof(int)
|
||||||
|
&& offsetof(ZSTD_rust_buildSeqStoreState, compressLdm)
|
||||||
|
== 7 * sizeof(void*) + sizeof(U32) + 5 * sizeof(int)
|
||||||
|
&& offsetof(ZSTD_rust_buildSeqStoreState, tryExternalSequenceProducer)
|
||||||
|
== 8 * sizeof(void*) + sizeof(U32) + 5 * sizeof(int)
|
||||||
|
&& offsetof(ZSTD_rust_buildSeqStoreState, compressInternal)
|
||||||
|
== 9 * sizeof(void*) + sizeof(U32) + 5 * sizeof(int)
|
||||||
|
&& offsetof(ZSTD_rust_buildSeqStoreState, clearLdmSeqStore)
|
||||||
|
== 10 * sizeof(void*) + sizeof(U32) + 5 * sizeof(int)
|
||||||
&& sizeof(ZSTD_rust_buildSeqStoreState)
|
&& sizeof(ZSTD_rust_buildSeqStoreState)
|
||||||
== 7 * sizeof(void*) + sizeof(U32) + sizeof(int))
|
== 11 * sizeof(void*) + sizeof(U32) + 5 * sizeof(int))
|
||||||
? 1 : -1];
|
? 1 : -1];
|
||||||
|
|
||||||
typedef size_t (*ZSTD_rust_externalSequenceTransfer_f)(
|
typedef size_t (*ZSTD_rust_externalSequenceTransfer_f)(
|
||||||
@@ -4672,106 +4700,97 @@ static void ZSTD_rust_buildSeqStore_prepareMatchState(void* context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t ZSTD_rust_buildSeqStore_selectSequences(
|
static size_t ZSTD_rust_buildSeqStore_compressExternalSequences(
|
||||||
void* context, SeqStore_t* seqStore, U32 nextRep[ZSTD_REP_NUM],
|
void* context, SeqStore_t* seqStore, U32 nextRep[ZSTD_REP_NUM],
|
||||||
const void* src, size_t srcSize, int* seqStoreComplete)
|
const void* src, size_t srcSize)
|
||||||
{
|
{
|
||||||
ZSTD_CCtx* const zc = (ZSTD_CCtx*)context;
|
ZSTD_CCtx* const zc = (ZSTD_CCtx*)context;
|
||||||
ZSTD_MatchState_t* const ms = &zc->blockState.matchState;
|
ZSTD_MatchState_t* const ms = &zc->blockState.matchState;
|
||||||
ZSTD_dictMode_e const dictMode = ZSTD_matchState_dictMode(ms);
|
|
||||||
size_t lastLLSize;
|
size_t lastLLSize;
|
||||||
*seqStoreComplete = 0;
|
assert(zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_disable);
|
||||||
|
lastLLSize = ZSTD_ldm_blockCompress(
|
||||||
if (zc->externSeqStore.pos < zc->externSeqStore.size) {
|
&zc->externSeqStore, ms, seqStore, nextRep,
|
||||||
assert(zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_disable);
|
zc->appliedParams.useRowMatchFinder, src, srcSize);
|
||||||
|
assert(zc->externSeqStore.pos <= zc->externSeqStore.size);
|
||||||
/* External matchfinder + LDM is technically possible, just not
|
|
||||||
* implemented yet. */
|
|
||||||
RETURN_ERROR_IF(
|
|
||||||
ZSTD_hasExtSeqProd(&zc->appliedParams),
|
|
||||||
parameter_combination_unsupported,
|
|
||||||
"Long-distance matching with external sequence producer enabled is not currently supported."
|
|
||||||
);
|
|
||||||
|
|
||||||
lastLLSize = ZSTD_ldm_blockCompress(
|
|
||||||
&zc->externSeqStore, ms, seqStore, nextRep,
|
|
||||||
zc->appliedParams.useRowMatchFinder, src, srcSize);
|
|
||||||
assert(zc->externSeqStore.pos <= zc->externSeqStore.size);
|
|
||||||
} else if (zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable) {
|
|
||||||
RawSeqStore_t ldmSeqStore = kNullRawSeqStore;
|
|
||||||
|
|
||||||
/* External matchfinder + LDM is technically possible, just not
|
|
||||||
* implemented yet. */
|
|
||||||
RETURN_ERROR_IF(
|
|
||||||
ZSTD_hasExtSeqProd(&zc->appliedParams),
|
|
||||||
parameter_combination_unsupported,
|
|
||||||
"Long-distance matching with external sequence producer enabled is not currently supported."
|
|
||||||
);
|
|
||||||
|
|
||||||
ldmSeqStore.seq = zc->ldmSequences;
|
|
||||||
ldmSeqStore.capacity = zc->maxNbLdmSequences;
|
|
||||||
FORWARD_IF_ERROR(ZSTD_ldm_generateSequences(
|
|
||||||
&zc->ldmState, &ldmSeqStore, &zc->appliedParams.ldmParams,
|
|
||||||
src, srcSize), "");
|
|
||||||
lastLLSize = ZSTD_ldm_blockCompress(
|
|
||||||
&ldmSeqStore, ms, seqStore, nextRep,
|
|
||||||
zc->appliedParams.useRowMatchFinder, src, srcSize);
|
|
||||||
assert(ldmSeqStore.pos == ldmSeqStore.size);
|
|
||||||
} else if (ZSTD_hasExtSeqProd(&zc->appliedParams)) {
|
|
||||||
size_t nbExternalSeqs = 0;
|
|
||||||
int allowFallback = 0;
|
|
||||||
ZSTD_rust_externalSequenceProducerState state;
|
|
||||||
size_t const windowSize = (size_t)1 << zc->appliedParams.cParams.windowLog;
|
|
||||||
assert(zc->extSeqBufCapacity >= ZSTD_sequenceBound(srcSize));
|
|
||||||
assert(zc->appliedParams.extSeqProdFunc != NULL);
|
|
||||||
|
|
||||||
state.callbackContext = zc;
|
|
||||||
state.producerState = zc->appliedParams.extSeqProdState;
|
|
||||||
state.producer = zc->appliedParams.extSeqProdFunc;
|
|
||||||
state.extSeqBuf = zc->extSeqBuf;
|
|
||||||
state.extSeqBufCapacity = &zc->extSeqBufCapacity;
|
|
||||||
state.src = src;
|
|
||||||
state.srcSize = &srcSize;
|
|
||||||
state.compressionLevel = &zc->appliedParams.compressionLevel;
|
|
||||||
state.windowSize = &windowSize;
|
|
||||||
state.transfer = ZSTD_rust_externalSequenceProducer_transfer;
|
|
||||||
state.externalSeqCount = &nbExternalSeqs;
|
|
||||||
state.seqStoreComplete = seqStoreComplete;
|
|
||||||
state.allowFallback = &allowFallback;
|
|
||||||
|
|
||||||
{ size_t const producerResult =
|
|
||||||
ZSTD_rust_tryExternalSequenceProducer(&state);
|
|
||||||
if (*seqStoreComplete) {
|
|
||||||
ms->ldmSeqStore = NULL;
|
|
||||||
DEBUGLOG(5, "Copied %lu sequences from external sequence producer to internal seqStore.",
|
|
||||||
(unsigned long)nbExternalSeqs);
|
|
||||||
return producerResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!allowFallback || !zc->appliedParams.enableMatchFinderFallback)
|
|
||||||
return producerResult;
|
|
||||||
|
|
||||||
{ ZSTD_BlockCompressor_f const blockCompressor =
|
|
||||||
ZSTD_selectBlockCompressor(
|
|
||||||
zc->appliedParams.cParams.strategy,
|
|
||||||
zc->appliedParams.useRowMatchFinder,
|
|
||||||
dictMode);
|
|
||||||
ms->ldmSeqStore = NULL;
|
|
||||||
DEBUGLOG(5, "External sequence producer returned error code %lu. Falling back to internal parser.",
|
|
||||||
(unsigned long)nbExternalSeqs);
|
|
||||||
lastLLSize = blockCompressor(ms, seqStore, nextRep, src, srcSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ZSTD_BlockCompressor_f const blockCompressor = ZSTD_selectBlockCompressor(
|
|
||||||
zc->appliedParams.cParams.strategy,
|
|
||||||
zc->appliedParams.useRowMatchFinder, dictMode);
|
|
||||||
ms->ldmSeqStore = NULL;
|
|
||||||
lastLLSize = blockCompressor(ms, seqStore, nextRep, src, srcSize);
|
|
||||||
}
|
|
||||||
return lastLLSize;
|
return lastLLSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t ZSTD_rust_buildSeqStore_compressLdm(
|
||||||
|
void* context, SeqStore_t* seqStore, U32 nextRep[ZSTD_REP_NUM],
|
||||||
|
const void* src, size_t srcSize)
|
||||||
|
{
|
||||||
|
ZSTD_CCtx* const zc = (ZSTD_CCtx*)context;
|
||||||
|
ZSTD_MatchState_t* const ms = &zc->blockState.matchState;
|
||||||
|
RawSeqStore_t ldmSeqStore = kNullRawSeqStore;
|
||||||
|
size_t result;
|
||||||
|
|
||||||
|
ldmSeqStore.seq = zc->ldmSequences;
|
||||||
|
ldmSeqStore.capacity = zc->maxNbLdmSequences;
|
||||||
|
result = ZSTD_ldm_generateSequences(
|
||||||
|
&zc->ldmState, &ldmSeqStore, &zc->appliedParams.ldmParams,
|
||||||
|
src, srcSize);
|
||||||
|
if (ERR_isError(result)) return result;
|
||||||
|
result = ZSTD_ldm_blockCompress(
|
||||||
|
&ldmSeqStore, ms, seqStore, nextRep,
|
||||||
|
zc->appliedParams.useRowMatchFinder, src, srcSize);
|
||||||
|
assert(ldmSeqStore.pos == ldmSeqStore.size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t ZSTD_rust_buildSeqStore_tryExternalSequenceProducer(
|
||||||
|
void* context, const void* src, size_t srcSize,
|
||||||
|
int* seqStoreComplete, int* allowFallback)
|
||||||
|
{
|
||||||
|
ZSTD_CCtx* const zc = (ZSTD_CCtx*)context;
|
||||||
|
size_t nbExternalSeqs = 0;
|
||||||
|
ZSTD_rust_externalSequenceProducerState state;
|
||||||
|
size_t const windowSize = (size_t)1 << zc->appliedParams.cParams.windowLog;
|
||||||
|
|
||||||
|
assert(zc->extSeqBufCapacity >= ZSTD_sequenceBound(srcSize));
|
||||||
|
assert(zc->appliedParams.extSeqProdFunc != NULL);
|
||||||
|
state.callbackContext = zc;
|
||||||
|
state.producerState = zc->appliedParams.extSeqProdState;
|
||||||
|
state.producer = zc->appliedParams.extSeqProdFunc;
|
||||||
|
state.extSeqBuf = zc->extSeqBuf;
|
||||||
|
state.extSeqBufCapacity = &zc->extSeqBufCapacity;
|
||||||
|
state.src = src;
|
||||||
|
state.srcSize = &srcSize;
|
||||||
|
state.compressionLevel = &zc->appliedParams.compressionLevel;
|
||||||
|
state.windowSize = &windowSize;
|
||||||
|
state.transfer = ZSTD_rust_externalSequenceProducer_transfer;
|
||||||
|
state.externalSeqCount = &nbExternalSeqs;
|
||||||
|
state.seqStoreComplete = seqStoreComplete;
|
||||||
|
state.allowFallback = allowFallback;
|
||||||
|
|
||||||
|
{
|
||||||
|
size_t const producerResult = ZSTD_rust_tryExternalSequenceProducer(&state);
|
||||||
|
if (*seqStoreComplete) {
|
||||||
|
DEBUGLOG(5, "Copied %lu sequences from external sequence producer to internal seqStore.",
|
||||||
|
(unsigned long)nbExternalSeqs);
|
||||||
|
}
|
||||||
|
return producerResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t ZSTD_rust_buildSeqStore_compressInternal(
|
||||||
|
void* context, SeqStore_t* seqStore, U32 nextRep[ZSTD_REP_NUM],
|
||||||
|
const void* src, size_t srcSize)
|
||||||
|
{
|
||||||
|
ZSTD_CCtx* const zc = (ZSTD_CCtx*)context;
|
||||||
|
ZSTD_MatchState_t* const ms = &zc->blockState.matchState;
|
||||||
|
ZSTD_BlockCompressor_f const blockCompressor = ZSTD_selectBlockCompressor(
|
||||||
|
zc->appliedParams.cParams.strategy,
|
||||||
|
zc->appliedParams.useRowMatchFinder,
|
||||||
|
ZSTD_matchState_dictMode(ms));
|
||||||
|
return blockCompressor(ms, seqStore, nextRep, src, srcSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ZSTD_rust_buildSeqStore_clearLdmSeqStore(void* context)
|
||||||
|
{
|
||||||
|
ZSTD_CCtx* const zc = (ZSTD_CCtx*)context;
|
||||||
|
zc->blockState.matchState.ldmSeqStore = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void ZSTD_initBuildSeqStoreState(
|
static void ZSTD_initBuildSeqStoreState(
|
||||||
ZSTD_CCtx* zc, ZSTD_rust_buildSeqStoreState* state)
|
ZSTD_CCtx* zc, ZSTD_rust_buildSeqStoreState* state)
|
||||||
{
|
{
|
||||||
@@ -4787,7 +4806,15 @@ static void ZSTD_initBuildSeqStoreState(
|
|||||||
#endif
|
#endif
|
||||||
state->skipSmallBlock = ZSTD_rust_buildSeqStore_skipSmallBlock;
|
state->skipSmallBlock = ZSTD_rust_buildSeqStore_skipSmallBlock;
|
||||||
state->prepareMatchState = ZSTD_rust_buildSeqStore_prepareMatchState;
|
state->prepareMatchState = ZSTD_rust_buildSeqStore_prepareMatchState;
|
||||||
state->selectSequences = ZSTD_rust_buildSeqStore_selectSequences;
|
state->hasExternalSequences = zc->externSeqStore.pos < zc->externSeqStore.size;
|
||||||
|
state->ldmEnabled = zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable;
|
||||||
|
state->hasExternalSequenceProducer = ZSTD_hasExtSeqProd(&zc->appliedParams);
|
||||||
|
state->enableMatchFinderFallback = zc->appliedParams.enableMatchFinderFallback;
|
||||||
|
state->compressExternalSequences = ZSTD_rust_buildSeqStore_compressExternalSequences;
|
||||||
|
state->compressLdm = ZSTD_rust_buildSeqStore_compressLdm;
|
||||||
|
state->tryExternalSequenceProducer = ZSTD_rust_buildSeqStore_tryExternalSequenceProducer;
|
||||||
|
state->compressInternal = ZSTD_rust_buildSeqStore_compressInternal;
|
||||||
|
state->clearLdmSeqStore = ZSTD_rust_buildSeqStore_clearLdmSeqStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
|
static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
|
||||||
|
|||||||
+4
-3
@@ -43,9 +43,10 @@ zstd ABI:
|
|||||||
Rust single-block serializer. C retains split discovery's context setup
|
Rust single-block serializer. C retains split discovery's context setup
|
||||||
and the outer block-dispatch decision. `zstd_compress` also owns ordinary
|
and the outer block-dispatch decision. `zstd_compress` also owns ordinary
|
||||||
sequence-block entropy emission, sequence collection, the legacy RLE
|
sequence-block entropy emission, sequence collection, the legacy RLE
|
||||||
compatibility gate, and sequence-store construction; C supplies only the
|
compatibility gate, sequence-store construction, and its branch/fallback
|
||||||
private matchfinder, LDM block-preparation/consumption, external-sequence-
|
policy; C supplies only the private matchfinder, LDM
|
||||||
producer, and state-preparation callbacks. Rust also owns the frame-chunk
|
block-preparation/consumption, external-sequence-producer, and
|
||||||
|
state-preparation callbacks. Rust also owns the frame-chunk
|
||||||
block loop, including block sizing, target/split/internal dispatch, output
|
block loop, including block sizing, target/split/internal dispatch, output
|
||||||
accounting, and frame-state
|
accounting, and frame-state
|
||||||
updates; C supplies the private block-compression callbacks. Rust also
|
updates; C supplies the private block-compression callbacks. Rust also
|
||||||
|
|||||||
+381
-15
@@ -177,14 +177,11 @@ type FrameChunkChecksumFn = unsafe extern "C" fn(*mut c_void, *const c_void, usi
|
|||||||
|
|
||||||
type BuildSeqStoreSkipFn = unsafe extern "C" fn(*mut c_void, usize);
|
type BuildSeqStoreSkipFn = unsafe extern "C" fn(*mut c_void, usize);
|
||||||
type BuildSeqStorePrepareFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
|
type BuildSeqStorePrepareFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
|
||||||
type BuildSeqStoreSelectFn = unsafe extern "C" fn(
|
type BuildSeqStoreCompressFn =
|
||||||
*mut c_void,
|
unsafe extern "C" fn(*mut c_void, *mut SeqStore_t, *mut u32, *const c_void, usize) -> usize;
|
||||||
*mut SeqStore_t,
|
type BuildSeqStoreTryExternalProducerFn =
|
||||||
*mut u32,
|
unsafe extern "C" fn(*mut c_void, *const c_void, usize, *mut c_int, *mut c_int) -> usize;
|
||||||
*const c_void,
|
type BuildSeqStoreClearLdmFn = unsafe extern "C" fn(*mut c_void);
|
||||||
usize,
|
|
||||||
*mut c_int,
|
|
||||||
) -> usize;
|
|
||||||
|
|
||||||
/// Explicit projection for the sequence-store builder.
|
/// Explicit projection for the sequence-store builder.
|
||||||
///
|
///
|
||||||
@@ -201,7 +198,15 @@ pub struct ZSTD_rust_buildSeqStoreState {
|
|||||||
validate_seq_store: c_int,
|
validate_seq_store: c_int,
|
||||||
skip_small_block: BuildSeqStoreSkipFn,
|
skip_small_block: BuildSeqStoreSkipFn,
|
||||||
prepare_match_state: BuildSeqStorePrepareFn,
|
prepare_match_state: BuildSeqStorePrepareFn,
|
||||||
select_sequences: BuildSeqStoreSelectFn,
|
has_external_sequences: c_int,
|
||||||
|
ldm_enabled: c_int,
|
||||||
|
has_external_sequence_producer: c_int,
|
||||||
|
enable_match_finder_fallback: c_int,
|
||||||
|
compress_external_sequences: BuildSeqStoreCompressFn,
|
||||||
|
compress_ldm: BuildSeqStoreCompressFn,
|
||||||
|
try_external_sequence_producer: BuildSeqStoreTryExternalProducerFn,
|
||||||
|
compress_internal: BuildSeqStoreCompressFn,
|
||||||
|
clear_ldm_seq_store: BuildSeqStoreClearLdmFn,
|
||||||
}
|
}
|
||||||
|
|
||||||
const _: () = {
|
const _: () = {
|
||||||
@@ -223,12 +228,44 @@ const _: () = {
|
|||||||
== 5 * size_of::<usize>() + size_of::<c_uint>() + size_of::<c_int>()
|
== 5 * size_of::<usize>() + size_of::<c_uint>() + size_of::<c_int>()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
offset_of!(ZSTD_rust_buildSeqStoreState, select_sequences)
|
offset_of!(ZSTD_rust_buildSeqStoreState, has_external_sequences)
|
||||||
== 6 * size_of::<usize>() + size_of::<c_uint>() + size_of::<c_int>()
|
== 6 * size_of::<usize>() + size_of::<c_uint>() + size_of::<c_int>()
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_buildSeqStoreState, ldm_enabled)
|
||||||
|
== 6 * size_of::<usize>() + size_of::<c_uint>() + 2 * size_of::<c_int>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_buildSeqStoreState, has_external_sequence_producer)
|
||||||
|
== 6 * size_of::<usize>() + size_of::<c_uint>() + 3 * size_of::<c_int>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_buildSeqStoreState, enable_match_finder_fallback)
|
||||||
|
== 6 * size_of::<usize>() + size_of::<c_uint>() + 4 * size_of::<c_int>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_buildSeqStoreState, compress_external_sequences)
|
||||||
|
== 6 * size_of::<usize>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_buildSeqStoreState, compress_ldm)
|
||||||
|
== 7 * size_of::<usize>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_buildSeqStoreState, try_external_sequence_producer)
|
||||||
|
== size_of::<[usize; 8]>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_buildSeqStoreState, compress_internal)
|
||||||
|
== 9 * size_of::<usize>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_buildSeqStoreState, clear_ldm_seq_store)
|
||||||
|
== 10 * size_of::<usize>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
size_of::<ZSTD_rust_buildSeqStoreState>()
|
size_of::<ZSTD_rust_buildSeqStoreState>()
|
||||||
== 7 * size_of::<usize>() + size_of::<c_uint>() + size_of::<c_int>()
|
== 11 * size_of::<usize>() + size_of::<c_uint>() + 5 * size_of::<c_int>()
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3567,7 +3604,84 @@ impl SingleBlockSeams {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rust-owned sequence-store boundary. C callbacks perform the operations
|
/// Rust-owned sequence-store branch policy. C callbacks perform the
|
||||||
|
/// operations which need the private matchfinder or CCtx
|
||||||
|
/// parameter/function-pointer state.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
unsafe fn build_seq_store_select_sequences_body_with(
|
||||||
|
state: &ZSTD_rust_buildSeqStoreState,
|
||||||
|
seq_store: *mut SeqStore_t,
|
||||||
|
next_rep: *mut u32,
|
||||||
|
src: *const c_void,
|
||||||
|
src_size: usize,
|
||||||
|
seq_store_complete: &mut c_int,
|
||||||
|
) -> usize {
|
||||||
|
if seq_store.is_null()
|
||||||
|
|| next_rep.is_null()
|
||||||
|
|| src.is_null()
|
||||||
|
|| state.compress_external_sequences as usize == 0
|
||||||
|
|| state.compress_ldm as usize == 0
|
||||||
|
|| state.try_external_sequence_producer as usize == 0
|
||||||
|
|| state.compress_internal as usize == 0
|
||||||
|
|| state.clear_ldm_seq_store as usize == 0
|
||||||
|
{
|
||||||
|
return ERROR(ZstdErrorCode::Generic);
|
||||||
|
}
|
||||||
|
|
||||||
|
*seq_store_complete = 0;
|
||||||
|
if state.has_external_sequences != 0 {
|
||||||
|
if state.has_external_sequence_producer != 0 {
|
||||||
|
return ERROR(ZstdErrorCode::ParameterCombinationUnsupported);
|
||||||
|
}
|
||||||
|
return unsafe {
|
||||||
|
(state.compress_external_sequences)(
|
||||||
|
state.callback_context,
|
||||||
|
seq_store,
|
||||||
|
next_rep,
|
||||||
|
src,
|
||||||
|
src_size,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.ldm_enabled != 0 {
|
||||||
|
if state.has_external_sequence_producer != 0 {
|
||||||
|
return ERROR(ZstdErrorCode::ParameterCombinationUnsupported);
|
||||||
|
}
|
||||||
|
return unsafe {
|
||||||
|
(state.compress_ldm)(state.callback_context, seq_store, next_rep, src, src_size)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.has_external_sequence_producer != 0 {
|
||||||
|
let mut allow_fallback = 0;
|
||||||
|
let producer_result = unsafe {
|
||||||
|
(state.try_external_sequence_producer)(
|
||||||
|
state.callback_context,
|
||||||
|
src,
|
||||||
|
src_size,
|
||||||
|
seq_store_complete,
|
||||||
|
&mut allow_fallback,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if *seq_store_complete != 0 {
|
||||||
|
unsafe { (state.clear_ldm_seq_store)(state.callback_context) };
|
||||||
|
return producer_result;
|
||||||
|
}
|
||||||
|
if allow_fallback == 0 || state.enable_match_finder_fallback == 0 {
|
||||||
|
return producer_result;
|
||||||
|
}
|
||||||
|
unsafe { (state.clear_ldm_seq_store)(state.callback_context) };
|
||||||
|
return unsafe {
|
||||||
|
(state.compress_internal)(state.callback_context, seq_store, next_rep, src, src_size)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { (state.clear_ldm_seq_store)(state.callback_context) };
|
||||||
|
unsafe { (state.compress_internal)(state.callback_context, seq_store, next_rep, src, src_size) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rust-owned sequence-store boundary. C callbacks perform the operations
|
||||||
/// which need the private matchfinder or CCtx parameter/function-pointer state.
|
/// which need the private matchfinder or CCtx parameter/function-pointer state.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
unsafe fn build_seq_store_body_with(
|
unsafe fn build_seq_store_body_with(
|
||||||
@@ -3578,7 +3692,6 @@ unsafe fn build_seq_store_body_with(
|
|||||||
if state.seq_store.is_null()
|
if state.seq_store.is_null()
|
||||||
|| state.skip_small_block as usize == 0
|
|| state.skip_small_block as usize == 0
|
||||||
|| state.prepare_match_state as usize == 0
|
|| state.prepare_match_state as usize == 0
|
||||||
|| state.select_sequences as usize == 0
|
|
||||||
{
|
{
|
||||||
return ERROR(ZstdErrorCode::Generic);
|
return ERROR(ZstdErrorCode::Generic);
|
||||||
}
|
}
|
||||||
@@ -3617,8 +3730,8 @@ unsafe fn build_seq_store_body_with(
|
|||||||
|
|
||||||
let mut seq_store_complete = 0;
|
let mut seq_store_complete = 0;
|
||||||
let last_literals_size = unsafe {
|
let last_literals_size = unsafe {
|
||||||
(state.select_sequences)(
|
build_seq_store_select_sequences_body_with(
|
||||||
state.callback_context,
|
state,
|
||||||
state.seq_store,
|
state.seq_store,
|
||||||
(*next_c_block).rep.as_mut_ptr(),
|
(*next_c_block).rep.as_mut_ptr(),
|
||||||
src,
|
src,
|
||||||
@@ -7700,6 +7813,259 @@ mod tests {
|
|||||||
const ZSTD_BTOPT: c_int = 7;
|
const ZSTD_BTOPT: c_int = 7;
|
||||||
const ZSTD_BTULTRA2: c_int = 9;
|
const ZSTD_BTULTRA2: c_int = 9;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct BuildSeqStoreSelectProbe {
|
||||||
|
events: Vec<&'static str>,
|
||||||
|
external_result: usize,
|
||||||
|
ldm_result: usize,
|
||||||
|
producer_result: usize,
|
||||||
|
producer_complete: c_int,
|
||||||
|
producer_allow_fallback: c_int,
|
||||||
|
internal_result: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn build_seq_store_select_probe(
|
||||||
|
context: *mut c_void,
|
||||||
|
) -> &'static mut BuildSeqStoreSelectProbe {
|
||||||
|
unsafe { &mut *context.cast::<BuildSeqStoreSelectProbe>() }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn build_seq_store_test_skip(_context: *mut c_void, _src_size: usize) {}
|
||||||
|
|
||||||
|
unsafe extern "C" fn build_seq_store_test_prepare(
|
||||||
|
_context: *mut c_void,
|
||||||
|
_src: *const c_void,
|
||||||
|
_src_size: usize,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn build_seq_store_test_external(
|
||||||
|
context: *mut c_void,
|
||||||
|
_seq_store: *mut SeqStore_t,
|
||||||
|
_next_rep: *mut u32,
|
||||||
|
_src: *const c_void,
|
||||||
|
_src_size: usize,
|
||||||
|
) -> usize {
|
||||||
|
let probe = unsafe { build_seq_store_select_probe(context) };
|
||||||
|
probe.events.push("external");
|
||||||
|
probe.external_result
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn build_seq_store_test_ldm(
|
||||||
|
context: *mut c_void,
|
||||||
|
_seq_store: *mut SeqStore_t,
|
||||||
|
_next_rep: *mut u32,
|
||||||
|
_src: *const c_void,
|
||||||
|
_src_size: usize,
|
||||||
|
) -> usize {
|
||||||
|
let probe = unsafe { build_seq_store_select_probe(context) };
|
||||||
|
probe.events.push("ldm");
|
||||||
|
probe.ldm_result
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn build_seq_store_test_producer(
|
||||||
|
context: *mut c_void,
|
||||||
|
_src: *const c_void,
|
||||||
|
_src_size: usize,
|
||||||
|
seq_store_complete: *mut c_int,
|
||||||
|
allow_fallback: *mut c_int,
|
||||||
|
) -> usize {
|
||||||
|
let probe = unsafe { build_seq_store_select_probe(context) };
|
||||||
|
probe.events.push("producer");
|
||||||
|
unsafe {
|
||||||
|
*seq_store_complete = probe.producer_complete;
|
||||||
|
*allow_fallback = probe.producer_allow_fallback;
|
||||||
|
}
|
||||||
|
probe.producer_result
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn build_seq_store_test_internal(
|
||||||
|
context: *mut c_void,
|
||||||
|
_seq_store: *mut SeqStore_t,
|
||||||
|
_next_rep: *mut u32,
|
||||||
|
_src: *const c_void,
|
||||||
|
_src_size: usize,
|
||||||
|
) -> usize {
|
||||||
|
let probe = unsafe { build_seq_store_select_probe(context) };
|
||||||
|
probe.events.push("internal");
|
||||||
|
probe.internal_result
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn build_seq_store_test_clear(context: *mut c_void) {
|
||||||
|
let probe = unsafe { build_seq_store_select_probe(context) };
|
||||||
|
probe.events.push("clear");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_seq_store_select_test_state(
|
||||||
|
probe: &mut BuildSeqStoreSelectProbe,
|
||||||
|
has_external_sequences: c_int,
|
||||||
|
ldm_enabled: c_int,
|
||||||
|
has_external_sequence_producer: c_int,
|
||||||
|
enable_match_finder_fallback: c_int,
|
||||||
|
) -> ZSTD_rust_buildSeqStoreState {
|
||||||
|
ZSTD_rust_buildSeqStoreState {
|
||||||
|
seq_store: ptr::null_mut(),
|
||||||
|
prev_c_block: ptr::null_mut(),
|
||||||
|
next_c_block: ptr::null_mut(),
|
||||||
|
callback_context: (probe as *mut BuildSeqStoreSelectProbe).cast(),
|
||||||
|
min_match: 3,
|
||||||
|
validate_seq_store: 0,
|
||||||
|
skip_small_block: build_seq_store_test_skip,
|
||||||
|
prepare_match_state: build_seq_store_test_prepare,
|
||||||
|
has_external_sequences,
|
||||||
|
ldm_enabled,
|
||||||
|
has_external_sequence_producer,
|
||||||
|
enable_match_finder_fallback,
|
||||||
|
compress_external_sequences: build_seq_store_test_external,
|
||||||
|
compress_ldm: build_seq_store_test_ldm,
|
||||||
|
try_external_sequence_producer: build_seq_store_test_producer,
|
||||||
|
compress_internal: build_seq_store_test_internal,
|
||||||
|
clear_ldm_seq_store: build_seq_store_test_clear,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_build_seq_store_select_test(state: &ZSTD_rust_buildSeqStoreState) -> (usize, c_int) {
|
||||||
|
let mut seq_store = unsafe { MaybeUninit::<SeqStore_t>::zeroed().assume_init() };
|
||||||
|
let mut next_rep = [0u32; ZSTD_REP_NUM];
|
||||||
|
let source = [0u8; 1];
|
||||||
|
let mut seq_store_complete = 99;
|
||||||
|
let result = unsafe {
|
||||||
|
build_seq_store_select_sequences_body_with(
|
||||||
|
state,
|
||||||
|
&mut seq_store,
|
||||||
|
next_rep.as_mut_ptr(),
|
||||||
|
source.as_ptr().cast(),
|
||||||
|
source.len(),
|
||||||
|
&mut seq_store_complete,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
(result, seq_store_complete)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_seq_store_selects_preloaded_external_sequences_first() {
|
||||||
|
let mut probe = BuildSeqStoreSelectProbe {
|
||||||
|
external_result: 11,
|
||||||
|
ldm_result: 12,
|
||||||
|
internal_result: 13,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let state = build_seq_store_select_test_state(&mut probe, 1, 1, 0, 0);
|
||||||
|
|
||||||
|
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
|
||||||
|
|
||||||
|
assert_eq!(result, probe.external_result);
|
||||||
|
assert_eq!(seq_store_complete, 0);
|
||||||
|
assert_eq!(probe.events, ["external"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_seq_store_uses_ldm_when_no_external_sequences_are_pending() {
|
||||||
|
let mut probe = BuildSeqStoreSelectProbe {
|
||||||
|
ldm_result: 19,
|
||||||
|
internal_result: 23,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let state = build_seq_store_select_test_state(&mut probe, 0, 1, 0, 0);
|
||||||
|
|
||||||
|
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
|
||||||
|
|
||||||
|
assert_eq!(result, probe.ldm_result);
|
||||||
|
assert_eq!(seq_store_complete, 0);
|
||||||
|
assert_eq!(probe.events, ["ldm"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_seq_store_rejects_external_producer_with_preloaded_sequences_or_ldm() {
|
||||||
|
for (has_external_sequences, ldm_enabled) in [(1, 0), (0, 1)] {
|
||||||
|
let mut probe = BuildSeqStoreSelectProbe::default();
|
||||||
|
let state = build_seq_store_select_test_state(
|
||||||
|
&mut probe,
|
||||||
|
has_external_sequences,
|
||||||
|
ldm_enabled,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
ERROR(ZstdErrorCode::ParameterCombinationUnsupported)
|
||||||
|
);
|
||||||
|
assert_eq!(seq_store_complete, 0);
|
||||||
|
assert!(probe.events.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_seq_store_clears_ldm_after_external_producer_completion() {
|
||||||
|
let mut probe = BuildSeqStoreSelectProbe {
|
||||||
|
producer_complete: 1,
|
||||||
|
producer_allow_fallback: 1,
|
||||||
|
producer_result: 0,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let state = build_seq_store_select_test_state(&mut probe, 0, 0, 1, 1);
|
||||||
|
|
||||||
|
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
|
||||||
|
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
assert_eq!(seq_store_complete, 1);
|
||||||
|
assert_eq!(probe.events, ["producer", "clear"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_seq_store_does_not_fallback_when_producer_disallows_it() {
|
||||||
|
let producer_result = ERROR(ZstdErrorCode::SequenceProducerFailed);
|
||||||
|
let mut probe = BuildSeqStoreSelectProbe {
|
||||||
|
producer_result,
|
||||||
|
producer_allow_fallback: 1,
|
||||||
|
internal_result: 17,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let state = build_seq_store_select_test_state(&mut probe, 0, 0, 1, 0);
|
||||||
|
|
||||||
|
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
|
||||||
|
|
||||||
|
assert_eq!(result, producer_result);
|
||||||
|
assert_eq!(seq_store_complete, 0);
|
||||||
|
assert_eq!(probe.events, ["producer"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_seq_store_falls_back_after_external_producer_error() {
|
||||||
|
let producer_result = ERROR(ZstdErrorCode::SequenceProducerFailed);
|
||||||
|
let mut probe = BuildSeqStoreSelectProbe {
|
||||||
|
producer_result,
|
||||||
|
producer_allow_fallback: 1,
|
||||||
|
internal_result: 23,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let state = build_seq_store_select_test_state(&mut probe, 0, 0, 1, 1);
|
||||||
|
|
||||||
|
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
|
||||||
|
|
||||||
|
assert_eq!(result, probe.internal_result);
|
||||||
|
assert_eq!(seq_store_complete, 0);
|
||||||
|
assert_eq!(probe.events, ["producer", "clear", "internal"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_seq_store_clears_ldm_before_internal_compression() {
|
||||||
|
let mut probe = BuildSeqStoreSelectProbe {
|
||||||
|
internal_result: 29,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let state = build_seq_store_select_test_state(&mut probe, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
let (result, seq_store_complete) = run_build_seq_store_select_test(&state);
|
||||||
|
|
||||||
|
assert_eq!(result, probe.internal_result);
|
||||||
|
assert_eq!(seq_store_complete, 0);
|
||||||
|
assert_eq!(probe.events, ["clear", "internal"]);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ResetMatchStateTestContext {
|
struct ResetMatchStateTestContext {
|
||||||
events: Vec<c_int>,
|
events: Vec<c_int>,
|
||||||
|
|||||||
Reference in New Issue
Block a user