fix(compress): restore optimal parser strategy compatibility
Match the original optimal-parser statistics for zero-possible symbols, complete the shortest-path fallback when the forward pass runs past its final position, and refresh the projected window state for the ultra2 second pass. Keep the simple Rust frame leaf restricted to the match finders it implements so lazy and optimal strategies retain their stateful lifecycle. Test Plan: - cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression (135 passed) - cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression zstd_opt (3 passed) - make -B -C tests -j2 test-cli-tests (41 passed) - make -B -C tests -j2 test-zstream (84 named tests and fuzzer suites passed) - cargo clippy --manifest-path rust/Cargo.toml - cargo clippy --manifest-path rust/Cargo.toml --benches - cargo clippy --manifest-path rust/Cargo.toml --tests
This commit is contained in:
@@ -4528,6 +4528,11 @@ int ZSTD_rust_simpleCompress2Level(const void* opaqueCctx)
|
||||
{
|
||||
ZSTD_CCtx const* const cctx = (ZSTD_CCtx const*)opaqueCctx;
|
||||
ZSTD_CCtx_params const* const params = cctx ? &cctx->requestedParams : NULL;
|
||||
ZSTD_compressionParameters const cParams = params
|
||||
? ZSTD_getCParams_internal(params->compressionLevel,
|
||||
ZSTD_CONTENTSIZE_UNKNOWN, 0,
|
||||
ZSTD_cpm_noAttachDict)
|
||||
: (ZSTD_compressionParameters){ 0 };
|
||||
if (params == NULL
|
||||
|| params->format != ZSTD_f_zstd1
|
||||
|| params->fParams.contentSizeFlag == 0
|
||||
@@ -4579,6 +4584,12 @@ int ZSTD_rust_simpleCompress2Level(const void* opaqueCctx)
|
||||
|| cctx->localDict.cdict != NULL) {
|
||||
return (-2147483647 - 1);
|
||||
}
|
||||
/* The Rust frame leaf currently implements only the fast and double-fast
|
||||
* match finders. Keep lazy and optimal strategies on the stateful path,
|
||||
* which owns their strategy-specific match-table lifecycle. */
|
||||
if (cParams.strategy != ZSTD_fast && cParams.strategy != ZSTD_dfast) {
|
||||
return (-2147483647 - 1);
|
||||
}
|
||||
return params->compressionLevel;
|
||||
}
|
||||
|
||||
|
||||
+76
-3
@@ -446,7 +446,10 @@ unsafe fn downscale_stats(
|
||||
let mut sum: u32 = 0;
|
||||
for index in 0..=last_index {
|
||||
let old = unsafe { *table.add(index) };
|
||||
let value = u32::from(base_one) + (old >> shift);
|
||||
/* `base_0possible` still gives every observed symbol a count of one.
|
||||
* Only symbols which were absent from the source may remain at zero. */
|
||||
let base = if base_one { 1 } else { u32::from(old != 0) };
|
||||
let value = base + (old >> shift);
|
||||
unsafe { *table.add(index) = value };
|
||||
sum = sum.wrapping_add(value);
|
||||
}
|
||||
@@ -1628,7 +1631,6 @@ unsafe fn compress_block_opt_generic(
|
||||
*reps.add(2) = path_rep[2];
|
||||
}
|
||||
resolved_early = true;
|
||||
cur = last_pos;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1675,7 +1677,7 @@ unsafe fn compress_block_opt_generic(
|
||||
cur += 1;
|
||||
}
|
||||
|
||||
if cur == last_pos && !resolved_early {
|
||||
if !resolved_early {
|
||||
/* The previous loop reached its end without an early path. */
|
||||
last_stretch = unsafe { *opt.add(last_pos as usize) };
|
||||
let path_rep = unsafe { [*reps, *reps.add(1), *reps.add(2)] };
|
||||
@@ -1736,9 +1738,19 @@ unsafe fn init_stats_ultra(
|
||||
*state.window_dict_limit = (*state.window_dict_limit).wrapping_add(src_size as u32);
|
||||
*state.window_low_limit = *state.window_dict_limit;
|
||||
*state.next_to_update = *state.window_dict_limit;
|
||||
refresh_window_projection(state);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn refresh_window_projection(state: &mut ZSTD_RustOptState) {
|
||||
/* The C implementation rebuilds its projected match state for the
|
||||
* second pass. Refresh the copied window fields before reusing this
|
||||
* Rust projection for that pass. */
|
||||
state.base = unsafe { *state.window_base };
|
||||
state.dict_limit = unsafe { *state.window_dict_limit };
|
||||
state.low_limit = unsafe { *state.window_low_limit };
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ZSTD_rust_opt_updateTree(
|
||||
state: *mut ZSTD_RustOptState,
|
||||
@@ -1834,3 +1846,64 @@ pub unsafe extern "C" fn ZSTD_rust_compressBlock_btultra2(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{downscale_stats, refresh_window_projection, ZSTD_RustOptState};
|
||||
use std::ptr;
|
||||
|
||||
#[test]
|
||||
fn ultra2_window_projection_tracks_the_second_pass_window() {
|
||||
let mut base = 0x2000usize as *const u8;
|
||||
let mut dict_limit = 128_u32;
|
||||
let mut low_limit = 128_u32;
|
||||
let mut next_to_update = 128_u32;
|
||||
let mut state = ZSTD_RustOptState {
|
||||
hash_table: ptr::null_mut(),
|
||||
chain_table: ptr::null_mut(),
|
||||
base: 0x1000usize as *const u8,
|
||||
dict_base: ptr::null(),
|
||||
dict_limit: 0,
|
||||
low_limit: 0,
|
||||
loaded_dict_end: 0,
|
||||
next_to_update: &mut next_to_update,
|
||||
hash_log: 0,
|
||||
chain_log: 0,
|
||||
search_log: 0,
|
||||
window_log: 0,
|
||||
use_c_predict: 0,
|
||||
hash_table3: ptr::null_mut(),
|
||||
hash_log3: 0,
|
||||
next_src: ptr::null(),
|
||||
min_match: 0,
|
||||
target_length: 0,
|
||||
opt: ptr::null_mut(),
|
||||
dict_match_state: ptr::null(),
|
||||
ldm_seq_store: ptr::null(),
|
||||
window_base: &mut base,
|
||||
window_dict_limit: &mut dict_limit,
|
||||
window_low_limit: &mut low_limit,
|
||||
huf_ctable: ptr::null(),
|
||||
huf_repeat_valid: 0,
|
||||
fse_litlength_ctable: ptr::null(),
|
||||
fse_matchlength_ctable: ptr::null(),
|
||||
fse_offcode_ctable: ptr::null(),
|
||||
};
|
||||
|
||||
unsafe { refresh_window_projection(&mut state) };
|
||||
|
||||
assert_eq!(state.base, base);
|
||||
assert_eq!(state.dict_limit, dict_limit);
|
||||
assert_eq!(state.low_limit, low_limit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downscale_stats_preserves_observed_symbols_with_base_zero_possible() {
|
||||
let mut frequencies = [0_u32, 1, 255, 256, 511];
|
||||
|
||||
let sum = unsafe { downscale_stats(frequencies.as_mut_ptr(), 4, 8, false) };
|
||||
|
||||
assert_eq!(frequencies, [0, 1, 1, 2, 2]);
|
||||
assert_eq!(sum, 6);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user