use super::utf8::is_mostly_utf8;
use crate::compressor::core::shared::fast_log::fast_log2;
const UTF8_WINDOW_HALF: usize = 495;
const BINARY_WINDOW_HALF: usize = 2000;
const PROLOGUE_LENGTH: usize = 2000;
const PROLOGUE_MULTIPLIER: f64 = 0.35 / 2000.0;
const PROLOGUE_BASE: f64 = 0.35;
const UTF8_NUDGE: f64 = 0.02905;
const BINARY_NUDGE: f64 = 0.029;
const UTF8_POSITIONS: usize = 3;
pub(crate) struct LiteralCostArena {
histogram: Vec<u32>,
}
impl LiteralCostArena {
pub(crate) fn retained_bytes(&self) -> usize {
self.histogram.capacity() * size_of::<u32>()
}
}
impl Default for LiteralCostArena {
fn default() -> Self {
Self {
histogram: vec![0u32; UTF8_POSITIONS * 256],
}
}
}
#[inline]
const fn utf8_position(last: usize, c: usize, clamp: usize) -> usize {
if c < 128 {
0
} else if c >= 192 {
if clamp < 1 { clamp } else { 1 }
} else if last < 0xE0 {
0
} else if clamp < 2 {
clamp
} else {
2
}
}
fn decide_multi_byte_stats_level(pos: usize, len: usize, mask: usize, data: &[u8]) -> usize {
let mut counts = [0usize; UTF8_POSITIONS];
let mut last_c = 0usize;
for index in 0..len {
let c = usize::from(data.get((pos + index) & mask).copied().unwrap_or(0));
counts[utf8_position(last_c, c, 2)] += 1;
last_c = c;
}
if counts[1] + counts[2] < 25 { 0 } else { 1 }
}
pub(crate) fn estimate_bit_costs_for_literals(
pos: usize,
len: usize,
mask: usize,
data: &[u8],
arena: &mut LiteralCostArena,
cost: &mut [f32],
) {
if is_mostly_utf8(data, pos, mask, len) {
estimate_utf8(pos, len, mask, data, arena, cost);
} else {
estimate_binary(pos, len, mask, data, arena, cost);
}
}
fn estimate_utf8(
pos: usize,
len: usize,
mask: usize,
data: &[u8],
arena: &mut LiteralCostArena,
cost: &mut [f32],
) {
let max_utf8 = decide_multi_byte_stats_level(pos, len, mask, data);
let window_half = UTF8_WINDOW_HALF;
let in_window = window_half.min(len);
let mut in_window_utf8 = [0usize; UTF8_POSITIONS];
let histogram = &mut arena.histogram;
histogram.fill(0);
let at = |index: usize| usize::from(data.get(index & mask).copied().unwrap_or(0));
{
let mut last_c = 0usize;
let mut utf8_pos = 0usize;
for index in 0..in_window {
let c = at(pos + index);
histogram[256 * utf8_pos + c] += 1;
in_window_utf8[utf8_pos] += 1;
utf8_pos = utf8_position(last_c, c, max_utf8);
last_c = c;
}
}
for index in 0..len {
if index >= window_half {
let c = if index < window_half + 1 {
0
} else {
at(pos + index - window_half - 1)
};
let last_c = if index < window_half + 2 {
0
} else {
at(pos + index - window_half - 2)
};
let utf8_pos = utf8_position(last_c, c, max_utf8);
histogram[256 * utf8_pos + at(pos + index - window_half)] -= 1;
in_window_utf8[utf8_pos] -= 1;
}
if index + window_half < len {
let c = at(pos + index + window_half - 1);
let last_c = at(pos + index + window_half - 2);
let utf8_pos = utf8_position(last_c, c, max_utf8);
histogram[256 * utf8_pos + at(pos + index + window_half)] += 1;
in_window_utf8[utf8_pos] += 1;
}
{
let c = if index < 1 { 0 } else { at(pos + index - 1) };
let last_c = if index < 2 { 0 } else { at(pos + index - 2) };
let utf8_pos = utf8_position(last_c, c, max_utf8);
let histo = histogram[256 * utf8_pos + at(pos + index)].max(1) as usize;
let mut lit_cost = fast_log2(in_window_utf8[utf8_pos]) - fast_log2(histo);
lit_cost += UTF8_NUDGE;
if lit_cost < 1.0 {
lit_cost *= 0.5;
lit_cost += 0.5;
}
if index < PROLOGUE_LENGTH {
lit_cost += PROLOGUE_BASE + PROLOGUE_MULTIPLIER * index as f64;
}
if let Some(slot) = cost.get_mut(index) {
*slot = lit_cost as f32;
}
}
}
}
fn estimate_binary(
pos: usize,
len: usize,
mask: usize,
data: &[u8],
arena: &mut LiteralCostArena,
cost: &mut [f32],
) {
let window_half = BINARY_WINDOW_HALF;
let mut in_window = window_half.min(len);
let histogram = &mut arena.histogram;
histogram[..256].fill(0);
let at = |index: usize| usize::from(data.get(index & mask).copied().unwrap_or(0));
for index in 0..in_window {
histogram[at(pos + index)] += 1;
}
for index in 0..len {
if index >= window_half {
histogram[at(pos + index - window_half)] -= 1;
in_window -= 1;
}
if index + window_half < len {
histogram[at(pos + index + window_half)] += 1;
in_window += 1;
}
let histo = histogram[at(pos + index)].max(1) as usize;
let mut lit_cost = fast_log2(in_window) - fast_log2(histo);
lit_cost += BINARY_NUDGE;
if lit_cost < 1.0 {
lit_cost *= 0.5;
lit_cost += 0.5;
}
if let Some(slot) = cost.get_mut(index) {
*slot = lit_cost as f32;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn costs(data: &[u8]) -> Vec<f32> {
let mut cost = vec![0f32; data.len()];
let mut arena = LiteralCostArena::default();
estimate_bit_costs_for_literals(0, data.len(), usize::MAX, data, &mut arena, &mut cost);
cost
}
#[test]
fn the_position_classes_follow_the_reference() {
assert_eq!(utf8_position(0, usize::from(b'a'), 2), 0);
assert_eq!(utf8_position(0, 0xC3, 2), 1);
assert_eq!(utf8_position(0xC3, 0xA9, 2), 0);
assert_eq!(utf8_position(0xE2, 0x82, 2), 2);
assert_eq!(utf8_position(0xE2, 0x82, 1), 1);
assert_eq!(utf8_position(0, 0xC3, 0), 0);
}
#[test]
fn barely_any_multi_byte_content_drops_to_one_class() {
let ascii = vec![b'a'; 4096];
assert_eq!(
decide_multi_byte_stats_level(0, ascii.len(), usize::MAX, &ascii),
0
);
let mut text = Vec::new();
while text.len() < 4096 {
text.extend_from_slice("héllo wörld ".as_bytes());
}
assert_eq!(
decide_multi_byte_stats_level(0, text.len(), usize::MAX, &text),
1
);
}
#[test]
fn a_predictable_byte_costs_less_than_a_surprising_one() {
let mut data = vec![b'a'; 3000];
data[2500] = b'Z';
let cost = costs(&data);
assert!(
cost[2500] > cost[2400] * 4.0,
"{} vs {}",
cost[2500],
cost[2400]
);
}
#[test]
fn no_cost_falls_below_the_half_bit_floor() {
let data = vec![b'q'; 8000];
for (index, &bits) in costs(&data).iter().enumerate() {
assert!(bits >= 0.5, "byte {index} cost {bits}");
}
}
#[test]
fn the_prologue_surcharge_grows_and_then_vanishes() {
let data = vec![b'x'; 8000];
let cost = costs(&data);
let base = cost[PROLOGUE_LENGTH];
assert!((f64::from(cost[0]) - f64::from(base) - PROLOGUE_BASE).abs() < 1e-6);
assert!(cost[0] < cost[100]);
assert!(cost[100] < cost[1900]);
assert!(cost[1999] > cost[PROLOGUE_LENGTH]);
assert_eq!(cost[PROLOGUE_LENGTH], cost[PROLOGUE_LENGTH + 500]);
let last = f64::from(cost[PROLOGUE_LENGTH - 1]) - f64::from(base);
assert!((last - (PROLOGUE_BASE + PROLOGUE_MULTIPLIER * 1999.0)).abs() < 1e-6);
}
#[test]
fn text_and_binary_take_different_paths() {
let mut text = Vec::new();
while text.len() < 4000 {
text.extend_from_slice("naïve café ".as_bytes());
}
text.truncate(4000);
assert!(is_mostly_utf8(&text, 0, usize::MAX, text.len()));
let binary: Vec<u8> = (0..4000u32).map(|i| (i * 37 % 256) as u8).collect();
assert!(!is_mostly_utf8(&binary, 0, usize::MAX, binary.len()));
for data in [text, binary] {
for &bits in &costs(&data) {
assert!(bits.is_finite() && bits > 0.0, "cost was {bits}");
}
}
}
#[test]
fn an_empty_block_prices_nothing() {
assert!(costs(b"").is_empty());
}
#[test]
fn a_wrapping_block_prices_the_same_bytes() {
let text = b"the quick brown fox jumps over the lazy dog, twice over";
let mut ring = vec![0u8; 128];
let mask = ring.len() - 1;
let start = ring.len() - 20;
for (offset, &byte) in text.iter().enumerate() {
ring[(start + offset) & mask] = byte;
}
let mut wrapped = vec![0f32; text.len()];
let mut arena = LiteralCostArena::default();
estimate_bit_costs_for_literals(start, text.len(), mask, &ring, &mut arena, &mut wrapped);
assert_eq!(wrapped, costs(text));
}
#[test]
fn the_arena_can_be_reused_without_changing_the_result() {
let first = b"the first block of literals, long enough to matter a bit";
let second: Vec<u8> = (0..3000u32).map(|i| (i * 11 % 256) as u8).collect();
let mut arena = LiteralCostArena::default();
let mut once = vec![0f32; second.len()];
estimate_bit_costs_for_literals(
0,
second.len(),
usize::MAX,
&second,
&mut arena,
&mut once,
);
let mut scratch = vec![0f32; first.len()];
let mut reused = LiteralCostArena::default();
estimate_bit_costs_for_literals(
0,
first.len(),
usize::MAX,
first,
&mut reused,
&mut scratch,
);
let mut twice = vec![0f32; second.len()];
estimate_bit_costs_for_literals(
0,
second.len(),
usize::MAX,
&second,
&mut reused,
&mut twice,
);
assert_eq!(once, twice);
}
}