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::boxed::Box;
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> {
#[cfg(not(feature = "caller-workspace"))]
pub huf: HuffmanScratch,
#[cfg(feature = "caller-workspace")]
pub huf: Box<HuffmanScratch>,
pub fse: Box<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, L: ByteScratch = Vec<u8>> {
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 L,
pub block_content_buffer: &'a mut L,
}
pub(crate) trait ByteScratch {
fn len(&self) -> usize;
fn clear(&mut self);
fn truncate(&mut self, len: usize);
fn reserve(&mut self, additional: usize);
fn push(&mut self, byte: u8);
fn resize(&mut self, new_len: usize, value: u8);
fn extend_from_slice(&mut self, bytes: &[u8]);
fn as_mut_ptr(&mut self) -> *mut u8;
unsafe fn set_len(&mut self, new_len: usize);
fn as_slice(&self) -> &[u8];
fn as_mut_slice(&mut self) -> &mut [u8];
}
impl ByteScratch for Vec<u8> {
#[inline(always)]
fn len(&self) -> usize {
Vec::len(self)
}
#[inline(always)]
fn clear(&mut self) {
Vec::clear(self)
}
#[inline(always)]
fn truncate(&mut self, len: usize) {
Vec::truncate(self, len)
}
#[inline(always)]
fn reserve(&mut self, additional: usize) {
Vec::reserve(self, additional)
}
#[inline(always)]
fn push(&mut self, byte: u8) {
Vec::push(self, byte)
}
#[inline(always)]
fn resize(&mut self, new_len: usize, value: u8) {
Vec::resize(self, new_len, value)
}
#[inline(always)]
fn extend_from_slice(&mut self, bytes: &[u8]) {
Vec::extend_from_slice(self, bytes)
}
#[inline(always)]
fn as_mut_ptr(&mut self) -> *mut u8 {
Vec::as_mut_ptr(self)
}
#[inline(always)]
unsafe fn set_len(&mut self, new_len: usize) {
unsafe { Vec::set_len(self, new_len) }
}
#[inline(always)]
fn as_slice(&self) -> &[u8] {
Vec::as_slice(self)
}
#[inline(always)]
fn as_mut_slice(&mut self) -> &mut [u8] {
Vec::as_mut_slice(self)
}
}
pub(crate) trait Workspace {
type Backend: BufferBackend;
type Bytes: ByteScratch;
fn split(&mut self) -> WorkspaceRef<'_, Self::Backend, Self::Bytes>;
}
impl<B: BufferBackend> Workspace for DecoderScratch<B> {
type Backend = B;
type Bytes = Vec<u8>;
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>;
type Bytes = Vec<u8>;
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 {
#[cfg(not(feature = "caller-workspace"))]
huf: HuffmanScratch {
table: HuffmanTable::new(),
table_source: TableSource::Local,
},
#[cfg(feature = "caller-workspace")]
huf: Box::new(HuffmanScratch::new()),
fse: Box::new(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: SeqTableSource::Local,
of_source: SeqTableSource::Local,
ml_source: SeqTableSource::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 {
let boxed_huf = if cfg!(feature = "caller-workspace") {
core::mem::size_of::<HuffmanScratch>()
} else {
0
};
self.buffer.capacity()
+ self.literals_buffer.capacity()
+ self.block_content_buffer.capacity()
+ self.huf.heap_bytes()
+ boxed_huf
+ core::mem::size_of::<FSEScratch>()
+ 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);
let literals_cap = block_cap + crate::WILDCOPY_OVERLENGTH;
if self.literals_buffer.capacity() < literals_cap {
self.literals_buffer.resize(literals_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);
reset_entropy(&mut self.huf, &mut self.fse);
}
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;
}
}
#[inline(always)]
pub(crate) fn reset_entropy(huf: &mut HuffmanScratch, fse: &mut FSEScratch) {
if fse.literal_lengths.is_populated() {
fse.literal_lengths.reset();
}
if fse.match_lengths.is_populated() {
fse.match_lengths.reset();
}
if fse.offsets.is_populated() {
fse.offsets.reset();
}
fse.offsets_long_share = 0;
fse.detach_dict();
fse.ddict_is_cold = false;
if huf.table.max_num_bits != 0 {
huf.table.reset();
}
huf.detach_dict();
}
#[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,
}
}
#[cfg(feature = "caller-workspace")]
pub(crate) unsafe fn init_in_place(this: *mut Self) {
use core::ptr::addr_of_mut;
unsafe {
HuffmanTable::init_in_place(addr_of_mut!((*this).table));
addr_of_mut!((*this).table_source).write(TableSource::Local);
}
}
pub fn heap_bytes(&self) -> usize {
self.table.heap_bytes()
}
pub const fn max_heap_bytes() -> usize {
HuffmanTable::max_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,
}
#[derive(Clone, Copy)]
enum SeqTableSource {
Local,
Dict,
#[cfg(feature = "std")]
Predefined(&'static SeqFSETable),
}
#[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: SeqTableSource,
of_source: SeqTableSource,
ml_source: SeqTableSource,
}
impl FSEScratch {
pub fn heap_bytes(&self) -> usize {
self.offsets.heap_bytes()
+ self.literal_lengths.heap_bytes()
+ self.match_lengths.heap_bytes()
}
pub const fn max_heap_bytes() -> usize {
3 * SeqFSETable::max_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: SeqTableSource::Local,
of_source: SeqTableSource::Local,
ml_source: SeqTableSource::Local,
}
}
#[cfg(feature = "caller-workspace")]
pub(crate) unsafe fn init_in_place(this: *mut Self) {
use crate::fse::SeqFSETable;
use core::ptr::addr_of_mut;
unsafe {
SeqFSETable::init_in_place(addr_of_mut!((*this).offsets.0), MAX_OFFSET_CODE);
SeqFSETable::init_in_place(
addr_of_mut!((*this).literal_lengths.0),
MAX_LITERAL_LENGTH_CODE,
);
SeqFSETable::init_in_place(
addr_of_mut!((*this).match_lengths.0),
MAX_MATCH_LENGTH_CODE,
);
addr_of_mut!((*this).offsets_long_share).write(0);
addr_of_mut!((*this).ddict_is_cold).write(false);
addr_of_mut!((*this).ll_source).write(SeqTableSource::Local);
addr_of_mut!((*this).of_source).write(SeqTableSource::Local);
addr_of_mut!((*this).ml_source).write(SeqTableSource::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 = SeqTableSource::Local;
self.of_source = SeqTableSource::Local;
self.ml_source = SeqTableSource::Local;
}
pub(crate) fn ll_table<'a>(&'a self, dict: Option<&'a Dictionary>) -> &'a SeqFSETable {
match self.ll_source {
SeqTableSource::Local => &self.literal_lengths,
SeqTableSource::Dict => &expect_dict(dict).fse.literal_lengths,
#[cfg(feature = "std")]
SeqTableSource::Predefined(table) => table,
}
}
pub(crate) fn of_table<'a>(&'a self, dict: Option<&'a Dictionary>) -> &'a SeqFSETable {
match self.of_source {
SeqTableSource::Local => &self.offsets,
SeqTableSource::Dict => &expect_dict(dict).fse.offsets,
#[cfg(feature = "std")]
SeqTableSource::Predefined(table) => table,
}
}
pub(crate) fn ml_table<'a>(&'a self, dict: Option<&'a Dictionary>) -> &'a SeqFSETable {
match self.ml_source {
SeqTableSource::Local => &self.match_lengths,
SeqTableSource::Dict => &expect_dict(dict).fse.match_lengths,
#[cfg(feature = "std")]
SeqTableSource::Predefined(table) => table,
}
}
pub(crate) fn attach_dict(&mut self, dict: &DictionaryHandle) {
self.offsets_long_share = dict.as_dict().fse.offsets_long_share;
self.ll_source = SeqTableSource::Dict;
self.of_source = SeqTableSource::Dict;
self.ml_source = SeqTableSource::Dict;
}
pub(crate) fn detach_dict(&mut self) {
self.ll_source = SeqTableSource::Local;
self.of_source = SeqTableSource::Local;
self.ml_source = SeqTableSource::Local;
}
#[inline]
pub(crate) fn mark_ll_local(&mut self) {
self.ll_source = SeqTableSource::Local;
}
#[inline]
pub(crate) fn mark_of_local(&mut self) {
self.of_source = SeqTableSource::Local;
}
#[inline]
pub(crate) fn mark_ml_local(&mut self) {
self.ml_source = SeqTableSource::Local;
}
#[cfg(feature = "std")]
#[inline]
pub(crate) fn mark_ll_predefined(&mut self, table: &'static SeqFSETable) {
self.ll_source = SeqTableSource::Predefined(table);
}
#[cfg(feature = "std")]
#[inline]
pub(crate) fn mark_of_predefined(&mut self, table: &'static SeqFSETable) {
self.of_source = SeqTableSource::Predefined(table);
}
#[cfg(feature = "std")]
#[inline]
pub(crate) fn mark_ml_predefined(&mut self, table: &'static SeqFSETable) {
self.ml_source = SeqTableSource::Predefined(table);
}
}
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 From<SeqFSETable> for AlignedFSETable {
fn from(table: SeqFSETable) -> Self {
Self(table)
}
}
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;