use crate::{
bit_helper::bit_length,
complevel_estimator::estimate_preflate_comp_level,
preflate_constants,
preflate_parse_config::*,
preflate_stream_info::{extract_preflate_info, PreflateStreamInfo},
preflate_token::PreflateTokenBlock,
statistical_codec::{PredictionDecoder, PredictionEncoder},
};
#[derive(Debug, Copy, Clone)]
pub enum PreflateStrategy {
Default,
RleOnly,
HuffOnly,
Store,
}
#[derive(Debug, Copy, Clone)]
pub enum PreflateHuffStrategy {
Dynamic,
Mixed,
Static,
}
#[derive(Debug, Copy, Clone)]
pub struct PreflateParameters {
pub strategy: PreflateStrategy,
pub huff_strategy: PreflateHuffStrategy,
pub zlib_compatible: bool,
pub window_bits: u32,
pub mem_level: u32,
pub far_len3_matches_detected: bool,
pub very_far_matches_detected: bool,
pub matches_to_start_detected: bool,
pub log2_of_max_chain_depth_m1: u32,
pub is_fast_compressor: bool,
pub good_length: u32,
pub max_lazy: u32,
pub nice_length: u32,
pub max_chain: u32,
}
impl PreflateParameters {
pub fn read<D: PredictionDecoder>(decoder: &mut D) -> Self {
let strategy = decoder.decode_value(4);
let huff_strategy = decoder.decode_value(4);
let zlib_compatible = decoder.decode_value(1) != 0;
let window_bits = decoder.decode_value(8);
let mem_level = decoder.decode_value(8);
let far_len3_matches_detected = decoder.decode_value(1) != 0;
let very_far_matches_detected = decoder.decode_value(1) != 0;
let matches_to_start_detected = decoder.decode_value(1) != 0;
let log2_of_max_chain_depth_m1 = decoder.decode_value(16);
let is_fast_compressor = decoder.decode_value(1) != 0;
let good_length = decoder.decode_value(16);
let max_lazy = decoder.decode_value(16);
let nice_length = decoder.decode_value(16);
let max_chain = decoder.decode_value(16);
PreflateParameters {
strategy: match strategy {
0 => PreflateStrategy::Default,
1 => PreflateStrategy::RleOnly,
2 => PreflateStrategy::HuffOnly,
3 => PreflateStrategy::Store,
_ => panic!("invalid strategy"),
},
huff_strategy: match huff_strategy {
0 => PreflateHuffStrategy::Dynamic,
1 => PreflateHuffStrategy::Mixed,
2 => PreflateHuffStrategy::Static,
_ => panic!("invalid huff strategy"),
},
zlib_compatible,
window_bits: window_bits.into(),
mem_level: mem_level.into(),
far_len3_matches_detected,
very_far_matches_detected,
matches_to_start_detected,
log2_of_max_chain_depth_m1: log2_of_max_chain_depth_m1.into(),
is_fast_compressor,
good_length: good_length.into(),
max_lazy: max_lazy.into(),
nice_length: nice_length.into(),
max_chain: max_chain.into(),
}
}
pub fn write<E: PredictionEncoder>(&self, encoder: &mut E) {
encoder.encode_value(self.strategy as u16, 4);
encoder.encode_value(self.huff_strategy as u16, 4);
encoder.encode_value(self.zlib_compatible as u16, 1);
encoder.encode_value(self.window_bits as u16, 8);
encoder.encode_value(self.mem_level as u16, 8);
encoder.encode_value(self.far_len3_matches_detected as u16, 1);
encoder.encode_value(self.very_far_matches_detected as u16, 1);
encoder.encode_value(self.matches_to_start_detected as u16, 1);
encoder.encode_value(self.log2_of_max_chain_depth_m1 as u16, 16);
encoder.encode_value(self.is_fast_compressor as u16, 1);
encoder.encode_value(self.good_length as u16, 16);
encoder.encode_value(self.max_lazy as u16, 16);
encoder.encode_value(self.nice_length as u16, 16);
encoder.encode_value(self.max_chain as u16, 16);
}
}
fn estimate_preflate_mem_level(max_block_size_: u32) -> u32 {
let mut max_block_size = max_block_size_;
let mut mbits = 0;
while max_block_size > 0 {
mbits += 1;
max_block_size >>= 1;
}
mbits = std::cmp::min(std::cmp::max(mbits, 7), 15);
mbits - 6
}
pub fn estimate_preflate_window_bits(max_dist_: u32) -> u32 {
let mut max_dist = max_dist_;
max_dist += preflate_constants::MIN_LOOKAHEAD;
let wbits = bit_length(max_dist - 1);
std::cmp::min(std::cmp::max(wbits, 9), 15)
}
pub fn estimate_preflate_strategy(info: &PreflateStreamInfo) -> PreflateStrategy {
if info.count_stored_blocks == info.count_blocks {
return PreflateStrategy::Store;
}
if info.count_huff_blocks == info.count_blocks {
return PreflateStrategy::HuffOnly;
}
if info.count_rle_blocks == info.count_blocks {
return PreflateStrategy::RleOnly;
}
PreflateStrategy::Default
}
pub fn estimate_preflate_huff_strategy(info: &PreflateStreamInfo) -> PreflateHuffStrategy {
if info.count_static_huff_tree_blocks == info.count_blocks {
return PreflateHuffStrategy::Static;
}
if info.count_static_huff_tree_blocks == 0 {
return PreflateHuffStrategy::Dynamic;
}
PreflateHuffStrategy::Mixed
}
pub fn estimate_preflate_parameters(
unpacked_output: &[u8],
blocks: &Vec<PreflateTokenBlock>,
) -> PreflateParameters {
let info = extract_preflate_info(blocks);
let window_bits = estimate_preflate_window_bits(info.max_dist);
let mem_level = estimate_preflate_mem_level(info.max_tokens_per_block);
let cl = estimate_preflate_comp_level(window_bits, mem_level, unpacked_output, blocks, false);
let config;
let comp_level = cl.recommended_compression_level;
let is_fast_compressor;
if (1..=3).contains(&comp_level) {
is_fast_compressor = true;
config = &FAST_PREFLATE_PARSER_SETTINGS[(comp_level - 1) as usize]
} else {
is_fast_compressor = false;
config = &SLOW_PREFLATE_PARSER_SETTINGS[if (4..=9).contains(&comp_level) {
(comp_level - 4) as usize
} else {
5
}]
}
PreflateParameters {
window_bits,
mem_level,
strategy: estimate_preflate_strategy(&info),
huff_strategy: estimate_preflate_huff_strategy(&info),
zlib_compatible: cl.zlib_compatible,
far_len3_matches_detected: cl.far_len_3_matches,
very_far_matches_detected: cl.very_far_matches,
matches_to_start_detected: cl.match_to_start,
log2_of_max_chain_depth_m1: if cl.max_chain_depth == 0 {
0
} else {
bit_length(cl.max_chain_depth as u32 - 1)
},
is_fast_compressor,
good_length: config.good_length,
max_lazy: config.max_lazy,
nice_length: config.nice_length,
max_chain: config.max_chain,
}
}