use super::buffer_backend::BufferBackend;
use super::decode_buffer::DecodeBuffer;
use super::ringbuffer::RingBuffer;
use crate::decoding::dictionary::{Dictionary, DictionaryHandle};
use crate::fse::SeqFSETable;
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 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 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,
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 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,
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(),
table_source: TableSource::Local,
},
fse: FSEScratch {
offsets: AlignedFSETable::new(MAX_OFFSET_CODE),
literal_lengths: AlignedFSETable::new(MAX_LITERAL_LENGTH_CODE),
match_lengths: AlignedFSETable::new(MAX_MATCH_LENGTH_CODE),
offsets_long_share: 0,
ddict_is_cold: false,
ll_source: TableSource::Local,
of_source: TableSource::Local,
ml_source: TableSource::Local,
},
buffer: DecodeBuffer::new(window_size),
offset_hist: [1, 4, 8],
block_content_buffer: Vec::new(),
literals_buffer: Vec::new(),
}
}
pub fn workspace_bytes(&self) -> usize {
self.buffer.capacity()
+ self.literals_buffer.capacity()
+ self.block_content_buffer.capacity()
+ self.huf.heap_bytes()
+ self.fse.heap_bytes()
}
pub fn reset(&mut self, window_size: usize) {
self.offset_hist = [1, 4, 8];
self.literals_buffer.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);
if self.fse.literal_lengths.is_populated() {
self.fse.literal_lengths.reset();
}
if self.fse.match_lengths.is_populated() {
self.fse.match_lengths.reset();
}
if self.fse.offsets.is_populated() {
self.fse.offsets.reset();
}
self.fse.offsets_long_share = 0;
self.fse.detach_dict();
self.fse.ddict_is_cold = false;
if self.huf.table.max_num_bits != 0 {
self.huf.table.reset();
}
self.huf.detach_dict();
}
pub fn init_from_dict(&mut self, dict: &DictionaryHandle) {
let d = dict.as_dict();
self.fse.attach_dict(dict);
self.huf.attach_dict();
self.offset_hist = d.offset_hist;
self.fse.ddict_is_cold = true;
}
}
#[derive(Clone)]
pub struct HuffmanScratch {
pub table: HuffmanTable,
table_source: TableSource,
}
impl HuffmanScratch {
pub fn new() -> HuffmanScratch {
HuffmanScratch {
table: HuffmanTable::new(),
table_source: TableSource::Local,
}
}
pub fn heap_bytes(&self) -> usize {
self.table.heap_bytes()
}
pub(crate) fn huf_table<'a>(&'a self, dict: Option<&'a Dictionary>) -> &'a HuffmanTable {
match self.table_source {
TableSource::Local => &self.table,
TableSource::Dict => &expect_dict(dict).huf.table,
}
}
pub(crate) fn attach_dict(&mut self) {
self.table_source = TableSource::Dict;
}
pub(crate) fn detach_dict(&mut self) {
self.table_source = TableSource::Local;
}
#[inline]
pub(crate) fn mark_table_local(&mut self) {
self.table_source = TableSource::Local;
}
pub(crate) fn reinit_resolved_from(
&mut self,
other: &HuffmanScratch,
dict: Option<&Dictionary>,
) {
self.table.reinit_from(other.huf_table(dict));
self.detach_dict();
}
}
impl Default for HuffmanScratch {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum TableSource {
Local,
Dict,
}
#[inline]
fn expect_dict(dict: Option<&Dictionary>) -> &Dictionary {
dict.expect("a Dict-sourced table requires the active dictionary borrow")
}
#[derive(Clone)]
pub struct FSEScratch {
pub offsets: AlignedFSETable,
pub literal_lengths: AlignedFSETable,
pub match_lengths: AlignedFSETable,
pub offsets_long_share: u32,
pub ddict_is_cold: bool,
ll_source: TableSource,
of_source: TableSource,
ml_source: TableSource,
}
impl FSEScratch {
pub fn heap_bytes(&self) -> usize {
self.offsets.heap_bytes()
+ self.literal_lengths.heap_bytes()
+ self.match_lengths.heap_bytes()
}
pub fn new() -> FSEScratch {
FSEScratch {
offsets: AlignedFSETable::new(MAX_OFFSET_CODE),
literal_lengths: AlignedFSETable::new(MAX_LITERAL_LENGTH_CODE),
match_lengths: AlignedFSETable::new(MAX_MATCH_LENGTH_CODE),
offsets_long_share: 0,
ddict_is_cold: false,
ll_source: TableSource::Local,
of_source: TableSource::Local,
ml_source: TableSource::Local,
}
}
pub fn reinit_from(&mut self, other: &Self, dict: Option<&Dictionary>) {
self.literal_lengths.reinit_from(other.ll_table(dict));
self.offsets.reinit_from(other.of_table(dict));
self.match_lengths.reinit_from(other.ml_table(dict));
self.offsets_long_share = other.offsets_long_share;
self.ddict_is_cold = false;
self.ll_source = TableSource::Local;
self.of_source = TableSource::Local;
self.ml_source = TableSource::Local;
}
pub(crate) fn ll_table<'a>(&'a self, dict: Option<&'a Dictionary>) -> &'a SeqFSETable {
match self.ll_source {
TableSource::Local => &self.literal_lengths,
TableSource::Dict => &expect_dict(dict).fse.literal_lengths,
}
}
pub(crate) fn of_table<'a>(&'a self, dict: Option<&'a Dictionary>) -> &'a SeqFSETable {
match self.of_source {
TableSource::Local => &self.offsets,
TableSource::Dict => &expect_dict(dict).fse.offsets,
}
}
pub(crate) fn ml_table<'a>(&'a self, dict: Option<&'a Dictionary>) -> &'a SeqFSETable {
match self.ml_source {
TableSource::Local => &self.match_lengths,
TableSource::Dict => &expect_dict(dict).fse.match_lengths,
}
}
pub(crate) fn attach_dict(&mut self, dict: &DictionaryHandle) {
self.offsets_long_share = dict.as_dict().fse.offsets_long_share;
self.ll_source = TableSource::Dict;
self.of_source = TableSource::Dict;
self.ml_source = TableSource::Dict;
}
pub(crate) fn detach_dict(&mut self) {
self.ll_source = TableSource::Local;
self.of_source = TableSource::Local;
self.ml_source = TableSource::Local;
}
#[inline]
pub(crate) fn mark_ll_local(&mut self) {
self.ll_source = TableSource::Local;
}
#[inline]
pub(crate) fn mark_of_local(&mut self) {
self.of_source = TableSource::Local;
}
#[inline]
pub(crate) fn mark_ml_local(&mut self) {
self.ml_source = TableSource::Local;
}
}
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)))]
#[derive(Clone)]
pub struct AlignedFSETable(SeqFSETable);
impl AlignedFSETable {
fn new(max_symbol: u8) -> Self {
Self(SeqFSETable::new(max_symbol))
}
}
impl Deref for AlignedFSETable {
type Target = SeqFSETable;
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;