#![allow(dead_code)]
use crate::huffman_encode::{self, HufCTable, HufOutput, HufRepeat};
const SET_BASIC: u32 = 0; const SET_RLE: u32 = 1;
const SET_COMPRESSED: u32 = 2;
const SET_REPEAT: u32 = 3;
#[derive(Clone, Default)]
pub(crate) struct HufState {
pub table: Option<HufCTable>,
pub repeat: HufRepeat,
}
pub(crate) fn raw_literals(src: &[u8]) -> Vec<u8> {
let n = src.len();
let fl_size = 1 + (n > 31) as usize + (n > 4095) as usize;
let mut out = Vec::with_capacity(fl_size + n);
write_basic_header(&mut out, SET_BASIC, n, fl_size);
out.extend_from_slice(src);
out
}
pub(crate) fn rle_literals(src: &[u8]) -> Vec<u8> {
let n = src.len();
let fl_size = 1 + (n > 31) as usize + (n > 4095) as usize;
let mut out = Vec::with_capacity(fl_size + 1);
write_basic_header(&mut out, SET_RLE, n, fl_size);
out.push(src[0]);
out
}
fn write_basic_header(out: &mut Vec<u8>, set_type: u32, n: usize, fl_size: usize) {
let n = n as u32;
match fl_size {
1 => out.push((set_type + (n << 3)) as u8), 2 => {
let v = set_type + (1 << 2) + (n << 4); out.extend_from_slice(&(v as u16).to_le_bytes());
}
_ => {
let v = set_type + (3 << 2) + (n << 4); let b = v.to_le_bytes();
out.extend_from_slice(&b[..3]);
}
}
}
fn min_literals_to_compress(strategy: i32, repeat: HufRepeat) -> usize {
if repeat == HufRepeat::Valid {
return 6;
}
let shift = (9 - strategy).clamp(0, 3);
8usize << shift
}
pub(crate) fn min_gain(src_size: usize, strategy: i32) -> usize {
let minlog = if strategy >= 8 {
strategy as u32 - 1
} else {
6
};
(src_size >> minlog) + 2
}
pub(crate) fn compress_literals(
src: &[u8],
strategy: i32,
suspect_uncompressible: bool,
disable_literal_compression: bool,
prev_huf: &HufState,
) -> (Vec<u8>, Option<HufState>) {
const ZSTD_LAZY: i32 = 4;
let n = src.len();
if disable_literal_compression {
return (raw_literals(src), None);
}
if n < min_literals_to_compress(strategy, prev_huf.repeat) {
return (raw_literals(src), None);
}
let lh_size = 3 + (n >= 1024) as usize + (n >= 16384) as usize;
let mut single_stream = n < 256;
if prev_huf.repeat == HufRepeat::Valid && lh_size == 3 {
single_stream = true;
}
let prefer_repeat = strategy < ZSTD_LAZY && n <= 1024;
const HUF_OPTIMAL_DEPTH_THRESHOLD: i32 = 8;
let optimal_depth = strategy >= HUF_OPTIMAL_DEPTH_THRESHOLD;
let outcome = huffman_encode::huf_compress(
src,
single_stream,
suspect_uncompressible,
prefer_repeat,
optimal_depth,
prev_huf.table.as_ref(),
prev_huf.repeat,
);
let (h_type, payload, next) = match outcome {
HufOutput::Raw => return (raw_literals(src), None),
HufOutput::Rle => return (rle_literals(src), None),
HufOutput::Repeat(streams) => (SET_REPEAT, streams, None),
HufOutput::Compressed(payload, new_table) => (
SET_COMPRESSED,
payload,
Some(HufState {
table: Some(*new_table),
repeat: HufRepeat::Check,
}),
),
};
let c_lit_size = payload.len();
if c_lit_size >= n - min_gain(n, strategy) {
return (raw_literals(src), None);
}
let mut out = compressed_header(n, c_lit_size, single_stream, h_type);
out.extend_from_slice(&payload);
(out, next)
}
fn compressed_header(
src_size: usize,
c_lit_size: usize,
single_stream: bool,
h_type: u32,
) -> Vec<u8> {
let lh_size = 3 + (src_size >= 1024) as usize + (src_size >= 16384) as usize;
let four = (!single_stream) as u32;
let srcs = src_size as u32;
let clits = c_lit_size as u32;
let mut out = Vec::with_capacity(lh_size);
match lh_size {
3 => {
let lhc = h_type + (four << 2) + (srcs << 4) + (clits << 14);
out.extend_from_slice(&lhc.to_le_bytes()[..3]);
}
4 => {
let lhc = h_type + (2 << 2) + (srcs << 4) + (clits << 18);
out.extend_from_slice(&lhc.to_le_bytes());
}
_ => {
let lhc = h_type + (3 << 2) + (srcs << 4) + (clits << 22);
out.extend_from_slice(&lhc.to_le_bytes());
out.push((c_lit_size >> 10) as u8);
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::block::{self, BLOCK_SIZE_MAX, FrameContext};
fn assert_round_trip(literals: &[u8]) {
let (section, _next) = compress_literals(literals, 3, false, false, &HufState::default());
let mut ctx = FrameContext::new();
let (decoded, used) = block::decode_literals(&mut ctx, §ion, BLOCK_SIZE_MAX)
.unwrap_or_else(|e| panic!("decode of {}-byte literals failed: {e}", literals.len()));
assert_eq!(decoded, literals, "literals round-trip mismatch");
assert_eq!(used, section.len(), "section length mismatch");
}
fn sample(seed: u64, len: usize, alphabet: u32) -> Vec<u8> {
let mut s = seed | 1;
let mut next = || {
s ^= s >> 12;
s ^= s << 25;
s ^= s >> 27;
s.wrapping_mul(0x2545_F491_4F6C_DD1D)
};
(0..len)
.map(|_| {
let a = next() % alphabet as u64;
let b = next() % alphabet as u64;
a.min(b) as u8
})
.collect()
}
#[test]
fn tiny_inputs_stay_raw() {
for n in 0..40usize {
let data = sample(0x1111 ^ n as u64, n, 20);
assert_round_trip(&data);
}
}
#[test]
fn rle_literals_round_trip() {
for &n in &[1usize, 5, 50, 5000, 100_000] {
assert_round_trip(&vec![0xABu8; n]);
}
}
#[test]
fn compressible_literals_use_huffman_and_round_trip() {
for &alphabet in &[8u32, 40, 120] {
for &len in &[200usize, 300, 2000, 20_000, 70_000] {
assert_round_trip(&sample(0xC0DE ^ (len as u64), len, alphabet));
}
}
}
#[test]
fn incompressible_literals_fall_back_to_raw() {
let mut s = 0x9E37_79B9_7F4A_7C15u64;
let data: Vec<u8> = (0..10_000)
.map(|_| {
s ^= s >> 12;
s ^= s << 25;
s ^= s >> 27;
(s.wrapping_mul(0x2545_F491_4F6C_DD1D) >> 33) as u8
})
.collect();
assert_round_trip(&data);
}
}