feat(compress): move sequence-store validation to Rust
Keep the C DEBUGLEVEL gate and delegate the read-only match-length validation through the existing SeqStore ABI. Preserve long-match side-band decoding and the original 3-or-4 byte lower-bound assertion. Test Plan: - cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression - cargo clippy --manifest-path rust/Cargo.toml --no-default-features --features compression --all-targets - cargo +nightly fmt --manifest-path rust/Cargo.toml - make -B -C lib -j2 lib - make -C tests -j2 fuzzer - tests/fuzzer -s4560 -t47 -i48 -v
This commit is contained in:
@@ -277,6 +277,7 @@ void ZSTD_rust_storeLastLiterals(SeqStore_t* seqStorePtr,
|
|||||||
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 ZSTD_rust_fastSequenceLengthSum(const ZSTD_Sequence* seqBuf,
|
||||||
size_t seqBufSize);
|
size_t seqBufSize);
|
||||||
|
void ZSTD_rust_validateSeqStore(const SeqStore_t* seqStore, U32 minMatch);
|
||||||
size_t ZSTD_rust_convertSequencesNoRepcodes(
|
size_t ZSTD_rust_convertSequencesNoRepcodes(
|
||||||
SeqDef* dstSeqs, const ZSTD_Sequence* inSeqs, size_t nbSequences);
|
SeqDef* dstSeqs, const ZSTD_Sequence* inSeqs, size_t nbSequences);
|
||||||
BlockSummary ZSTD_rust_get1BlockSummary(const ZSTD_Sequence* seqs,
|
BlockSummary ZSTD_rust_get1BlockSummary(const ZSTD_Sequence* seqs,
|
||||||
@@ -2348,15 +2349,7 @@ static size_t ZSTD_fastSequenceLengthSum(ZSTD_Sequence const* seqBuf, size_t seq
|
|||||||
static void ZSTD_validateSeqStore(const SeqStore_t* seqStore, const ZSTD_compressionParameters* cParams)
|
static void ZSTD_validateSeqStore(const SeqStore_t* seqStore, const ZSTD_compressionParameters* cParams)
|
||||||
{
|
{
|
||||||
#if DEBUGLEVEL >= 1
|
#if DEBUGLEVEL >= 1
|
||||||
const SeqDef* seq = seqStore->sequencesStart;
|
ZSTD_rust_validateSeqStore(seqStore, (U32)cParams->minMatch);
|
||||||
const SeqDef* const seqEnd = seqStore->sequences;
|
|
||||||
size_t const matchLenLowerBound = cParams->minMatch == 3 ? 3 : 4;
|
|
||||||
for (; seq < seqEnd; ++seq) {
|
|
||||||
const ZSTD_SequenceLength seqLength = ZSTD_getSequenceLength(seqStore, seq);
|
|
||||||
assert(seqLength.matchLength >= matchLenLowerBound);
|
|
||||||
(void)seqLength;
|
|
||||||
(void)matchLenLowerBound;
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
(void)seqStore;
|
(void)seqStore;
|
||||||
(void)cParams;
|
(void)cParams;
|
||||||
|
|||||||
@@ -1151,6 +1151,31 @@ pub unsafe extern "C" fn ZSTD_rust_fastSequenceLengthSum(
|
|||||||
lit_len_sum.wrapping_add(match_len_sum)
|
lit_len_sum.wrapping_add(match_len_sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates the match lengths produced in a sequence store. This is the
|
||||||
|
/// Rust leaf for C's `ZSTD_validateSeqStore()`; the C wrapper retains the
|
||||||
|
/// `DEBUGLEVEL` gate so release builds keep the original no-op behavior.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn ZSTD_rust_validateSeqStore(
|
||||||
|
seq_store: *const SeqStore_t,
|
||||||
|
min_match: c_uint,
|
||||||
|
) {
|
||||||
|
let seq_store = unsafe { &*seq_store };
|
||||||
|
let match_len_lower_bound = if min_match == 3 { 3 } else { 4 };
|
||||||
|
let nb_sequences =
|
||||||
|
unsafe { seq_store.sequences.offset_from(seq_store.sequencesStart) } as usize;
|
||||||
|
|
||||||
|
for index in 0..nb_sequences {
|
||||||
|
let sequence = unsafe { *seq_store.sequencesStart.add(index) };
|
||||||
|
let mut match_length = sequence.mlBase as usize + MINMATCH;
|
||||||
|
if index == seq_store.longLengthPos as usize
|
||||||
|
&& seq_store.longLengthType == ZSTD_LLT_MATCH_LENGTH
|
||||||
|
{
|
||||||
|
match_length += 0x10000;
|
||||||
|
}
|
||||||
|
assert!(match_length >= match_len_lower_bound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether all bytes in the input have the same value.
|
/// Returns whether all bytes in the input have the same value.
|
||||||
///
|
///
|
||||||
/// This keeps the original C helper's precondition that `src` points to at
|
/// This keeps the original C helper's precondition that `src` points to at
|
||||||
@@ -3378,6 +3403,45 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_seq_store_accepts_ordinary_match_lengths() {
|
||||||
|
let mut fixture = TestSeqStore::new(
|
||||||
|
vec![
|
||||||
|
SeqDef {
|
||||||
|
offBase: 1,
|
||||||
|
litLength: 0,
|
||||||
|
mlBase: 0,
|
||||||
|
},
|
||||||
|
SeqDef {
|
||||||
|
offBase: 1,
|
||||||
|
litLength: 0,
|
||||||
|
mlBase: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
let seq_store = fixture.seq_store();
|
||||||
|
|
||||||
|
unsafe { ZSTD_rust_validateSeqStore(&seq_store, 3) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_seq_store_accounts_for_long_match_metadata() {
|
||||||
|
let mut fixture = TestSeqStore::new(
|
||||||
|
vec![SeqDef {
|
||||||
|
offBase: 1,
|
||||||
|
litLength: 0,
|
||||||
|
mlBase: 0,
|
||||||
|
}],
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
let mut seq_store = fixture.seq_store();
|
||||||
|
seq_store.longLengthType = ZSTD_LLT_MATCH_LENGTH;
|
||||||
|
seq_store.longLengthPos = 0;
|
||||||
|
|
||||||
|
unsafe { ZSTD_rust_validateSeqStore(&seq_store, 4) };
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn post_process_rejects_external_count_overflow() {
|
fn post_process_rejects_external_count_overflow() {
|
||||||
let mut out = [ZSTD_Sequence {
|
let mut out = [ZSTD_Sequence {
|
||||||
|
|||||||
Reference in New Issue
Block a user