feat(compress): move external sequence sizing leaves to Rust

Port repcode offBase finalization and explicit-delimiter block sizing through the Rust compression ABI. Preserve wrapped sequence arithmetic, external-sequence error codes, and the no-delimiter target-size policy while keeping the C control flow intact.

Test Plan: cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression (176 passed); cargo clippy root, benches, and tests; make -B -C lib -j2 lib; make -B -C tests -j2 test-zstream (84 named tests plus 6,230 and 8,598 fuzz cases passed).
This commit is contained in:
2026-07-18 04:31:22 +02:00
parent 42481fb366
commit d5ae2a450b
2 changed files with 224 additions and 52 deletions
+214
View File
@@ -158,6 +158,99 @@ pub struct ZSTD_Sequence {
pub rep: u32,
}
/// Converts a raw sequence offset to the stored offBase representation.
///
/// This is the Rust leaf for C's `ZSTD_finalizeOffBase()`. The repcode
/// numbering and the special `rep[0] - 1` form are part of the public sequence
/// ABI used by the external-sequence path.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_finalizeOffBase(
raw_offset: u32,
rep: *const u32,
ll0: u32,
) -> u32 {
let rep = unsafe { std::slice::from_raw_parts(rep, ZSTD_REP_NUM) };
let mut off_base = raw_offset.wrapping_add(ZSTD_REP_NUM as u32);
if ll0 == 0 && raw_offset == rep[0] {
off_base = 1;
} else if raw_offset == rep[1] {
off_base = 2u32.wrapping_sub(ll0);
} else if raw_offset == rep[2] {
off_base = 3u32.wrapping_sub(ll0);
} else if ll0 != 0 && raw_offset == rep[0].wrapping_sub(1) {
off_base = 3;
}
off_base
}
/// 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
/// included, while a missing delimiter returns the C external-sequences
/// error. The delimiter's literal length is part of the block size, but its
/// match length must be zero.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_blockSizeExplicitDelimiter(
in_seqs: *const ZSTD_Sequence,
in_seqs_size: usize,
seq_idx: u32,
) -> usize {
let mut end = false;
let mut block_size = 0usize;
let mut sequence_index = seq_idx as usize;
debug_assert!(sequence_index <= in_seqs_size);
while sequence_index < in_seqs_size {
let sequence = unsafe { *in_seqs.add(sequence_index) };
end = sequence.offset == 0;
let sequence_size = sequence.litLength.wrapping_add(sequence.matchLength) as usize;
block_size = block_size.wrapping_add(sequence_size);
if end {
if sequence.matchLength != 0 {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
break;
}
sequence_index += 1;
}
if !end {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
block_size
}
/// Determines the next external-sequence block size.
///
/// Mode `0` is the no-delimiter mode and returns the target size capped at the
/// remaining source. Mode `1` scans an explicit delimiter and rejects blocks
/// larger than either configured block size or the remaining frame.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_determineBlockSize(
mode: c_int,
block_size: usize,
remaining: usize,
in_seqs: *const ZSTD_Sequence,
in_seqs_size: usize,
seq_idx: u32,
) -> usize {
if mode == 0 {
return remaining.min(block_size);
}
debug_assert_eq!(mode, 1);
let explicit_block_size =
unsafe { ZSTD_rust_blockSizeExplicitDelimiter(in_seqs, in_seqs_size, seq_idx) };
if ERR_isError(explicit_block_size) {
return explicit_block_size;
}
if explicit_block_size > block_size || explicit_block_size > remaining {
return ERROR(ZstdErrorCode::ExternalSequencesInvalid);
}
explicit_block_size
}
/// Validates and post-processes sequences returned by an external sequence
/// producer. This is the Rust leaf for C's
/// `ZSTD_postProcessSequenceProducerResult()`.
@@ -2300,4 +2393,125 @@ mod tests {
unsafe { ZSTD_rust_copyBlockSequences(&mut collector, &seq_store, reps.as_ptr()) };
assert_eq!(result, ERROR(ZstdErrorCode::DstSizeTooSmall));
}
#[test]
fn finalize_off_base_preserves_repcode_boundaries() {
let reps = [10u32, 20, 30];
let finalize =
|raw_offset, ll0| unsafe { ZSTD_rust_finalizeOffBase(raw_offset, reps.as_ptr(), ll0) };
assert_eq!(finalize(10, 0), 1); // repcode 1 with literals
assert_eq!(finalize(10, 1), 13); // raw offset, not repcode 1
assert_eq!(finalize(20, 0), 2); // repcode 2 with literals
assert_eq!(finalize(20, 1), 1); // repcode 1 without literals
assert_eq!(finalize(30, 0), 3); // repcode 3 with literals
assert_eq!(finalize(30, 1), 2); // repcode 2 without literals
assert_eq!(finalize(9, 1), 3); // repcode 3's rep[0] - 1 form
assert_eq!(finalize(31, 0), 34); // ordinary raw offset
}
#[test]
fn determine_block_size_without_delimiters_returns_minimum() {
let result = unsafe { ZSTD_rust_determineBlockSize(0, 128, 50, ptr::null(), 0, 0) };
assert_eq!(result, 50);
let result = unsafe { ZSTD_rust_determineBlockSize(0, 32, 50, ptr::null(), 0, 0) };
assert_eq!(result, 32);
}
#[test]
fn explicit_delimiter_size_includes_delimiter_literals() {
let sequences = [
ZSTD_Sequence {
offset: 7,
litLength: 3,
matchLength: 4,
rep: 0,
},
ZSTD_Sequence {
offset: 0,
litLength: 5,
matchLength: 0,
rep: 0,
},
];
let result =
unsafe { ZSTD_rust_blockSizeExplicitDelimiter(sequences.as_ptr(), sequences.len(), 0) };
assert_eq!(result, 12);
let result = unsafe {
ZSTD_rust_determineBlockSize(1, 12, 12, sequences.as_ptr(), sequences.len(), 0)
};
assert_eq!(result, 12);
}
#[test]
fn explicit_delimiter_size_rejects_missing_delimiter() {
let sequences = [ZSTD_Sequence {
offset: 7,
litLength: 3,
matchLength: 4,
rep: 0,
}];
let result =
unsafe { ZSTD_rust_blockSizeExplicitDelimiter(sequences.as_ptr(), sequences.len(), 0) };
assert_eq!(result, ERROR(ZstdErrorCode::ExternalSequencesInvalid));
}
#[test]
fn explicit_delimiter_size_rejects_match_length() {
let sequences = [ZSTD_Sequence {
offset: 0,
litLength: 3,
matchLength: 4,
rep: 0,
}];
let result =
unsafe { ZSTD_rust_blockSizeExplicitDelimiter(sequences.as_ptr(), sequences.len(), 0) };
assert_eq!(result, ERROR(ZstdErrorCode::ExternalSequencesInvalid));
}
#[test]
fn determine_block_size_rejects_too_large_block() {
let sequences = [
ZSTD_Sequence {
offset: 7,
litLength: 3,
matchLength: 4,
rep: 0,
},
ZSTD_Sequence {
offset: 0,
litLength: 1,
matchLength: 0,
rep: 0,
},
];
let result = unsafe {
ZSTD_rust_determineBlockSize(1, 7, 8, sequences.as_ptr(), sequences.len(), 0)
};
assert_eq!(result, ERROR(ZstdErrorCode::ExternalSequencesInvalid));
}
#[test]
fn determine_block_size_rejects_too_long_frame() {
let sequences = [
ZSTD_Sequence {
offset: 7,
litLength: 3,
matchLength: 4,
rep: 0,
},
ZSTD_Sequence {
offset: 0,
litLength: 1,
matchLength: 0,
rep: 0,
},
];
let result = unsafe {
ZSTD_rust_determineBlockSize(1, 8, 7, sequences.as_ptr(), sequences.len(), 0)
};
assert_eq!(result, ERROR(ZstdErrorCode::ExternalSequencesInvalid));
}
}