#![allow(dead_code)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum BackendTag {
Simple,
Dfast,
Row,
HashChain,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum ParseMode {
Greedy,
Lazy,
Lazy2,
Optimal,
}
impl ParseMode {
pub(crate) const fn lazy_depth(self) -> u8 {
match self {
Self::Greedy => 0,
Self::Lazy => 1,
Self::Lazy2 => 2,
Self::Optimal => 0,
}
}
pub(crate) const fn from_lazy_depth(depth: u8) -> Self {
match depth {
0 => Self::Greedy,
1 => Self::Lazy,
_ => Self::Lazy2,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum SearchMethod {
Fast,
DoubleFast,
RowHash,
HashChain,
BinaryTree,
}
impl SearchMethod {
pub(crate) const fn backend(self) -> BackendTag {
match self {
Self::Fast => BackendTag::Simple,
Self::DoubleFast => BackendTag::Dfast,
Self::RowHash => BackendTag::Row,
Self::HashChain | Self::BinaryTree => BackendTag::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 TWO_PASS_SEED: bool = false;
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::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 Btlazy2;
impl Strategy for Btlazy2 {
const BACKEND: BackendTag = BackendTag::HashChain;
const MIN_MATCH: usize = 5;
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 = 64;
const SUFFICIENT_MATCH_LEN: usize = usize::MAX;
}
#[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 = 3;
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 = 64;
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 = 3;
const ACCURATE_PRICE: bool = true;
const FAVOR_SMALL_OFFSETS: bool = false;
const USE_HASH3: bool = true;
const TWO_PASS_SEED: 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,
Btlazy2,
BtOpt,
BtUltra,
BtUltra2,
}
impl StrategyTag {
pub(crate) const fn for_level(level: u8) -> Self {
match level {
1 | 2 => Self::Fast,
3 | 4 => Self::Dfast,
5 => Self::Greedy,
6..=12 => Self::Lazy,
13..=15 => Self::Btlazy2,
16 | 17 => Self::BtOpt,
18 => Self::BtUltra,
19 => Self::BtUltra2,
_ => 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 | Self::Lazy => BackendTag::Row,
Self::Btlazy2 | Self::BtOpt | Self::BtUltra | Self::BtUltra2 => BackendTag::HashChain,
}
}
pub(crate) const fn search(self) -> SearchMethod {
match self {
Self::Fast => SearchMethod::Fast,
Self::Dfast => SearchMethod::DoubleFast,
Self::Greedy | Self::Lazy => SearchMethod::RowHash,
Self::Btlazy2 | Self::BtOpt | Self::BtUltra | Self::BtUltra2 => {
SearchMethod::BinaryTree
}
}
}
pub(crate) const fn parse_mode(self) -> ParseMode {
match self {
Self::Fast | Self::Dfast | Self::Greedy => ParseMode::Greedy,
Self::Lazy => ParseMode::Lazy,
Self::Btlazy2 => ParseMode::Lazy2,
Self::BtOpt | Self::BtUltra | Self::BtUltra2 => ParseMode::Optimal,
}
}
}
#[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::<Btlazy2>(StrategyTag::Btlazy2);
assert_strategy_matches_tag::<BtOpt>(StrategyTag::BtOpt);
assert_strategy_matches_tag::<BtUltra>(StrategyTag::BtUltra);
assert_strategy_matches_tag::<BtUltra2>(StrategyTag::BtUltra2);
}
#[test]
fn btlazy2_tag_bridge_contract() {
assert_eq!(StrategyTag::Btlazy2.backend(), BackendTag::HashChain);
assert_eq!(StrategyTag::Btlazy2.search(), SearchMethod::BinaryTree);
assert_eq!(StrategyTag::Btlazy2.parse_mode(), ParseMode::Lazy2);
assert_eq!(Btlazy2::MAX_CHAIN_DEPTH, 64);
assert_eq!(Btlazy2::SUFFICIENT_MATCH_LEN, usize::MAX);
}
#[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_default_table() {
assert_eq!(StrategyTag::for_level(1), StrategyTag::Fast);
assert_eq!(StrategyTag::for_level(2), StrategyTag::Fast);
assert_eq!(StrategyTag::for_level(3), StrategyTag::Dfast);
assert_eq!(StrategyTag::for_level(4), StrategyTag::Dfast);
assert_eq!(StrategyTag::for_level(5), StrategyTag::Greedy);
assert_eq!(StrategyTag::for_level(9), StrategyTag::Lazy);
assert_eq!(StrategyTag::for_level(12), StrategyTag::Lazy);
assert_eq!(StrategyTag::for_level(13), StrategyTag::Btlazy2);
assert_eq!(StrategyTag::for_level(15), StrategyTag::Btlazy2);
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::BtUltra2);
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!(Btlazy2::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!(!Btlazy2::USE_HASH3);
assert!(!Btlazy2::TWO_PASS_SEED);
assert!(!BtOpt::USE_HASH3);
assert!(BtUltra::USE_HASH3);
assert!(BtUltra2::USE_HASH3);
assert!(!BtOpt::TWO_PASS_SEED);
assert!(!BtUltra::TWO_PASS_SEED);
assert!(BtUltra2::TWO_PASS_SEED);
};
const _COST_MODEL_LAYOUT: () = {
assert!(!Lazy::ACCURATE_PRICE && Lazy::FAVOR_SMALL_OFFSETS);
assert!(!Btlazy2::ACCURATE_PRICE && Btlazy2::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!(Btlazy2::MAX_CHAIN_DEPTH == 64);
assert!(Btlazy2::SUFFICIENT_MATCH_LEN == usize::MAX);
assert!(BtOpt::MAX_CHAIN_DEPTH == 32);
assert!(BtUltra::MAX_CHAIN_DEPTH == 64);
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);
};
}