diff --git a/lib/compress/zstd_preSplit.rs b/lib/compress/zstd_preSplit.rs index 664f3c8a..1d2163a8 100644 --- a/lib/compress/zstd_preSplit.rs +++ b/lib/compress/zstd_preSplit.rs @@ -6,7 +6,6 @@ use crate::lib::compress::hist::HIST_add; pub const ZSTD_SLIPBLOCK_WORKSPACESIZE: usize = 8208; -pub const BLOCKSIZE_MIN: c_int = 3500; pub const THRESHOLD_PENALTY_RATE: c_int = 16; pub const THRESHOLD_BASE: c_int = THRESHOLD_PENALTY_RATE - 2; pub const THRESHOLD_PENALTY: c_int = 3; @@ -48,80 +47,75 @@ pub struct BorderStats { pub middleEvents: Fingerprint<256>, } -#[inline(always)] -fn addEvents_generic(fp: &mut Fingerprint, src: &[u8], samplingRate: usize, hashLog: c_uint) { - let limit = src.len().wrapping_sub(HASHLENGTH).wrapping_add(1); +impl Fingerprint { + #[inline(always)] + fn add_events(&mut self, src: &[u8], samplingRate: usize, hashLog: c_uint) { + debug_assert!(src.len() >= HASHLENGTH); + let limit = src.len() - HASHLENGTH + 1; + + for i in 0..limit.div_ceil(samplingRate) { + let n = i * samplingRate; + self.events[hash2([src[n], src[n + 1]], hashLog) as usize] += 1; + } - debug_assert!(src.len() >= HASHLENGTH); - for i in 0..limit.div_ceil(samplingRate) { - let n = i * samplingRate; - fp.events[hash2([src[n], src[n + 1]], hashLog) as usize] += 1; + self.nbEvents += limit / samplingRate; } - fp.nbEvents += limit / samplingRate; -} + #[inline(always)] + fn record(&mut self, src: &[u8]) { + self.events[..1usize << HASH_LOG].fill(0); + self.nbEvents = 0; + self.add_events(src, SAMPLING_RATE, HASH_LOG); + } -#[inline(always)] -fn recordFingerprint_generic( - fp: &mut Fingerprint, - src: &[u8], -) { - fp.events[..1usize << HASH_LOG].fill(0); - fp.nbEvents = 0; - addEvents_generic(fp, src, SAMPLING_RATE, HASH_LOG); -} + fn distance(&self, other: &Self, hashLog: c_uint) -> u64 { + let mut distance = 0u64; -fn fpDistance(fp1: &Fingerprint, fp2: &Fingerprint, hashLog: c_uint) -> u64 { - let mut distance = 0u64; + debug_assert!(1usize << hashLog <= N); - debug_assert!(1usize << hashLog <= N); + for n in 0..(1 << hashLog) { + let dst1 = self.events[n] as i64 * other.nbEvents as i64; + let dst2 = other.events[n] as i64 * self.nbEvents as i64; + distance += dst1.abs_diff(dst2); + } - for n in 0..(1 << hashLog) { - let dst1 = fp1.events[n] as i64 * fp2.nbEvents as i64; - let dst2 = fp2.events[n] as i64 * fp1.nbEvents as i64; - distance = distance.wrapping_add(dst1.abs_diff(dst2)); + distance } - distance -} - -/// Compares new events with past events. -/// -/// Returns `true` when the fingerprints are considered "too different". -fn compareFingerprints( - ref_0: &Fingerprint, - newfp: &Fingerprint, - penalty: c_int, - hashLog: c_uint, -) -> bool { - debug_assert!(ref_0.nbEvents > 0); - debug_assert!(newfp.nbEvents > 0); - - let p50 = ref_0.nbEvents * newfp.nbEvents; - let deviation = fpDistance(ref_0, newfp, hashLog); - let threshold = p50 as u64 * (THRESHOLD_BASE + penalty) as u64 / THRESHOLD_PENALTY_RATE as u64; - deviation >= threshold -} + /// Compares new events with past events. + /// + /// Returns `true` when the fingerprints are considered "too different". + fn compare(&self, newfp: &Self, penalty: c_int, hashLog: c_uint) -> bool { + debug_assert!(self.nbEvents > 0); + debug_assert!(newfp.nbEvents > 0); + + let p50 = self.nbEvents * newfp.nbEvents; + let deviation = self.distance(newfp, hashLog); + let threshold = + p50 as u64 * (THRESHOLD_BASE + penalty) as u64 / THRESHOLD_PENALTY_RATE as u64; + deviation >= threshold + } -fn mergeEvents(acc: &mut Fingerprint, newfp: &Fingerprint) { - for n in 0..HASHTABLESIZE { - acc.events[n] += newfp.events[n]; + fn merge_events(&mut self, newfp: &Self) { + for n in 0..N { + self.events[n] += newfp.events[n]; + } + self.nbEvents += newfp.nbEvents; } - acc.nbEvents += newfp.nbEvents; } -pub const CHUNKSIZE: c_int = 8 << 10; +pub const CHUNKSIZE: usize = 8 << 10; /// `fpstats` must be zeroed before each call. fn ZSTD_splitBlock_byChunks(block: &[u8; 128 << 10], level: c_int, fpstats: &mut FPStats) -> usize { let blockSize = block.len(); // NOTE: the sampling rates go from coarse to fine, so that the hash log of each // entry matches the corresponding entry of `hashParams` below. - static records_fs: [fn(&mut Fingerprint, &[u8]) -> (); 4] = [ - recordFingerprint_generic::<43, 8>, - recordFingerprint_generic::<11, 9>, - recordFingerprint_generic::<5, 10>, - recordFingerprint_generic::<1, 10>, + static records_fs: [fn(&mut Fingerprint, &[u8]); 4] = [ + Fingerprint::record::<43, 8>, + Fingerprint::record::<11, 9>, + Fingerprint::record::<5, 10>, + Fingerprint::record::<1, 10>, ]; static hashParams: [c_uint; 4] = [8, 9, 10, 10]; @@ -129,28 +123,22 @@ fn ZSTD_splitBlock_byChunks(block: &[u8; 128 << 10], level: c_int, fpstats: &mut let record_f = records_fs[level as usize]; let mut penalty = THRESHOLD_PENALTY; - let chunk = CHUNKSIZE as usize; - - record_f(&mut fpstats.pastEvents, &block[..chunk]); - let mut pos = CHUNKSIZE as usize; - while pos <= blockSize.wrapping_sub(CHUNKSIZE as usize) { - record_f(&mut fpstats.newEvents, &block[pos..][..chunk]); - if compareFingerprints( - &fpstats.pastEvents, - &fpstats.newEvents, - penalty, - hashParams[level as usize], - ) { - return pos; - } else { - mergeEvents(&mut fpstats.pastEvents, &fpstats.newEvents); - if penalty > 0 { - penalty -= 1; - } + debug_assert!(blockSize.is_multiple_of(CHUNKSIZE)); + let (chunks, _) = block.as_chunks::(); + record_f(&mut fpstats.pastEvents, &chunks[0]); + for (i, chunk) in chunks[1..].iter().enumerate() { + record_f(&mut fpstats.newEvents, chunk); + if fpstats + .pastEvents + .compare(&fpstats.newEvents, penalty, hashParams[level as usize]) + { + return (i + 1) * CHUNKSIZE; + } + fpstats.pastEvents.merge_events(&fpstats.newEvents); + if penalty > 0 { + penalty -= 1; } - pos = pos.wrapping_add(CHUNKSIZE as usize); } - debug_assert_eq!(pos, blockSize); blockSize } @@ -168,32 +156,35 @@ fn ZSTD_splitBlock_byChunks(block: &[u8; 128 << 10], level: c_int, fpstats: &mut /// /// `stats` must be zeroed before each call. fn ZSTD_splitBlock_fromBorders(block: &[u8; 128 << 10], stats: &mut BorderStats) -> usize { - const SEGMENT_SIZE: c_int = 512; + const SEGMENT_SIZE: usize = 512; let blockSize = block.len(); - let segment = SEGMENT_SIZE as usize; - - HIST_add(&mut stats.pastEvents.events, &block[..segment]); - HIST_add(&mut stats.newEvents.events, &block[blockSize - segment..]); - stats.newEvents.nbEvents = SEGMENT_SIZE as usize; + HIST_add(&mut stats.pastEvents.events, &block[..SEGMENT_SIZE]); + HIST_add( + &mut stats.newEvents.events, + &block[blockSize - SEGMENT_SIZE..], + ); + stats.newEvents.nbEvents = SEGMENT_SIZE; stats.pastEvents.nbEvents = stats.newEvents.nbEvents; - if !compareFingerprints(&stats.pastEvents, &stats.newEvents, 0, 8) { + if !stats.pastEvents.compare(&stats.newEvents, 0, 8) { return blockSize; } - let middle = blockSize / 2 - segment / 2; - HIST_add(&mut stats.middleEvents.events, &block[middle..][..segment]); - stats.middleEvents.nbEvents = SEGMENT_SIZE as usize; - let distFromBegin = fpDistance(&stats.pastEvents, &stats.middleEvents, 8); - let distFromEnd = fpDistance(&stats.newEvents, &stats.middleEvents, 8); + let middle = blockSize / 2 - SEGMENT_SIZE / 2; + HIST_add( + &mut stats.middleEvents.events, + &block[middle..][..SEGMENT_SIZE], + ); + stats.middleEvents.nbEvents = SEGMENT_SIZE; + let distFromBegin = stats.pastEvents.distance(&stats.middleEvents, 8); + let distFromEnd = stats.newEvents.distance(&stats.middleEvents, 8); let minDistance = (SEGMENT_SIZE * SEGMENT_SIZE / 3) as u64; if distFromBegin.abs_diff(distFromEnd) < minDistance { - return 64 * (1 << 10); - } - (if distFromBegin > distFromEnd { + 64 * (1 << 10) + } else if distFromBegin > distFromEnd { 32 * (1 << 10) } else { 96 * (1 << 10) - }) as usize + } } /// Splits a block to find the optimal boundary for compression. diff --git a/test-libzstd-rs-sys/src/compress.rs b/test-libzstd-rs-sys/src/compress.rs index 67c0ec40..a3e0ce7c 100644 --- a/test-libzstd-rs-sys/src/compress.rs +++ b/test-libzstd-rs-sys/src/compress.rs @@ -1095,7 +1095,7 @@ mod pre_split { [usize; ZSTD_SLIPBLOCK_WORKSPACESIZE.div_ceil(size_of::())], >::uninit(); for boundary in [1, 8, 15, 16] { - let split = boundary * CHUNKSIZE as usize; + let split = boundary * CHUNKSIZE; block.fill(0); block[split..].fill(255); for level in 1..=4 {