#![allow(dead_code)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum BackendTag {
Simple,
Dfast,
Row,
HashChain,
}
pub(crate) trait Strategy: Copy + 'static {
const BACKEND: BackendTag;
const MIN_MATCH: usize;
const ACCURATE_PRICE: bool;
const FAVOR_SMALL_OFFSETS: bool;
const USE_HASH3: bool;
const USE_BT: bool;
const OPT_LEVEL: u8;
const MAX_CHAIN_DEPTH: usize;
const SUFFICIENT_MATCH_LEN: usize;
}
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct Fast;
impl Strategy for Fast {
const BACKEND: BackendTag = BackendTag::Simple;
const MIN_MATCH: usize = 4;
const ACCURATE_PRICE: bool = false;
const FAVOR_SMALL_OFFSETS: bool = true;
const USE_HASH3: bool = false;
const USE_BT: bool = false;
const OPT_LEVEL: u8 = 0;
const MAX_CHAIN_DEPTH: usize = 8;
const SUFFICIENT_MATCH_LEN: usize = 32;
}
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct Dfast;
impl Strategy for Dfast {
const BACKEND: BackendTag = BackendTag::Dfast;
const MIN_MATCH: usize = 4;
const ACCURATE_PRICE: bool = false;
const FAVOR_SMALL_OFFSETS: bool = true;
const USE_HASH3: bool = false;
const USE_BT: bool = false;
const OPT_LEVEL: u8 = 0;
const MAX_CHAIN_DEPTH: usize = 8;
const SUFFICIENT_MATCH_LEN: usize = 32;
}
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct Greedy;
impl Strategy for Greedy {
const BACKEND: BackendTag = BackendTag::Row;
const MIN_MATCH: usize = 4;
const ACCURATE_PRICE: bool = false;
const FAVOR_SMALL_OFFSETS: bool = true;
const USE_HASH3: bool = false;
const USE_BT: bool = false;
const OPT_LEVEL: u8 = 0;
const MAX_CHAIN_DEPTH: usize = 8;
const SUFFICIENT_MATCH_LEN: usize = 32;
}
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct Lazy;
impl Strategy for Lazy {
const BACKEND: BackendTag = BackendTag::HashChain;
const MIN_MATCH: usize = 4;
const ACCURATE_PRICE: bool = false;
const FAVOR_SMALL_OFFSETS: bool = true;
const USE_HASH3: bool = false;
const USE_BT: bool = false;
const OPT_LEVEL: u8 = 0;
const MAX_CHAIN_DEPTH: usize = 8;
const SUFFICIENT_MATCH_LEN: usize = 32;
}
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct BtOpt;
impl Strategy for BtOpt {
const BACKEND: BackendTag = BackendTag::HashChain;
const MIN_MATCH: usize = 4;
const ACCURATE_PRICE: bool = false;
const FAVOR_SMALL_OFFSETS: bool = true;
const USE_HASH3: bool = false;
const USE_BT: bool = true;
const OPT_LEVEL: u8 = 0;
const MAX_CHAIN_DEPTH: usize = 32;
const SUFFICIENT_MATCH_LEN: usize = usize::MAX;
}
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct BtUltra;
impl Strategy for BtUltra {
const BACKEND: BackendTag = BackendTag::HashChain;
const MIN_MATCH: usize = 4;
const ACCURATE_PRICE: bool = true;
const FAVOR_SMALL_OFFSETS: bool = false;
const USE_HASH3: bool = false;
const USE_BT: bool = true;
const OPT_LEVEL: u8 = 2;
const MAX_CHAIN_DEPTH: usize = 32;
const SUFFICIENT_MATCH_LEN: usize = usize::MAX;
}
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct BtUltra2;
impl Strategy for BtUltra2 {
const BACKEND: BackendTag = BackendTag::HashChain;
const MIN_MATCH: usize = 4;
const ACCURATE_PRICE: bool = true;
const FAVOR_SMALL_OFFSETS: bool = false;
const USE_HASH3: bool = true;
const USE_BT: bool = true;
const OPT_LEVEL: u8 = 2;
const MAX_CHAIN_DEPTH: usize = 512;
const SUFFICIENT_MATCH_LEN: usize = usize::MAX;
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum StrategyTag {
Fast,
Dfast,
Greedy,
Lazy,
BtOpt,
BtUltra,
BtUltra2,
}
impl StrategyTag {
pub(crate) const fn for_level(level: u8) -> Self {
match level {
1 => Self::Fast,
2 | 3 => Self::Dfast,
4 => Self::Greedy,
5..=15 => Self::Lazy,
16 | 17 => Self::BtOpt,
18 | 19 => Self::BtUltra,
_ => Self::BtUltra2,
}
}
pub(crate) fn for_compression_level(level: crate::encoding::CompressionLevel) -> Self {
use crate::encoding::CompressionLevel;
match level {
CompressionLevel::Uncompressed => Self::Fast,
CompressionLevel::Fastest => Self::Fast,
CompressionLevel::Default => Self::Dfast,
CompressionLevel::Better => Self::Lazy,
CompressionLevel::Best => Self::Lazy,
CompressionLevel::Level(n) => {
if n <= 0 {
if n == 0 { Self::Dfast } else { Self::Fast }
} else {
let clamped_i32 = n.clamp(1, CompressionLevel::MAX_LEVEL);
Self::for_level(clamped_i32 as u8)
}
}
}
}
pub(crate) const fn backend(self) -> BackendTag {
match self {
Self::Fast => BackendTag::Simple,
Self::Dfast => BackendTag::Dfast,
Self::Greedy => BackendTag::Row,
Self::Lazy | Self::BtOpt | Self::BtUltra | Self::BtUltra2 => BackendTag::HashChain,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_strategy_matches_tag<S: Strategy>(tag: StrategyTag) {
assert_eq!(S::BACKEND, tag.backend(), "backend mismatch");
}
#[test]
fn strategy_consts_match_tag_bridge() {
assert_strategy_matches_tag::<Fast>(StrategyTag::Fast);
assert_strategy_matches_tag::<Dfast>(StrategyTag::Dfast);
assert_strategy_matches_tag::<Greedy>(StrategyTag::Greedy);
assert_strategy_matches_tag::<Lazy>(StrategyTag::Lazy);
assert_strategy_matches_tag::<BtOpt>(StrategyTag::BtOpt);
assert_strategy_matches_tag::<BtUltra>(StrategyTag::BtUltra);
assert_strategy_matches_tag::<BtUltra2>(StrategyTag::BtUltra2);
}
#[test]
fn for_compression_level_clamps_oversized_numeric_levels_to_btultra2() {
use crate::encoding::CompressionLevel;
assert_eq!(
StrategyTag::for_compression_level(CompressionLevel::Level(23)),
StrategyTag::BtUltra2,
);
assert_eq!(
StrategyTag::for_compression_level(CompressionLevel::Level(255)),
StrategyTag::BtUltra2,
);
assert_eq!(
StrategyTag::for_compression_level(CompressionLevel::Level(256)),
StrategyTag::BtUltra2,
);
assert_eq!(
StrategyTag::for_compression_level(CompressionLevel::Level(257)),
StrategyTag::BtUltra2,
);
assert_eq!(
StrategyTag::for_compression_level(CompressionLevel::Level(i32::MAX)),
StrategyTag::BtUltra2,
);
}
#[test]
fn level_to_tag_matches_donor_table() {
assert_eq!(StrategyTag::for_level(1), StrategyTag::Fast);
assert_eq!(StrategyTag::for_level(2), StrategyTag::Dfast);
assert_eq!(StrategyTag::for_level(3), StrategyTag::Dfast);
assert_eq!(StrategyTag::for_level(4), StrategyTag::Greedy);
assert_eq!(StrategyTag::for_level(5), StrategyTag::Lazy);
assert_eq!(StrategyTag::for_level(9), StrategyTag::Lazy);
assert_eq!(StrategyTag::for_level(15), StrategyTag::Lazy);
assert_eq!(StrategyTag::for_level(16), StrategyTag::BtOpt);
assert_eq!(StrategyTag::for_level(17), StrategyTag::BtOpt);
assert_eq!(StrategyTag::for_level(18), StrategyTag::BtUltra);
assert_eq!(StrategyTag::for_level(19), StrategyTag::BtUltra);
assert_eq!(StrategyTag::for_level(20), StrategyTag::BtUltra2);
assert_eq!(StrategyTag::for_level(22), StrategyTag::BtUltra2);
}
const _USE_BT_LAYOUT: () = {
assert!(!Fast::USE_BT);
assert!(!Dfast::USE_BT);
assert!(!Greedy::USE_BT);
assert!(!Lazy::USE_BT);
assert!(BtOpt::USE_BT);
assert!(BtUltra::USE_BT);
assert!(BtUltra2::USE_BT);
};
const _USE_HASH3_LAYOUT: () = {
assert!(!Fast::USE_HASH3);
assert!(!Dfast::USE_HASH3);
assert!(!Greedy::USE_HASH3);
assert!(!Lazy::USE_HASH3);
assert!(!BtOpt::USE_HASH3);
assert!(!BtUltra::USE_HASH3);
assert!(BtUltra2::USE_HASH3);
};
const _COST_MODEL_LAYOUT: () = {
assert!(!Lazy::ACCURATE_PRICE && Lazy::FAVOR_SMALL_OFFSETS);
assert!(!BtOpt::ACCURATE_PRICE && BtOpt::FAVOR_SMALL_OFFSETS);
assert!(BtUltra::ACCURATE_PRICE && !BtUltra::FAVOR_SMALL_OFFSETS);
assert!(BtUltra2::ACCURATE_PRICE && !BtUltra2::FAVOR_SMALL_OFFSETS);
assert!(BtOpt::MAX_CHAIN_DEPTH == 32);
assert!(BtUltra::MAX_CHAIN_DEPTH == 32);
assert!(BtUltra2::MAX_CHAIN_DEPTH == 512);
assert!(BtOpt::SUFFICIENT_MATCH_LEN == usize::MAX);
assert!(BtUltra::SUFFICIENT_MATCH_LEN == usize::MAX);
assert!(BtUltra2::SUFFICIENT_MATCH_LEN == usize::MAX);
};
}