use super::super::blocks::sequence_section::ModeType;
use super::super::blocks::sequence_section::Sequence;
use super::super::blocks::sequence_section::SequencesHeader;
use super::scratch::FSEScratch;
use crate::bit_io::BitReaderReversed;
use crate::blocks::sequence_section::{
MAX_LITERAL_LENGTH_CODE, MAX_MATCH_LENGTH_CODE, MAX_OFFSET_CODE,
};
use crate::common::MAX_BLOCK_SIZE;
use crate::decoding::errors::{DecodeSequenceError, DecompressBlockError, ExecuteSequencesError};
use crate::decoding::sequence_execution::do_offset_history;
use crate::fse::SeqFSEDecoder;
pub(crate) const ADVANCE: usize = 8;
pub(crate) const ADVANCE_MASK: usize = ADVANCE - 1;
const _: () = assert!(
ADVANCE.is_power_of_two(),
"ADVANCE must be a power of two; ring indexing uses `i & (ADVANCE - 1)` as `i % ADVANCE`"
);
#[inline]
pub(crate) fn compute_use_long_pipeline(
num_sequences: usize,
ddict_is_cold: bool,
total_history: usize,
offsets_long_share: u32,
) -> bool {
#[cfg(target_pointer_width = "64")]
const MIN_LONG_OFFSET_SHARE: u32 = 7;
#[cfg(not(target_pointer_width = "64"))]
const MIN_LONG_OFFSET_SHARE: u32 = 20;
const HISTORY_THRESHOLD_FOR_PREFETCH: usize = 1 << 24;
num_sequences >= ADVANCE * 2
&& (ddict_is_cold
|| (total_history > HISTORY_THRESHOLD_FOR_PREFETCH
&& offsets_long_share >= MIN_LONG_OFFSET_SHARE))
}
pub(crate) struct SeqStreamSetup<'src, 'fse, K: crate::cpu_kernel::CpuKernel> {
pub(crate) br: BitReaderReversed<'src, K>,
pub(crate) ll_dec: SeqFSEDecoder<'fse>,
pub(crate) ml_dec: SeqFSEDecoder<'fse>,
pub(crate) of_dec: SeqFSEDecoder<'fse>,
pub(crate) max_update_bits: u8,
pub(crate) old_buffer_size: usize,
pub(crate) num_sequences: usize,
pub(crate) use_long_pipeline: bool,
}
pub(crate) fn init_sequence_stream<'src, 'fse, B, K>(
section: &SequencesHeader,
source: &'src [u8],
fse: &'fse mut FSEScratch,
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&'fse crate::decoding::dictionary::Dictionary>,
) -> Result<SeqStreamSetup<'src, 'fse, K>, DecompressBlockError>
where
B: super::buffer_backend::BufferBackend,
K: crate::cpu_kernel::CpuKernel,
{
let ddict_is_cold = fse.ddict_is_cold;
fse.ddict_is_cold = false;
let bytes_read = maybe_update_fse_tables(section, source, fse)?;
vprintln!("Updating tables used {} bytes", bytes_read);
let bit_stream = &source[bytes_read..];
let mut br = BitReaderReversed::<K>::new(bit_stream);
let mut skipped_bits = 0;
loop {
let val = br.get_bits(1);
skipped_bits += 1;
if val == 1 || skipped_bits > 8 {
break;
}
}
if skipped_bits > 8 {
return Err(DecodeSequenceError::ExtraPadding { skipped_bits }.into());
}
let mut ll_dec = SeqFSEDecoder::new(fse.ll_table(dict));
let mut ml_dec = SeqFSEDecoder::new(fse.ml_table(dict));
let mut of_dec = SeqFSEDecoder::new(fse.of_table(dict));
ll_dec
.init_state(&mut br)
.map_err(DecodeSequenceError::from)?;
of_dec
.init_state(&mut br)
.map_err(DecodeSequenceError::from)?;
ml_dec
.init_state(&mut br)
.map_err(DecodeSequenceError::from)?;
let max_update_bits = fse.ll_table(dict).accuracy_log
+ fse.ml_table(dict).accuracy_log
+ fse.of_table(dict).accuracy_log;
debug_assert!(
max_update_bits <= 56,
"sequence section update bits exceed 56-bit budget"
);
buffer.reserve_exact(MAX_BLOCK_SIZE as usize);
buffer.set_block_output_ceiling(MAX_BLOCK_SIZE as usize);
let old_buffer_size = buffer.len();
let num_sequences = section.num_sequences as usize;
let total_history = match buffer
.window_size
.checked_add(buffer.dict_content(dict).len())
{
Some(sum) => sum,
None => usize::MAX,
};
let use_long_pipeline = compute_use_long_pipeline(
num_sequences,
ddict_is_cold,
total_history,
fse.offsets_long_share,
);
Ok(SeqStreamSetup {
br,
ll_dec,
ml_dec,
of_dec,
max_update_bits,
old_buffer_size,
num_sequences,
use_long_pipeline,
})
}
pub fn decode_and_execute_sequences<'fse, B: super::buffer_backend::BufferBackend>(
section: &SequencesHeader,
source: &[u8],
fse: &'fse mut FSEScratch,
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
offset_hist: &mut [u32; 3],
literals_buffer: &[u8],
dict: Option<&'fse crate::decoding::dictionary::Dictionary>,
) -> Result<(), DecompressBlockError> {
#[cfg(all(target_arch = "aarch64", feature = "kernel_neon"))]
use crate::cpu_kernel::NeonKernel;
#[cfg(all(
target_arch = "aarch64",
feature = "kernel_sve",
any(feature = "std", target_feature = "sve"),
))]
use crate::cpu_kernel::SveKernel;
use crate::cpu_kernel::{CpuKernelTag, detect_cpu_kernel};
match detect_cpu_kernel() {
CpuKernelTag::Scalar => {
super::seq_decoder_scalar::decode_and_execute_sequences_scalar::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
dict,
)
}
#[cfg(all(target_arch = "x86_64", feature = "kernel_sse2"))]
CpuKernelTag::Sse2 => {
super::seq_decoder_scalar::decode_and_execute_sequences_scalar::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
dict,
)
}
#[cfg(all(target_arch = "x86_64", feature = "kernel_bmi2"))]
CpuKernelTag::Bmi2 => {
unsafe {
super::seq_decoder_bmi2::decode_and_execute_sequences_bmi2::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
dict,
)
}
}
#[cfg(all(target_arch = "x86_64", feature = "kernel_avx2"))]
CpuKernelTag::Avx2 => {
unsafe {
super::seq_decoder_avx2::decode_and_execute_sequences_avx2::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
dict,
)
}
}
#[cfg(all(target_arch = "x86_64", feature = "kernel_vbmi2"))]
CpuKernelTag::Vbmi2 => {
unsafe {
super::seq_decoder_vbmi2::decode_and_execute_sequences_vbmi2::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
dict,
)
}
}
#[cfg(all(target_arch = "aarch64", feature = "kernel_neon"))]
CpuKernelTag::Neon => decode_and_execute_sequences_impl::<B, NeonKernel>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
dict,
),
#[cfg(all(
target_arch = "aarch64",
feature = "kernel_sve",
any(feature = "std", target_feature = "sve"),
))]
CpuKernelTag::Sve => decode_and_execute_sequences_impl::<B, SveKernel>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
dict,
),
}
}
#[allow(dead_code)]
pub(crate) fn decode_and_execute_sequences_impl<
'fse,
B: super::buffer_backend::BufferBackend,
K: crate::cpu_kernel::CpuKernel,
>(
section: &SequencesHeader,
source: &[u8],
fse: &'fse mut FSEScratch,
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
offset_hist: &mut [u32; 3],
literals_buffer: &[u8],
dict: Option<&'fse crate::decoding::dictionary::Dictionary>,
) -> Result<(), DecompressBlockError> {
let SeqStreamSetup {
mut br,
mut ll_dec,
mut ml_dec,
mut of_dec,
max_update_bits,
old_buffer_size,
num_sequences,
use_long_pipeline,
} = init_sequence_stream::<B, K>(section, source, fse, buffer, dict)?;
let literals_buffer_len = literals_buffer.len();
let mut lit_cur: usize = 0;
let mut seq_sum: u32 = 0;
let buffer_checkpoint = buffer.checkpoint();
let saved_offset_hist = *offset_hist;
if use_long_pipeline {
let pipeline_result = run_pipelined_sequence_loop(
&mut br,
&mut ll_dec,
&mut ml_dec,
&mut of_dec,
buffer,
dict,
offset_hist,
literals_buffer,
&mut lit_cur,
literals_buffer_len,
num_sequences,
old_buffer_size,
max_update_bits,
&mut seq_sum,
);
if let Err(e) = pipeline_result {
if buffer.try_restore_checkpoint(buffer_checkpoint) {
*offset_hist = saved_offset_hist;
}
return Err(e);
}
} else {
let mut shadow_hist = *offset_hist;
let mut fallback_err: Option<DecompressBlockError> = None;
for i in 0..num_sequences {
let seq = decode_one_sequence_inline(&mut ll_dec, &mut ml_dec, &mut of_dec, &mut br);
let resolved_offset = do_offset_history(seq.of, seq.ll, &mut shadow_hist);
if let Err(e) = execute_one_sequence_pipelined(
buffer,
dict,
literals_buffer,
&mut lit_cur,
literals_buffer_len,
seq,
resolved_offset,
) {
fallback_err = Some(e);
break;
}
seq_sum = seq_sum.wrapping_add(seq.ll).wrapping_add(seq.ml);
if i + 1 < num_sequences {
br.ensure_bits(max_update_bits);
ll_dec.update_state_fast(&mut br);
ml_dec.update_state_fast(&mut br);
of_dec.update_state_fast(&mut br);
}
}
if let Some(e) = fallback_err {
let _ = buffer.try_restore_checkpoint(buffer_checkpoint);
return Err(e);
}
*offset_hist = shadow_hist;
}
let remaining = br.bits_remaining();
if remaining != 0 {
if buffer.try_restore_checkpoint(buffer_checkpoint) {
*offset_hist = saved_offset_hist;
}
if remaining < 0 {
return Err(DecodeSequenceError::NotEnoughBytesForNumSequences.into());
}
return Err(DecodeSequenceError::ExtraBits {
bits_remaining: remaining,
}
.into());
}
if lit_cur < literals_buffer_len {
let rest = &literals_buffer[lit_cur..];
buffer.try_push(rest).map_err(ExecuteSequencesError::from)?;
seq_sum = seq_sum.wrapping_add(rest.len() as u32);
}
let diff = buffer.len() - old_buffer_size;
debug_assert_eq!(
seq_sum as usize, diff,
"seq_sum {seq_sum} != buffer growth {diff}"
);
Ok(())
}
#[allow(clippy::too_many_arguments, dead_code)]
fn run_pipelined_sequence_loop<
B: super::buffer_backend::BufferBackend,
K: crate::cpu_kernel::CpuKernel,
>(
br: &mut BitReaderReversed<'_, K>,
ll_dec: &mut SeqFSEDecoder<'_>,
ml_dec: &mut SeqFSEDecoder<'_>,
of_dec: &mut SeqFSEDecoder<'_>,
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&crate::decoding::dictionary::Dictionary>,
offset_hist: &mut [u32; 3],
literals_buffer: &[u8],
lit_cur: &mut usize,
literals_buffer_len: usize,
num_sequences: usize,
old_buffer_size: usize,
max_update_bits: u8,
seq_sum: &mut u32,
) -> Result<(), DecompressBlockError> {
let mut prefetch_pos: usize = old_buffer_size;
let mut shadow_hist: [u32; 3] = *offset_hist;
let mut ring: [ExecSeq; ADVANCE] = [ExecSeq {
ll: 0,
ml: 0,
actual_offset: 0,
}; ADVANCE];
for slot in ring.iter_mut() {
let seq = decode_one_sequence_inline(ll_dec, ml_dec, of_dec, br);
let actual_offset = do_offset_history(seq.of, seq.ll, &mut shadow_hist);
let match_start = prefetch_pos.wrapping_add(seq.ll as usize);
let source_idx = match_start.wrapping_sub(actual_offset as usize);
buffer.prefetch_lookahead_match_source(source_idx);
prefetch_pos = match_start.wrapping_add(seq.ml as usize);
*slot = ExecSeq {
ll: seq.ll,
ml: seq.ml,
actual_offset,
};
br.ensure_bits(max_update_bits);
ll_dec.update_state_fast(br);
ml_dec.update_state_fast(br);
of_dec.update_state_fast(br);
}
#[cfg(target_arch = "x86_64")]
unsafe {
core::arch::asm!(
".p2align 6",
"nop",
".p2align 5",
"nop",
".p2align 3",
options(nomem, nostack, preserves_flags)
);
}
for i in ADVANCE..num_sequences {
let seq = decode_one_sequence_inline(ll_dec, ml_dec, of_dec, br);
let actual_offset = do_offset_history(seq.of, seq.ll, &mut shadow_hist);
let match_start = prefetch_pos.wrapping_add(seq.ll as usize);
let source_idx = match_start.wrapping_sub(actual_offset as usize);
buffer.prefetch_lookahead_match_source(source_idx);
prefetch_pos = match_start.wrapping_add(seq.ml as usize);
let slot = i & ADVANCE_MASK;
let exec_seq = ring[slot];
ring[slot] = ExecSeq {
ll: seq.ll,
ml: seq.ml,
actual_offset,
};
execute_one_sequence_pipelined_resolved(
buffer,
dict,
literals_buffer,
lit_cur,
literals_buffer_len,
exec_seq,
)?;
*seq_sum = seq_sum.wrapping_add(exec_seq.ll).wrapping_add(exec_seq.ml);
if i + 1 < num_sequences {
br.ensure_bits(max_update_bits);
ll_dec.update_state_fast(br);
ml_dec.update_state_fast(br);
of_dec.update_state_fast(br);
}
}
for k in 0..ADVANCE {
let slot = (num_sequences + k) & ADVANCE_MASK;
let exec_seq = ring[slot];
execute_one_sequence_pipelined_resolved(
buffer,
dict,
literals_buffer,
lit_cur,
literals_buffer_len,
exec_seq,
)?;
*seq_sum = seq_sum.wrapping_add(exec_seq.ll).wrapping_add(exec_seq.ml);
}
*offset_hist = shadow_hist;
Ok(())
}
#[derive(Copy, Clone)]
pub(crate) struct ExecSeq {
pub(crate) ll: u32,
pub(crate) ml: u32,
pub(crate) actual_offset: u32,
}
#[inline(always)]
#[allow(dead_code)] pub(crate) fn execute_one_sequence_pipelined_resolved<B: super::buffer_backend::BufferBackend>(
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&crate::decoding::dictionary::Dictionary>,
literals: &[u8],
lit_cur: &mut usize,
lit_len: usize,
exec_seq: ExecSeq,
) -> Result<(), DecompressBlockError> {
execute_one_sequence_pipelined(
buffer,
dict,
literals,
lit_cur,
lit_len,
Sequence {
ll: exec_seq.ll,
ml: exec_seq.ml,
of: 0,
},
exec_seq.actual_offset,
)
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "bmi2")]
#[inline]
#[allow(dead_code)] pub(crate) unsafe fn execute_one_sequence_pipelined_bmi2<
B: super::buffer_backend::BufferBackend,
>(
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&crate::decoding::dictionary::Dictionary>,
literals: &[u8],
lit_cur: &mut usize,
lit_len: usize,
seq: Sequence,
resolved_offset: u32,
) -> Result<(), DecompressBlockError> {
execute_one_sequence_pipelined(
buffer,
dict,
literals,
lit_cur,
lit_len,
seq,
resolved_offset,
)
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "bmi2")]
#[inline]
#[allow(dead_code)] pub(crate) unsafe fn execute_one_sequence_pipelined_resolved_bmi2<
B: super::buffer_backend::BufferBackend,
>(
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&crate::decoding::dictionary::Dictionary>,
literals: &[u8],
lit_cur: &mut usize,
lit_len: usize,
exec_seq: ExecSeq,
) -> Result<(), DecompressBlockError> {
execute_one_sequence_pipelined_resolved(buffer, dict, literals, lit_cur, lit_len, exec_seq)
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "bmi2,avx2,avx512vbmi2,avx512f,avx512vl,avx512bw")]
#[inline]
#[allow(dead_code)] pub(crate) unsafe fn execute_one_sequence_pipelined_vbmi2<
B: super::buffer_backend::BufferBackend,
>(
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&crate::decoding::dictionary::Dictionary>,
literals: &[u8],
lit_cur: &mut usize,
lit_len: usize,
seq: Sequence,
resolved_offset: u32,
) -> Result<(), DecompressBlockError> {
unsafe {
execute_one_sequence_pipelined_avx2(
buffer,
dict,
literals,
lit_cur,
lit_len,
seq,
resolved_offset,
)
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "bmi2,avx2,avx512vbmi2,avx512f,avx512vl,avx512bw")]
#[inline]
#[allow(dead_code)] pub(crate) unsafe fn execute_one_sequence_pipelined_resolved_vbmi2<
B: super::buffer_backend::BufferBackend,
>(
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&crate::decoding::dictionary::Dictionary>,
literals: &[u8],
lit_cur: &mut usize,
lit_len: usize,
exec_seq: ExecSeq,
) -> Result<(), DecompressBlockError> {
unsafe {
execute_one_sequence_pipelined_resolved_avx2(
buffer, dict, literals, lit_cur, lit_len, exec_seq,
)
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2,bmi2")]
#[inline]
#[allow(dead_code)] pub(crate) unsafe fn execute_one_sequence_pipelined_resolved_avx2<
B: super::buffer_backend::BufferBackend,
>(
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&crate::decoding::dictionary::Dictionary>,
literals: &[u8],
lit_cur: &mut usize,
lit_len: usize,
exec_seq: ExecSeq,
) -> Result<(), DecompressBlockError> {
unsafe {
execute_one_sequence_pipelined_avx2(
buffer,
dict,
literals,
lit_cur,
lit_len,
Sequence {
ll: exec_seq.ll,
ml: exec_seq.ml,
of: 0,
},
exec_seq.actual_offset,
)
}
}
#[inline(always)]
#[allow(dead_code)] pub(crate) fn execute_one_sequence_pipelined<B: super::buffer_backend::BufferBackend>(
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&crate::decoding::dictionary::Dictionary>,
literals: &[u8],
lit_cur: &mut usize,
lit_len: usize,
seq: Sequence,
resolved_offset: u32,
) -> Result<(), DecompressBlockError> {
let lit_cur_before = *lit_cur;
let high = lit_cur_before
.checked_add(seq.ll as usize)
.filter(|&h| h <= lit_len)
.ok_or(ExecuteSequencesError::NotEnoughBytesForSequence {
wanted: lit_cur_before.saturating_add(seq.ll as usize),
have: lit_len,
})?;
let lits = unsafe { literals.get_unchecked(lit_cur_before..high) };
*lit_cur = high;
if resolved_offset == 0 {
return Err(ExecuteSequencesError::ZeroOffset.into());
}
let inline_path_safe = B::SUPPORTS_INLINE_SEQUENCE_EXEC
&& buffer.buffer_mut().inline_exec_ok(
seq.ll as usize,
seq.ml as usize,
resolved_offset as usize,
)
&& lit_cur_before.checked_add(16).is_some_and(|b| b <= lit_len)
&& (seq.ll as usize <= 16
|| lit_cur_before
.checked_add((seq.ll as usize).next_multiple_of(16))
.is_some_and(|b| b <= lit_len));
if inline_path_safe {
let buf_len = buffer.len();
let offset = resolved_offset as usize;
let prefix_end = buf_len.checked_add(lits.len()).filter(|end| offset <= *end);
if prefix_end.is_none() {
buffer.try_push(lits).map_err(ExecuteSequencesError::from)?;
buffer
.repeat_lookahead_prefetched(dict, offset, seq.ml as usize)
.map_err(ExecuteSequencesError::from)?;
return Ok(());
}
let lit_src = unsafe { literals.as_ptr().add(lit_cur_before) };
unsafe {
buffer
.buffer_mut()
.exec_sequence_inline(lit_src, seq.ll as usize, offset, seq.ml as usize)
.map_err(DecompressBlockError::ExecuteSequencesError)?;
}
if B::INLINE_EXEC_MAINTAINS_OUTPUT_COUNTER {
buffer.advance_output_counter((seq.ll + seq.ml) as u64);
}
return Ok(());
}
buffer.try_push(lits).map_err(ExecuteSequencesError::from)?;
buffer
.repeat_lookahead_prefetched(dict, resolved_offset as usize, seq.ml as usize)
.map_err(ExecuteSequencesError::from)?;
Ok(())
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2,bmi2")]
#[inline]
#[allow(dead_code)] pub(crate) unsafe fn execute_one_sequence_pipelined_avx2<
B: super::buffer_backend::BufferBackend,
>(
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&crate::decoding::dictionary::Dictionary>,
literals: &[u8],
lit_cur: &mut usize,
lit_len: usize,
seq: Sequence,
resolved_offset: u32,
) -> Result<(), DecompressBlockError> {
let lit_cur_before = *lit_cur;
let high = lit_cur_before
.checked_add(seq.ll as usize)
.filter(|&h| h <= lit_len)
.ok_or(ExecuteSequencesError::NotEnoughBytesForSequence {
wanted: lit_cur_before.saturating_add(seq.ll as usize),
have: lit_len,
})?;
let lits = unsafe { literals.get_unchecked(lit_cur_before..high) };
*lit_cur = high;
if resolved_offset == 0 {
return Err(ExecuteSequencesError::ZeroOffset.into());
}
let inline_path_safe = B::SUPPORTS_INLINE_SEQUENCE_EXEC
&& buffer.buffer_mut().inline_exec_ok(
seq.ll as usize,
seq.ml as usize,
resolved_offset as usize,
)
&& lit_cur_before.checked_add(16).is_some_and(|b| b <= lit_len)
&& (seq.ll as usize <= 16
|| lit_cur_before
.checked_add((seq.ll as usize).next_multiple_of(16))
.is_some_and(|b| b <= lit_len));
if inline_path_safe {
let buf_len = buffer.len();
let offset = resolved_offset as usize;
let prefix_end = buf_len.checked_add(lits.len()).filter(|end| offset <= *end);
if prefix_end.is_none() {
buffer.try_push(lits).map_err(ExecuteSequencesError::from)?;
buffer
.repeat_lookahead_prefetched(dict, offset, seq.ml as usize)
.map_err(ExecuteSequencesError::from)?;
return Ok(());
}
let lit_src = unsafe { literals.as_ptr().add(lit_cur_before) };
unsafe {
buffer
.buffer_mut()
.exec_sequence_inline_avx2(lit_src, seq.ll as usize, offset, seq.ml as usize)
.map_err(DecompressBlockError::ExecuteSequencesError)?;
}
if B::INLINE_EXEC_MAINTAINS_OUTPUT_COUNTER {
buffer.advance_output_counter((seq.ll + seq.ml) as u64);
}
return Ok(());
}
buffer.try_push(lits).map_err(ExecuteSequencesError::from)?;
buffer
.repeat_lookahead_prefetched(dict, resolved_offset as usize, seq.ml as usize)
.map_err(ExecuteSequencesError::from)?;
Ok(())
}
#[inline(always)]
#[allow(dead_code)] fn decode_one_sequence_inline<K: crate::cpu_kernel::CpuKernel>(
ll_dec: &mut SeqFSEDecoder<'_>,
ml_dec: &mut SeqFSEDecoder<'_>,
of_dec: &mut SeqFSEDecoder<'_>,
br: &mut BitReaderReversed<'_, K>,
) -> Sequence {
let ll_state = ll_dec.state;
let ml_state = ml_dec.state;
let of_state = of_dec.state;
let ll_value = ll_state.base_value;
let ll_num_bits = ll_state.num_additional_bits;
let ml_value = ml_state.base_value;
let ml_num_bits = ml_state.num_additional_bits;
let of_num_bits = of_state.num_additional_bits;
let of_base = of_state.base_value;
debug_assert!(of_num_bits <= MAX_OFFSET_CODE);
let (obits, ml_add, ll_add) = br.get_bits_triple(of_num_bits, ml_num_bits, ll_num_bits);
let offset = obits as u32 + of_base;
debug_assert_ne!(offset, 0);
Sequence {
ll: ll_value + ll_add as u32,
ml: ml_value + ml_add as u32,
of: offset,
}
}
pub(crate) const LL_META: [u32; 36] = pack_code_meta(
&[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24, 28, 32, 40, 48,
64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536,
],
&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16,
],
);
pub(crate) const ML_META: [u32; 53] = pack_code_meta(
&[
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, 43, 47, 51, 59, 67, 83, 99, 131, 259, 515,
1027, 2051, 4099, 8195, 16387, 32771, 65539,
],
&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
],
);
const fn pack_code_meta<const N: usize>(bases: &[u32; N], extra_bits: &[u8; N]) -> [u32; N] {
let mut out = [0u32; N];
let mut i = 0;
while i < N {
assert!(bases[i] & 0xFF00_0000 == 0, "baseline must fit in 24 bits");
assert!(extra_bits[i] <= 16, "extra_bits exceeds zstd format limit");
out[i] = bases[i] | ((extra_bits[i] as u32) << 24);
i += 1;
}
out
}
pub const LL_MAX_LOG: u8 = 9;
pub const ML_MAX_LOG: u8 = 9;
pub const OF_MAX_LOG: u8 = 8;
pub(crate) fn compute_offsets_long_share(offsets: &crate::fse::SeqFSETable) -> u32 {
const OFFSET_FSE_LOG: u32 = 8;
const LONG_OFFSET_CODE_THRESHOLD: u32 = 22;
let table_log = offsets.accuracy_log as u32;
let raw = offsets
.decode()
.iter()
.filter(|entry| u32::from(entry.num_additional_bits) > LONG_OFFSET_CODE_THRESHOLD)
.count() as u32;
raw << OFFSET_FSE_LOG.saturating_sub(table_log)
}
pub(crate) fn maybe_update_fse_tables(
section: &SequencesHeader,
source: &[u8],
scratch: &mut FSEScratch,
) -> Result<usize, DecodeSequenceError> {
let modes = section
.modes
.ok_or(DecodeSequenceError::MissingCompressionMode)?;
let mut bytes_read = 0;
let ll_mode = modes.ll_mode();
match ll_mode {
ModeType::FSECompressed => {
let bytes = scratch.literal_lengths.build_decoder_fused(
source,
LL_MAX_LOG,
crate::fse::SeqMeta::Packed(&LL_META),
)?;
bytes_read += bytes;
vprintln!("Updating ll table");
vprintln!("Used bytes: {}", bytes);
}
ModeType::RLE => {
vprintln!("Use RLE ll table");
if source.is_empty() {
return Err(DecodeSequenceError::MissingByteForRleLlTable);
}
bytes_read += 1;
if source[0] > MAX_LITERAL_LENGTH_CODE {
return Err(DecodeSequenceError::InvalidRleCode {
axis: "LL",
code: source[0],
});
}
scratch.literal_lengths.build_rle(source[0]);
scratch
.literal_lengths
.enrich_with_packed_seq_meta(&LL_META);
}
ModeType::Predefined => {
vprintln!("Use predefined ll table");
#[cfg(feature = "std")]
{
scratch.literal_lengths.reinit_from(predefined_ll_table());
}
#[cfg(not(feature = "std"))]
{
scratch.literal_lengths.build_from_probabilities(
LL_DEFAULT_ACC_LOG,
&LITERALS_LENGTH_DEFAULT_DISTRIBUTION,
)?;
scratch
.literal_lengths
.enrich_with_packed_seq_meta(&LL_META);
}
}
ModeType::Repeat => {
vprintln!("Repeat ll table");
}
};
if !matches!(ll_mode, ModeType::Repeat) {
scratch.mark_ll_local();
}
let of_source = &source[bytes_read..];
let of_mode = modes.of_mode();
match of_mode {
ModeType::FSECompressed => {
let bytes = scratch.offsets.build_decoder_fused(
of_source,
OF_MAX_LOG,
crate::fse::SeqMeta::Offsets,
)?;
vprintln!("Updating of table");
vprintln!("Used bytes: {}", bytes);
bytes_read += bytes;
scratch.offsets_long_share = compute_offsets_long_share(&scratch.offsets);
}
ModeType::RLE => {
vprintln!("Use RLE of table");
if of_source.is_empty() {
return Err(DecodeSequenceError::MissingByteForRleOfTable);
}
bytes_read += 1;
if of_source[0] > MAX_OFFSET_CODE {
return Err(DecodeSequenceError::InvalidRleCode {
axis: "OF",
code: of_source[0],
});
}
scratch.offsets.build_rle(of_source[0]);
scratch.offsets.enrich_for_offsets();
scratch.offsets_long_share = compute_offsets_long_share(&scratch.offsets);
}
ModeType::Predefined => {
vprintln!("Use predefined of table");
#[cfg(feature = "std")]
{
let (cached, long_share) = predefined_of_table();
scratch.offsets.reinit_from(cached);
scratch.offsets_long_share = long_share;
}
#[cfg(not(feature = "std"))]
{
scratch
.offsets
.build_from_probabilities(OF_DEFAULT_ACC_LOG, &OFFSET_DEFAULT_DISTRIBUTION)?;
scratch.offsets.enrich_for_offsets();
scratch.offsets_long_share = compute_offsets_long_share(&scratch.offsets);
}
}
ModeType::Repeat => {
vprintln!("Repeat of table");
}
};
if !matches!(of_mode, ModeType::Repeat) {
scratch.mark_of_local();
}
let ml_source = &source[bytes_read..];
let ml_mode = modes.ml_mode();
match ml_mode {
ModeType::FSECompressed => {
let bytes = scratch.match_lengths.build_decoder_fused(
ml_source,
ML_MAX_LOG,
crate::fse::SeqMeta::Packed(&ML_META),
)?;
bytes_read += bytes;
vprintln!("Updating ml table");
vprintln!("Used bytes: {}", bytes);
}
ModeType::RLE => {
vprintln!("Use RLE ml table");
if ml_source.is_empty() {
return Err(DecodeSequenceError::MissingByteForRleMlTable);
}
bytes_read += 1;
if ml_source[0] > MAX_MATCH_LENGTH_CODE {
return Err(DecodeSequenceError::InvalidRleCode {
axis: "ML",
code: ml_source[0],
});
}
scratch.match_lengths.build_rle(ml_source[0]);
scratch.match_lengths.enrich_with_packed_seq_meta(&ML_META);
}
ModeType::Predefined => {
vprintln!("Use predefined ml table");
#[cfg(feature = "std")]
{
scratch.match_lengths.reinit_from(predefined_ml_table());
}
#[cfg(not(feature = "std"))]
{
scratch.match_lengths.build_from_probabilities(
ML_DEFAULT_ACC_LOG,
&MATCH_LENGTH_DEFAULT_DISTRIBUTION,
)?;
scratch.match_lengths.enrich_with_packed_seq_meta(&ML_META);
}
}
ModeType::Repeat => {
vprintln!("Repeat ml table");
}
};
if !matches!(ml_mode, ModeType::Repeat) {
scratch.mark_ml_local();
}
Ok(bytes_read)
}
const LL_DEFAULT_ACC_LOG: u8 = 6;
const LITERALS_LENGTH_DEFAULT_DISTRIBUTION: [i32; 36] = [
4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1,
-1, -1, -1, -1,
];
#[cfg(feature = "std")]
fn predefined_ll_table() -> &'static crate::fse::SeqFSETable {
use std::sync::OnceLock;
static CACHED: OnceLock<crate::fse::SeqFSETable> = OnceLock::new();
CACHED.get_or_init(|| {
let mut t = crate::fse::SeqFSETable::new(MAX_LITERAL_LENGTH_CODE);
t.build_from_probabilities(LL_DEFAULT_ACC_LOG, &LITERALS_LENGTH_DEFAULT_DISTRIBUTION)
.expect("LITERALS_LENGTH_DEFAULT_DISTRIBUTION is a static RFC 8878 constant");
t.enrich_with_packed_seq_meta(&LL_META);
t
})
}
#[cfg(feature = "std")]
fn predefined_ml_table() -> &'static crate::fse::SeqFSETable {
use std::sync::OnceLock;
static CACHED: OnceLock<crate::fse::SeqFSETable> = OnceLock::new();
CACHED.get_or_init(|| {
let mut t = crate::fse::SeqFSETable::new(MAX_MATCH_LENGTH_CODE);
t.build_from_probabilities(ML_DEFAULT_ACC_LOG, &MATCH_LENGTH_DEFAULT_DISTRIBUTION)
.expect("MATCH_LENGTH_DEFAULT_DISTRIBUTION is a static RFC 8878 constant");
t.enrich_with_packed_seq_meta(&ML_META);
t
})
}
#[cfg(feature = "std")]
fn predefined_of_table() -> (&'static crate::fse::SeqFSETable, u32) {
use std::sync::OnceLock;
static CACHED: OnceLock<(crate::fse::SeqFSETable, u32)> = OnceLock::new();
let cache = CACHED.get_or_init(|| {
let mut t = crate::fse::SeqFSETable::new(MAX_OFFSET_CODE);
t.build_from_probabilities(OF_DEFAULT_ACC_LOG, &OFFSET_DEFAULT_DISTRIBUTION)
.expect("OFFSET_DEFAULT_DISTRIBUTION is a static RFC 8878 constant");
t.enrich_for_offsets();
let share = compute_offsets_long_share(&t);
(t, share)
});
(&cache.0, cache.1)
}
const ML_DEFAULT_ACC_LOG: u8 = 6;
const MATCH_LENGTH_DEFAULT_DISTRIBUTION: [i32; 53] = [
1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
];
const OF_DEFAULT_ACC_LOG: u8 = 5;
const OFFSET_DEFAULT_DISTRIBUTION: [i32; 29] = [
1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,
];
#[cfg(test)]
mod tests;