use crate::{
blocks::block::BlockType,
common::MAX_BLOCK_SIZE,
encoding::{
CompressionLevel, Matcher,
block_header::BlockHeader,
blocks::{compress_block, compress_block_with_post_split},
frame_compressor::CompressState,
incompressible::{
block_looks_incompressible, block_looks_incompressible_dict,
block_looks_incompressible_strict, compression_level_allows_raw_fast_path,
},
match_generator::MatchGeneratorDriver,
},
};
use alloc::vec::Vec;
#[allow(clippy::too_many_arguments)]
#[inline]
pub(crate) fn compress_block_encoded<M: Matcher>(
state: &mut CompressState<M>,
compression_level: CompressionLevel,
last_block: bool,
uncompressed_data: Vec<u8>,
output: &mut Vec<u8>,
dict_active: bool,
#[cfg(feature = "lsm")] block_decompressed_sizes: Option<&mut Vec<u32>>,
#[cfg(all(feature = "lsm", feature = "hash"))] block_checksums: Option<&mut Vec<u32>>,
) -> BlockType {
let block_size = uncompressed_data.len() as u32;
if uncompressed_data.iter().all(|x| uncompressed_data[0].eq(x)) {
let rle_byte = uncompressed_data[0];
#[cfg(feature = "lsm")]
if let Some(sink) = block_decompressed_sizes {
sink.push(block_size);
}
#[cfg(all(feature = "lsm", feature = "hash"))]
if let Some(sink) = block_checksums {
sink.push(crate::encoding::frame_compressor::xxh64_block_low32(
&uncompressed_data,
));
}
state.matcher.commit_space(uncompressed_data);
state.matcher.skip_matching_with_hint(Some(false));
let header = BlockHeader {
last_block,
block_type: BlockType::RLE,
block_size,
};
header.serialize(output);
output.push(rle_byte);
BlockType::RLE
} else if !(dict_active && state.matcher.block_samples_match_dict(&uncompressed_data))
&& should_emit_raw_fast_path(
compression_level,
state.matcher.window_size(),
&uncompressed_data,
dict_active,
)
{
#[cfg(feature = "lsm")]
if let Some(sink) = block_decompressed_sizes {
sink.push(block_size);
}
#[cfg(all(feature = "lsm", feature = "hash"))]
if let Some(sink) = block_checksums {
sink.push(crate::encoding::frame_compressor::xxh64_block_low32(
&uncompressed_data,
));
}
state.matcher.commit_space(uncompressed_data);
state.matcher.skip_matching_with_hint(Some(true));
let header = BlockHeader {
last_block,
block_type: BlockType::Raw,
block_size,
};
header.serialize(output);
output.extend_from_slice(state.matcher.get_last_space());
BlockType::Raw
} else {
let uncompressed_len = uncompressed_data.len();
state.matcher.commit_space(uncompressed_data);
if matches!(compression_level, CompressionLevel::Level(16..=22))
&& state.matcher.window_size() >= (1 << 17)
{
#[cfg(all(feature = "lsm", feature = "hash"))]
compress_block_with_post_split(
state,
last_block,
output,
block_decompressed_sizes,
block_checksums,
);
#[cfg(all(feature = "lsm", not(feature = "hash")))]
compress_block_with_post_split(state, last_block, output, block_decompressed_sizes);
#[cfg(not(feature = "lsm"))]
compress_block_with_post_split(state, last_block, output);
return BlockType::Compressed;
}
#[cfg(feature = "lsm")]
if let Some(sink) = block_decompressed_sizes {
sink.push(block_size);
}
#[cfg(all(feature = "lsm", feature = "hash"))]
if let Some(sink) = block_checksums {
let space = state.matcher.get_last_space();
let start = space.len() - uncompressed_len;
sink.push(crate::encoding::frame_compressor::xxh64_block_low32(
&space[start..],
));
}
#[cfg(not(all(feature = "lsm", feature = "hash")))]
let _ = uncompressed_len;
let saved_offset_hist = state.offset_hist;
let mut saved_huff_table = core::mem::take(&mut state.block_scratch.huff_rollback);
saved_huff_table.clone_from(&state.last_huff_table);
let saved_ll_previous = state.fse_tables.ll_previous.clone();
let saved_ml_previous = state.fse_tables.ml_previous.clone();
let saved_of_previous = state.fse_tables.of_previous.clone();
let hdr_off = output.len();
output.extend_from_slice(&[0u8; 3]);
let payload_off = output.len();
compress_block(state, output);
let payload_len = output.len() - payload_off;
if payload_len >= MAX_BLOCK_SIZE as usize || payload_len >= block_size as usize {
output.truncate(hdr_off);
state.offset_hist = saved_offset_hist;
core::mem::swap(&mut state.last_huff_table, &mut saved_huff_table);
state.fse_tables.ll_previous = saved_ll_previous;
state.fse_tables.ml_previous = saved_ml_previous;
state.fse_tables.of_previous = saved_of_previous;
state.block_scratch.huff_rollback = saved_huff_table;
let header = BlockHeader {
last_block,
block_type: BlockType::Raw,
block_size,
};
header.serialize(output);
output.extend_from_slice(state.matcher.get_last_space());
BlockType::Raw
} else {
state.block_scratch.huff_rollback = saved_huff_table;
let header = BlockHeader {
last_block,
block_type: BlockType::Compressed,
block_size: payload_len as u32,
};
output[hdr_off..hdr_off + 3].copy_from_slice(&header.to_le_bytes());
BlockType::Compressed
}
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn compress_block_encoded_borrowed(
state: &mut CompressState<MatchGeneratorDriver>,
compression_level: CompressionLevel,
last_block: bool,
block: &[u8],
block_start: usize,
block_end: usize,
dict_active: bool,
output: &mut Vec<u8>,
#[cfg(feature = "lsm")] block_decompressed_sizes: Option<&mut Vec<u32>>,
#[cfg(all(feature = "lsm", feature = "hash"))] block_checksums: Option<&mut Vec<u32>>,
) -> BlockType {
debug_assert!(
state.matcher.borrowed_supported(),
"borrowed one-shot path reached for an unsupported backend/search config",
);
let block_size = block.len() as u32;
if !block.is_empty() && block.iter().all(|x| block[0].eq(x)) {
let rle_byte = block[0];
#[cfg(feature = "lsm")]
if let Some(sink) = block_decompressed_sizes {
sink.push(block_size);
}
#[cfg(all(feature = "lsm", feature = "hash"))]
if let Some(sink) = block_checksums {
sink.push(crate::encoding::frame_compressor::xxh64_block_low32(block));
}
state.matcher.set_borrowed_block(block_start, block_end);
state.matcher.skip_matching_with_hint(Some(false));
let header = BlockHeader {
last_block,
block_type: BlockType::RLE,
block_size,
};
header.serialize(output);
output.push(rle_byte);
BlockType::RLE
} else if !(dict_active && state.matcher.block_samples_match_dict(block))
&& should_emit_raw_fast_path(
compression_level,
state.matcher.window_size(),
block,
dict_active,
)
{
#[cfg(feature = "lsm")]
if let Some(sink) = block_decompressed_sizes {
sink.push(block_size);
}
#[cfg(all(feature = "lsm", feature = "hash"))]
if let Some(sink) = block_checksums {
sink.push(crate::encoding::frame_compressor::xxh64_block_low32(block));
}
state.matcher.set_borrowed_block(block_start, block_end);
state.matcher.skip_matching_with_hint(Some(true));
let header = BlockHeader {
last_block,
block_type: BlockType::Raw,
block_size,
};
header.serialize(output);
output.extend_from_slice(block);
BlockType::Raw
} else {
state.matcher.set_borrowed_block(block_start, block_end);
#[cfg(feature = "lsm")]
if let Some(sink) = block_decompressed_sizes {
sink.push(block_size);
}
#[cfg(all(feature = "lsm", feature = "hash"))]
if let Some(sink) = block_checksums {
sink.push(crate::encoding::frame_compressor::xxh64_block_low32(block));
}
let saved_offset_hist = state.offset_hist;
let mut saved_huff_table = core::mem::take(&mut state.block_scratch.huff_rollback);
saved_huff_table.clone_from(&state.last_huff_table);
let saved_ll_previous = state.fse_tables.ll_previous.clone();
let saved_ml_previous = state.fse_tables.ml_previous.clone();
let saved_of_previous = state.fse_tables.of_previous.clone();
let hdr_off = output.len();
output.extend_from_slice(&[0u8; 3]);
let payload_off = output.len();
compress_block(state, output);
let payload_len = output.len() - payload_off;
if payload_len >= MAX_BLOCK_SIZE as usize || payload_len >= block_size as usize {
output.truncate(hdr_off);
state.offset_hist = saved_offset_hist;
core::mem::swap(&mut state.last_huff_table, &mut saved_huff_table);
state.fse_tables.ll_previous = saved_ll_previous;
state.fse_tables.ml_previous = saved_ml_previous;
state.fse_tables.of_previous = saved_of_previous;
state.block_scratch.huff_rollback = saved_huff_table;
let header = BlockHeader {
last_block,
block_type: BlockType::Raw,
block_size,
};
header.serialize(output);
output.extend_from_slice(block);
BlockType::Raw
} else {
state.block_scratch.huff_rollback = saved_huff_table;
let header = BlockHeader {
last_block,
block_type: BlockType::Compressed,
block_size: payload_len as u32,
};
output[hdr_off..hdr_off + 3].copy_from_slice(&header.to_le_bytes());
BlockType::Compressed
}
}
}
#[inline]
fn should_emit_raw_fast_path(
level: CompressionLevel,
window_size: u64,
block: &[u8],
has_dict: bool,
) -> bool {
if !compression_level_allows_raw_fast_path(level, window_size) {
return false;
}
if has_dict {
return block_looks_incompressible_dict(block);
}
if matches!(level, CompressionLevel::Best) {
return block_looks_incompressible_strict(block);
}
block_looks_incompressible(block)
}
#[cfg(test)]
mod tests;