use alloc::vec::Vec;
use super::utf8::is_mostly_utf8;
use crate::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>,
block: Vec<u8>,
}
impl LiteralCostArena {
pub(crate) fn retained_bytes(&self) -> usize {
self.histogram.capacity() * size_of::<u32>() + self.block.capacity()
}
}
impl Default for LiteralCostArena {
fn default() -> Self {
Self {
histogram: vec![0u32; UTF8_POSITIONS * 256],
block: Vec::new(),
}
}
}
pub(crate) fn contiguous_block<'a>(
data: &'a [u8],
pos: usize,
mask: usize,
len: usize,
scratch: &'a mut Vec<u8>,
) -> &'a [u8] {
let end = pos + len;
let direct = end <= data.len() && (end.saturating_sub(1) <= mask || len == 0);
if let (true, Some(block)) = (direct, data.get(pos..end)) {
return block;
}
scratch.clear();
scratch.extend((pos..end).map(|index| data.get(index & mask).copied().unwrap_or(0)));
scratch
}
#[derive(Clone, Copy)]
struct Log2Memo {
value: usize,
log: f64,
}
impl Log2Memo {
const EMPTY: Self = Self { value: 0, log: 0.0 };
#[inline(always)]
fn get(&mut self, value: usize) -> f64 {
if value != self.value {
self.value = value;
self.log = fast_log2(value);
}
self.log
}
}
#[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(block: &[u8]) -> usize {
let mut counts = [0usize; UTF8_POSITIONS];
let mut last_c = 0usize;
for &byte in block {
let c = usize::from(byte);
counts[utf8_position(last_c, c, 2)] += 1;
last_c = c;
}
if counts[1] + counts[2] < 25 { 0 } else { 1 }
}
#[cfg_attr(all(feature = "hotpath", not(feature = "no_std")), hotpath::measure)]
pub(crate) fn estimate_bit_costs_for_literals(
pos: usize,
len: usize,
mask: usize,
data: &[u8],
arena: &mut LiteralCostArena,
cost: &mut [f32],
) {
let LiteralCostArena { histogram, block } = arena;
let block = contiguous_block(data, pos, mask, len, block);
let Some(cost) = cost.get_mut(..len) else {
return;
};
if is_mostly_utf8(block, 0, usize::MAX, len) {
estimate_utf8(block, histogram, cost);
} else {
estimate_binary(block, histogram, cost);
}
}
fn estimate_utf8(block: &[u8], histogram: &mut [u32], cost: &mut [f32]) {
let len = block.len().min(cost.len());
let block = &block[..len];
let cost = &mut cost[..len];
let Some(histogram) = histogram.get_mut(..UTF8_POSITIONS * 256) else {
return;
};
let max_utf8 = decide_multi_byte_stats_level(block);
let window_half = UTF8_WINDOW_HALF;
let in_window = window_half.min(len);
let mut in_window_utf8 = [0usize; UTF8_POSITIONS];
let mut window_log = [Log2Memo::EMPTY; UTF8_POSITIONS];
let mut histo_log = Log2Memo::EMPTY;
histogram.fill(0);
let at = |index: usize| usize::from(block[index]);
{
let mut last_c = 0usize;
let mut utf8_pos = 0usize;
for &byte in &block[..in_window] {
let c = usize::from(byte);
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(index - window_half - 1)
};
let last_c = if index < window_half + 2 {
0
} else {
at(index - window_half - 2)
};
let utf8_pos = utf8_position(last_c, c, max_utf8);
histogram[256 * utf8_pos + at(index - window_half)] -= 1;
in_window_utf8[utf8_pos] -= 1;
}
if index + window_half < len {
let c = at(index + window_half - 1);
let last_c = at(index + window_half - 2);
let utf8_pos = utf8_position(last_c, c, max_utf8);
histogram[256 * utf8_pos + at(index + window_half)] += 1;
in_window_utf8[utf8_pos] += 1;
}
{
let c = if index < 1 { 0 } else { at(index - 1) };
let last_c = if index < 2 { 0 } else { at(index - 2) };
let utf8_pos = utf8_position(last_c, c, max_utf8);
let histo = histogram[256 * utf8_pos + at(index)].max(1) as usize;
let mut lit_cost =
window_log[utf8_pos].get(in_window_utf8[utf8_pos]) - histo_log.get(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;
}
cost[index] = lit_cost as f32;
}
}
}
fn estimate_binary(block: &[u8], histogram: &mut [u32], cost: &mut [f32]) {
let len = block.len().min(cost.len());
let block = &block[..len];
let cost = &mut cost[..len];
let Some(histogram) = histogram.get_mut(..256) else {
return;
};
let window_half = BINARY_WINDOW_HALF;
let mut in_window = window_half.min(len);
let mut window_log = Log2Memo::EMPTY;
let mut histo_log = Log2Memo::EMPTY;
histogram.fill(0);
let at = |index: usize| usize::from(block[index]);
for &byte in &block[..in_window] {
histogram[usize::from(byte)] += 1;
}
for index in 0..len {
if index >= window_half {
histogram[at(index - window_half)] -= 1;
in_window -= 1;
}
if index + window_half < len {
histogram[at(index + window_half)] += 1;
in_window += 1;
}
let histo = histogram[at(index)].max(1) as usize;
let mut lit_cost = window_log.get(in_window) - histo_log.get(histo);
lit_cost += BINARY_NUDGE;
if lit_cost < 1.0 {
lit_cost *= 0.5;
lit_cost += 0.5;
}
cost[index] = 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(&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(&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);
}
}