#![allow(dead_code)]
#![allow(clippy::wrong_self_convention)]
use super::frequency::FrequencyCounter;
use crate::error::Result;
use crate::huffman::HuffmanEncodeTable;
#[derive(Clone, Copy, Debug)]
pub struct Token {
pub context: u8,
pub symbol: u8,
pub extra_bits: u16,
pub num_extra: u8,
}
impl Token {
#[inline]
pub const fn new(context: u8, symbol: u8, extra_bits: u16, num_extra: u8) -> Self {
Self {
context,
symbol,
extra_bits,
num_extra,
}
}
#[inline]
pub fn dc(context: u8, diff: i16) -> Self {
let category = crate::entropy::category(diff);
let extra = crate::entropy::additional_bits_with_cat(diff, category);
Self::new(context, category, extra, category)
}
#[inline]
pub fn ac(context: u8, run: u8, value: i16) -> Self {
if value == 0 {
if run == 0 {
Self::new(context, 0x00, 0, 0)
} else {
Self::new(context, 0xF0, 0, 0)
}
} else {
let category = crate::entropy::category(value);
let extra = crate::entropy::additional_bits_with_cat(value, category);
let symbol = (run << 4) | category;
Self::new(context, symbol, extra, category)
}
}
#[cfg(feature = "__debug-tokens")]
pub fn to_debug_json(&self) -> String {
format!(
r#"{{"context":{},"symbol":{},"extra_bits":{},"num_extra":{}}}"#,
self.context, self.symbol, self.extra_bits, self.num_extra
)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct RefToken {
pub symbol: u8,
pub refbits: u8,
}
impl RefToken {
#[inline]
pub const fn new(symbol: u8, refbits: u8) -> Self {
Self { symbol, refbits }
}
#[inline]
pub fn eob(run: u16, refbits: u8) -> Self {
let symbol = if run == 0 {
0
} else {
let log2 = 15 - run.leading_zeros() as u8;
(log2 << 4) | ((run - (1 << log2)) as u8 & 0x0F)
};
Self::new(symbol, refbits)
}
#[cfg(feature = "__debug-tokens")]
pub fn to_debug_json(&self) -> String {
format!(r#"{{"symbol":{},"refbits":{}}}"#, self.symbol, self.refbits)
}
}
#[derive(Clone, Debug, Default)]
pub struct ScanTokenInfo {
pub token_offset: usize,
pub num_tokens: usize,
pub ref_tokens: Vec<RefToken>,
pub refbits: Vec<u8>,
pub eobruns: Vec<u16>,
pub restarts: Vec<usize>,
pub context: u8,
pub ss: u8,
pub se: u8,
pub ah: u8,
pub al: u8,
}
impl ScanTokenInfo {
pub fn new(context: u8, ss: u8, se: u8, ah: u8, al: u8) -> Self {
Self {
token_offset: 0,
num_tokens: 0,
ref_tokens: Vec::new(),
refbits: Vec::new(),
eobruns: Vec::new(),
restarts: Vec::new(),
context,
ss,
se,
ah,
al,
}
}
pub fn with_capacity_refinement(
context: u8,
ss: u8,
se: u8,
ah: u8,
al: u8,
num_blocks: usize,
) -> Result<Self> {
use crate::error::Error;
let mut info = Self::new(context, ss, se, ah, al);
let token_capacity = num_blocks.saturating_mul(2);
info.ref_tokens
.try_reserve(token_capacity)
.map_err(|_| Error::allocation_failed(token_capacity * 2, "refinement tokens"))?;
let refbits_capacity = num_blocks.saturating_mul(4);
info.refbits
.try_reserve(refbits_capacity)
.map_err(|_| Error::allocation_failed(refbits_capacity, "refinement bits"))?;
let eobrun_capacity = num_blocks / 10 + 16;
info.eobruns
.try_reserve(eobrun_capacity)
.map_err(|_| Error::allocation_failed(eobrun_capacity * 2, "EOB runs"))?;
Ok(info)
}
#[inline]
pub fn is_refinement(&self) -> bool {
self.ss > 0 && self.ah > 0
}
#[inline]
pub fn is_dc(&self) -> bool {
self.ss == 0 && self.se == 0
}
#[allow(dead_code)]
pub fn debug_dump(&self, scan_index: usize) {
if self.is_refinement() {
eprintln!(
"=== Rust AC Refinement Scan {} ===\nSs={} Se={} Ah={} Al={}\nnum_blocks=? num_tokens={} num_refbits={} num_eobruns={}",
scan_index,
self.ss,
self.se,
self.ah,
self.al,
self.ref_tokens.len(),
self.refbits.len(),
self.eobruns.len()
);
eprintln!("TOKENS:");
for (i, t) in self.ref_tokens.iter().take(20).enumerate() {
eprintln!(" [{}] symbol=0x{:02x} refbits={}", i, t.symbol, t.refbits);
}
if self.ref_tokens.len() > 20 {
eprintln!(" ... ({} more tokens)", self.ref_tokens.len() - 20);
}
eprintln!("=== End Rust AC Refinement Scan {} ===\n", scan_index);
}
}
}
#[derive(Clone, Debug, Default)]
pub struct TokenBuffer {
tokens: Vec<Token>,
counters: Vec<FrequencyCounter>,
}
impl TokenBuffer {
#[must_use]
pub fn new(num_contexts: usize) -> Self {
Self {
tokens: Vec::new(),
counters: vec![FrequencyCounter::new(); num_contexts],
}
}
pub fn clear(&mut self) {
self.tokens.clear();
for counter in &mut self.counters {
counter.reset();
}
}
#[inline]
pub fn push(&mut self, token: Token) {
if (token.context as usize) < self.counters.len() {
self.counters[token.context as usize].count(token.symbol);
}
self.tokens.push(token);
}
#[must_use]
pub fn len(&self) -> usize {
self.tokens.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.tokens.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &Token> {
self.tokens.iter()
}
#[must_use]
pub fn counter(&self, context: usize) -> Option<&FrequencyCounter> {
self.counters.get(context)
}
pub fn generate_tables(&self) -> Result<Vec<HuffmanEncodeTable>> {
self.counters.iter().map(|c| c.generate_table()).collect()
}
#[must_use]
pub fn estimate_size(&self, tables: &[HuffmanEncodeTable]) -> u64 {
let mut total = 0u64;
for token in &self.tokens {
if let Some(table) = tables.get(token.context as usize) {
let (_, len) = table.encode(token.symbol);
total += len as u64 + token.num_extra as u64;
}
}
total
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_dc() {
let token = Token::dc(0, 5);
assert_eq!(token.context, 0);
assert_eq!(token.symbol, 3); assert_eq!(token.extra_bits, 5);
assert_eq!(token.num_extra, 3);
let token = Token::dc(0, -5);
assert_eq!(token.symbol, 3); }
#[test]
fn test_token_ac() {
let token = Token::ac(1, 2, 7);
assert_eq!(token.context, 1);
assert_eq!(token.symbol, (2 << 4) | 3); assert_eq!(token.num_extra, 3);
let eob = Token::ac(1, 0, 0);
assert_eq!(eob.symbol, 0x00);
let zrl = Token::ac(1, 16, 0);
assert_eq!(zrl.symbol, 0xF0);
}
#[test]
fn test_token_buffer() {
let mut buffer = TokenBuffer::new(2);
buffer.push(Token::dc(0, 10));
buffer.push(Token::ac(1, 0, 5));
buffer.push(Token::ac(1, 0, 0));
assert_eq!(buffer.len(), 3);
assert!(!buffer.is_empty());
assert_eq!(buffer.counter(0).unwrap().num_symbols(), 1); assert_eq!(buffer.counter(1).unwrap().num_symbols(), 2); }
#[test]
fn test_ref_token_new() {
let token = RefToken::new(0x12, 5);
assert_eq!(token.symbol, 0x12);
assert_eq!(token.refbits, 5);
}
#[test]
fn test_ref_token_eob() {
let eob0 = RefToken::eob(0, 0);
assert_eq!(eob0.symbol, 0);
let eob1 = RefToken::eob(1, 0);
assert_eq!(eob1.symbol, 0);
let eob2 = RefToken::eob(2, 0);
assert_eq!(eob2.symbol, 0x10);
let eob3 = RefToken::eob(3, 0);
assert_eq!(eob3.symbol, 0x11);
let eob4 = RefToken::eob(4, 0);
assert_eq!(eob4.symbol, 0x20);
}
#[test]
fn test_scan_token_info() {
let info = ScanTokenInfo::new(4, 1, 63, 0, 2);
assert_eq!(info.context, 4);
assert_eq!(info.ss, 1);
assert_eq!(info.se, 63);
assert_eq!(info.ah, 0);
assert_eq!(info.al, 2);
assert!(!info.is_refinement()); assert!(!info.is_dc()); }
#[test]
fn test_scan_token_info_dc() {
let info = ScanTokenInfo::new(0, 0, 0, 0, 1);
assert!(info.is_dc());
assert!(!info.is_refinement());
}
#[test]
fn test_scan_token_info_refinement() {
let info = ScanTokenInfo::new(4, 1, 63, 2, 1);
assert!(info.is_refinement()); assert!(!info.is_dc());
}
}