feat(compress): move hash and window reset policies to Rust
Move hash-salt mixing and context-reset overflow predicates behind scalar Rust ABI shims. Keep the C-owned match state and window arithmetic in the C wrapper while preserving exact architecture-dependent thresholds. Test Plan: - cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression (186 tests) - cargo clippy for library, benches, tests, and CLI all-targets - make -B -C lib -j2 lib - make -B -C tests -j2 test-zstream (84 deterministic tests; fuzzers 6451 and 9879 cases)
This commit is contained in:
@@ -28,7 +28,7 @@
|
||||
#include "zstd_opt.h"
|
||||
#include "zstd_ldm.h"
|
||||
#include "zstd_compress_superblock.h"
|
||||
#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_rotateRight_U64 */
|
||||
#include "../common/bits.h" /* ZSTD_highbit32 */
|
||||
|
||||
/* Frame serialization lives in Rust. Keep its interface scalar so the
|
||||
* large, configuration-sensitive CCtx parameter structure stays in C. */
|
||||
@@ -54,6 +54,9 @@ int ZSTD_rust_simpleCompress2Level(const void* cctx);
|
||||
int ZSTD_rust_simpleCompressStream2Level(const void* cctx);
|
||||
void ZSTD_rust_reduceTable(U32* table, U32 size, U32 reducerValue,
|
||||
int preserveMark);
|
||||
U64 ZSTD_rust_advanceHashSalt(U64 hashSalt, U64 hashSaltEntropy);
|
||||
int ZSTD_rust_indexTooCloseToMax(size_t nextSrcBaseOffset);
|
||||
int ZSTD_rust_dictTooBig(size_t loadedDictSize);
|
||||
|
||||
/* Context-free compression-parameter selection and sizing leaves live in
|
||||
* Rust (rust/src/zstd_compress_params.rs). This file retains
|
||||
@@ -1445,18 +1448,9 @@ typedef enum {
|
||||
ZSTD_resetTarget_CCtx
|
||||
} ZSTD_resetTarget_e;
|
||||
|
||||
/* Mixes bits in a 64 bits in a value, based on XXH3_rrmxmx */
|
||||
static U64 ZSTD_bitmix(U64 val, U64 len) {
|
||||
val ^= ZSTD_rotateRight_U64(val, 49) ^ ZSTD_rotateRight_U64(val, 24);
|
||||
val *= 0x9FB21C651E98DF25ULL;
|
||||
val ^= (val >> 35) + len ;
|
||||
val *= 0x9FB21C651E98DF25ULL;
|
||||
return val ^ (val >> 28);
|
||||
}
|
||||
|
||||
/* Mixes in the hashSalt and hashSaltEntropy to create a new hashSalt */
|
||||
static void ZSTD_advanceHashSalt(ZSTD_MatchState_t* ms) {
|
||||
ms->hashSalt = ZSTD_bitmix(ms->hashSalt, 8) ^ ZSTD_bitmix((U64) ms->hashSaltEntropy, 4);
|
||||
ms->hashSalt = ZSTD_rust_advanceHashSalt(ms->hashSalt, (U64)ms->hashSaltEntropy);
|
||||
}
|
||||
|
||||
static size_t
|
||||
@@ -1556,7 +1550,7 @@ ZSTD_reset_matchState(ZSTD_MatchState_t* ms,
|
||||
#define ZSTD_INDEXOVERFLOW_MARGIN (16 MB)
|
||||
static int ZSTD_indexTooCloseToMax(ZSTD_window_t w)
|
||||
{
|
||||
return (size_t)(w.nextSrc - w.base) > (ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN);
|
||||
return ZSTD_rust_indexTooCloseToMax((size_t)(w.nextSrc - w.base));
|
||||
}
|
||||
|
||||
/** ZSTD_dictTooBig():
|
||||
@@ -1566,7 +1560,7 @@ static int ZSTD_indexTooCloseToMax(ZSTD_window_t w)
|
||||
*/
|
||||
static int ZSTD_dictTooBig(size_t const loadedDictSize)
|
||||
{
|
||||
return loadedDictSize > ZSTD_CHUNKSIZE_MAX;
|
||||
return ZSTD_rust_dictTooBig(loadedDictSize);
|
||||
}
|
||||
|
||||
/*! ZSTD_resetCCtx_internal() :
|
||||
|
||||
@@ -64,6 +64,13 @@ const ZSTD_CONTENTSIZE_UNKNOWN: u64 = u64::MAX;
|
||||
const ZSTD_ROWSIZE: usize = 16;
|
||||
const ZSTD_WINDOW_START_INDEX: u32 = 2;
|
||||
const ZSTD_DUBT_UNSORTED_MARK: u32 = 1;
|
||||
const ZSTD_INDEXOVERFLOW_MARGIN: usize = 16usize << 20;
|
||||
const ZSTD_CURRENT_MAX: usize = if size_of::<usize>() == 8 {
|
||||
3500usize << 20
|
||||
} else {
|
||||
2000usize << 20
|
||||
};
|
||||
const ZSTD_CHUNKSIZE_MAX: usize = u32::MAX as usize - ZSTD_CURRENT_MAX;
|
||||
#[cfg(not(test))]
|
||||
const ZSTD_E_END: c_int = 2;
|
||||
|
||||
@@ -89,6 +96,48 @@ pub struct ZSTD_outBuffer {
|
||||
* supported pointer widths. */
|
||||
const TMP_WORKSPACE_SIZE: usize = 16 << 10;
|
||||
|
||||
#[inline]
|
||||
fn bitmix(mut val: u64, len: u64) -> u64 {
|
||||
val ^= val.rotate_right(49) ^ val.rotate_right(24);
|
||||
val = val.wrapping_mul(0x9FB21C651E98DF25);
|
||||
val ^= (val >> 35).wrapping_add(len);
|
||||
val = val.wrapping_mul(0x9FB21C651E98DF25);
|
||||
val ^ (val >> 28)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn advance_hash_salt(hash_salt: u64, hash_salt_entropy: u64) -> u64 {
|
||||
bitmix(hash_salt, 8) ^ bitmix(hash_salt_entropy, 4)
|
||||
}
|
||||
|
||||
/// Advance the row-matchfinder salt without exposing C's private match state.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ZSTD_rust_advanceHashSalt(hash_salt: u64, hash_salt_entropy: u64) -> u64 {
|
||||
advance_hash_salt(hash_salt, hash_salt_entropy)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn index_too_close_to_max(next_src_base_offset: usize) -> bool {
|
||||
next_src_base_offset > ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN
|
||||
}
|
||||
|
||||
/// Return whether a scalar C window offset is within the overflow margin.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ZSTD_rust_indexTooCloseToMax(next_src_base_offset: usize) -> c_int {
|
||||
index_too_close_to_max(next_src_base_offset) as c_int
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dict_too_big(loaded_dict_size: usize) -> bool {
|
||||
loaded_dict_size > ZSTD_CHUNKSIZE_MAX
|
||||
}
|
||||
|
||||
/// Return whether a dictionary exceeds the maximum loadable chunk size.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ZSTD_rust_dictTooBig(loaded_dict_size: usize) -> c_int {
|
||||
dict_too_big(loaded_dict_size) as c_int
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reduce_table_internal(table: &mut [u32], reducer_value: u32, preserve_mark: bool) {
|
||||
debug_assert_eq!(table.len() % ZSTD_ROWSIZE, 0);
|
||||
@@ -657,6 +706,37 @@ mod tests {
|
||||
assert_eq!(table[ZSTD_ROWSIZE * 2 - 1], u32::MAX - 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitmix_and_hash_salt_match_the_c_arithmetic() {
|
||||
assert_eq!(bitmix(0x0123_4567_89ab_cdef, 8), 0xd498_d855_4e8d_d8cb);
|
||||
assert_eq!(
|
||||
advance_hash_salt(0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210),
|
||||
0xe5ee_f172_e5ff_3e57
|
||||
);
|
||||
assert_eq!(
|
||||
ZSTD_rust_advanceHashSalt(0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210),
|
||||
0xe5ee_f172_e5ff_3e57
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn index_too_close_to_max_uses_a_strict_margin_boundary() {
|
||||
let threshold = ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN;
|
||||
assert!(!index_too_close_to_max(threshold));
|
||||
assert!(index_too_close_to_max(threshold + 1));
|
||||
assert_eq!(ZSTD_rust_indexTooCloseToMax(threshold), 0);
|
||||
assert_eq!(ZSTD_rust_indexTooCloseToMax(threshold + 1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dict_too_big_uses_a_strict_chunk_size_boundary() {
|
||||
assert!(!dict_too_big(0));
|
||||
assert!(!dict_too_big(ZSTD_CHUNKSIZE_MAX));
|
||||
assert!(dict_too_big(ZSTD_CHUNKSIZE_MAX + 1));
|
||||
assert_eq!(ZSTD_rust_dictTooBig(ZSTD_CHUNKSIZE_MAX), 0);
|
||||
assert_eq!(ZSTD_rust_dictTooBig(ZSTD_CHUNKSIZE_MAX + 1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_one_shot_abi_is_c_compatible() {
|
||||
let entry: unsafe extern "C" fn(*mut c_void, usize, *const c_void, usize, c_int) -> usize =
|
||||
|
||||
Reference in New Issue
Block a user