feat(compress): move external sequence validation to Rust
Port the external-sequence window, offset, and minimum-match checks through a scalar C ABI leaf while retaining the C caller's control flow and diagnostics. Test Plan: cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression; cargo clippy --manifest-path rust/Cargo.toml; cargo clippy --manifest-path rust/Cargo.toml --benches; cargo clippy --manifest-path rust/Cargo.toml --tests; make -B -C lib -j2 lib; make -B -C tests -j2 test-zstream (84 deterministic, 7063 and 8665 fuzzer cases).
This commit is contained in:
@@ -265,6 +265,9 @@ size_t ZSTD_rust_blockSizeExplicitDelimiter(const ZSTD_Sequence* inSeqs,
|
|||||||
size_t ZSTD_rust_determineBlockSize(int mode, size_t blockSize, size_t remaining,
|
size_t ZSTD_rust_determineBlockSize(int mode, size_t blockSize, size_t remaining,
|
||||||
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
|
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
|
||||||
U32 seqIdx);
|
U32 seqIdx);
|
||||||
|
size_t ZSTD_rust_validateSequence(U32 offBase, U32 matchLength, U32 minMatch,
|
||||||
|
size_t posInSrc, U32 windowLog, size_t dictSize,
|
||||||
|
int useSequenceProducer);
|
||||||
|
|
||||||
typedef char ZSTD_rust_stats_seqdef_layout[(sizeof(SeqDef) == 8) ? 1 : -1];
|
typedef char ZSTD_rust_stats_seqdef_layout[(sizeof(SeqDef) == 8) ? 1 : -1];
|
||||||
typedef char ZSTD_rust_stats_seqstore_long_length_pos[
|
typedef char ZSTD_rust_stats_seqstore_long_length_pos[
|
||||||
@@ -5339,18 +5342,9 @@ static size_t
|
|||||||
ZSTD_validateSequence(U32 offBase, U32 matchLength, U32 minMatch,
|
ZSTD_validateSequence(U32 offBase, U32 matchLength, U32 minMatch,
|
||||||
size_t posInSrc, U32 windowLog, size_t dictSize, int useSequenceProducer)
|
size_t posInSrc, U32 windowLog, size_t dictSize, int useSequenceProducer)
|
||||||
{
|
{
|
||||||
U32 const windowSize = 1u << windowLog;
|
return ZSTD_rust_validateSequence(offBase, matchLength, minMatch,
|
||||||
/* posInSrc represents the amount of data the decoder would decode up to this point.
|
posInSrc, windowLog, dictSize,
|
||||||
* As long as the amount of data decoded is less than or equal to window size, offsets may be
|
useSequenceProducer);
|
||||||
* larger than the total length of output decoded in order to reference the dict, even larger than
|
|
||||||
* window size. After output surpasses windowSize, we're limited to windowSize offsets again.
|
|
||||||
*/
|
|
||||||
size_t const offsetBound = posInSrc > windowSize ? (size_t)windowSize : posInSrc + (size_t)dictSize;
|
|
||||||
size_t const matchLenLowerBound = (minMatch == 3 || useSequenceProducer) ? 3 : 4;
|
|
||||||
RETURN_ERROR_IF(offBase > OFFSET_TO_OFFBASE(offsetBound), externalSequences_invalid, "Offset too large!");
|
|
||||||
/* Validate maxNbSeq is large enough for the given matchLength and minMatch */
|
|
||||||
RETURN_ERROR_IF(matchLength < matchLenLowerBound, externalSequences_invalid, "Matchlength too small for the minMatch");
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns an offset code, given a sequence's raw offset, the ongoing repcode array, and whether litLength == 0 */
|
/* Returns an offset code, given a sequence's raw offset, the ongoing repcode array, and whether litLength == 0 */
|
||||||
|
|||||||
@@ -184,6 +184,46 @@ pub unsafe extern "C" fn ZSTD_rust_finalizeOffBase(
|
|||||||
off_base
|
off_base
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates one external sequence against the decoder window and match-size
|
||||||
|
/// rules. This is the Rust leaf for C's `ZSTD_validateSequence()`.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn ZSTD_rust_validateSequence(
|
||||||
|
off_base: u32,
|
||||||
|
match_length: u32,
|
||||||
|
min_match: u32,
|
||||||
|
pos_in_src: usize,
|
||||||
|
window_log: u32,
|
||||||
|
dict_size: usize,
|
||||||
|
use_sequence_producer: c_int,
|
||||||
|
) -> usize {
|
||||||
|
let window_size = 1u32 << window_log;
|
||||||
|
/* As long as the decoded position is within the window, the dictionary
|
||||||
|
* can extend the largest valid offset. Once it is past the window, the
|
||||||
|
* offset is limited to the window size itself. */
|
||||||
|
let offset_bound = if pos_in_src > window_size as usize {
|
||||||
|
window_size as usize
|
||||||
|
} else {
|
||||||
|
pos_in_src.wrapping_add(dict_size)
|
||||||
|
};
|
||||||
|
debug_assert!(offset_bound > 0);
|
||||||
|
|
||||||
|
let offset_bound_off_base = offset_bound.wrapping_add(ZSTD_REP_NUM);
|
||||||
|
if off_base as usize > offset_bound_off_base {
|
||||||
|
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
let match_len_lower_bound = if min_match == 3 || use_sequence_producer != 0 {
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
4
|
||||||
|
};
|
||||||
|
if match_length < match_len_lower_bound {
|
||||||
|
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
/// Finds the next explicit block delimiter and returns the represented size.
|
/// Finds the next explicit block delimiter and returns the represented size.
|
||||||
///
|
///
|
||||||
/// The scan is half-open at `inSeqsSize`: a delimiter at the final element is
|
/// The scan is half-open at `inSeqsSize`: a delimiter at the final element is
|
||||||
@@ -2410,6 +2450,36 @@ mod tests {
|
|||||||
assert_eq!(finalize(31, 0), 34); // ordinary raw offset
|
assert_eq!(finalize(31, 0), 34); // ordinary raw offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_sequence_accepts_valid_input_and_caps_at_window_size() {
|
||||||
|
let result = ZSTD_rust_validateSequence(1027, 4, 4, 2048, 10, 99, 0);
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_sequence_rejects_oversized_offset() {
|
||||||
|
let result = ZSTD_rust_validateSequence(1028, 4, 4, 2048, 10, 99, 0);
|
||||||
|
assert_eq!(result, ERROR(ZstdErrorCode::ExternalSequencesInvalid));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_sequence_rejects_too_short_match() {
|
||||||
|
let result = ZSTD_rust_validateSequence(103, 3, 4, 100, 10, 0, 0);
|
||||||
|
assert_eq!(result, ERROR(ZstdErrorCode::ExternalSequencesInvalid));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_sequence_accepts_three_byte_match_for_min_match_three() {
|
||||||
|
let result = ZSTD_rust_validateSequence(103, 3, 3, 100, 10, 0, 0);
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_sequence_accepts_three_byte_match_from_sequence_producer() {
|
||||||
|
let result = ZSTD_rust_validateSequence(103, 3, 4, 100, 10, 0, 1);
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn determine_block_size_without_delimiters_returns_minimum() {
|
fn determine_block_size_without_delimiters_returns_minimum() {
|
||||||
let result = unsafe { ZSTD_rust_determineBlockSize(0, 128, 50, ptr::null(), 0, 0) };
|
let result = unsafe { ZSTD_rust_determineBlockSize(0, 128, 50, ptr::null(), 0, 0) };
|
||||||
|
|||||||
Reference in New Issue
Block a user