use super::super::blocks::sequence_section::Sequence;
use super::buffer_backend::BufferBackend;
use super::decode_buffer::DecodeBuffer;
use super::ringbuffer::RingBuffer;
use crate::decoding::dictionary::Dictionary;
use crate::fse::FSETable;
use crate::huff0::HuffmanTable;
use alloc::vec::Vec;
use core::ops::{Deref, DerefMut};
use crate::blocks::sequence_section::{
MAX_LITERAL_LENGTH_CODE, MAX_MATCH_LENGTH_CODE, MAX_OFFSET_CODE,
};
pub struct DecoderScratch<B: BufferBackend = RingBuffer> {
pub huf: HuffmanScratch,
pub fse: FSEScratch,
pub buffer: DecodeBuffer<B>,
pub offset_hist: [u32; 3],
pub literals_buffer: Vec<u8>,
pub sequences: Vec<Sequence>,
pub block_content_buffer: Vec<u8>,
}
pub struct WorkspaceRef<'a, B: BufferBackend> {
pub huf: &'a mut HuffmanScratch,
pub fse: &'a mut FSEScratch,
pub buffer: &'a mut DecodeBuffer<B>,
pub offset_hist: &'a mut [u32; 3],
pub literals_buffer: &'a mut Vec<u8>,
pub sequences: &'a mut Vec<Sequence>,
pub block_content_buffer: &'a mut Vec<u8>,
}
pub(crate) trait Workspace {
type Backend: BufferBackend;
fn split(&mut self) -> WorkspaceRef<'_, Self::Backend>;
}
impl<B: BufferBackend> Workspace for DecoderScratch<B> {
type Backend = B;
fn split(&mut self) -> WorkspaceRef<'_, B> {
WorkspaceRef {
huf: &mut self.huf,
fse: &mut self.fse,
buffer: &mut self.buffer,
offset_hist: &mut self.offset_hist,
literals_buffer: &mut self.literals_buffer,
sequences: &mut self.sequences,
block_content_buffer: &mut self.block_content_buffer,
}
}
}
pub struct DirectScratch<'o, 'p> {
pub huf: &'p mut HuffmanScratch,
pub fse: &'p mut FSEScratch,
pub buffer: DecodeBuffer<super::user_slice_buf::UserSliceBackend<'o>>,
pub offset_hist: &'p mut [u32; 3],
pub literals_buffer: &'p mut Vec<u8>,
pub sequences: &'p mut Vec<Sequence>,
pub block_content_buffer: &'p mut Vec<u8>,
}
impl<'o, 'p> Workspace for DirectScratch<'o, 'p> {
type Backend = super::user_slice_buf::UserSliceBackend<'o>;
fn split(&mut self) -> WorkspaceRef<'_, Self::Backend> {
WorkspaceRef {
huf: &mut *self.huf,
fse: &mut *self.fse,
buffer: &mut self.buffer,
offset_hist: &mut *self.offset_hist,
literals_buffer: &mut *self.literals_buffer,
sequences: &mut *self.sequences,
block_content_buffer: &mut *self.block_content_buffer,
}
}
}
impl<B: BufferBackend> DecoderScratch<B> {
pub fn new(window_size: usize) -> DecoderScratch<B> {
DecoderScratch {
huf: HuffmanScratch {
table: HuffmanTable::new(),
},
fse: FSEScratch {
offsets: AlignedFSETable::new(MAX_OFFSET_CODE),
of_rle: None,
literal_lengths: AlignedFSETable::new(MAX_LITERAL_LENGTH_CODE),
ll_rle: None,
match_lengths: AlignedFSETable::new(MAX_MATCH_LENGTH_CODE),
ml_rle: None,
offsets_long_share: 0,
ddict_is_cold: false,
},
buffer: DecodeBuffer::new(window_size),
offset_hist: [1, 4, 8],
block_content_buffer: Vec::new(),
literals_buffer: Vec::new(),
sequences: Vec::new(),
}
}
pub fn reset(&mut self, window_size: usize) {
self.offset_hist = [1, 4, 8];
self.literals_buffer.clear();
self.sequences.clear();
self.block_content_buffer.clear();
let block_cap = (window_size.min(crate::common::MAX_BLOCK_SIZE as usize)).max(8);
if self.literals_buffer.capacity() < block_cap {
self.literals_buffer.resize(block_cap, 0);
self.literals_buffer.clear();
}
if self.block_content_buffer.capacity() < block_cap {
self.block_content_buffer.resize(block_cap, 0);
self.block_content_buffer.clear();
}
self.buffer.reset(window_size);
self.fse.literal_lengths.reset();
self.fse.match_lengths.reset();
self.fse.offsets.reset();
self.fse.ll_rle = None;
self.fse.ml_rle = None;
self.fse.of_rle = None;
self.fse.offsets_long_share = 0;
self.fse.ddict_is_cold = false;
self.huf.table.reset();
}
pub fn init_from_dict(&mut self, dict: &Dictionary) {
self.fse.reinit_from(&dict.fse);
self.huf.table.reinit_from(&dict.huf.table);
self.offset_hist = dict.offset_hist;
self.buffer.dict_content.clear();
self.buffer
.dict_content
.extend_from_slice(&dict.dict_content);
self.fse.ddict_is_cold = true;
}
}
pub struct HuffmanScratch {
pub table: HuffmanTable,
}
impl HuffmanScratch {
pub fn new() -> HuffmanScratch {
HuffmanScratch {
table: HuffmanTable::new(),
}
}
}
impl Default for HuffmanScratch {
fn default() -> Self {
Self::new()
}
}
pub struct FSEScratch {
pub offsets: AlignedFSETable,
pub of_rle: Option<u8>,
pub literal_lengths: AlignedFSETable,
pub ll_rle: Option<u8>,
pub match_lengths: AlignedFSETable,
pub ml_rle: Option<u8>,
pub offsets_long_share: u32,
pub ddict_is_cold: bool,
}
impl FSEScratch {
pub fn new() -> FSEScratch {
FSEScratch {
offsets: AlignedFSETable::new(MAX_OFFSET_CODE),
of_rle: None,
literal_lengths: AlignedFSETable::new(MAX_LITERAL_LENGTH_CODE),
ll_rle: None,
match_lengths: AlignedFSETable::new(MAX_MATCH_LENGTH_CODE),
ml_rle: None,
offsets_long_share: 0,
ddict_is_cold: false,
}
}
pub fn reinit_from(&mut self, other: &Self) {
self.offsets.reinit_from(&other.offsets);
self.literal_lengths.reinit_from(&other.literal_lengths);
self.match_lengths.reinit_from(&other.match_lengths);
self.of_rle = other.of_rle;
self.ll_rle = other.ll_rle;
self.ml_rle = other.ml_rle;
self.offsets_long_share =
super::sequence_section_decoder::compute_offsets_long_share(&self.offsets);
}
}
impl Default for FSEScratch {
fn default() -> Self {
Self::new()
}
}
#[cfg_attr(target_arch = "aarch64", repr(align(128)))]
#[cfg_attr(not(target_arch = "aarch64"), repr(align(64)))]
pub struct AlignedFSETable(FSETable);
impl AlignedFSETable {
fn new(max_symbol: u8) -> Self {
Self(FSETable::new(max_symbol))
}
}
impl Deref for AlignedFSETable {
type Target = FSETable;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for AlignedFSETable {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::decoding::dictionary::Dictionary;
#[test]
fn init_from_dict_marks_fse_ddict_is_cold() {
extern crate std;
let dict_raw =
std::fs::read("./dict_tests/dictionary").expect("dictionary fixture should load");
let dict = Dictionary::decode_dict(&dict_raw).expect("dictionary should parse");
let mut scratch: DecoderScratch = DecoderScratch::new(1024);
assert!(
!scratch.fse.ddict_is_cold,
"fresh DecoderScratch must not advertise a cold dict"
);
scratch.init_from_dict(&dict);
assert!(
scratch.fse.ddict_is_cold,
"init_from_dict must set ddict_is_cold = true"
);
}
}