feat(compress): move MT rolling hash prime power to Rust
The MT rsync initialization previously computed the rolling-hash prime power through the C-only ZSTD_rollingHash_primePower helper. Move only that scalar exponentiation behind the existing Rust MT ABI boundary. The C caller still owns the MT context assignment, RSYNC_LENGTH constant, and surrounding initialization. The Rust helper mirrors ZSTD_ipow with wrapping u64 multiplication and applies u32 wrapping subtraction before widening the exponent, preserving the C length == 0 behavior. Rolling-hash scanning, rotation, and all other MT state remain unchanged. Test Plan: - Focused rolling_hash_prime_power Rust tests -- passed (2 tests). - Required clippy/fmt sequence in normal, bench, and test modes -- passed. - make -B -C lib -j2 lib -- passed. - make -C tests test-rust-lib-smoke -- passed. - tests/fuzzer -s4560 -t56 -i57 -v -- passed (57 tests). - make -C tests -j2 test-zstream -- passed (84 named, 5,305 + 7,351 randomized). - git diff --check and git diff --cached --check -- passed.
This commit is contained in:
@@ -150,6 +150,7 @@ void ZSTDMT_rust_findSynchronizationPoint(const void* inputSrc, size_t inputSize
|
||||
const void* inBuffStart, size_t inBuffFilled,
|
||||
int rsyncable, U64 primePower, U64 hitMask,
|
||||
size_t* toLoad, int* flush);
|
||||
U64 ZSTDMT_rust_rollingHashPrimePower(U32 length);
|
||||
size_t ZSTDMT_rust_nextInputSizeHint(size_t targetSectionSize,
|
||||
size_t inBuffFilled);
|
||||
size_t ZSTDMT_rust_sizeofCCtx(size_t mtctxSize, size_t factorySize,
|
||||
@@ -1253,7 +1254,7 @@ size_t ZSTDMT_initCStream_internal(
|
||||
DEBUGLOG(4, "rsyncLog = %u", rsyncBits);
|
||||
mtctx->rsync.hash = 0;
|
||||
mtctx->rsync.hitMask = (1ULL << rsyncBits) - 1;
|
||||
mtctx->rsync.primePower = ZSTD_rollingHash_primePower(RSYNC_LENGTH);
|
||||
mtctx->rsync.primePower = ZSTDMT_rust_rollingHashPrimePower(RSYNC_LENGTH);
|
||||
}
|
||||
if (mtctx->targetSectionSize < mtctx->targetPrefixSize) mtctx->targetSectionSize = mtctx->targetPrefixSize; /* job size must be >= overlap size */
|
||||
DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(mtctx->targetSectionSize>>10), (U32)params.jobSize);
|
||||
|
||||
@@ -173,6 +173,27 @@ fn rolling_hash_rotate(hash: u64, to_remove: u8, to_add: u8, prime_power: u64) -
|
||||
.wrapping_add(u64::from(to_add).wrapping_add(ROLL_HASH_CHAR_OFFSET))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rolling_hash_prime_power(length: c_uint) -> u64 {
|
||||
let mut base = PRIME8_BYTES;
|
||||
let mut exponent = u64::from(length.wrapping_sub(1));
|
||||
let mut power: u64 = 1;
|
||||
while exponent != 0 {
|
||||
if exponent & 1 != 0 {
|
||||
power = power.wrapping_mul(base);
|
||||
}
|
||||
exponent >>= 1;
|
||||
base = base.wrapping_mul(base);
|
||||
}
|
||||
power
|
||||
}
|
||||
|
||||
/// C ABI for the rolling-hash prime power used by rsyncable MT compression.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ZSTDMT_rust_rollingHashPrimePower(length: c_uint) -> u64 {
|
||||
rolling_hash_prime_power(length)
|
||||
}
|
||||
|
||||
/// Search for a synchronization point without exposing the C MT context or
|
||||
/// its private buffer types across the FFI boundary.
|
||||
#[inline]
|
||||
@@ -1056,6 +1077,22 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rolling_hash_prime_power_matches_c_ipow_for_common_lengths() {
|
||||
assert_eq!(ZSTDMT_rust_rollingHashPrimePower(1), 0x0000_0000_0000_0001);
|
||||
assert_eq!(ZSTDMT_rust_rollingHashPrimePower(2), 0xCF1B_BCDC_B7A5_6463);
|
||||
assert_eq!(
|
||||
ZSTDMT_rust_rollingHashPrimePower(RSYNC_LENGTH as c_uint),
|
||||
0xF550_7FE3_5F91_F8CB
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rolling_hash_prime_power_preserves_zero_length_underflow() {
|
||||
assert_eq!(0u32.wrapping_sub(1), c_uint::MAX);
|
||||
assert_eq!(ZSTDMT_rust_rollingHashPrimePower(0), 0x12A9_3A33_31E0_3D4B);
|
||||
}
|
||||
|
||||
fn call_synchronization_point(
|
||||
input: &[u8],
|
||||
input_pos: usize,
|
||||
|
||||
Reference in New Issue
Block a user