use super::buffer_backend::BufferBackend;
use super::decode_buffer::DecodeBuffer;
use super::scratch::FSEScratch;
use super::sequence_section_decoder::{
ADVANCE, ADVANCE_MASK, ExecSeq, SeqStreamSetup, init_sequence_stream,
};
use crate::bit_io::BitReaderReversed;
use crate::blocks::sequence_section::{MAX_OFFSET_CODE, Sequence, SequencesHeader};
use crate::cpu_kernel::{CpuKernel, ScalarKernel};
use crate::decoding::errors::{DecodeSequenceError, DecompressBlockError, ExecuteSequencesError};
use crate::decoding::sequence_execution::do_offset_history;
use crate::fse::SeqFSEDecoder;
macro_rules! decode_one_body {
($ll_dec:expr, $ml_dec:expr, $of_dec:expr, $br:expr) => {{
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,
}
}};
}
#[inline(always)]
fn read_additional_bits<K: CpuKernel>(br: &mut BitReaderReversed<'_, K>, n: u8) -> u32 {
debug_assert!(n > 0 && n <= 31);
debug_assert!(br.bits_consumed + n <= 64);
let value = br.bit_container.wrapping_shl(u32::from(br.bits_consumed)) >> (64 - u32::from(n));
br.consume(n);
value as u32
}
#[inline(always)]
pub(super) unsafe fn decode_resolved<K: CpuKernel>(
ll_dec: &SeqFSEDecoder<'_>,
ml_dec: &SeqFSEDecoder<'_>,
of_dec: &SeqFSEDecoder<'_>,
br: &mut BitReaderReversed<'_, K>,
hist: &mut [u32; 3],
) -> Sequence {
let ll = ll_dec.state;
let ml = ml_dec.state;
let of = of_dec.state;
debug_assert!(of.num_additional_bits <= MAX_OFFSET_CODE);
debug_assert!(ml.num_additional_bits <= 16 && ll.num_additional_bits <= 16);
debug_assert!(br.bits_consumed <= 8);
let offset = if of.num_additional_bits > 1 {
let offset = of.base_value + read_additional_bits(br, of.num_additional_bits) - 3;
hist[2] = hist[1];
hist[1] = hist[0];
hist[0] = offset;
offset
} else if of.num_additional_bits == 0 {
debug_assert_eq!(of.base_value, 1);
let offset = if ll.base_value == 0 { hist[1] } else { hist[0] };
hist[1] = if ll.base_value == 0 { hist[0] } else { hist[1] };
hist[0] = offset;
offset
} else {
debug_assert_eq!(of.base_value, 2);
let index = 1 + usize::from(ll.base_value == 0) + read_additional_bits(br, 1) as usize;
let offset = if index == 3 {
hist[0].wrapping_sub(1)
} else {
hist[index]
};
if index != 1 {
hist[2] = hist[1];
}
hist[1] = hist[0];
hist[0] = offset;
offset
};
let ml_add = if ml.num_additional_bits == 0 {
0
} else {
read_additional_bits(br, ml.num_additional_bits)
};
let ll_budget = ll.num_additional_bits + 9 + 9 + 8;
debug_assert!(ll_budget <= 56);
if br.bits_consumed + ll_budget > 64 {
unsafe { br.refill_sequence() };
}
let ll_add = if ll.num_additional_bits == 0 {
0
} else {
read_additional_bits(br, ll.num_additional_bits)
};
Sequence {
ll: ll.base_value + ll_add,
ml: ml.base_value + ml_add,
of: offset,
}
}
macro_rules! execute_one_body {
(
$buffer:expr,
$dict:expr,
$dict_content:expr,
$literals_buffer:expr,
$lit_cur:expr,
$literals_buffer_len:expr,
$seq_ll:expr,
$seq_ml:expr,
$resolved_offset:expr
) => {{
let resolved_offset_v: u32 = $resolved_offset;
let seq = Sequence {
ll: $seq_ll,
ml: $seq_ml,
of: resolved_offset_v,
};
super::sequence_section_decoder::execute_one_sequence_pipelined::<_, K>(
$buffer,
$dict,
$dict_content,
$literals_buffer,
$lit_cur,
$literals_buffer_len,
seq,
resolved_offset_v,
)
}};
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
fn decode_long_sequences<B: BufferBackend, K: CpuKernel>(
br: &mut BitReaderReversed<'_, K>,
ll_dec: &mut SeqFSEDecoder<'_>,
ml_dec: &mut SeqFSEDecoder<'_>,
of_dec: &mut SeqFSEDecoder<'_>,
buffer: &mut DecodeBuffer<B>,
offset_hist: &mut [u32; 3],
literals_buffer: &[u8],
lit_cur: &mut usize,
literals_buffer_len: usize,
dict: Option<&crate::decoding::dictionary::Dictionary>,
dict_content: &[u8],
old_buffer_size: usize,
num_sequences: usize,
max_update_bits: u8,
) -> Result<u32, DecompressBlockError> {
let mut seq_sum = 0u32;
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_body!(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);
}
let mut pipeline_err: Option<DecompressBlockError> = None;
for i in ADVANCE..num_sequences {
let seq = decode_one_body!(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,
};
let r = execute_one_body!(
buffer,
dict,
dict_content,
literals_buffer,
lit_cur,
literals_buffer_len,
exec_seq.ll,
exec_seq.ml,
exec_seq.actual_offset
);
if let Err(e) = r {
pipeline_err = Some(e);
break;
}
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);
}
}
if pipeline_err.is_none() {
for k in 0..ADVANCE {
let slot = (num_sequences + k) & ADVANCE_MASK;
let exec_seq = ring[slot];
let r = execute_one_body!(
buffer,
dict,
dict_content,
literals_buffer,
lit_cur,
literals_buffer_len,
exec_seq.ll,
exec_seq.ml,
exec_seq.actual_offset
);
if let Err(e) = r {
pipeline_err = Some(e);
break;
}
seq_sum = seq_sum.wrapping_add(exec_seq.ll).wrapping_add(exec_seq.ml);
}
}
if let Some(e) = pipeline_err {
return Err(e);
}
*offset_hist = shadow_hist;
Ok(seq_sum)
}
#[allow(clippy::too_many_lines)]
#[allow(clippy::too_many_arguments)]
#[cfg_attr(target_arch = "x86_64", inline(never))]
pub(crate) fn decode_and_execute_sequences_scalar<'fse, B: BufferBackend>(
section: &SequencesHeader,
source: &[u8],
fse: &'fse mut FSEScratch,
buffer: &mut DecodeBuffer<B>,
offset_hist: &mut [u32; 3],
literals_buffer: &[u8],
literals_len: usize,
dict: Option<&'fse crate::decoding::dictionary::Dictionary>,
) -> Result<(), DecompressBlockError> {
decode_and_execute_sequences_impl::<B, ScalarKernel>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
)
}
#[allow(clippy::too_many_lines, clippy::too_many_arguments)]
#[inline(always)]
pub(super) fn decode_and_execute_sequences_impl<'fse, B: BufferBackend, K: CpuKernel>(
section: &SequencesHeader,
source: &[u8],
fse: &'fse mut FSEScratch,
buffer: &mut DecodeBuffer<B>,
offset_hist: &mut [u32; 3],
literals_buffer: &[u8],
literals_len: usize,
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_len;
debug_assert!(
literals_buffer.len() >= literals_len + crate::WILDCOPY_OVERLENGTH,
"literals view lacks the copiers' read slack: {} bytes for {literals_len} literals",
literals_buffer.len(),
);
let mut lit_cur: usize = 0;
let mut seq_sum: u32 = 0;
let dict_content: &[u8] = match dict {
Some(d) => &d.dict_content,
None => &[],
};
let buffer_checkpoint = buffer.checkpoint();
let saved_offset_hist = *offset_hist;
if use_long_pipeline {
seq_sum = match decode_long_sequences(
&mut br,
&mut ll_dec,
&mut ml_dec,
&mut of_dec,
buffer,
offset_hist,
literals_buffer,
&mut lit_cur,
literals_buffer_len,
dict,
dict_content,
old_buffer_size,
num_sequences,
max_update_bits,
) {
Ok(sum) => sum,
Err(e) => {
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;
debug_assert!(max_update_bits <= 9 + 9 + 8);
unsafe { br.refill_sequence() };
for i in 0..num_sequences {
let seq =
unsafe { decode_resolved(&ll_dec, &ml_dec, &of_dec, &mut br, &mut shadow_hist) };
let resolved_offset = seq.of;
if i + 1 < num_sequences {
ll_dec.update_state_fast(&mut br);
ml_dec.update_state_fast(&mut br);
of_dec.update_state_fast(&mut br);
unsafe { br.refill_sequence() };
}
let r = execute_one_body!(
buffer,
dict,
dict_content,
literals_buffer,
&mut lit_cur,
literals_buffer_len,
seq.ll,
seq.ml,
resolved_offset
);
if let Err(e) = r {
fallback_err = Some(e);
break;
}
seq_sum = seq_sum.wrapping_add(seq.ll).wrapping_add(seq.ml);
}
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..literals_buffer_len];
buffer
.try_push::<K>(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(())
}
#[cfg(test)]
mod tests;