feat(compress): move external producer success path into Rust
Move block-level external sequence producer invocation, result post-processing, source-length validation, and transfer ordering into a Rust-owned ABI leaf. Retain the private CCtx transfer callback and C-side fallback/block-compressor selection, preserving producer-error fallback eligibility and direct invalid-sequence/transfer failures. Test Plan: - cargo test --manifest-path rust/Cargo.toml --lib - cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings - make -B -C programs -j1 zstd - make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s - focused external_sequence_producer unit tests
This commit is contained in:
@@ -1163,14 +1163,9 @@ void ZSTD_rust_confirmRepcodesAndEntropyTables(
|
|||||||
void ZSTD_rust_storeLastLiterals(SeqStore_t* seqStorePtr,
|
void ZSTD_rust_storeLastLiterals(SeqStore_t* seqStorePtr,
|
||||||
const BYTE* anchor, size_t lastLLSize);
|
const BYTE* anchor, size_t lastLLSize);
|
||||||
void ZSTD_rust_resetSeqStore(SeqStore_t* ssPtr);
|
void ZSTD_rust_resetSeqStore(SeqStore_t* ssPtr);
|
||||||
size_t ZSTD_rust_fastSequenceLengthSum(const ZSTD_Sequence* seqBuf,
|
|
||||||
size_t seqBufSize);
|
|
||||||
void ZSTD_rust_validateSeqStore(const SeqStore_t* seqStore, U32 minMatch);
|
void ZSTD_rust_validateSeqStore(const SeqStore_t* seqStore, U32 minMatch);
|
||||||
BlockSummary ZSTD_rust_get1BlockSummary(const ZSTD_Sequence* seqs,
|
BlockSummary ZSTD_rust_get1BlockSummary(const ZSTD_Sequence* seqs,
|
||||||
size_t nbSeqs);
|
size_t nbSeqs);
|
||||||
size_t ZSTD_rust_postProcessSequenceProducerResult(
|
|
||||||
ZSTD_Sequence* outSeqs, size_t nbExternalSeqs,
|
|
||||||
size_t outSeqsCapacity, size_t srcSize);
|
|
||||||
size_t ZSTD_rust_deriveBlockSplits(
|
size_t ZSTD_rust_deriveBlockSplits(
|
||||||
U32* partitions, U32 nbSeq,
|
U32* partitions, U32 nbSeq,
|
||||||
const SeqStore_t* originalSeqStore,
|
const SeqStore_t* originalSeqStore,
|
||||||
@@ -1344,6 +1339,57 @@ typedef char ZSTD_rust_build_seq_store_state_layout[
|
|||||||
== 7 * sizeof(void*) + sizeof(U32) + sizeof(int))
|
== 7 * sizeof(void*) + sizeof(U32) + sizeof(int))
|
||||||
? 1 : -1];
|
? 1 : -1];
|
||||||
|
|
||||||
|
typedef size_t (*ZSTD_rust_externalSequenceTransfer_f)(
|
||||||
|
void* context, ZSTD_SequencePosition* seqPos,
|
||||||
|
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
|
||||||
|
const void* src, size_t blockSize);
|
||||||
|
typedef struct {
|
||||||
|
void* callbackContext;
|
||||||
|
void* producerState;
|
||||||
|
ZSTD_sequenceProducer_F producer;
|
||||||
|
ZSTD_Sequence* extSeqBuf;
|
||||||
|
const size_t* extSeqBufCapacity;
|
||||||
|
const void* src;
|
||||||
|
const size_t* srcSize;
|
||||||
|
const int* compressionLevel;
|
||||||
|
const size_t* windowSize;
|
||||||
|
ZSTD_rust_externalSequenceTransfer_f transfer;
|
||||||
|
size_t* externalSeqCount;
|
||||||
|
int* seqStoreComplete;
|
||||||
|
int* allowFallback;
|
||||||
|
} ZSTD_rust_externalSequenceProducerState;
|
||||||
|
size_t ZSTD_rust_tryExternalSequenceProducer(
|
||||||
|
const ZSTD_rust_externalSequenceProducerState* state);
|
||||||
|
typedef char ZSTD_rust_external_sequence_producer_state_layout[
|
||||||
|
(offsetof(ZSTD_rust_externalSequenceProducerState, callbackContext) == 0
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, producerState)
|
||||||
|
== sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, producer)
|
||||||
|
== 2 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, extSeqBuf)
|
||||||
|
== 3 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, extSeqBufCapacity)
|
||||||
|
== 4 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, src)
|
||||||
|
== 5 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, srcSize)
|
||||||
|
== 6 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, compressionLevel)
|
||||||
|
== 7 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, windowSize)
|
||||||
|
== 8 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, transfer)
|
||||||
|
== 9 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, externalSeqCount)
|
||||||
|
== 10 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, seqStoreComplete)
|
||||||
|
== 11 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_externalSequenceProducerState, allowFallback)
|
||||||
|
== 12 * sizeof(void*)
|
||||||
|
&& sizeof(ZSTD_rust_externalSequenceProducerState)
|
||||||
|
== 13 * sizeof(void*))
|
||||||
|
? 1 : -1];
|
||||||
|
|
||||||
/* The sequence-compression loop receives only the state it actually reads or
|
/* The sequence-compression loop receives only the state it actually reads or
|
||||||
* updates. In particular, neither ZSTD_CCtx nor a C function pointer crosses
|
* updates. In particular, neither ZSTD_CCtx nor a C function pointer crosses
|
||||||
* the Rust ABI. */
|
* the Rust ABI. */
|
||||||
@@ -3419,29 +3465,6 @@ void ZSTD_resetSeqStore(SeqStore_t* ssPtr)
|
|||||||
ZSTD_rust_resetSeqStore(ssPtr);
|
ZSTD_rust_resetSeqStore(ssPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ZSTD_postProcessSequenceProducerResult() :
|
|
||||||
* Validates and post-processes sequences obtained through the external matchfinder API:
|
|
||||||
* - Checks whether nbExternalSeqs represents an error condition.
|
|
||||||
* - Appends a block delimiter to outSeqs if one is not already present.
|
|
||||||
* See zstd.h for context regarding block delimiters.
|
|
||||||
* Returns the number of sequences after post-processing, or an error code. */
|
|
||||||
static size_t ZSTD_postProcessSequenceProducerResult(
|
|
||||||
ZSTD_Sequence* outSeqs, size_t nbExternalSeqs, size_t outSeqsCapacity, size_t srcSize
|
|
||||||
) {
|
|
||||||
return ZSTD_rust_postProcessSequenceProducerResult(
|
|
||||||
outSeqs, nbExternalSeqs, outSeqsCapacity, srcSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ZSTD_fastSequenceLengthSum() :
|
|
||||||
* Returns sum(litLen) + sum(matchLen) + lastLits for *seqBuf*.
|
|
||||||
* Similar to another function in zstd_compress.c (determine_blockSize),
|
|
||||||
* except it doesn't check for a block delimiter to end summation.
|
|
||||||
* Removing the early exit allows the compiler to auto-vectorize (https://godbolt.org/z/cY1cajz9P).
|
|
||||||
* This function can be deleted and replaced by determine_blockSize after we resolve issue #3456. */
|
|
||||||
static size_t ZSTD_fastSequenceLengthSum(ZSTD_Sequence const* seqBuf, size_t seqBufSize) {
|
|
||||||
return ZSTD_rust_fastSequenceLengthSum(seqBuf, seqBufSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
ZSTD_transferSequences_wBlockDelim(ZSTD_CCtx* cctx,
|
ZSTD_transferSequences_wBlockDelim(ZSTD_CCtx* cctx,
|
||||||
ZSTD_SequencePosition* seqPos,
|
ZSTD_SequencePosition* seqPos,
|
||||||
@@ -3449,6 +3472,17 @@ ZSTD_transferSequences_wBlockDelim(ZSTD_CCtx* cctx,
|
|||||||
const void* src, size_t blockSize,
|
const void* src, size_t blockSize,
|
||||||
ZSTD_ParamSwitch_e externalRepSearch);
|
ZSTD_ParamSwitch_e externalRepSearch);
|
||||||
|
|
||||||
|
static size_t ZSTD_rust_externalSequenceProducer_transfer(
|
||||||
|
void* context, ZSTD_SequencePosition* seqPos,
|
||||||
|
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
|
||||||
|
const void* src, size_t blockSize)
|
||||||
|
{
|
||||||
|
ZSTD_CCtx* const zc = (ZSTD_CCtx*)context;
|
||||||
|
return ZSTD_transferSequences_wBlockDelim(
|
||||||
|
zc, seqPos, inSeqs, inSeqsSize, src, blockSize,
|
||||||
|
zc->appliedParams.searchForExternalRepcodes);
|
||||||
|
}
|
||||||
|
|
||||||
typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_BuildSeqStore_e;
|
typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_BuildSeqStore_e;
|
||||||
|
|
||||||
static void ZSTD_rust_buildSeqStore_skipSmallBlock(void* context, size_t srcSize)
|
static void ZSTD_rust_buildSeqStore_skipSmallBlock(void* context, size_t srcSize)
|
||||||
@@ -3537,41 +3571,38 @@ static size_t ZSTD_rust_buildSeqStore_selectSequences(
|
|||||||
zc->appliedParams.useRowMatchFinder, src, srcSize);
|
zc->appliedParams.useRowMatchFinder, src, srcSize);
|
||||||
assert(ldmSeqStore.pos == ldmSeqStore.size);
|
assert(ldmSeqStore.pos == ldmSeqStore.size);
|
||||||
} else if (ZSTD_hasExtSeqProd(&zc->appliedParams)) {
|
} 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->extSeqBufCapacity >= ZSTD_sequenceBound(srcSize));
|
||||||
assert(zc->appliedParams.extSeqProdFunc != NULL);
|
assert(zc->appliedParams.extSeqProdFunc != NULL);
|
||||||
|
|
||||||
{ U32 const windowSize = (U32)1 << zc->appliedParams.cParams.windowLog;
|
state.callbackContext = zc;
|
||||||
size_t const nbExternalSeqs = (zc->appliedParams.extSeqProdFunc)(
|
state.producerState = zc->appliedParams.extSeqProdState;
|
||||||
zc->appliedParams.extSeqProdState,
|
state.producer = zc->appliedParams.extSeqProdFunc;
|
||||||
zc->extSeqBuf, zc->extSeqBufCapacity,
|
state.extSeqBuf = zc->extSeqBuf;
|
||||||
src, srcSize, NULL, 0,
|
state.extSeqBufCapacity = &zc->extSeqBufCapacity;
|
||||||
zc->appliedParams.compressionLevel, windowSize);
|
state.src = src;
|
||||||
size_t const nbPostProcessedSeqs = ZSTD_postProcessSequenceProducerResult(
|
state.srcSize = &srcSize;
|
||||||
zc->extSeqBuf, nbExternalSeqs,
|
state.compressionLevel = &zc->appliedParams.compressionLevel;
|
||||||
zc->extSeqBufCapacity, srcSize);
|
state.windowSize = &windowSize;
|
||||||
|
state.transfer = ZSTD_rust_externalSequenceProducer_transfer;
|
||||||
|
state.externalSeqCount = &nbExternalSeqs;
|
||||||
|
state.seqStoreComplete = seqStoreComplete;
|
||||||
|
state.allowFallback = &allowFallback;
|
||||||
|
|
||||||
/* Return early if there is no error, since the delimiter already
|
{ size_t const producerResult =
|
||||||
* carries the block's final literals. */
|
ZSTD_rust_tryExternalSequenceProducer(&state);
|
||||||
if (!ZSTD_isError(nbPostProcessedSeqs)) {
|
if (*seqStoreComplete) {
|
||||||
ZSTD_SequencePosition seqPos = {0,0,0};
|
|
||||||
size_t const seqLenSum = ZSTD_fastSequenceLengthSum(
|
|
||||||
zc->extSeqBuf, nbPostProcessedSeqs);
|
|
||||||
RETURN_ERROR_IF(seqLenSum > srcSize, externalSequences_invalid,
|
|
||||||
"External sequences imply too large a block!");
|
|
||||||
FORWARD_IF_ERROR(ZSTD_transferSequences_wBlockDelim(
|
|
||||||
zc, &seqPos, zc->extSeqBuf, nbPostProcessedSeqs,
|
|
||||||
src, srcSize,
|
|
||||||
zc->appliedParams.searchForExternalRepcodes),
|
|
||||||
"Failed to copy external sequences to seqStore!");
|
|
||||||
ms->ldmSeqStore = NULL;
|
ms->ldmSeqStore = NULL;
|
||||||
*seqStoreComplete = 1;
|
|
||||||
DEBUGLOG(5, "Copied %lu sequences from external sequence producer to internal seqStore.",
|
DEBUGLOG(5, "Copied %lu sequences from external sequence producer to internal seqStore.",
|
||||||
(unsigned long)nbExternalSeqs);
|
(unsigned long)nbExternalSeqs);
|
||||||
return 0;
|
return producerResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!zc->appliedParams.enableMatchFinderFallback)
|
if (!allowFallback || !zc->appliedParams.enableMatchFinderFallback)
|
||||||
return nbPostProcessedSeqs;
|
return producerResult;
|
||||||
|
|
||||||
{ ZSTD_BlockCompressor_f const blockCompressor =
|
{ ZSTD_BlockCompressor_f const blockCompressor =
|
||||||
ZSTD_selectBlockCompressor(
|
ZSTD_selectBlockCompressor(
|
||||||
|
|||||||
+460
-4
@@ -37,8 +37,9 @@ use crate::zstd_compress_stats::{
|
|||||||
ZSTD_rust_convertSequencesNoRepcodes, ZSTD_rust_copyBlockSequences,
|
ZSTD_rust_convertSequencesNoRepcodes, ZSTD_rust_copyBlockSequences,
|
||||||
ZSTD_rust_countSeqStoreLiteralsBytes, ZSTD_rust_countSeqStoreMatchBytes,
|
ZSTD_rust_countSeqStoreLiteralsBytes, ZSTD_rust_countSeqStoreMatchBytes,
|
||||||
ZSTD_rust_deriveSeqStoreChunk, ZSTD_rust_determineBlockSize, ZSTD_rust_entropyCompressSeqStore,
|
ZSTD_rust_deriveSeqStoreChunk, ZSTD_rust_determineBlockSize, ZSTD_rust_entropyCompressSeqStore,
|
||||||
ZSTD_rust_entropyCompressSeqStore_internal, ZSTD_rust_finalizeOffBase,
|
ZSTD_rust_entropyCompressSeqStore_internal, ZSTD_rust_fastSequenceLengthSum,
|
||||||
ZSTD_rust_get1BlockSummary, ZSTD_rust_isRLE, ZSTD_rust_maybeRLE, ZSTD_rust_resetSeqStore,
|
ZSTD_rust_finalizeOffBase, ZSTD_rust_get1BlockSummary, ZSTD_rust_isRLE, ZSTD_rust_maybeRLE,
|
||||||
|
ZSTD_rust_postProcessSequenceProducerResult, ZSTD_rust_resetSeqStore,
|
||||||
ZSTD_rust_seqStore_resolveOffCodes, ZSTD_rust_storeLastLiterals,
|
ZSTD_rust_seqStore_resolveOffCodes, ZSTD_rust_storeLastLiterals,
|
||||||
ZSTD_rust_transferSequencesNoDelim, ZSTD_rust_transferSequencesWBlockDelim,
|
ZSTD_rust_transferSequencesNoDelim, ZSTD_rust_transferSequencesWBlockDelim,
|
||||||
ZSTD_rust_validateSeqStore, ZSTD_LLT_LITERAL_LENGTH, ZSTD_LLT_MATCH_LENGTH,
|
ZSTD_rust_validateSeqStore, ZSTD_LLT_LITERAL_LENGTH, ZSTD_LLT_MATCH_LENGTH,
|
||||||
@@ -184,8 +185,8 @@ type BuildSeqStoreSelectFn = unsafe extern "C" fn(
|
|||||||
/// Explicit projection for the sequence-store builder.
|
/// Explicit projection for the sequence-store builder.
|
||||||
///
|
///
|
||||||
/// Rust owns the threshold/reset/repcode/literal-store orchestration. The
|
/// Rust owns the threshold/reset/repcode/literal-store orchestration. The
|
||||||
/// callbacks retain the private matchfinder, LDM, and external sequence
|
/// callbacks retain the private matchfinder, LDM, and external-producer
|
||||||
/// producer operations in C without passing `ZSTD_CCtx` across the ABI.
|
/// fallback operations in C without passing `ZSTD_CCtx` across the ABI.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct ZSTD_rust_buildSeqStoreState {
|
pub struct ZSTD_rust_buildSeqStoreState {
|
||||||
seq_store: *mut SeqStore_t,
|
seq_store: *mut SeqStore_t,
|
||||||
@@ -227,6 +228,181 @@ const _: () = {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ExternalSequenceProducerFn = unsafe extern "C" fn(
|
||||||
|
*mut c_void,
|
||||||
|
*mut ZSTD_Sequence,
|
||||||
|
usize,
|
||||||
|
*const c_void,
|
||||||
|
usize,
|
||||||
|
*const c_void,
|
||||||
|
usize,
|
||||||
|
c_int,
|
||||||
|
usize,
|
||||||
|
) -> usize;
|
||||||
|
type ExternalSequenceTransferFn = unsafe extern "C" fn(
|
||||||
|
*mut c_void,
|
||||||
|
*mut ZSTD_SequencePosition,
|
||||||
|
*const ZSTD_Sequence,
|
||||||
|
usize,
|
||||||
|
*const c_void,
|
||||||
|
usize,
|
||||||
|
) -> usize;
|
||||||
|
|
||||||
|
/// Projection for the successful block-level external sequence-producer path.
|
||||||
|
///
|
||||||
|
/// Rust owns producer invocation, result post-processing, length validation,
|
||||||
|
/// and transfer ordering. The transfer callback retains the private CCtx
|
||||||
|
/// match-state projection; C owns fallback selection after this leaf returns.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ZSTD_rust_externalSequenceProducerState {
|
||||||
|
callback_context: *mut c_void,
|
||||||
|
producer_state: *mut c_void,
|
||||||
|
producer: Option<ExternalSequenceProducerFn>,
|
||||||
|
ext_seq_buf: *mut ZSTD_Sequence,
|
||||||
|
ext_seq_buf_capacity: *const usize,
|
||||||
|
src: *const c_void,
|
||||||
|
src_size: *const usize,
|
||||||
|
compression_level: *const c_int,
|
||||||
|
window_size: *const usize,
|
||||||
|
transfer: Option<ExternalSequenceTransferFn>,
|
||||||
|
external_seq_count: *mut usize,
|
||||||
|
seq_store_complete: *mut c_int,
|
||||||
|
allow_fallback: *mut c_int,
|
||||||
|
}
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
assert!(offset_of!(ZSTD_rust_externalSequenceProducerState, callback_context) == 0);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_externalSequenceProducerState, producer_state) == size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_externalSequenceProducerState, producer) == 2 * size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_externalSequenceProducerState, ext_seq_buf) == 3 * size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(
|
||||||
|
ZSTD_rust_externalSequenceProducerState,
|
||||||
|
ext_seq_buf_capacity
|
||||||
|
) == 4 * size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(offset_of!(ZSTD_rust_externalSequenceProducerState, src) == 5 * size_of::<usize>());
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_externalSequenceProducerState, src_size) == 6 * size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_externalSequenceProducerState, compression_level)
|
||||||
|
== 7 * size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_externalSequenceProducerState, window_size) == size_of::<[usize; 8]>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_externalSequenceProducerState, transfer) == 9 * size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_externalSequenceProducerState, external_seq_count)
|
||||||
|
== 10 * size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_externalSequenceProducerState, seq_store_complete)
|
||||||
|
== 11 * size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_externalSequenceProducerState, allow_fallback)
|
||||||
|
== 12 * size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(size_of::<ZSTD_rust_externalSequenceProducerState>() == 13 * size_of::<usize>());
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Try an external sequence producer; C retains only the fallback decision.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn ZSTD_rust_tryExternalSequenceProducer(
|
||||||
|
state: *const ZSTD_rust_externalSequenceProducerState,
|
||||||
|
) -> usize {
|
||||||
|
if state.is_null() {
|
||||||
|
return ERROR(ZstdErrorCode::Generic);
|
||||||
|
}
|
||||||
|
let state = unsafe { &*state };
|
||||||
|
let Some(producer) = state.producer else {
|
||||||
|
return ERROR(ZstdErrorCode::Generic);
|
||||||
|
};
|
||||||
|
let Some(transfer) = state.transfer else {
|
||||||
|
return ERROR(ZstdErrorCode::Generic);
|
||||||
|
};
|
||||||
|
if state.ext_seq_buf.is_null()
|
||||||
|
|| state.ext_seq_buf_capacity.is_null()
|
||||||
|
|| state.src.is_null()
|
||||||
|
|| state.src_size.is_null()
|
||||||
|
|| state.compression_level.is_null()
|
||||||
|
|| state.window_size.is_null()
|
||||||
|
|| state.external_seq_count.is_null()
|
||||||
|
|| state.seq_store_complete.is_null()
|
||||||
|
|| state.allow_fallback.is_null()
|
||||||
|
{
|
||||||
|
return ERROR(ZstdErrorCode::Generic);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*state.seq_store_complete = 0;
|
||||||
|
*state.allow_fallback = 0;
|
||||||
|
}
|
||||||
|
let ext_seq_buf_capacity = unsafe { *state.ext_seq_buf_capacity };
|
||||||
|
let src_size = unsafe { *state.src_size };
|
||||||
|
let nb_external_seqs = unsafe {
|
||||||
|
producer(
|
||||||
|
state.producer_state,
|
||||||
|
state.ext_seq_buf,
|
||||||
|
ext_seq_buf_capacity,
|
||||||
|
state.src,
|
||||||
|
src_size,
|
||||||
|
ptr::null(),
|
||||||
|
0,
|
||||||
|
*state.compression_level,
|
||||||
|
*state.window_size,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
unsafe { *state.external_seq_count = nb_external_seqs };
|
||||||
|
|
||||||
|
let nb_post_processed_seqs = unsafe {
|
||||||
|
ZSTD_rust_postProcessSequenceProducerResult(
|
||||||
|
state.ext_seq_buf,
|
||||||
|
nb_external_seqs,
|
||||||
|
ext_seq_buf_capacity,
|
||||||
|
src_size,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if ERR_isError(nb_post_processed_seqs) {
|
||||||
|
unsafe { *state.allow_fallback = 1 };
|
||||||
|
return nb_post_processed_seqs;
|
||||||
|
}
|
||||||
|
|
||||||
|
let seq_len_sum =
|
||||||
|
unsafe { ZSTD_rust_fastSequenceLengthSum(state.ext_seq_buf, nb_post_processed_seqs) };
|
||||||
|
if seq_len_sum > src_size {
|
||||||
|
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut seq_pos = ZSTD_SequencePosition::default();
|
||||||
|
let transfer_result = unsafe {
|
||||||
|
transfer(
|
||||||
|
state.callback_context,
|
||||||
|
&mut seq_pos,
|
||||||
|
state.ext_seq_buf,
|
||||||
|
nb_post_processed_seqs,
|
||||||
|
state.src,
|
||||||
|
src_size,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if ERR_isError(transfer_result) {
|
||||||
|
return transfer_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { *state.seq_store_complete = 1 };
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
/// Explicit projection of the state used by `ZSTD_compress_frameChunk`.
|
/// Explicit projection of the state used by `ZSTD_compress_frameChunk`.
|
||||||
///
|
///
|
||||||
/// The Rust side owns the per-frame block loop and its savings/dispatch
|
/// The Rust side owns the per-frame block loop and its savings/dispatch
|
||||||
@@ -6038,6 +6214,286 @@ 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 ExternalSequenceProducerProbe {
|
||||||
|
events: Vec<&'static str>,
|
||||||
|
sequences: Vec<ZSTD_Sequence>,
|
||||||
|
producer_result: usize,
|
||||||
|
producer_dict: *const c_void,
|
||||||
|
producer_dict_size: usize,
|
||||||
|
producer_level: c_int,
|
||||||
|
producer_window_size: usize,
|
||||||
|
transfer_sequence_count: usize,
|
||||||
|
transfer_src_size: usize,
|
||||||
|
transfer_result: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn external_sequence_producer_probe(
|
||||||
|
context: *mut c_void,
|
||||||
|
) -> &'static mut ExternalSequenceProducerProbe {
|
||||||
|
unsafe { &mut *context.cast::<ExternalSequenceProducerProbe>() }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn external_sequence_producer_test_producer(
|
||||||
|
context: *mut c_void,
|
||||||
|
out_seqs: *mut ZSTD_Sequence,
|
||||||
|
out_seqs_capacity: usize,
|
||||||
|
_src: *const c_void,
|
||||||
|
_src_size: usize,
|
||||||
|
dict: *const c_void,
|
||||||
|
dict_size: usize,
|
||||||
|
compression_level: c_int,
|
||||||
|
window_size: usize,
|
||||||
|
) -> usize {
|
||||||
|
let probe = unsafe { external_sequence_producer_probe(context) };
|
||||||
|
probe.events.push("produce");
|
||||||
|
probe.producer_dict = dict;
|
||||||
|
probe.producer_dict_size = dict_size;
|
||||||
|
probe.producer_level = compression_level;
|
||||||
|
probe.producer_window_size = window_size;
|
||||||
|
assert!(probe.sequences.len() <= out_seqs_capacity);
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(probe.sequences.as_ptr(), out_seqs, probe.sequences.len());
|
||||||
|
}
|
||||||
|
probe.producer_result
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn external_sequence_producer_test_transfer(
|
||||||
|
context: *mut c_void,
|
||||||
|
_seq_pos: *mut ZSTD_SequencePosition,
|
||||||
|
_in_seqs: *const ZSTD_Sequence,
|
||||||
|
in_seqs_size: usize,
|
||||||
|
_src: *const c_void,
|
||||||
|
block_size: usize,
|
||||||
|
) -> usize {
|
||||||
|
let probe = unsafe { external_sequence_producer_probe(context) };
|
||||||
|
probe.events.push("transfer");
|
||||||
|
probe.transfer_sequence_count = in_seqs_size;
|
||||||
|
probe.transfer_src_size = block_size;
|
||||||
|
probe.transfer_result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn external_sequence_producer_test_state(
|
||||||
|
probe: &mut ExternalSequenceProducerProbe,
|
||||||
|
ext_seq_buf: &mut [ZSTD_Sequence],
|
||||||
|
ext_seq_buf_capacity: &usize,
|
||||||
|
src: &[u8],
|
||||||
|
src_size: &usize,
|
||||||
|
compression_level: &c_int,
|
||||||
|
window_size: &usize,
|
||||||
|
external_seq_count: &mut usize,
|
||||||
|
seq_store_complete: &mut c_int,
|
||||||
|
allow_fallback: &mut c_int,
|
||||||
|
) -> ZSTD_rust_externalSequenceProducerState {
|
||||||
|
let callback_context = (probe as *mut ExternalSequenceProducerProbe).cast();
|
||||||
|
ZSTD_rust_externalSequenceProducerState {
|
||||||
|
callback_context,
|
||||||
|
producer_state: callback_context,
|
||||||
|
producer: Some(external_sequence_producer_test_producer),
|
||||||
|
ext_seq_buf: ext_seq_buf.as_mut_ptr(),
|
||||||
|
ext_seq_buf_capacity,
|
||||||
|
src: src.as_ptr().cast(),
|
||||||
|
src_size,
|
||||||
|
compression_level,
|
||||||
|
window_size,
|
||||||
|
transfer: Some(external_sequence_producer_test_transfer),
|
||||||
|
external_seq_count,
|
||||||
|
seq_store_complete,
|
||||||
|
allow_fallback,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn external_sequence_producer_success_transfers_after_post_processing() {
|
||||||
|
let source = [1u8, 2, 3, 4, 5];
|
||||||
|
let mut probe = ExternalSequenceProducerProbe {
|
||||||
|
sequences: vec![ZSTD_Sequence {
|
||||||
|
offset: 0,
|
||||||
|
litLength: source.len() as u32,
|
||||||
|
matchLength: 0,
|
||||||
|
rep: 0,
|
||||||
|
}],
|
||||||
|
producer_result: 1,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut ext_seq_buf = [ZSTD_Sequence {
|
||||||
|
offset: 0,
|
||||||
|
litLength: 0,
|
||||||
|
matchLength: 0,
|
||||||
|
rep: 0,
|
||||||
|
}; 2];
|
||||||
|
let ext_seq_buf_capacity = ext_seq_buf.len();
|
||||||
|
let src_size = source.len();
|
||||||
|
let compression_level = 7;
|
||||||
|
let window_size = 1 << 20;
|
||||||
|
let mut external_seq_count = 0;
|
||||||
|
let mut seq_store_complete = 0;
|
||||||
|
let mut allow_fallback = 0;
|
||||||
|
let state = external_sequence_producer_test_state(
|
||||||
|
&mut probe,
|
||||||
|
&mut ext_seq_buf,
|
||||||
|
&ext_seq_buf_capacity,
|
||||||
|
&source,
|
||||||
|
&src_size,
|
||||||
|
&compression_level,
|
||||||
|
&window_size,
|
||||||
|
&mut external_seq_count,
|
||||||
|
&mut seq_store_complete,
|
||||||
|
&mut allow_fallback,
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_tryExternalSequenceProducer(&state) };
|
||||||
|
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
assert_eq!(probe.events, ["produce", "transfer"]);
|
||||||
|
assert!(probe.producer_dict.is_null());
|
||||||
|
assert_eq!(probe.producer_dict_size, 0);
|
||||||
|
assert_eq!(probe.producer_level, compression_level);
|
||||||
|
assert_eq!(probe.producer_window_size, window_size);
|
||||||
|
assert_eq!(probe.transfer_sequence_count, 1);
|
||||||
|
assert_eq!(probe.transfer_src_size, source.len());
|
||||||
|
assert_eq!(external_seq_count, 1);
|
||||||
|
assert_eq!(seq_store_complete, 1);
|
||||||
|
assert_eq!(allow_fallback, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn external_sequence_producer_errors_enable_fallback_before_transfer() {
|
||||||
|
let source = [1u8, 2, 3];
|
||||||
|
let mut probe = ExternalSequenceProducerProbe {
|
||||||
|
producer_result: ERROR(ZstdErrorCode::SequenceProducerFailed),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut ext_seq_buf = [ZSTD_Sequence {
|
||||||
|
offset: 0,
|
||||||
|
litLength: 0,
|
||||||
|
matchLength: 0,
|
||||||
|
rep: 0,
|
||||||
|
}; 2];
|
||||||
|
let ext_seq_buf_capacity = ext_seq_buf.len();
|
||||||
|
let src_size = source.len();
|
||||||
|
let compression_level = 3;
|
||||||
|
let window_size = 1 << 20;
|
||||||
|
let mut external_seq_count = 0;
|
||||||
|
let mut seq_store_complete = 0;
|
||||||
|
let mut allow_fallback = 0;
|
||||||
|
let state = external_sequence_producer_test_state(
|
||||||
|
&mut probe,
|
||||||
|
&mut ext_seq_buf,
|
||||||
|
&ext_seq_buf_capacity,
|
||||||
|
&source,
|
||||||
|
&src_size,
|
||||||
|
&compression_level,
|
||||||
|
&window_size,
|
||||||
|
&mut external_seq_count,
|
||||||
|
&mut seq_store_complete,
|
||||||
|
&mut allow_fallback,
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_tryExternalSequenceProducer(&state) };
|
||||||
|
|
||||||
|
assert_eq!(result, ERROR(ZstdErrorCode::SequenceProducerFailed));
|
||||||
|
assert_eq!(probe.events, ["produce"]);
|
||||||
|
assert_eq!(external_seq_count, result);
|
||||||
|
assert_eq!(seq_store_complete, 0);
|
||||||
|
assert_eq!(allow_fallback, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn external_sequence_producer_invalid_length_does_not_enable_fallback() {
|
||||||
|
let source = [1u8, 2, 3];
|
||||||
|
let mut probe = ExternalSequenceProducerProbe {
|
||||||
|
sequences: vec![ZSTD_Sequence {
|
||||||
|
offset: 0,
|
||||||
|
litLength: (source.len() + 1) as u32,
|
||||||
|
matchLength: 0,
|
||||||
|
rep: 0,
|
||||||
|
}],
|
||||||
|
producer_result: 1,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut ext_seq_buf = [ZSTD_Sequence {
|
||||||
|
offset: 0,
|
||||||
|
litLength: 0,
|
||||||
|
matchLength: 0,
|
||||||
|
rep: 0,
|
||||||
|
}; 2];
|
||||||
|
let ext_seq_buf_capacity = ext_seq_buf.len();
|
||||||
|
let src_size = source.len();
|
||||||
|
let compression_level = 3;
|
||||||
|
let window_size = 1 << 20;
|
||||||
|
let mut external_seq_count = 0;
|
||||||
|
let mut seq_store_complete = 0;
|
||||||
|
let mut allow_fallback = 0;
|
||||||
|
let state = external_sequence_producer_test_state(
|
||||||
|
&mut probe,
|
||||||
|
&mut ext_seq_buf,
|
||||||
|
&ext_seq_buf_capacity,
|
||||||
|
&source,
|
||||||
|
&src_size,
|
||||||
|
&compression_level,
|
||||||
|
&window_size,
|
||||||
|
&mut external_seq_count,
|
||||||
|
&mut seq_store_complete,
|
||||||
|
&mut allow_fallback,
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_tryExternalSequenceProducer(&state) };
|
||||||
|
|
||||||
|
assert_eq!(result, ERROR(ZstdErrorCode::ExternalSequencesInvalid));
|
||||||
|
assert_eq!(probe.events, ["produce"]);
|
||||||
|
assert_eq!(seq_store_complete, 0);
|
||||||
|
assert_eq!(allow_fallback, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn external_sequence_producer_transfer_errors_do_not_enable_fallback() {
|
||||||
|
let source = [1u8, 2, 3];
|
||||||
|
let mut probe = ExternalSequenceProducerProbe {
|
||||||
|
sequences: vec![ZSTD_Sequence {
|
||||||
|
offset: 0,
|
||||||
|
litLength: source.len() as u32,
|
||||||
|
matchLength: 0,
|
||||||
|
rep: 0,
|
||||||
|
}],
|
||||||
|
producer_result: 1,
|
||||||
|
transfer_result: ERROR(ZstdErrorCode::ExternalSequencesInvalid),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut ext_seq_buf = [ZSTD_Sequence {
|
||||||
|
offset: 0,
|
||||||
|
litLength: 0,
|
||||||
|
matchLength: 0,
|
||||||
|
rep: 0,
|
||||||
|
}; 2];
|
||||||
|
let ext_seq_buf_capacity = ext_seq_buf.len();
|
||||||
|
let src_size = source.len();
|
||||||
|
let compression_level = 3;
|
||||||
|
let window_size = 1 << 20;
|
||||||
|
let mut external_seq_count = 0;
|
||||||
|
let mut seq_store_complete = 0;
|
||||||
|
let mut allow_fallback = 0;
|
||||||
|
let state = external_sequence_producer_test_state(
|
||||||
|
&mut probe,
|
||||||
|
&mut ext_seq_buf,
|
||||||
|
&ext_seq_buf_capacity,
|
||||||
|
&source,
|
||||||
|
&src_size,
|
||||||
|
&compression_level,
|
||||||
|
&window_size,
|
||||||
|
&mut external_seq_count,
|
||||||
|
&mut seq_store_complete,
|
||||||
|
&mut allow_fallback,
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_tryExternalSequenceProducer(&state) };
|
||||||
|
|
||||||
|
assert_eq!(result, ERROR(ZstdErrorCode::ExternalSequencesInvalid));
|
||||||
|
assert_eq!(probe.events, ["produce", "transfer"]);
|
||||||
|
assert_eq!(seq_store_complete, 0);
|
||||||
|
assert_eq!(allow_fallback, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Compress2TestContext {
|
struct Compress2TestContext {
|
||||||
events: Vec<&'static str>,
|
events: Vec<&'static str>,
|
||||||
|
|||||||
Reference in New Issue
Block a user