use core::fmt;
use thiserror::Error;
use super::core::params::{
CHAIN_LOG_BOUNDS, HASH_LOG_BOUNDS, LDM_BUCKET_SIZE_LOG_BOUNDS, LDM_HASH_LOG_BOUNDS,
LDM_HASH_RATE_LOG_BOUNDS, LDM_MIN_MATCH_BOUNDS, MIN_MATCH_BOUNDS, SEARCH_LOG_BOUNDS,
TARGET_LENGTH_BOUNDS, WINDOW_LOG_BOUNDS,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Level(i32);
impl Level {
pub const MIN: Self = Self(super::core::params::MIN_CLEVEL);
pub const MAX: Self = Self(super::core::params::MAX_CLEVEL);
pub const DEFAULT: Self = Self(super::core::params::DEFAULT_CLEVEL);
#[must_use]
pub const fn get(self) -> i32 {
self.0
}
}
impl Default for Level {
fn default() -> Self {
Self::DEFAULT
}
}
impl TryFrom<i32> for Level {
type Error = ConfigError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value == 0 {
return Ok(Self::DEFAULT);
}
if !(Self::MIN.0..=Self::MAX.0).contains(&value) {
return Err(ConfigError::OutOfRange {
parameter: Parameter::Level,
value: i64::from(value),
min: i64::from(Self::MIN.0),
max: i64::from(Self::MAX.0),
});
}
Ok(Self(value))
}
}
impl From<Level> for i32 {
fn from(level: Level) -> Self {
level.0
}
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[non_exhaustive]
pub enum Strategy {
Fast,
DoubleFast,
Greedy,
Lazy,
Lazy2,
BtLazy2,
BtOpt,
BtUltra,
BtUltra2,
}
impl Strategy {
pub(crate) const fn to_core(self) -> super::core::params::Strategy {
use super::core::params::Strategy as Core;
match self {
Self::Fast => Core::Fast,
Self::DoubleFast => Core::DFast,
Self::Greedy => Core::Greedy,
Self::Lazy => Core::Lazy,
Self::Lazy2 => Core::Lazy2,
Self::BtLazy2 => Core::BtLazy2,
Self::BtOpt => Core::BtOpt,
Self::BtUltra => Core::BtUltra,
Self::BtUltra2 => Core::BtUltra2,
}
}
}
impl TryFrom<u8> for Strategy {
type Error = ConfigError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
1 => Self::Fast,
2 => Self::DoubleFast,
3 => Self::Greedy,
4 => Self::Lazy,
5 => Self::Lazy2,
6 => Self::BtLazy2,
7 => Self::BtOpt,
8 => Self::BtUltra,
9 => Self::BtUltra2,
_ => {
return Err(ConfigError::OutOfRange {
parameter: Parameter::Strategy,
value: i64::from(value),
min: 1,
max: 9,
});
}
})
}
}
impl From<Strategy> for u8 {
fn from(strategy: Strategy) -> Self {
strategy.to_core().value() as u8
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
pub struct CompressionParams {
window_log: Option<u32>,
hash_log: Option<u32>,
chain_log: Option<u32>,
search_log: Option<u32>,
min_match: Option<u32>,
target_length: Option<u32>,
strategy: Option<Strategy>,
}
macro_rules! param_accessors {
($($(#[$doc:meta])* $field:ident, $with:ident, $range:literal;)*) => {$(
$(#[$doc])*
#[doc = concat!("\n\nValid values: ", $range, ".")]
#[must_use]
pub const fn $with(mut self, value: u32) -> Self {
self.$field = Some(value);
self
}
#[doc = concat!("The value set by [`Self::", stringify!($with), "`], if any.")]
#[must_use]
pub const fn $field(&self) -> Option<u32> {
self.$field
}
)*};
}
impl CompressionParams {
param_accessors! {
window_log, with_window_log, "`10..=31`";
hash_log, with_hash_log, "`6..=30`";
chain_log, with_chain_log, "`6..=30`";
search_log, with_search_log, "`1..=30`";
min_match, with_min_match, "`3..=7`";
target_length, with_target_length, "`0..=131072`";
}
#[must_use]
pub const fn with_strategy(mut self, strategy: Strategy) -> Self {
self.strategy = Some(strategy);
self
}
#[must_use]
pub const fn strategy(&self) -> Option<Strategy> {
self.strategy
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
pub struct LdmParams {
hash_log: Option<u32>,
min_match: Option<u32>,
bucket_size_log: Option<u32>,
hash_rate_log: Option<u32>,
}
impl LdmParams {
param_accessors! {
hash_log, with_hash_log, "`6..=30`";
min_match, with_min_match, "`4..=4096`";
bucket_size_log, with_bucket_size_log, "`1..=8`";
hash_rate_log, with_hash_rate_log, "`0..=25`";
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
pub enum LongDistanceMatching {
#[default]
Auto,
Disabled,
Enabled(LdmParams),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct EncoderConfig {
level: Level,
params: CompressionParams,
checksum: bool,
content_size: bool,
long_distance: LongDistanceMatching,
size_hint: Option<u32>,
}
impl Default for EncoderConfig {
fn default() -> Self {
Self::new()
}
}
impl EncoderConfig {
#[must_use]
pub const fn new() -> Self {
Self {
level: Level::DEFAULT,
params: CompressionParams {
window_log: None,
hash_log: None,
chain_log: None,
search_log: None,
min_match: None,
target_length: None,
strategy: None,
},
checksum: false,
content_size: true,
long_distance: LongDistanceMatching::Auto,
size_hint: None,
}
}
#[must_use]
pub const fn with_level(mut self, level: Level) -> Self {
self.level = level;
self
}
#[must_use]
pub const fn level(&self) -> Level {
self.level
}
#[must_use]
pub const fn with_params(mut self, params: CompressionParams) -> Self {
self.params = params;
self
}
#[must_use]
pub const fn params(&self) -> CompressionParams {
self.params
}
#[must_use]
pub const fn with_checksum(mut self, checksum: bool) -> Self {
self.checksum = checksum;
self
}
#[must_use]
pub const fn checksum(&self) -> bool {
self.checksum
}
#[must_use]
pub const fn with_content_size(mut self, content_size: bool) -> Self {
self.content_size = content_size;
self
}
#[must_use]
pub const fn content_size(&self) -> bool {
self.content_size
}
#[must_use]
pub const fn with_long_distance_matching(mut self, ldm: LongDistanceMatching) -> Self {
self.long_distance = ldm;
self
}
#[must_use]
pub const fn long_distance_matching(&self) -> LongDistanceMatching {
self.long_distance
}
#[must_use]
pub const fn with_size_hint(mut self, size_hint: Option<u32>) -> Self {
self.size_hint = size_hint;
self
}
#[must_use]
pub const fn size_hint(&self) -> Option<u32> {
self.size_hint
}
pub(crate) fn validate(&self) -> Result<(), ConfigError> {
let p = &self.params;
check(Parameter::WindowLog, p.window_log, WINDOW_LOG_BOUNDS)?;
check(Parameter::HashLog, p.hash_log, HASH_LOG_BOUNDS)?;
check(Parameter::ChainLog, p.chain_log, CHAIN_LOG_BOUNDS)?;
check(Parameter::SearchLog, p.search_log, SEARCH_LOG_BOUNDS)?;
check(Parameter::MinMatch, p.min_match, MIN_MATCH_BOUNDS)?;
check(
Parameter::TargetLength,
p.target_length,
TARGET_LENGTH_BOUNDS,
)?;
if let LongDistanceMatching::Enabled(ldm) = self.long_distance {
check(Parameter::LdmHashLog, ldm.hash_log, LDM_HASH_LOG_BOUNDS)?;
check(Parameter::LdmMinMatch, ldm.min_match, LDM_MIN_MATCH_BOUNDS)?;
check(
Parameter::LdmBucketSizeLog,
ldm.bucket_size_log,
LDM_BUCKET_SIZE_LOG_BOUNDS,
)?;
check(
Parameter::LdmHashRateLog,
ldm.hash_rate_log,
LDM_HASH_RATE_LOG_BOUNDS,
)?;
}
if let Some(hint) = self.size_hint
&& hint > i32::MAX as u32
{
return Err(ConfigError::OutOfRange {
parameter: Parameter::SizeHint,
value: i64::from(hint),
min: 0,
max: i64::from(i32::MAX),
});
}
Ok(())
}
}
fn check(
parameter: Parameter,
value: Option<u32>,
bounds: super::core::params::Bounds,
) -> Result<(), ConfigError> {
match value {
Some(value) if !bounds.contains(i64::from(value)) => Err(ConfigError::OutOfRange {
parameter,
value: i64::from(value),
min: bounds.lower,
max: bounds.upper,
}),
_ => Ok(()),
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[non_exhaustive]
pub enum Parameter {
Level,
Strategy,
WindowLog,
HashLog,
ChainLog,
SearchLog,
MinMatch,
TargetLength,
LdmHashLog,
LdmMinMatch,
LdmBucketSizeLog,
LdmHashRateLog,
SizeHint,
SegmentSize,
TaskCount,
}
impl fmt::Display for Parameter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Level => "compression level",
Self::Strategy => "strategy",
Self::WindowLog => "window log",
Self::HashLog => "hash log",
Self::ChainLog => "chain log",
Self::SearchLog => "search log",
Self::MinMatch => "minimum match",
Self::TargetLength => "target length",
Self::LdmHashLog => "LDM hash log",
Self::LdmMinMatch => "LDM minimum match",
Self::LdmBucketSizeLog => "LDM bucket size log",
Self::LdmHashRateLog => "LDM hash rate log",
Self::SizeHint => "size hint",
Self::SegmentSize => "segment size",
Self::TaskCount => "task count",
})
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Error)]
#[non_exhaustive]
pub enum ConfigError {
#[error("{parameter} {value} is outside {min}..={max}")]
OutOfRange {
parameter: Parameter,
value: i64,
min: i64,
max: i64,
},
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash, Error)]
#[error("the compressed-size bound does not fit in usize")]
pub struct SizeOverflow;