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,
|
||||
const BYTE* anchor, size_t lastLLSize);
|
||||
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);
|
||||
BlockSummary ZSTD_rust_get1BlockSummary(const ZSTD_Sequence* seqs,
|
||||
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(
|
||||
U32* partitions, U32 nbSeq,
|
||||
const SeqStore_t* originalSeqStore,
|
||||
@@ -1344,6 +1339,57 @@ typedef char ZSTD_rust_build_seq_store_state_layout[
|
||||
== 7 * sizeof(void*) + sizeof(U32) + sizeof(int))
|
||||
? 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
|
||||
* updates. In particular, neither ZSTD_CCtx nor a C function pointer crosses
|
||||
* the Rust ABI. */
|
||||
@@ -3419,29 +3465,6 @@ void ZSTD_resetSeqStore(SeqStore_t* 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
|
||||
ZSTD_transferSequences_wBlockDelim(ZSTD_CCtx* cctx,
|
||||
ZSTD_SequencePosition* seqPos,
|
||||
@@ -3449,6 +3472,17 @@ ZSTD_transferSequences_wBlockDelim(ZSTD_CCtx* cctx,
|
||||
const void* src, size_t blockSize,
|
||||
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;
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
{ U32 const windowSize = (U32)1 << zc->appliedParams.cParams.windowLog;
|
||||
size_t const nbExternalSeqs = (zc->appliedParams.extSeqProdFunc)(
|
||||
zc->appliedParams.extSeqProdState,
|
||||
zc->extSeqBuf, zc->extSeqBufCapacity,
|
||||
src, srcSize, NULL, 0,
|
||||
zc->appliedParams.compressionLevel, windowSize);
|
||||
size_t const nbPostProcessedSeqs = ZSTD_postProcessSequenceProducerResult(
|
||||
zc->extSeqBuf, nbExternalSeqs,
|
||||
zc->extSeqBufCapacity, srcSize);
|
||||
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;
|
||||
|
||||
/* Return early if there is no error, since the delimiter already
|
||||
* carries the block's final literals. */
|
||||
if (!ZSTD_isError(nbPostProcessedSeqs)) {
|
||||
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!");
|
||||
{ size_t const producerResult =
|
||||
ZSTD_rust_tryExternalSequenceProducer(&state);
|
||||
if (*seqStoreComplete) {
|
||||
ms->ldmSeqStore = NULL;
|
||||
*seqStoreComplete = 1;
|
||||
DEBUGLOG(5, "Copied %lu sequences from external sequence producer to internal seqStore.",
|
||||
(unsigned long)nbExternalSeqs);
|
||||
return 0;
|
||||
return producerResult;
|
||||
}
|
||||
|
||||
if (!zc->appliedParams.enableMatchFinderFallback)
|
||||
return nbPostProcessedSeqs;
|
||||
if (!allowFallback || !zc->appliedParams.enableMatchFinderFallback)
|
||||
return producerResult;
|
||||
|
||||
{ ZSTD_BlockCompressor_f const blockCompressor =
|
||||
ZSTD_selectBlockCompressor(
|
||||
|
||||
+460
-4
@@ -37,8 +37,9 @@ use crate::zstd_compress_stats::{
|
||||
ZSTD_rust_convertSequencesNoRepcodes, ZSTD_rust_copyBlockSequences,
|
||||
ZSTD_rust_countSeqStoreLiteralsBytes, ZSTD_rust_countSeqStoreMatchBytes,
|
||||
ZSTD_rust_deriveSeqStoreChunk, ZSTD_rust_determineBlockSize, ZSTD_rust_entropyCompressSeqStore,
|
||||
ZSTD_rust_entropyCompressSeqStore_internal, ZSTD_rust_finalizeOffBase,
|
||||
ZSTD_rust_get1BlockSummary, ZSTD_rust_isRLE, ZSTD_rust_maybeRLE, ZSTD_rust_resetSeqStore,
|
||||
ZSTD_rust_entropyCompressSeqStore_internal, ZSTD_rust_fastSequenceLengthSum,
|
||||
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_transferSequencesNoDelim, ZSTD_rust_transferSequencesWBlockDelim,
|
||||
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.
|
||||
///
|
||||
/// Rust owns the threshold/reset/repcode/literal-store orchestration. The
|
||||
/// callbacks retain the private matchfinder, LDM, and external sequence
|
||||
/// producer operations in C without passing `ZSTD_CCtx` across the ABI.
|
||||
/// callbacks retain the private matchfinder, LDM, and external-producer
|
||||
/// fallback operations in C without passing `ZSTD_CCtx` across the ABI.
|
||||
#[repr(C)]
|
||||
pub struct ZSTD_rust_buildSeqStoreState {
|
||||
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`.
|
||||
///
|
||||
/// 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_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)]
|
||||
struct Compress2TestContext {
|
||||
events: Vec<&'static str>,
|
||||
|
||||
Reference in New Issue
Block a user