#![allow(non_snake_case)]
#[derive(Debug, Clone, Copy, Default)]
pub struct ZSTD_compressionParameters {
pub windowLog: u32,
pub chainLog: u32,
pub hashLog: u32,
pub searchLog: u32,
pub minMatch: u32,
pub targetLength: u32,
pub strategy: u32,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ZSTD_window_t {
pub nextSrc: u32,
pub base_offset: u32,
pub dictBase_offset: u32,
pub dictLimit: u32,
pub lowLimit: u32,
pub nbOverflowCorrections: u32,
}
pub const ZSTD_WINDOW_START_INDEX: u32 = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ZSTD_dictMode_e {
#[default]
ZSTD_noDict = 0,
ZSTD_extDict = 1,
ZSTD_dictMatchState = 2,
ZSTD_dedicatedDictSearch = 3,
}
pub fn ZSTD_matchState_dictMode(ms: &ZSTD_MatchState_t) -> ZSTD_dictMode_e {
if ZSTD_window_hasExtDict(&ms.window) {
return ZSTD_dictMode_e::ZSTD_extDict;
}
if let Some(dict_ms) = ms.dictMatchState.as_ref() {
if dict_ms.dedicatedDictSearch != 0 {
return ZSTD_dictMode_e::ZSTD_dedicatedDictSearch;
}
return ZSTD_dictMode_e::ZSTD_dictMatchState;
}
ZSTD_dictMode_e::ZSTD_noDict
}
pub const ZSTD_CURRENT_MAX: u32 = if crate::common::mem::MEM_32bits() != 0 {
2000u32 * (1u32 << 20)
} else {
3500u32 * (1u32 << 20)
};
pub const ZSTD_CHUNKSIZE_MAX: u32 = u32::MAX - ZSTD_CURRENT_MAX;
#[inline]
pub const fn ZSTD_dictTooBig(loadedDictSize: usize) -> bool {
loadedDictSize > ZSTD_CHUNKSIZE_MAX as usize
}
pub const ZSTD_INDEXOVERFLOW_MARGIN: u32 = 16 * (1 << 20);
#[inline]
pub fn ZSTD_indexTooCloseToMax(w: &ZSTD_window_t) -> bool {
w.nextSrc as u64 > (ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN) as u64
}
pub const ZSTD_DUBT_UNSORTED_MARK: u32 = 1;
pub const ZSTD_ROWSIZE: usize = 16;
pub const ZSTD_ROW_HASH_CACHE_SIZE: usize = 8;
pub fn ZSTD_reduceTable_internal(table: &mut [u32], reducerValue: u32, preserveMark: bool) {
let size = table.len();
debug_assert_eq!(size & (ZSTD_ROWSIZE - 1), 0);
debug_assert!(size < (1usize << 31));
let reducerThreshold = reducerValue.wrapping_add(ZSTD_WINDOW_START_INDEX);
for cell in table.iter_mut() {
*cell = if preserveMark && *cell == ZSTD_DUBT_UNSORTED_MARK {
ZSTD_DUBT_UNSORTED_MARK
} else if *cell < reducerThreshold {
0
} else {
cell.wrapping_sub(reducerValue)
};
}
}
#[inline]
pub fn ZSTD_reduceTable(table: &mut [u32], reducerValue: u32) {
ZSTD_reduceTable_internal(table, reducerValue, false);
}
#[inline]
pub fn ZSTD_reduceTable_btlazy2(table: &mut [u32], reducerValue: u32) {
ZSTD_reduceTable_internal(table, reducerValue, true);
}
#[inline]
pub fn ZSTD_rowMatchFinderSupported(strategy: u32) -> bool {
(3..=5).contains(&strategy)
}
pub fn ZSTD_resolveRowMatchFinderMode(
mode: crate::compress::zstd_ldm::ZSTD_ParamSwitch_e,
cParams: &ZSTD_compressionParameters,
) -> crate::compress::zstd_ldm::ZSTD_ParamSwitch_e {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
const K_WINDOW_LOG_LOWER_BOUND: u32 = 14;
if mode != ZSTD_ParamSwitch_e::ZSTD_ps_auto {
return mode;
}
if !ZSTD_rowMatchFinderSupported(cParams.strategy) {
return ZSTD_ParamSwitch_e::ZSTD_ps_disable;
}
if cParams.windowLog > K_WINDOW_LOG_LOWER_BOUND {
ZSTD_ParamSwitch_e::ZSTD_ps_enable
} else {
ZSTD_ParamSwitch_e::ZSTD_ps_disable
}
}
pub fn ZSTD_resolveBlockSplitterMode(
mode: crate::compress::zstd_ldm::ZSTD_ParamSwitch_e,
cParams: &ZSTD_compressionParameters,
) -> crate::compress::zstd_ldm::ZSTD_ParamSwitch_e {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
if mode != ZSTD_ParamSwitch_e::ZSTD_ps_auto {
return mode;
}
if cParams.strategy >= 7 && cParams.windowLog >= 17 {
ZSTD_ParamSwitch_e::ZSTD_ps_enable
} else {
ZSTD_ParamSwitch_e::ZSTD_ps_disable
}
}
pub fn ZSTD_resolveEnableLdm(
mode: crate::compress::zstd_ldm::ZSTD_ParamSwitch_e,
cParams: &ZSTD_compressionParameters,
) -> crate::compress::zstd_ldm::ZSTD_ParamSwitch_e {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
if mode != ZSTD_ParamSwitch_e::ZSTD_ps_auto {
return mode;
}
if cParams.strategy >= 7 && cParams.windowLog >= 27 {
ZSTD_ParamSwitch_e::ZSTD_ps_enable
} else {
ZSTD_ParamSwitch_e::ZSTD_ps_disable
}
}
#[inline]
pub fn ZSTD_resolveMaxBlockSize(maxBlockSize: usize) -> usize {
use crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX;
if maxBlockSize == 0 {
ZSTD_BLOCKSIZE_MAX
} else {
maxBlockSize
}
}
#[inline]
pub fn ZSTD_resolveExternalSequenceValidation(mode: i32) -> i32 {
mode
}
pub fn ZSTD_resolveExternalRepcodeSearch(
value: crate::compress::zstd_ldm::ZSTD_ParamSwitch_e,
cLevel: i32,
) -> crate::compress::zstd_ldm::ZSTD_ParamSwitch_e {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
if value != ZSTD_ParamSwitch_e::ZSTD_ps_auto {
return value;
}
if cLevel < 10 {
ZSTD_ParamSwitch_e::ZSTD_ps_disable
} else {
ZSTD_ParamSwitch_e::ZSTD_ps_enable
}
}
#[inline]
pub fn ZSTD_CDictIndicesAreTagged(cParams: &ZSTD_compressionParameters) -> bool {
cParams.strategy == 1 || cParams.strategy == 2
}
#[inline]
pub fn ZSTD_rowMatchFinderUsed(
strategy: u32,
mode: crate::compress::zstd_ldm::ZSTD_ParamSwitch_e,
) -> bool {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
debug_assert!(mode != ZSTD_ParamSwitch_e::ZSTD_ps_auto);
ZSTD_rowMatchFinderSupported(strategy) && mode == ZSTD_ParamSwitch_e::ZSTD_ps_enable
}
#[inline]
pub fn ZSTD_allocateChainTable(
strategy: u32,
useRowMatchFinder: crate::compress::zstd_ldm::ZSTD_ParamSwitch_e,
forDDSDict: u32,
) -> bool {
forDDSDict != 0 || (strategy != 1 && !ZSTD_rowMatchFinderUsed(strategy, useRowMatchFinder))
}
#[inline]
pub fn ZSTD_bitmix(mut val: u64, len: u64) -> u64 {
use crate::common::bits::ZSTD_rotateRight_U64;
val ^= ZSTD_rotateRight_U64(val, 49) ^ ZSTD_rotateRight_U64(val, 24);
val = val.wrapping_mul(0x9FB21C651E98DF25u64);
val ^= (val >> 35) + len;
val = val.wrapping_mul(0x9FB21C651E98DF25u64);
val ^ (val >> 28)
}
#[inline]
pub fn ZSTD_advanceHashSalt(ms: &mut ZSTD_MatchState_t) {
ms.hashSalt = ZSTD_bitmix(ms.hashSalt, 8) ^ ZSTD_bitmix(ms.hashSaltEntropy as u64, 4);
}
pub fn ZSTD_invalidateMatchState(ms: &mut ZSTD_MatchState_t) {
ZSTD_window_clear(&mut ms.window);
ms.nextToUpdate = ms.window.dictLimit;
ms.dictMatchState = None;
ms.loadedDictEnd = 0;
ms.opt.litLengthSum = 0;
}
pub fn ZSTD_reduceIndex(
ms: &mut ZSTD_MatchState_t,
useRowMatchFinder: crate::compress::zstd_ldm::ZSTD_ParamSwitch_e,
reducerValue: u32,
) {
let hSize = 1usize << ms.cParams.hashLog;
ZSTD_reduceTable(&mut ms.hashTable[..hSize], reducerValue);
if ZSTD_allocateChainTable(
ms.cParams.strategy,
useRowMatchFinder,
ms.dedicatedDictSearch,
) && !ms.chainTable.is_empty()
{
let chainSize = 1usize << ms.cParams.chainLog;
if ms.cParams.strategy == 6 {
ZSTD_reduceTable_btlazy2(&mut ms.chainTable[..chainSize], reducerValue);
} else {
ZSTD_reduceTable(&mut ms.chainTable[..chainSize], reducerValue);
}
}
if ms.hashLog3 > 0 && !ms.hashTable3.is_empty() {
let h3Size = 1usize << ms.hashLog3;
ZSTD_reduceTable(&mut ms.hashTable3[..h3Size], reducerValue);
}
}
pub const ZSTD_SHORT_CACHE_TAG_BITS: u32 = 8;
pub const ZSTD_SHORT_CACHE_TAG_MASK: u32 = (1u32 << ZSTD_SHORT_CACHE_TAG_BITS) - 1;
#[inline]
pub fn ZSTD_index_overlap_check(prefixLowestIndex: u32, repIndex: u32) -> bool {
prefixLowestIndex.wrapping_sub(1).wrapping_sub(repIndex) >= 3
}
#[inline]
pub fn ZSTD_writeTaggedIndex(hashTable: &mut [u32], hashAndTag: usize, index: u32) {
let hash = hashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS;
let tag = (hashAndTag as u32) & ZSTD_SHORT_CACHE_TAG_MASK;
debug_assert!(
index >> (32 - ZSTD_SHORT_CACHE_TAG_BITS) == 0,
"index must fit below the tag bits",
);
hashTable[hash] = (index << ZSTD_SHORT_CACHE_TAG_BITS) | tag;
}
#[inline]
pub fn ZSTD_comparePackedTags(packedTag1: usize, packedTag2: usize) -> bool {
let t1 = (packedTag1 as u32) & ZSTD_SHORT_CACHE_TAG_MASK;
let t2 = (packedTag2 as u32) & ZSTD_SHORT_CACHE_TAG_MASK;
t1 == t2
}
pub fn ZSTD_window_clear(window: &mut ZSTD_window_t) {
let end = window.nextSrc;
window.lowLimit = end;
window.dictLimit = end;
}
pub fn ZSTD_window_update(
window: &mut ZSTD_window_t,
src_abs: u32,
srcSize: usize,
forceNonContiguous: bool,
) -> bool {
let mut contiguous = true;
if srcSize == 0 {
return contiguous;
}
let mut segmentStartIndex = src_abs;
if src_abs != window.nextSrc || forceNonContiguous {
let distanceFromBase = window.nextSrc;
window.lowLimit = window.dictLimit;
window.dictLimit = distanceFromBase;
window.dictBase_offset = window.base_offset;
window.base_offset = distanceFromBase;
segmentStartIndex = distanceFromBase;
if window.dictLimit.wrapping_sub(window.lowLimit)
< crate::compress::zstd_fast::HASH_READ_SIZE as u32
{
window.lowLimit = window.dictLimit;
}
contiguous = false;
}
let src_end_for_overlap = src_abs.wrapping_add(srcSize as u32);
let src_end_abs = segmentStartIndex.wrapping_add(srcSize as u32);
window.nextSrc = src_end_abs;
if src_end_for_overlap > window.lowLimit && src_abs < window.dictLimit {
window.lowLimit = src_end_for_overlap.min(window.dictLimit);
}
contiguous
}
#[inline]
pub fn ZSTD_window_isEmpty(window: &ZSTD_window_t) -> bool {
window.dictLimit == ZSTD_WINDOW_START_INDEX
&& window.lowLimit == ZSTD_WINDOW_START_INDEX
&& window.nextSrc == ZSTD_WINDOW_START_INDEX
}
#[inline]
pub fn ZSTD_window_hasExtDict(window: &ZSTD_window_t) -> bool {
window.lowLimit < window.dictLimit
}
pub fn ZSTD_window_canOverflowCorrect(
window: &ZSTD_window_t,
cycleLog: u32,
maxDist: u32,
loadedDictEnd: u32,
src_abs: u32,
) -> bool {
let cycleSize = 1u32 << cycleLog;
let curr = src_abs;
let minIndexToOverflowCorrect = cycleSize
.wrapping_add(maxDist.max(cycleSize))
.wrapping_add(ZSTD_WINDOW_START_INDEX);
let adjustment = window.nbOverflowCorrections + 1;
let adjustedIndex = minIndexToOverflowCorrect
.wrapping_mul(adjustment)
.max(minIndexToOverflowCorrect);
let indexLargeEnough = curr > adjustedIndex;
let dictionaryInvalidated = curr > maxDist.wrapping_add(loadedDictEnd);
indexLargeEnough && dictionaryInvalidated
}
pub fn ZSTD_window_correctOverflow(
window: &mut ZSTD_window_t,
cycleLog: u32,
maxDist: u32,
src_abs: u32,
) -> u32 {
let cycleSize = 1u32 << cycleLog;
let cycleMask = cycleSize - 1;
let curr = src_abs;
let currentCycle = curr & cycleMask;
let currentCycleCorrection = if currentCycle < ZSTD_WINDOW_START_INDEX {
cycleSize.max(ZSTD_WINDOW_START_INDEX)
} else {
0
};
let newCurrent = currentCycle + currentCycleCorrection + maxDist.max(cycleSize);
let correction = curr - newCurrent;
debug_assert!(maxDist & (maxDist - 1) == 0, "maxDist must be power of two");
debug_assert_eq!(curr & cycleMask, newCurrent & cycleMask);
debug_assert!(curr > newCurrent);
window.base_offset = window.base_offset.wrapping_sub(correction);
window.dictBase_offset = window.dictBase_offset.wrapping_sub(correction);
window.nextSrc = window.nextSrc.wrapping_sub(correction);
let correctionThreshold = correction.wrapping_add(ZSTD_WINDOW_START_INDEX);
window.lowLimit = if window.lowLimit < correctionThreshold {
ZSTD_WINDOW_START_INDEX
} else {
window.lowLimit.wrapping_sub(correction)
};
window.dictLimit = if window.dictLimit < correctionThreshold {
ZSTD_WINDOW_START_INDEX
} else {
window.dictLimit.wrapping_sub(correction)
};
debug_assert!(newCurrent >= maxDist);
debug_assert!(newCurrent - maxDist >= ZSTD_WINDOW_START_INDEX);
debug_assert!(window.lowLimit <= newCurrent);
debug_assert!(window.dictLimit <= newCurrent);
window.nbOverflowCorrections += 1;
correction
}
pub fn ZSTD_window_init(window: &mut ZSTD_window_t) {
*window = ZSTD_window_t::default();
window.base_offset = ZSTD_WINDOW_START_INDEX;
window.dictBase_offset = ZSTD_WINDOW_START_INDEX;
window.dictLimit = ZSTD_WINDOW_START_INDEX;
window.lowLimit = ZSTD_WINDOW_START_INDEX;
window.nextSrc = ZSTD_WINDOW_START_INDEX;
}
pub fn ZSTD_window_enforceMaxDist(
window: &mut ZSTD_window_t,
blockEnd_abs: u32,
maxDist: u32,
loadedDictEnd: &mut u32,
) -> bool {
let blockEndIdx = blockEnd_abs;
let loadedEnd = *loadedDictEnd;
if blockEndIdx > maxDist.wrapping_add(loadedEnd) {
let newLowLimit = blockEndIdx.wrapping_sub(maxDist);
if window.lowLimit < newLowLimit {
window.lowLimit = newLowLimit;
}
if window.dictLimit < window.lowLimit {
window.dictLimit = window.lowLimit;
}
*loadedDictEnd = 0;
return true;
}
false
}
pub fn ZSTD_matchState_enforceMaxDist(
ms: &mut ZSTD_MatchState_t,
blockEnd_abs: u32,
maxDist: u32,
) -> bool {
let invalidated =
ZSTD_window_enforceMaxDist(&mut ms.window, blockEnd_abs, maxDist, &mut ms.loadedDictEnd);
if invalidated {
ms.dictMatchState = None;
}
invalidated
}
pub fn ZSTD_checkDictValidity(
window: &ZSTD_window_t,
blockEnd_abs: u32,
maxDist: u32,
loadedDictEnd: &mut u32,
) -> bool {
let loadedEnd = *loadedDictEnd;
if blockEnd_abs > loadedEnd.wrapping_add(maxDist) || loadedEnd != window.dictLimit {
*loadedDictEnd = 0;
return true;
}
false
}
pub fn ZSTD_matchState_checkDictValidity(
ms: &mut ZSTD_MatchState_t,
blockEnd_abs: u32,
maxDist: u32,
) -> bool {
let invalidated =
ZSTD_checkDictValidity(&ms.window, blockEnd_abs, maxDist, &mut ms.loadedDictEnd);
if invalidated {
ms.dictMatchState = None;
}
invalidated
}
pub fn ZSTD_window_needOverflowCorrection(
_window: &ZSTD_window_t,
_cycleLog: u32,
_maxDist: u32,
_loadedDictEnd: u32,
_src_abs: u32,
srcEnd_abs: u32,
) -> bool {
srcEnd_abs > ZSTD_CURRENT_MAX
}
pub fn ZSTD_overflowCorrectIfNeeded(
ms: &mut ZSTD_MatchState_t,
useRowMatchFinder: crate::compress::zstd_ldm::ZSTD_ParamSwitch_e,
_dedicatedDictSearch: u32,
windowLog: u32,
chainLog: u32,
strategy: u32,
src_abs: u32,
srcEnd_abs: u32,
) {
let cycleLog = crate::compress::zstd_compress::ZSTD_cycleLog(chainLog, strategy);
let maxDist: u32 = 1u32 << windowLog;
if ZSTD_window_needOverflowCorrection(
&ms.window,
cycleLog,
maxDist,
ms.loadedDictEnd,
src_abs,
srcEnd_abs,
) {
let correction = ZSTD_window_correctOverflow(&mut ms.window, cycleLog, maxDist, src_abs);
ZSTD_reduceIndex(ms, useRowMatchFinder, correction);
if ms.nextToUpdate < correction {
ms.nextToUpdate = 0;
} else {
ms.nextToUpdate -= correction;
}
ms.loadedDictEnd = 0;
ms.dictMatchState = None;
}
}
#[derive(Debug, Clone)]
pub struct ZSTD_MatchState_t {
pub window: ZSTD_window_t,
pub entropySeed: Option<crate::compress::zstd_compress::ZSTD_entropyCTables_t>,
pub ldmSeqStore: Option<crate::compress::zstd_ldm::RawSeqStore_t>,
pub dictMatchState: Option<Box<ZSTD_MatchState_t>>,
pub dedicatedDictSearch: u32,
pub dictContent: Vec<u8>,
pub loadedDictEnd: u32,
pub forceNonContiguous: bool,
pub nextToUpdate: u32,
pub hashTable: Vec<u32>,
pub hashTable3: Vec<u32>,
pub hashLog3: u32,
pub rowHashLog: u32,
pub tagTable: Vec<u8>,
pub hashCache: [u32; ZSTD_ROW_HASH_CACHE_SIZE],
pub hashSalt: u64,
pub hashSaltEntropy: u32,
pub lazySkipping: u32,
pub chainTable: Vec<u32>,
pub opt: crate::compress::zstd_opt::optState_t,
pub cParams: ZSTD_compressionParameters,
pub prefetchCDictTables: bool,
}
impl ZSTD_MatchState_t {
pub fn new(cParams: ZSTD_compressionParameters) -> Self {
let hashSize = 1usize << cParams.hashLog;
Self {
window: ZSTD_window_t {
base_offset: ZSTD_WINDOW_START_INDEX,
nextSrc: ZSTD_WINDOW_START_INDEX,
dictBase_offset: ZSTD_WINDOW_START_INDEX,
dictLimit: ZSTD_WINDOW_START_INDEX,
lowLimit: ZSTD_WINDOW_START_INDEX,
nbOverflowCorrections: 0,
},
entropySeed: None,
ldmSeqStore: None,
dictMatchState: None,
dedicatedDictSearch: 0,
dictContent: Vec::new(),
loadedDictEnd: 0,
forceNonContiguous: false,
nextToUpdate: ZSTD_WINDOW_START_INDEX,
hashTable: vec![0u32; hashSize],
hashTable3: Vec::new(),
hashLog3: 0,
rowHashLog: 0,
tagTable: Vec::new(),
hashCache: [0; ZSTD_ROW_HASH_CACHE_SIZE],
hashSalt: 0,
hashSaltEntropy: 0,
lazySkipping: 0,
chainTable: Vec::new(),
opt: crate::compress::zstd_opt::optState_t::default(),
cParams,
prefetchCDictTables: false,
}
}
pub fn reset(&mut self) {
self.window = ZSTD_window_t {
base_offset: ZSTD_WINDOW_START_INDEX,
nextSrc: ZSTD_WINDOW_START_INDEX,
dictBase_offset: ZSTD_WINDOW_START_INDEX,
dictLimit: ZSTD_WINDOW_START_INDEX,
lowLimit: ZSTD_WINDOW_START_INDEX,
nbOverflowCorrections: 0,
};
self.entropySeed = None;
self.ldmSeqStore = None;
self.dictMatchState = None;
self.dedicatedDictSearch = 0;
self.dictContent.clear();
self.loadedDictEnd = 0;
self.forceNonContiguous = false;
self.nextToUpdate = ZSTD_WINDOW_START_INDEX;
for c in self.hashTable.iter_mut() {
*c = 0;
}
for c in self.hashTable3.iter_mut() {
*c = 0;
}
for c in self.tagTable.iter_mut() {
*c = 0;
}
for c in self.chainTable.iter_mut() {
*c = 0;
}
self.rowHashLog = 0;
self.hashCache = [0; ZSTD_ROW_HASH_CACHE_SIZE];
self.hashSalt = 0;
self.hashSaltEntropy = 0;
self.lazySkipping = 0;
self.opt = crate::compress::zstd_opt::optState_t::default();
}
}
#[cfg(test)]
#[allow(clippy::field_reassign_with_default)]
mod tests {
use super::*;
#[test]
fn ZSTD_CURRENT_MAX_and_CHUNKSIZE_MAX_match_upstream_formula() {
let expected = if crate::common::mem::MEM_32bits() != 0 {
2000u32 * (1u32 << 20)
} else {
3500u32 * (1u32 << 20)
};
assert_eq!(ZSTD_CURRENT_MAX, expected);
assert_eq!(ZSTD_CHUNKSIZE_MAX, u32::MAX - ZSTD_CURRENT_MAX);
assert!(!ZSTD_dictTooBig(ZSTD_CHUNKSIZE_MAX as usize));
assert!(ZSTD_dictTooBig(ZSTD_CHUNKSIZE_MAX as usize + 1));
}
#[test]
fn ZSTD_dictMode_e_discriminants_match_upstream() {
assert_eq!(ZSTD_dictMode_e::ZSTD_noDict as u32, 0);
assert_eq!(ZSTD_dictMode_e::ZSTD_extDict as u32, 1);
assert_eq!(ZSTD_dictMode_e::ZSTD_dictMatchState as u32, 2);
assert_eq!(ZSTD_dictMode_e::ZSTD_dedicatedDictSearch as u32, 3);
}
#[test]
fn new_state_sizes_hash_table_from_hashlog() {
let cp = ZSTD_compressionParameters {
hashLog: 12,
minMatch: 5,
..Default::default()
};
let ms = ZSTD_MatchState_t::new(cp);
assert_eq!(ms.hashTable.len(), 1 << 12);
assert_eq!(ms.cParams.minMatch, 5);
assert_eq!(ms.nextToUpdate, ZSTD_WINDOW_START_INDEX);
assert_eq!(ms.window.base_offset, ZSTD_WINDOW_START_INDEX);
assert_eq!(ms.window.dictBase_offset, ZSTD_WINDOW_START_INDEX);
}
#[test]
fn window_isEmpty_true_at_init() {
let w = ZSTD_window_t {
nextSrc: ZSTD_WINDOW_START_INDEX,
base_offset: ZSTD_WINDOW_START_INDEX,
dictBase_offset: 0,
dictLimit: ZSTD_WINDOW_START_INDEX,
lowLimit: ZSTD_WINDOW_START_INDEX,
nbOverflowCorrections: 0,
};
assert!(ZSTD_window_isEmpty(&w));
}
#[test]
fn window_isEmpty_false_after_nextSrc_advances() {
let mut w = ZSTD_window_t {
nextSrc: ZSTD_WINDOW_START_INDEX,
base_offset: ZSTD_WINDOW_START_INDEX,
dictBase_offset: 0,
dictLimit: ZSTD_WINDOW_START_INDEX,
lowLimit: ZSTD_WINDOW_START_INDEX,
nbOverflowCorrections: 0,
};
w.nextSrc += 1;
assert!(!ZSTD_window_isEmpty(&w));
}
#[test]
fn window_hasExtDict_tracks_lowLimit_vs_dictLimit() {
let mut w = ZSTD_window_t::default();
w.lowLimit = 5;
w.dictLimit = 10;
assert!(ZSTD_window_hasExtDict(&w));
w.lowLimit = 10;
assert!(!ZSTD_window_hasExtDict(&w));
w.lowLimit = 11;
assert!(!ZSTD_window_hasExtDict(&w));
}
#[test]
fn window_clear_collapses_limits_to_nextSrc() {
let mut w = ZSTD_window_t::default();
w.nextSrc = 1000;
w.dictLimit = 50;
w.lowLimit = 10;
ZSTD_window_clear(&mut w);
assert_eq!(w.dictLimit, 1000);
assert_eq!(w.lowLimit, 1000);
}
#[test]
fn window_update_contiguous_only_advances_nextsrc() {
let mut w = ZSTD_window_t {
base_offset: 100,
dictBase_offset: 80,
dictLimit: 120,
lowLimit: 90,
nextSrc: 150,
nbOverflowCorrections: 0,
};
let contiguous = ZSTD_window_update(&mut w, 150, 20, false);
assert!(contiguous);
assert_eq!(w.base_offset, 100);
assert_eq!(w.dictBase_offset, 80);
assert_eq!(w.dictLimit, 120);
assert_eq!(w.lowLimit, 90);
assert_eq!(w.nextSrc, 170);
}
#[test]
fn window_update_non_contiguous_rotates_prefix_into_extdict() {
let mut w = ZSTD_window_t {
base_offset: 100,
dictBase_offset: 50,
dictLimit: 110,
lowLimit: 70,
nextSrc: 180,
nbOverflowCorrections: 0,
};
let contiguous = ZSTD_window_update(&mut w, 300, 16, true);
assert!(!contiguous);
assert_eq!(w.dictBase_offset, 100);
assert_eq!(w.dictLimit, 180);
assert_eq!(w.base_offset, 180);
assert_eq!(w.lowLimit, 110);
assert_eq!(w.nextSrc, 196);
}
#[test]
fn window_update_non_contiguous_trims_overlapping_prefix() {
let mut w = ZSTD_window_t {
base_offset: 100,
dictBase_offset: 50,
dictLimit: 110,
lowLimit: 70,
nextSrc: 180,
nbOverflowCorrections: 0,
};
let contiguous = ZSTD_window_update(&mut w, 170, 16, true);
assert!(!contiguous);
assert_eq!(w.dictBase_offset, 100);
assert_eq!(w.dictLimit, 180);
assert_eq!(w.base_offset, 180);
assert_eq!(w.lowLimit, 180);
assert_eq!(w.nextSrc, 196);
}
#[test]
fn window_update_first_forced_segment_starts_at_window_start_index() {
let mut w = ZSTD_window_t::default();
ZSTD_window_init(&mut w);
let contiguous = ZSTD_window_update(&mut w, ZSTD_WINDOW_START_INDEX, 64, true);
assert!(!contiguous);
assert_eq!(w.base_offset, ZSTD_WINDOW_START_INDEX);
assert_eq!(w.dictLimit, ZSTD_WINDOW_START_INDEX);
assert_eq!(w.lowLimit, ZSTD_WINDOW_START_INDEX);
assert_eq!(w.nextSrc, ZSTD_WINDOW_START_INDEX + 64);
}
#[test]
fn canOverflowCorrect_false_when_curr_too_small() {
let w = ZSTD_window_t::default();
assert!(!ZSTD_window_canOverflowCorrect(&w, 10, 1 << 20, 0, 1000));
}
#[test]
fn canOverflowCorrect_requires_dict_invalidation() {
let w = ZSTD_window_t::default();
let cycleLog = 10;
let cycleSize = 1u32 << cycleLog;
let maxDist = 1u32 << 20;
let minIdx = cycleSize + maxDist.max(cycleSize) + ZSTD_WINDOW_START_INDEX;
let curr = minIdx + 100;
let loaded = curr - 100;
assert!(!ZSTD_window_canOverflowCorrect(
&w, cycleLog, maxDist, loaded, curr
));
assert!(ZSTD_window_canOverflowCorrect(
&w, cycleLog, maxDist, 0, curr
));
}
#[test]
fn canOverflowCorrect_wraps_backoff_multiply_then_clamps() {
let cycleLog = 10;
let cycleSize = 1u32 << cycleLog;
let maxDist = 1u32 << 20;
let minIdx = cycleSize
.wrapping_add(maxDist.max(cycleSize))
.wrapping_add(ZSTD_WINDOW_START_INDEX);
let adjustment = u32::MAX / minIdx + 1;
let wrappedAdjusted = minIdx.wrapping_mul(adjustment);
assert!(wrappedAdjusted < minIdx);
let w = ZSTD_window_t {
nbOverflowCorrections: adjustment - 1,
..Default::default()
};
assert!(ZSTD_window_canOverflowCorrect(
&w,
cycleLog,
maxDist,
0,
minIdx + 1
));
}
#[test]
fn needOverflowCorrection_tracks_ZSTD_CURRENT_MAX() {
let w = ZSTD_window_t::default();
assert!(!ZSTD_window_needOverflowCorrection(
&w,
10,
1 << 20,
0,
0,
1000
));
assert!(!ZSTD_window_needOverflowCorrection(
&w,
10,
1 << 20,
0,
0,
ZSTD_CURRENT_MAX
));
assert!(ZSTD_window_needOverflowCorrection(
&w,
10,
1 << 20,
0,
0,
ZSTD_CURRENT_MAX + 1,
));
}
#[test]
fn correctOverflow_preserves_cycle_alignment() {
let mut w = ZSTD_window_t::default();
w.dictLimit = 1 << 24;
w.lowLimit = 1 << 20;
let cycleLog = 16;
let maxDist = 1u32 << 20;
let src_abs: u32 = ZSTD_CURRENT_MAX + 1000;
let correction = ZSTD_window_correctOverflow(&mut w, cycleLog, maxDist, src_abs);
let cycleMask = (1u32 << cycleLog) - 1;
let newCurrent = src_abs - correction;
assert_eq!(src_abs & cycleMask, newCurrent & cycleMask);
assert_eq!(w.nbOverflowCorrections, 1);
assert!(newCurrent >= maxDist);
}
#[test]
fn correctOverflow_floors_lowLimit_when_small() {
let mut w = ZSTD_window_t::default();
w.lowLimit = 5;
w.dictLimit = 1 << 24;
let src_abs: u32 = ZSTD_CURRENT_MAX + 500;
ZSTD_window_correctOverflow(&mut w, 16, 1u32 << 20, src_abs);
assert_eq!(w.lowLimit, ZSTD_WINDOW_START_INDEX);
}
#[test]
fn correctOverflow_returns_positive_correction() {
let mut w = ZSTD_window_t::default();
w.dictLimit = 1 << 24;
let src_abs: u32 = ZSTD_CURRENT_MAX + 42;
let c = ZSTD_window_correctOverflow(&mut w, 16, 1u32 << 20, src_abs);
assert!(c > (1u32 << 28));
}
#[test]
fn correctOverflow_rebases_offsets_downward() {
let src_abs: u32 = ZSTD_CURRENT_MAX + 4096;
let mut w = ZSTD_window_t {
base_offset: src_abs - 1024,
dictBase_offset: src_abs - 8192,
dictLimit: src_abs - 4096,
lowLimit: src_abs - (1 << 20),
nextSrc: src_abs,
nbOverflowCorrections: 0,
};
let old_base = w.base_offset;
let old_dict_base = w.dictBase_offset;
let old_next_src = w.nextSrc;
let correction = ZSTD_window_correctOverflow(&mut w, 16, 1u32 << 20, src_abs);
assert_eq!(w.base_offset, old_base.wrapping_sub(correction));
assert_eq!(w.dictBase_offset, old_dict_base.wrapping_sub(correction));
assert_eq!(w.nextSrc, old_next_src.wrapping_sub(correction));
}
#[test]
fn window_init_sets_all_sentinels() {
let mut w = ZSTD_window_t {
nextSrc: 999,
base_offset: 42,
dictBase_offset: 17,
dictLimit: 500,
lowLimit: 400,
nbOverflowCorrections: 5,
};
ZSTD_window_init(&mut w);
assert_eq!(w.nextSrc, ZSTD_WINDOW_START_INDEX);
assert_eq!(w.base_offset, ZSTD_WINDOW_START_INDEX);
assert_eq!(w.dictBase_offset, ZSTD_WINDOW_START_INDEX);
assert_eq!(w.dictLimit, ZSTD_WINDOW_START_INDEX);
assert_eq!(w.lowLimit, ZSTD_WINDOW_START_INDEX);
assert_eq!(w.nbOverflowCorrections, 0);
assert!(ZSTD_window_isEmpty(&w));
}
#[test]
fn enforceMaxDist_noop_when_within_dist() {
let mut w = ZSTD_window_t::default();
w.lowLimit = 5;
w.dictLimit = 10;
let mut dictEnd = 0u32;
assert!(!ZSTD_window_enforceMaxDist(&mut w, 50, 100, &mut dictEnd));
assert_eq!(w.lowLimit, 5);
assert_eq!(w.dictLimit, 10);
}
#[test]
fn enforceMaxDist_advances_lowLimit_and_invalidates_dict() {
let mut w = ZSTD_window_t::default();
w.lowLimit = 5;
w.dictLimit = 10;
let mut dictEnd = 50u32;
assert!(ZSTD_window_enforceMaxDist(&mut w, 1200, 100, &mut dictEnd));
assert_eq!(w.lowLimit, 1100);
assert_eq!(w.dictLimit, 1100);
assert_eq!(dictEnd, 0);
}
#[test]
fn enforceMaxDist_uses_wrapping_loaded_end_distance() {
let mut w = ZSTD_window_t::default();
w.lowLimit = 5;
w.dictLimit = 10;
let maxDist = 100;
let mut dictEnd = u32::MAX - 50;
assert!(ZSTD_window_enforceMaxDist(
&mut w,
60,
maxDist,
&mut dictEnd
));
assert_eq!(w.lowLimit, 60u32.wrapping_sub(maxDist));
assert_eq!(w.dictLimit, w.lowLimit);
assert_eq!(dictEnd, 0);
}
#[test]
fn matchState_enforceMaxDist_clears_dictMatchState() {
let cp = ZSTD_compressionParameters::default();
let mut ms = ZSTD_MatchState_t::new(cp);
ms.window.lowLimit = 5;
ms.window.dictLimit = 10;
ms.loadedDictEnd = 50;
ms.dictMatchState = Some(Box::new(ZSTD_MatchState_t::new(cp)));
assert!(ZSTD_matchState_enforceMaxDist(&mut ms, 1200, 100));
assert_eq!(ms.loadedDictEnd, 0);
assert!(ms.dictMatchState.is_none());
}
#[test]
fn checkDictValidity_invalidates_past_window() {
let w = ZSTD_window_t {
dictLimit: 100,
..Default::default()
};
let mut dictEnd = 100u32;
assert!(ZSTD_checkDictValidity(&w, 500, 100, &mut dictEnd));
assert_eq!(dictEnd, 0);
}
#[test]
fn checkDictValidity_uses_wrapping_loaded_end_distance() {
let loadedEnd = u32::MAX - 50;
let w = ZSTD_window_t {
dictLimit: loadedEnd,
..Default::default()
};
let mut dictEnd = loadedEnd;
assert!(ZSTD_checkDictValidity(&w, 60, 100, &mut dictEnd));
assert_eq!(dictEnd, 0);
}
#[test]
fn checkDictValidity_invalidates_on_noncontig_jump() {
let w = ZSTD_window_t {
dictLimit: 100,
..Default::default()
};
let mut dictEnd = 80u32; assert!(ZSTD_checkDictValidity(&w, 150, 1000, &mut dictEnd));
assert_eq!(dictEnd, 0);
}
#[test]
fn matchState_checkDictValidity_clears_dictMatchState() {
let cp = ZSTD_compressionParameters::default();
let mut ms = ZSTD_MatchState_t::new(cp);
ms.window.dictLimit = 100;
ms.loadedDictEnd = 80;
ms.dictMatchState = Some(Box::new(ZSTD_MatchState_t::new(cp)));
assert!(ZSTD_matchState_checkDictValidity(&mut ms, 150, 1000));
assert_eq!(ms.loadedDictEnd, 0);
assert!(ms.dictMatchState.is_none());
}
#[test]
fn index_overlap_check_reports_non_overlap() {
assert!(ZSTD_index_overlap_check(100, 50));
}
#[test]
fn index_overlap_check_detects_guardband_overlap() {
assert!(!ZSTD_index_overlap_check(100, 98));
assert!(!ZSTD_index_overlap_check(100, 99));
}
#[test]
fn index_overlap_check_pass_when_in_prefix() {
assert!(ZSTD_index_overlap_check(100, 100));
assert!(ZSTD_index_overlap_check(100, 500));
}
#[test]
fn writeTaggedIndex_roundtrip() {
let mut t = vec![0u32; 16];
let hashAndTag: usize = (5 << ZSTD_SHORT_CACHE_TAG_BITS) | 0xAB;
ZSTD_writeTaggedIndex(&mut t, hashAndTag, 0x1234);
assert_eq!(t[5], (0x1234u32 << 8) | 0xAB);
}
#[test]
fn comparePackedTags_matches_low_byte_only() {
let a: usize = (0xDEADBEEF << 8) | 0x42;
let b: usize = (0x12345678 << 8) | 0x42;
let c: usize = (0x12345678 << 8) | 0x41;
assert!(ZSTD_comparePackedTags(a, b));
assert!(!ZSTD_comparePackedTags(a, c));
}
#[test]
fn reduceTable_shifts_above_threshold_and_floors_below() {
let mut t = vec![0u32; ZSTD_ROWSIZE];
t[0] = 100;
t[1] = 50;
t[2] = 42;
t[3] = 2; ZSTD_reduceTable(&mut t, 40);
assert_eq!(t[0], 60);
assert_eq!(t[1], 10);
assert_eq!(t[2], 2); assert_eq!(t[3], 0); }
#[test]
fn reduceTable_btlazy2_preserves_unsorted_mark() {
let mut t = vec![0u32; ZSTD_ROWSIZE];
t[0] = 100;
t[1] = ZSTD_DUBT_UNSORTED_MARK;
t[2] = ZSTD_DUBT_UNSORTED_MARK;
t[3] = 50;
ZSTD_reduceTable_btlazy2(&mut t, 40);
assert_eq!(t[0], 60);
assert_eq!(t[1], ZSTD_DUBT_UNSORTED_MARK); assert_eq!(t[2], ZSTD_DUBT_UNSORTED_MARK); assert_eq!(t[3], 10);
}
#[test]
fn reduceTable_plain_does_not_preserve_unsorted_mark() {
let mut t = vec![0u32; ZSTD_ROWSIZE];
t[1] = ZSTD_DUBT_UNSORTED_MARK;
ZSTD_reduceTable(&mut t, 40);
assert_eq!(t[1], 0);
}
#[test]
fn reduceTable_uses_wrapping_u32_arithmetic() {
let mut t = vec![0u32; ZSTD_ROWSIZE];
t[0] = 0;
t[1] = 1;
t[2] = u32::MAX;
ZSTD_reduceTable(&mut t, u32::MAX);
assert_eq!(t[0], 0);
assert_eq!(t[1], 2);
assert_eq!(t[2], 0);
}
#[test]
fn rowMatchFinderSupported_only_for_greedy_lazy_lazy2() {
assert!(!ZSTD_rowMatchFinderSupported(1)); assert!(!ZSTD_rowMatchFinderSupported(2)); assert!(ZSTD_rowMatchFinderSupported(3)); assert!(ZSTD_rowMatchFinderSupported(4)); assert!(ZSTD_rowMatchFinderSupported(5)); assert!(!ZSTD_rowMatchFinderSupported(6)); assert!(!ZSTD_rowMatchFinderSupported(7)); }
#[test]
fn allocateChainTable_rules() {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
assert!(!ZSTD_allocateChainTable(
1,
ZSTD_ParamSwitch_e::ZSTD_ps_disable,
0
));
assert!(ZSTD_allocateChainTable(
1,
ZSTD_ParamSwitch_e::ZSTD_ps_disable,
1
));
assert!(ZSTD_allocateChainTable(
2,
ZSTD_ParamSwitch_e::ZSTD_ps_disable,
0
));
assert!(!ZSTD_allocateChainTable(
3,
ZSTD_ParamSwitch_e::ZSTD_ps_enable,
0
));
assert!(ZSTD_allocateChainTable(
3,
ZSTD_ParamSwitch_e::ZSTD_ps_disable,
0
));
assert!(ZSTD_allocateChainTable(
6,
ZSTD_ParamSwitch_e::ZSTD_ps_enable,
0
));
}
#[test]
fn reduceIndex_touches_hash_chain_and_hash3() {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
let cp = ZSTD_compressionParameters {
hashLog: 4, chainLog: 4,
strategy: 2, ..Default::default()
};
let mut ms = ZSTD_MatchState_t::new(cp);
ms.chainTable = vec![100u32; 16];
ms.hashTable3 = vec![100u32; 16];
ms.hashLog3 = 4;
for h in ms.hashTable.iter_mut() {
*h = 100;
}
ZSTD_reduceIndex(&mut ms, ZSTD_ParamSwitch_e::ZSTD_ps_disable, 40);
assert!(ms.hashTable.iter().all(|&x| x == 60));
assert!(ms.chainTable.iter().all(|&x| x == 60));
assert!(ms.hashTable3.iter().all(|&x| x == 60));
}
#[test]
fn reduceIndex_uses_c_table_sizes_not_vec_tail() {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
let cp = ZSTD_compressionParameters {
hashLog: 4,
chainLog: 4,
strategy: 2,
..Default::default()
};
let mut ms = ZSTD_MatchState_t::new(cp);
ms.hashTable.resize(32, 100);
ms.chainTable = vec![100u32; 32];
ms.hashTable3 = vec![100u32; 32];
ms.hashLog3 = 4;
for h in ms.hashTable.iter_mut() {
*h = 100;
}
ZSTD_reduceIndex(&mut ms, ZSTD_ParamSwitch_e::ZSTD_ps_disable, 40);
assert!(ms.hashTable[..16].iter().all(|&x| x == 60));
assert!(ms.hashTable[16..].iter().all(|&x| x == 100));
assert!(ms.chainTable[..16].iter().all(|&x| x == 60));
assert!(ms.chainTable[16..].iter().all(|&x| x == 100));
assert!(ms.hashTable3[..16].iter().all(|&x| x == 60));
assert!(ms.hashTable3[16..].iter().all(|&x| x == 100));
}
#[test]
fn reduceIndex_skips_chain_table_for_fast_strategy() {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
let cp = ZSTD_compressionParameters {
hashLog: 4,
chainLog: 4,
strategy: 1, ..Default::default()
};
let mut ms = ZSTD_MatchState_t::new(cp);
ms.chainTable = vec![100u32; 16];
ZSTD_reduceIndex(&mut ms, ZSTD_ParamSwitch_e::ZSTD_ps_disable, 40);
assert!(ms.chainTable.iter().all(|&x| x == 100));
}
#[test]
fn reduceIndex_uses_match_state_dedicated_dict_search_for_chain_allocation() {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
let cp = ZSTD_compressionParameters {
hashLog: 4,
chainLog: 4,
strategy: 1, ..Default::default()
};
let mut ms = ZSTD_MatchState_t::new(cp);
ms.dedicatedDictSearch = 1;
ms.chainTable = vec![100u32; 16];
ZSTD_reduceIndex(&mut ms, ZSTD_ParamSwitch_e::ZSTD_ps_disable, 40);
assert!(ms.chainTable.iter().all(|&x| x == 60));
}
#[test]
fn resolveRowMatchFinderMode_respects_explicit_mode() {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
let cp = ZSTD_compressionParameters {
strategy: 3,
windowLog: 10,
..Default::default()
};
assert_eq!(
ZSTD_resolveRowMatchFinderMode(ZSTD_ParamSwitch_e::ZSTD_ps_enable, &cp),
ZSTD_ParamSwitch_e::ZSTD_ps_enable,
);
}
#[test]
fn resolveRowMatchFinderMode_auto_requires_windowLog_above_14() {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
let lower_bound = 14;
let mut cp = ZSTD_compressionParameters {
strategy: 3,
windowLog: lower_bound,
..Default::default()
};
assert_eq!(
ZSTD_resolveRowMatchFinderMode(ZSTD_ParamSwitch_e::ZSTD_ps_auto, &cp),
ZSTD_ParamSwitch_e::ZSTD_ps_disable,
);
cp.windowLog = lower_bound + 1;
assert_eq!(
ZSTD_resolveRowMatchFinderMode(ZSTD_ParamSwitch_e::ZSTD_ps_auto, &cp),
ZSTD_ParamSwitch_e::ZSTD_ps_enable,
);
cp.strategy = 7;
assert_eq!(
ZSTD_resolveRowMatchFinderMode(ZSTD_ParamSwitch_e::ZSTD_ps_auto, &cp),
ZSTD_ParamSwitch_e::ZSTD_ps_disable,
);
}
#[test]
fn resolveBlockSplitterMode_auto_gates_on_btopt_and_wlog_17() {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
let cp_lazy = ZSTD_compressionParameters {
strategy: 3,
windowLog: 20,
..Default::default()
};
assert_eq!(
ZSTD_resolveBlockSplitterMode(ZSTD_ParamSwitch_e::ZSTD_ps_auto, &cp_lazy),
ZSTD_ParamSwitch_e::ZSTD_ps_disable,
);
let cp_btopt_small = ZSTD_compressionParameters {
strategy: 7,
windowLog: 16,
..Default::default()
};
assert_eq!(
ZSTD_resolveBlockSplitterMode(ZSTD_ParamSwitch_e::ZSTD_ps_auto, &cp_btopt_small),
ZSTD_ParamSwitch_e::ZSTD_ps_disable,
);
let cp_btopt_big = ZSTD_compressionParameters {
strategy: 7,
windowLog: 17,
..Default::default()
};
assert_eq!(
ZSTD_resolveBlockSplitterMode(ZSTD_ParamSwitch_e::ZSTD_ps_auto, &cp_btopt_big),
ZSTD_ParamSwitch_e::ZSTD_ps_enable,
);
}
#[test]
fn resolveEnableLdm_auto_enables_for_btopt_with_large_window() {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
let cp_btopt_small = ZSTD_compressionParameters {
strategy: 7,
windowLog: 26,
..Default::default()
};
assert_eq!(
ZSTD_resolveEnableLdm(ZSTD_ParamSwitch_e::ZSTD_ps_auto, &cp_btopt_small),
ZSTD_ParamSwitch_e::ZSTD_ps_disable,
);
let cp_btopt_huge = ZSTD_compressionParameters {
strategy: 9,
windowLog: 27,
..Default::default()
};
assert_eq!(
ZSTD_resolveEnableLdm(ZSTD_ParamSwitch_e::ZSTD_ps_auto, &cp_btopt_huge),
ZSTD_ParamSwitch_e::ZSTD_ps_enable,
);
assert_eq!(
ZSTD_resolveEnableLdm(ZSTD_ParamSwitch_e::ZSTD_ps_enable, &cp_btopt_huge),
ZSTD_ParamSwitch_e::ZSTD_ps_enable,
);
}
#[test]
fn resolveMaxBlockSize_zero_means_default() {
use crate::decompress::zstd_decompress_block::ZSTD_BLOCKSIZE_MAX;
assert_eq!(ZSTD_resolveMaxBlockSize(0), ZSTD_BLOCKSIZE_MAX);
assert_eq!(ZSTD_resolveMaxBlockSize(12345), 12345);
}
#[test]
fn matchState_dictMode_reports_noDict_on_fresh() {
let cp = ZSTD_compressionParameters::default();
let ms = ZSTD_MatchState_t::new(cp);
assert_eq!(ZSTD_matchState_dictMode(&ms), ZSTD_dictMode_e::ZSTD_noDict);
}
#[test]
fn matchState_dictMode_reports_extDict_when_lowLimit_below_dictLimit() {
let cp = ZSTD_compressionParameters::default();
let mut ms = ZSTD_MatchState_t::new(cp);
ms.window.lowLimit = 5;
ms.window.dictLimit = 10;
assert_eq!(ZSTD_matchState_dictMode(&ms), ZSTD_dictMode_e::ZSTD_extDict);
}
#[test]
fn matchState_dictMode_reports_dictMatchState_when_attached_with_zero_loadedDictEnd() {
let cp = ZSTD_compressionParameters::default();
let mut ms = ZSTD_MatchState_t::new(cp);
ms.dictMatchState = Some(Box::new(ZSTD_MatchState_t::new(cp)));
ms.loadedDictEnd = 0;
assert_eq!(
ZSTD_matchState_dictMode(&ms),
ZSTD_dictMode_e::ZSTD_dictMatchState
);
}
#[test]
fn matchState_dictMode_reports_dedicatedDictSearch_from_attached_state() {
let cp = ZSTD_compressionParameters::default();
let mut ms = ZSTD_MatchState_t::new(cp);
let mut dict_ms = ZSTD_MatchState_t::new(cp);
dict_ms.dedicatedDictSearch = 1;
ms.dictMatchState = Some(Box::new(dict_ms));
assert_eq!(
ZSTD_matchState_dictMode(&ms),
ZSTD_dictMode_e::ZSTD_dedicatedDictSearch
);
}
#[test]
fn matchState_dictMode_prioritizes_extDict_over_dictMatchState() {
let cp = ZSTD_compressionParameters::default();
let mut ms = ZSTD_MatchState_t::new(cp);
ms.dictMatchState = Some(Box::new(ZSTD_MatchState_t::new(cp)));
ms.loadedDictEnd = 32;
ms.window.lowLimit = 5;
ms.window.dictLimit = 10;
assert_eq!(ZSTD_matchState_dictMode(&ms), ZSTD_dictMode_e::ZSTD_extDict);
}
#[test]
fn matchState_dictMode_prioritizes_extDict_over_dedicatedDictSearch() {
let cp = ZSTD_compressionParameters::default();
let mut ms = ZSTD_MatchState_t::new(cp);
let mut dict_ms = ZSTD_MatchState_t::new(cp);
dict_ms.dedicatedDictSearch = 1;
ms.dictMatchState = Some(Box::new(dict_ms));
ms.window.lowLimit = 5;
ms.window.dictLimit = 10;
assert_eq!(ZSTD_matchState_dictMode(&ms), ZSTD_dictMode_e::ZSTD_extDict);
}
#[test]
fn resolveExternalRepcodeSearch_auto_flips_at_level_10() {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
assert_eq!(
ZSTD_resolveExternalRepcodeSearch(ZSTD_ParamSwitch_e::ZSTD_ps_auto, 9),
ZSTD_ParamSwitch_e::ZSTD_ps_disable,
);
assert_eq!(
ZSTD_resolveExternalRepcodeSearch(ZSTD_ParamSwitch_e::ZSTD_ps_auto, 10),
ZSTD_ParamSwitch_e::ZSTD_ps_enable,
);
assert_eq!(
ZSTD_resolveExternalRepcodeSearch(ZSTD_ParamSwitch_e::ZSTD_ps_enable, 1),
ZSTD_ParamSwitch_e::ZSTD_ps_enable,
);
}
#[test]
fn CDictIndicesAreTagged_fast_family() {
assert!(ZSTD_CDictIndicesAreTagged(&ZSTD_compressionParameters {
strategy: 1,
..Default::default()
}));
assert!(ZSTD_CDictIndicesAreTagged(&ZSTD_compressionParameters {
strategy: 2,
..Default::default()
}));
assert!(!ZSTD_CDictIndicesAreTagged(&ZSTD_compressionParameters {
strategy: 3,
..Default::default()
}));
assert!(!ZSTD_CDictIndicesAreTagged(&ZSTD_compressionParameters {
strategy: 9,
..Default::default()
}));
}
#[test]
fn resolveExternalSequenceValidation_is_identity() {
assert_eq!(ZSTD_resolveExternalSequenceValidation(0), 0);
assert_eq!(ZSTD_resolveExternalSequenceValidation(1), 1);
assert_eq!(ZSTD_resolveExternalSequenceValidation(42), 42);
}
#[test]
fn bitmix_is_non_identity() {
assert_ne!(ZSTD_bitmix(0xDEADBEEF, 8), 0xDEADBEEF);
assert_eq!(ZSTD_bitmix(42, 8), ZSTD_bitmix(42, 8));
}
#[test]
fn advanceHashSalt_changes_salt() {
let cp = ZSTD_compressionParameters::default();
let mut ms = ZSTD_MatchState_t::new(cp);
ms.hashSaltEntropy = 0x1234;
let before = ms.hashSalt;
ZSTD_advanceHashSalt(&mut ms);
assert_ne!(ms.hashSalt, before);
}
#[test]
fn invalidateMatchState_resets_window_and_nextToUpdate() {
let cp = ZSTD_compressionParameters::default();
let mut ms = ZSTD_MatchState_t::new(cp);
ms.window.nextSrc = 500;
ms.window.dictLimit = 50;
ms.window.lowLimit = 25;
ms.nextToUpdate = 400;
ms.loadedDictEnd = 99;
ms.dictMatchState = Some(Box::new(ZSTD_MatchState_t::new(cp)));
ms.opt.litLengthSum = 77;
ZSTD_invalidateMatchState(&mut ms);
assert_eq!(ms.window.dictLimit, 500);
assert_eq!(ms.window.lowLimit, 500);
assert_eq!(ms.nextToUpdate, 500); assert_eq!(ms.loadedDictEnd, 0);
assert!(ms.dictMatchState.is_none());
assert_eq!(ms.opt.litLengthSum, 0);
}
#[test]
fn overflowCorrectIfNeeded_clears_dictMatchState() {
use crate::compress::zstd_ldm::ZSTD_ParamSwitch_e;
let cp = ZSTD_compressionParameters {
hashLog: 4,
windowLog: 20,
chainLog: 16,
strategy: 1,
..Default::default()
};
let mut ms = ZSTD_MatchState_t::new(cp);
ms.loadedDictEnd = 99;
ms.dictMatchState = Some(Box::new(ZSTD_MatchState_t::new(cp)));
let src_abs = ZSTD_CURRENT_MAX + 4096;
ZSTD_overflowCorrectIfNeeded(
&mut ms,
ZSTD_ParamSwitch_e::ZSTD_ps_disable,
0,
cp.windowLog,
cp.chainLog,
cp.strategy,
src_abs,
src_abs,
);
assert_eq!(ms.loadedDictEnd, 0);
assert!(ms.dictMatchState.is_none());
}
#[test]
fn reset_preserves_allocation_but_zeros_table() {
let cp = ZSTD_compressionParameters {
hashLog: 10,
minMatch: 4,
..Default::default()
};
let mut ms = ZSTD_MatchState_t::new(cp);
for c in ms.hashTable.iter_mut().take(16) {
*c = 0xdead;
}
ms.nextToUpdate = 100;
ms.reset();
assert!(ms.hashTable.iter().all(|&c| c == 0));
assert_eq!(ms.nextToUpdate, ZSTD_WINDOW_START_INDEX);
assert_eq!(ms.window.dictBase_offset, ZSTD_WINDOW_START_INDEX);
assert_eq!(ms.hashTable.len(), 1 << 10);
}
}