feat(compress): move target block policy to Rust

Port the target-sized block action policy behind a scalar Rust ABI while keeping sequence inspection, superblock compression, block-state confirmation, diagnostics, and raw/RLE emission in C. Preserve the strict three-byte header boundary, destination-too-small fallback, and first-block RLE guard with focused tests.

Test Plan:

- cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression zstd_compress::tests::target_block

- cargo clippy --manifest-path rust/Cargo.toml

- cargo clippy --manifest-path rust/Cargo.toml --benches

- cargo clippy --manifest-path rust/Cargo.toml --tests

- cargo +nightly fmt --manifest-path rust/Cargo.toml --all

- make -B -C lib -j2 lib
This commit is contained in:
2026-07-18 06:37:07 +02:00
parent b19ca569f7
commit 21f8e8958c
2 changed files with 209 additions and 18 deletions
+173
View File
@@ -19,6 +19,7 @@ use crate::zstd_compress_api::ZSTD_compressBound;
use crate::zstd_compress_frame::{
write_raw_block, ZSTD_rust_writeFrameHeader, ZSTD_writeLastEmptyBlock,
};
use crate::zstd_compress_literals::min_gain;
use crate::zstd_compress_params::{
ZSTD_rust_params_adjustCParams, ZSTD_rust_params_maxNbSeq, ZSTD_rust_params_selectCParams,
ZSTD_RUST_CPM_NO_ATTACH_DICT, ZSTD_RUST_PS_DISABLE,
@@ -62,6 +63,8 @@ const ZSTD_FAST: c_int = 1;
const ZSTD_DFAST: c_int = 2;
const ZSTD_BLOCKSIZE_MAX: usize = 1 << 17;
const ZSTD_CONTENTSIZE_UNKNOWN: u64 = u64::MAX;
const ZSTD_TARGET_CBLOCK_BSS_COMPRESS: c_int = 0;
const ZSTD_BLOCK_HEADER_SIZE: usize = 3;
const ZSTD_ROWSIZE: usize = 16;
const ZSTD_WINDOW_START_INDEX: u32 = 2;
const ZSTD_DUBT_UNSORTED_MARK: u32 = 1;
@@ -75,6 +78,71 @@ const ZSTD_CHUNKSIZE_MAX: usize = u32::MAX as usize - ZSTD_CURRENT_MAX;
#[cfg(not(test))]
const ZSTD_E_END: c_int = 2;
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum TargetCBlockAction {
Raw = 0,
Rle = 1,
Compressed = 2,
Error = 3,
}
#[inline]
fn target_c_block_size_action(
bss: c_int,
is_first_block: c_int,
maybe_rle: c_int,
is_rle: c_int,
c_size: usize,
src_size: usize,
strategy: c_int,
) -> TargetCBlockAction {
if bss != ZSTD_TARGET_CBLOCK_BSS_COMPRESS {
return TargetCBlockAction::Raw;
}
if is_first_block == 0 && maybe_rle != 0 && is_rle != 0 {
return TargetCBlockAction::Rle;
}
if c_size == 0 || c_size == ERROR(ZstdErrorCode::DstSizeTooSmall) {
return TargetCBlockAction::Raw;
}
if ERR_isError(c_size) {
return TargetCBlockAction::Error;
}
let max_c_size = src_size.wrapping_sub(min_gain(src_size, strategy));
if c_size < max_c_size.wrapping_add(ZSTD_BLOCK_HEADER_SIZE) {
TargetCBlockAction::Compressed
} else {
TargetCBlockAction::Raw
}
}
/// Classify the target-sized block policy without crossing the C context ABI.
///
/// C calls this once before the superblock attempt to classify the RLE
/// precondition, then again with the superblock result and zero RLE flags.
#[no_mangle]
pub extern "C" fn ZSTD_rust_targetCBlockSizeAction(
bss: c_int,
is_first_block: c_int,
maybe_rle: c_int,
is_rle: c_int,
c_size: usize,
src_size: usize,
strategy: c_int,
) -> c_int {
target_c_block_size_action(
bss,
is_first_block,
maybe_rle,
is_rle,
c_size,
src_size,
strategy,
) as c_int
}
#[cfg(not(test))]
#[repr(C)]
pub struct ZSTD_inBuffer {
@@ -752,6 +820,111 @@ mod tests {
assert_eq!(ZSTD_rust_dictTooBig(ZSTD_CHUNKSIZE_MAX + 1), 1);
}
#[test]
fn target_block_policy_requires_compression_and_nonfirst_rle() {
assert_eq!(
target_c_block_size_action(0, 0, 1, 1, 0, 128, ZSTD_FAST),
TargetCBlockAction::Rle
);
assert_eq!(
target_c_block_size_action(0, 1, 1, 1, 0, 128, ZSTD_FAST),
TargetCBlockAction::Raw
);
assert_eq!(
target_c_block_size_action(0, 0, 0, 1, 0, 128, ZSTD_FAST),
TargetCBlockAction::Raw
);
assert_eq!(
target_c_block_size_action(0, 0, 1, 0, 0, 128, ZSTD_FAST),
TargetCBlockAction::Raw
);
assert_eq!(
ZSTD_rust_targetCBlockSizeAction(0, 0, 1, 1, 0, 128, ZSTD_FAST),
TargetCBlockAction::Rle as c_int
);
}
#[test]
fn target_block_policy_falls_back_for_uncompressed_and_small_results() {
assert_eq!(
target_c_block_size_action(1, 0, 0, 0, 0, 128, ZSTD_FAST),
TargetCBlockAction::Raw
);
assert_eq!(
target_c_block_size_action(
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
0,
0,
ERROR(ZstdErrorCode::DstSizeTooSmall),
128,
ZSTD_FAST,
),
TargetCBlockAction::Raw
);
assert_eq!(
target_c_block_size_action(
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
0,
0,
ERROR(ZstdErrorCode::Generic),
128,
ZSTD_FAST,
),
TargetCBlockAction::Error
);
assert_eq!(
target_c_block_size_action(ZSTD_TARGET_CBLOCK_BSS_COMPRESS, 0, 0, 0, 0, 128, ZSTD_FAST,),
TargetCBlockAction::Raw
);
}
#[test]
fn target_block_policy_uses_a_strict_three_byte_header_boundary() {
let src_size = 128;
let max_c_size = src_size - min_gain(src_size, ZSTD_FAST);
let compressed = max_c_size + ZSTD_BLOCK_HEADER_SIZE - 1;
let raw = max_c_size + ZSTD_BLOCK_HEADER_SIZE;
assert_eq!(
target_c_block_size_action(
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
0,
0,
compressed,
src_size,
ZSTD_FAST,
),
TargetCBlockAction::Compressed
);
assert_eq!(
target_c_block_size_action(
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
0,
0,
raw,
src_size,
ZSTD_FAST,
),
TargetCBlockAction::Raw
);
assert_eq!(
ZSTD_rust_targetCBlockSizeAction(
ZSTD_TARGET_CBLOCK_BSS_COMPRESS,
0,
0,
0,
compressed,
src_size,
ZSTD_FAST,
),
TargetCBlockAction::Compressed as c_int
);
}
#[test]
fn frame_progress_unknown_pledge_updates_counters() {
let mut consumed = 7;