use std::collections::{HashMap, VecDeque};
pub const N_CLASSES: usize = 5;
pub const N_DECAYS: usize = 5;
pub const N_BANDS: usize = N_CLASSES * N_DECAYS;
pub const TAUS: [f32; N_DECAYS] = [2.0, 8.0, 32.0, 128.0, 512.0];
#[must_use]
pub fn classify(b: u8) -> usize {
if b.is_ascii_digit() {
0
} else if b == b'_' || b.is_ascii_alphabetic() {
1
} else if b.is_ascii_whitespace() {
2
} else if b.is_ascii_graphic() {
3
} else {
4
}
}
#[must_use]
pub fn char_class(ch: char) -> usize {
let combining = matches!(ch as u32, 0x0300..=0x036F | 0x1AB0..=0x1AFF | 0x1DC0..=0x1DFF | 0x20D0..=0x20FF | 0xFE20..=0xFE2F);
if ch.is_alphabetic() || combining {
1
} else if ch.is_numeric() {
0
} else if ch.is_whitespace() {
2
} else {
3
}
}
pub(crate) fn class_symbols(input: &[u8], ascii: impl Fn(u8) -> usize) -> Vec<u32> {
let mut out: Vec<u32> = Vec::with_capacity(input.len());
for chunk in input.utf8_chunks() {
for ch in chunk.valid().chars() {
let k = ch.len_utf8();
if k == 1 {
out.push(ascii(ch as u8) as u32);
continue;
}
let c = char_class(ch) as u32;
if c == 1 {
out.extend(std::iter::repeat_n(1, k));
} else {
out.push(c);
out.extend(std::iter::repeat_n(N_CLASSES as u32, k - 1));
}
}
out.extend(std::iter::repeat_n(4, chunk.invalid().len()));
}
out
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Release {
class: usize,
count: usize,
age: usize,
}
#[derive(Clone, Copy, Debug, Default)]
struct Utf8Classes {
held: [u8; 4],
len: u8,
need: u8,
}
impl Utf8Classes {
fn idle(&self) -> bool {
self.len == 0
}
fn abandon(&mut self) -> Option<Release> {
let count = usize::from(self.len);
self.len = 0;
self.need = 0;
(count > 0).then_some(Release { class: 4, count, age: 1 })
}
fn push(&mut self, b: u8) -> [Option<Release>; 2] {
match b {
0x80..=0xBF if self.len > 0 => {
self.held[usize::from(self.len)] = b;
self.len += 1;
if self.len < self.need {
return [None, None];
}
let count = usize::from(self.len);
let class = std::str::from_utf8(&self.held[..count])
.ok()
.and_then(|s| s.chars().next())
.map_or(4, char_class);
self.len = 0;
self.need = 0;
[Some(Release { class, count, age: 0 }), None]
}
0xC2..=0xF4 => {
let abandoned = self.abandon();
self.held[0] = b;
self.len = 1;
self.need = match b {
0xC2..=0xDF => 2,
0xE0..=0xEF => 3,
_ => 4,
};
[abandoned, None]
}
_ => {
let abandoned = self.abandon();
let class = if b < 0x80 { classify(b) } else { 4 };
[abandoned, Some(Release { class, count: 1, age: 0 })]
}
}
}
fn finish(&mut self) -> Option<Release> {
self.abandon().map(|r| Release { age: 0, ..r })
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Texture {
Prose,
Code,
Math,
Data,
Mixed,
}
impl Texture {
#[must_use]
pub fn label(self) -> &'static str {
match self {
Texture::Prose => "prose",
Texture::Code => "code",
Texture::Math => "math",
Texture::Data => "data",
Texture::Mixed => "mixed",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct SpectralFrame {
pub bands: [f32; N_BANDS],
pub entropy: f32,
pub period: u16,
pub period_strength: f32,
pub novelty: f32,
pub(crate) wide: f32,
}
impl SpectralFrame {
#[must_use]
pub fn texture_mix(&self) -> [f32; N_CLASSES] {
let base = 2 * N_CLASSES;
let mut out = [0.0f32; N_CLASSES];
out.copy_from_slice(&self.bands[base..base + N_CLASSES]);
out
}
}
#[derive(Clone, Copy, Debug)]
pub struct SpectralConfig {
pub hop: usize,
pub entropy_window: usize,
pub period_window: usize,
pub max_lag: usize,
pub period_hop: usize,
pub ngram: usize,
pub novelty_window: usize,
pub cp_threshold: f32,
pub cp_floor: f32,
pub cp_min_gap: usize,
}
impl Default for SpectralConfig {
fn default() -> Self {
Self {
hop: 16,
entropy_window: 64,
period_window: 512,
max_lag: 128,
period_hop: 64,
ngram: 4,
novelty_window: 4096,
cp_threshold: 4.0,
cp_floor: 0.05,
cp_min_gap: 4,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct SpectralField {
pub len: usize,
pub hop: usize,
pub frames: Vec<SpectralFrame>,
pub boundaries: Vec<usize>,
pub needs: Needs,
}
impl Default for Needs {
fn default() -> Needs {
Needs::all()
}
}
impl SpectralField {
pub fn assert_carries(&self, want: Needs, who: &str) {
debug_assert!(
!(want.entropy && !self.needs.entropy)
&& !(want.period && !self.needs.period)
&& !(want.bands && !self.needs.bands)
&& !(want.onset && !self.needs.onset)
&& !(want.novelty && !self.needs.novelty),
"{who} reads {want:?} from a field built for {:?}",
self.needs
);
}
fn idx_of(&self, byte: usize) -> usize {
if self.frames.is_empty() {
return 0;
}
(byte / self.hop.max(1)).min(self.frames.len() - 1)
}
#[must_use]
pub fn frame_at(&self, byte: usize) -> SpectralFrame {
if self.frames.is_empty() {
return SpectralFrame::default();
}
self.frames[self.idx_of(byte)]
}
#[must_use]
pub fn signature(&self, start: usize, end: usize) -> SpectralFrame {
if self.frames.is_empty() || end <= start {
return SpectralFrame::default();
}
let lo = self.idx_of(start);
let hi = self.idx_of(end.saturating_sub(1));
let mut acc = SpectralFrame::default();
let mut n = 0.0f32;
let mut best_strength = -1.0f32;
for f in &self.frames[lo..=hi] {
for (a, b) in acc.bands.iter_mut().zip(f.bands.iter()) {
*a += *b;
}
acc.entropy += f.entropy;
acc.novelty += f.novelty;
acc.wide += f.wide;
if f.period_strength > best_strength {
best_strength = f.period_strength;
acc.period = f.period;
acc.period_strength = f.period_strength;
}
n += 1.0;
}
if n > 0.0 {
for a in &mut acc.bands {
*a /= n;
}
acc.entropy /= n;
acc.novelty /= n;
acc.wide /= n;
}
acc
}
#[must_use]
pub fn boundary_near(&self, byte: usize, tol: usize) -> bool {
let i = self.boundaries.partition_point(|&b| b.saturating_add(tol) < byte);
self.boundaries.get(i).is_some_and(|&b| b <= byte.saturating_add(tol))
}
#[must_use]
pub fn boundary_in(&self, start: usize, end: usize) -> bool {
let i = self.boundaries.partition_point(|&b| b < start);
self.boundaries.get(i).is_some_and(|&b| b < end)
}
}
fn over_entropy_gate(f: &SpectralFrame, gate: f32) -> bool {
f.entropy > gate + (1.0 - gate) * f.wide
}
#[must_use]
pub fn texture_of(f: &SpectralFrame) -> Texture {
let m = f.texture_mix();
let (digit, alpha, _space, punct, high) = (m[0], m[1], m[2], m[3], m[4]);
if over_entropy_gate(f, 0.85) || high > 0.30 {
return Texture::Data;
}
let total = digit + alpha + punct + high + m[2] + 1e-6;
let punct_frac = punct / total;
let alpha_frac = alpha / total;
let digit_frac = digit / total;
if alpha_frac > 0.55 && punct_frac < 0.12 {
Texture::Prose
} else if punct_frac > 0.20 && alpha_frac > 0.12 {
Texture::Code
} else if punct_frac > 0.12 && digit_frac > 0.10 {
Texture::Math
} else {
Texture::Mixed
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum CodeTexture {
Code,
Blob,
Prose,
Numeric,
Mixed,
}
impl CodeTexture {
#[must_use]
pub fn key_suffix(self) -> Option<char> {
match self {
CodeTexture::Code | CodeTexture::Mixed => None,
CodeTexture::Blob => Some('b'),
CodeTexture::Prose => Some('p'),
CodeTexture::Numeric => Some('n'),
}
}
}
#[must_use]
pub fn code_texture(f: &SpectralFrame) -> CodeTexture {
let m = f.texture_mix();
let (digit, alpha, _space, punct, high) = (m[0], m[1], m[2], m[3], m[4]);
if over_entropy_gate(f, 0.78) || high > 0.30 {
return CodeTexture::Blob;
}
let total = digit + alpha + punct + high + m[2] + 1e-6;
let punct_frac = punct / total;
let alpha_frac = alpha / total;
let digit_frac = digit / total;
if digit_frac > 0.45 && alpha_frac < 0.30 {
CodeTexture::Numeric
} else if alpha_frac > 0.60 && punct_frac < 0.08 {
CodeTexture::Prose
} else if punct_frac > 0.15 {
CodeTexture::Code
} else {
CodeTexture::Mixed
}
}
pub(crate) fn log_table() -> &'static [f64] {
const N: usize = 4096;
static LUT: std::sync::OnceLock<Vec<f64>> = std::sync::OnceLock::new();
LUT.get_or_init(|| {
(0..N as u32).map(|c| if c > 1 { f64::from(c) * f64::from(c).log2() } else { 0.0 }).collect()
})
}
pub(crate) const FIXED_LOG_SCALE: f64 = 4_294_967_296.0;
pub(crate) fn fixed_log_table() -> &'static [i64] {
const N: usize = 4096;
static LUT: std::sync::OnceLock<Vec<i64>> = std::sync::OnceLock::new();
LUT.get_or_init(|| (0..N as u32).map(fixed_term_computed).collect())
}
pub(crate) fn fixed_log_delta_table() -> &'static [i64] {
const N: usize = 4096;
static LUT: std::sync::OnceLock<Vec<i64>> = std::sync::OnceLock::new();
LUT.get_or_init(|| (0..N as u32).map(|c| fixed_term_computed(c + 1) - fixed_term_computed(c)).collect())
}
#[inline]
pub(crate) fn fixed_term(lut: &[i64], c: u32) -> i64 {
if let Some(&v) = lut.get(c as usize) {
return v;
}
fixed_term_computed(c)
}
fn fixed_term_computed(c: u32) -> i64 {
if c > 1 {
let cf = f64::from(c);
(cf * cf.log2() * FIXED_LOG_SCALE).round() as i64
} else {
0
}
}
#[inline]
pub fn log_term(c: u32) -> f64 {
let lut = log_table();
if let Some(&v) = lut.get(c as usize) {
return v;
}
if c > 1 {
let cf = f64::from(c);
cf * cf.log2()
} else {
0.0
}
}
struct PeriodScanner {
win: usize,
max_lag: usize,
cross: Vec<i32>,
prefix: Vec<i64>,
prefix_sq: Vec<i64>,
at: Option<usize>,
}
impl PeriodScanner {
fn new(input: &[u8], win: usize, max_lag: usize) -> Self {
let mut prefix = Vec::with_capacity(input.len() + 1);
let mut prefix_sq = Vec::with_capacity(input.len() + 1);
let (mut s, mut q) = (0i64, 0i64);
prefix.push(0);
prefix_sq.push(0);
for &b in input {
let v = i64::from(b);
s += v;
q += v * v;
prefix.push(s);
prefix_sq.push(q);
}
let hi = max_lag.min(win / 2);
PeriodScanner {
win,
max_lag,
cross: vec![0; hi.saturating_sub(1)],
prefix,
prefix_sq,
at: None,
}
}
fn hi(&self) -> usize {
self.max_lag.min(self.win / 2)
}
fn seed(&mut self, input: &[u8], end: usize) {
let start = end - self.win;
let hi = self.hi();
self.cross.fill(0);
for t in start..end.saturating_sub(2) {
let x = i32::from(input[t]);
let last = hi.min(end - 1 - t);
let partners = &input[t + 2..=t + last];
for (c, &y) in self.cross.iter_mut().zip(partners) {
*c += x * i32::from(y);
}
}
self.at = Some(end);
}
fn advance(&mut self, input: &[u8], end: usize) {
let Some(prev) = self.at else {
self.seed(input, end);
return;
};
let step = end - prev;
if step >= self.win {
self.seed(input, end);
return;
}
let old_start = prev - self.win;
let new_start = end - self.win;
let hi = self.hi();
for t in old_start..new_start {
let x = i32::from(input[t]);
let partners = &input[t + 2..=t + hi];
for (c, &y) in self.cross.iter_mut().zip(partners) {
*c -= x * i32::from(y);
}
}
for u in prev..end {
let x = i32::from(input[u]);
let partners = &input[u - hi..=u - 2];
for (c, &y) in self.cross.iter_mut().zip(partners.iter().rev()) {
*c += x * i32::from(y);
}
}
self.at = Some(end);
}
fn eval(&mut self, input: &[u8], end: usize) -> (u16, f32) {
if self.win < 4 || end < self.win {
return (0, 0.0);
}
self.advance(input, end);
let start = end - self.win;
let n = self.win as f64;
let total = (self.prefix[end] - self.prefix[start]) as f64;
let m = total / n;
let sumsq = (self.prefix_sq[end] - self.prefix_sq[start]) as f64;
let denom = sumsq - n * m * m;
if denom <= f64::EPSILON {
return (0, 0.0);
}
let (mut best_lag, mut best) = (0usize, 0.0f64);
for lag in 2..=self.hi() {
let a = (self.prefix[end - lag] - self.prefix[start]) as f64;
let b = (self.prefix[end] - self.prefix[start + lag]) as f64;
let len = (self.win - lag) as f64;
let corr = f64::from(self.cross[lag - 2]) - m * (a + b) + len * m * m;
let r = corr / denom;
if r > best {
best = r;
best_lag = lag;
}
}
let best = best as f32;
if best < PERIOD_FLOOR { (0, best.max(0.0)) } else { (best_lag as u16, best) }
}
}
pub(crate) const PERIOD_FLOOR: f32 = 0.20;
const LANES: usize = 16;
pub fn dominant_period(win: &[u8], max_lag: usize) -> (u16, f32) {
let n = win.len();
if n < 4 {
return (0, 0.0);
}
let mean = win.iter().map(|&b| f32::from(b)).sum::<f32>() / n as f32;
let c: Vec<f32> = win.iter().map(|&b| f32::from(b) - mean).collect();
let denom: f32 = c.iter().map(|&x| x * x).sum();
if denom <= f32::EPSILON {
return (0, 0.0);
}
let hi = max_lag.min(n / 2);
let mut best_lag = 0usize;
let mut best = 0.0f32;
for lag in 2..=hi {
let a = &c[lag..];
let b = &c[..n - lag];
let (a_ch, a_rest) = a.as_chunks::<LANES>();
let (b_ch, b_rest) = b.as_chunks::<LANES>();
let mut acc = [0.0f32; LANES];
for (ca, cb) in a_ch.iter().zip(b_ch) {
for ((acck, &x), &y) in acc.iter_mut().zip(ca).zip(cb) {
*acck += x * y;
}
}
let mut sum: f32 = acc.iter().sum();
for (&x, &y) in a_rest.iter().zip(b_rest) {
sum += x * y;
}
let r = sum / denom;
if r > best {
best = r;
best_lag = lag;
}
}
if best < PERIOD_FLOOR {
(0, best.max(0.0))
} else {
(best_lag as u16, best)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Needs {
pub entropy: bool,
pub period: bool,
pub bands: bool,
pub onset: bool,
pub novelty: bool,
}
impl Needs {
#[must_use]
pub fn all() -> Needs {
Needs { entropy: true, period: true, bands: true, onset: true, novelty: true }
}
#[must_use]
pub fn none() -> Needs {
Needs { entropy: false, period: false, bands: false, onset: false, novelty: false }
}
#[must_use]
pub fn of(pred: &crate::ast::SpectralPred) -> Needs {
use crate::ast::SpectralPred as P;
let mut needs = Needs::none();
match pred {
P::EntropyGe(_) | P::EntropyLe(_) => needs.entropy = true,
P::PeriodEq(_) | P::PeriodAny => needs.period = true,
P::Texture(_) => {
needs.entropy = true;
needs.bands = true;
}
P::Onset => {
needs.entropy = true;
needs.bands = true;
needs.onset = true;
}
}
needs
}
#[must_use]
pub fn and(self, other: Needs) -> Needs {
Needs {
entropy: self.entropy || other.entropy,
period: self.period || other.period,
bands: self.bands || other.bands,
onset: self.onset || other.onset,
novelty: self.novelty || other.novelty,
}
}
#[must_use]
pub fn any(self) -> bool {
self != Needs::none()
}
}
#[must_use]
pub fn analyze(input: &[u8]) -> SpectralField {
analyze_with(input, &SpectralConfig::default())
}
#[must_use]
pub fn analyze_with(input: &[u8], cfg: &SpectralConfig) -> SpectralField {
analyze_needing(input, cfg, Needs::all())
}
#[must_use]
pub fn analyze_needing(input: &[u8], cfg: &SpectralConfig, needs: Needs) -> SpectralField {
let n = input.len();
let hop = cfg.hop.max(1);
if n == 0 {
return SpectralField { len: 0, hop, frames: Vec::new(), boundaries: Vec::new(), needs };
}
let mut a = [0.0f32; N_DECAYS];
let mut oma = [0.0f32; N_DECAYS];
for ((ad, od), &tau) in a.iter_mut().zip(oma.iter_mut()).zip(TAUS.iter()) {
*ad = (-1.0 / tau).exp();
*od = 1.0 - *ad;
}
let mut bands = [0.0f32; N_BANDS];
let mut decay = [0.0f32; N_BANDS];
for (d, &ad) in a.iter().enumerate() {
decay[d * N_CLASSES..(d + 1) * N_CLASSES].fill(ad);
}
let mut held_pow = [[1.0f32; 5]; N_DECAYS];
for (row, &ad) in held_pow.iter_mut().zip(a.iter()) {
for k in 1..row.len() {
row[k] = row[k - 1] * ad;
}
}
let mut utf8 = Utf8Classes::default();
let mut wide = 0.0f32;
let w = cfg.entropy_window.max(1);
let mut hist = [0u32; 256];
let mut s = 0i64;
let entropy_norm = (w.min(256) as f32).max(2.0).log2();
let k = cfg.ngram.max(1);
let nov_win = cfg.novelty_window.max(1);
let mut ngram_counts: HashMap<u64, u32, std::hash::BuildHasherDefault<crate::tokutil::IdHash>> =
HashMap::default();
let mut ngram_ring: VecDeque<u64> = VecDeque::with_capacity(nov_win + 1);
let mut cur_novelty = 0.0f32;
let pwin = cfg.period_window.max(8);
let max_lag = cfg.max_lag.clamp(2, pwin / 2);
let per_eval = pwin.saturating_mul(max_lag).max(1);
const PERIOD_OP_BUDGET: usize = 1_000_000_000;
let max_evals = (PERIOD_OP_BUDGET / per_eval).max(1);
let period_hop = (n / max_evals).max(cfg.period_hop).max(1);
let mut period_evals = 0u64;
let mut cur_period = 0u16;
let mut cur_strength = 0.0f32;
let mut period_scanner = PeriodScanner::new(input, pwin, max_lag);
let ent_lut = fixed_log_table();
let ent_full_log2 = (w as f64).log2();
let ent_full_recip = 1.0 / (w as f64 * FIXED_LOG_SCALE);
const CP_ALPHA: f32 = 0.02;
const CP_CLIP: f32 = 3.0;
const CP_NOISE: f32 = 1e-6;
const CP_WARMUP: usize = 32;
const CP_RETURN_SHARE: f32 = 0.5;
const CP_RETURN_SUSTAIN: u32 = 12;
const ENT_SLOW_A: f32 = 0.992;
const ENT_WEIGHT: f32 = 2.0;
let mut ent_slow = 0.0f32;
let mut pow_f = 1.0f32;
let mut pow_s = 1.0f32;
let mut pow_e = 1.0f32;
let mut settled = false;
let mut cp_mean = 0.0f32;
let mut cp_mad = 0.0f32;
let mut cp_armed = true;
let mut regime: Option<([f32; N_CLASSES], f32, u32, u32)> = None;
let mut last_boundary: isize = -(cfg.cp_min_gap as isize);
let mut boundaries: Vec<usize> = Vec::new();
let mut frames: Vec<SpectralFrame> = Vec::with_capacity(n / hop + 1);
for i in 0..n {
let b = input[i];
if needs.bands {
for (v, &dk) in bands.iter_mut().zip(decay.iter()) {
*v *= dk;
}
wide *= a[2];
if b < 0x80 && utf8.idle() {
let c = classify(b);
for (d, &od) in oma.iter().enumerate() {
bands[d * N_CLASSES + c] += od;
}
} else {
let [first, second] = utf8.push(b);
let last = if i + 1 == n { utf8.finish() } else { None };
for r in [first, second, last].into_iter().flatten() {
if r.count > 1 && r.class != 1 && r.class != 4 {
let k = r.count;
for (d, p) in held_pow.iter().enumerate() {
let row = &mut bands[d * N_CLASSES..(d + 1) * N_CLASSES];
let target = row.iter().sum::<f32>() + (1.0 - p[k]);
let mut mix = [0.0f32; N_CLASSES];
for (c, v) in mix.iter_mut().enumerate() {
*v = row[c] / p[k - 1] + if c == r.class { 1.0 - p[1] } else { 0.0 };
}
let scale = target / mix.iter().sum::<f32>();
for (v, m) in row.iter_mut().zip(mix.iter()) {
*v = m * scale;
}
}
} else {
for (d, p) in held_pow.iter().enumerate() {
bands[d * N_CLASSES + r.class] += p[r.age] * (1.0 - p[r.count]);
}
}
if r.count > 1 && r.class != 4 {
let p = &held_pow[2];
wide += p[r.age] * (1.0 - p[r.count]);
}
}
}
}
let cur_entropy = if needs.entropy {
let nb = hist[b as usize];
hist[b as usize] = nb + 1;
let mut delta = fixed_term(ent_lut, nb + 1) - fixed_term(ent_lut, nb);
if i >= w {
let ob = input[i - w] as usize;
let oc = hist[ob];
hist[ob] = oc - 1;
delta -= fixed_term(ent_lut, oc) - fixed_term(ent_lut, oc - 1);
}
s += delta;
let (log_nwin, recip) = if i + 1 >= w {
(ent_full_log2, ent_full_recip)
} else {
let nwin = f64::from((i + 1) as u32);
(nwin.log2(), 1.0 / (nwin * FIXED_LOG_SCALE))
};
let hbits = (log_nwin - s as f64 * recip) as f32;
(hbits / entropy_norm).clamp(0.0, 1.0)
} else {
0.0
};
if needs.novelty && i + 1 >= k {
let mut h = 1_469_598_103_934_665_603u64;
for &x in &input[i + 1 - k..=i] {
h ^= u64::from(x);
h = h.wrapping_mul(1_099_511_628_211);
}
let count = ngram_counts.entry(h).or_insert(0);
cur_novelty = 1.0 / (1.0 + *count as f32);
*count += 1;
ngram_ring.push_back(h);
if ngram_ring.len() > nov_win
&& let Some(old) = ngram_ring.pop_front()
&& let Some(cc) = ngram_counts.get_mut(&old)
{
*cc -= 1;
if *cc == 0 {
ngram_counts.remove(&old);
}
}
}
if needs.period && i + 1 >= pwin && i % period_hop == 0 {
period_evals += 1;
let (p, st) = period_scanner.eval(input, i + 1);
cur_period = p;
cur_strength = st;
}
if needs.onset {
let (corr_f, corr_s, corr_e);
if settled {
corr_f = 1.0;
corr_s = 1.0;
corr_e = 1.0;
} else {
pow_f *= a[1];
pow_s *= a[3];
pow_e *= ENT_SLOW_A;
corr_f = 1.0 / (1.0 - pow_f).max(1e-4);
corr_s = 1.0 / (1.0 - pow_s).max(1e-4);
corr_e = (1.0 - pow_e).max(1e-4);
settled = corr_f == 1.0 && corr_s == 1.0 && corr_e == 1.0;
}
ent_slow += (1.0 - ENT_SLOW_A) * (cur_entropy - ent_slow);
let ent_slow_c = if corr_e == 1.0 { ent_slow } else { ent_slow / corr_e };
let fast_mix = &bands[N_CLASSES..2 * N_CLASSES];
let slow_mix = &bands[3 * N_CLASSES..4 * N_CLASSES];
let pairs = fast_mix.iter().zip(slow_mix.iter());
let class_div: f32 = if settled {
pairs.map(|(x, y)| (x - y).powi(2)).sum()
} else {
pairs.map(|(x, y)| (x * corr_f - y * corr_s).powi(2)).sum()
};
let dist = class_div.sqrt() + ENT_WEIGHT * (cur_entropy - ent_slow_c).abs();
if i == 0 {
cp_mean = dist;
cp_mad = dist * 0.5 + 0.01;
} else {
let floored = if cfg.cp_floor > 0.0 && cfg.cp_threshold > 0.0 {
cp_mad.max(cfg.cp_floor * cp_mean / cfg.cp_threshold)
} else {
cp_mad
};
let unit = floored.max(CP_NOISE);
let dev = if i < CP_WARMUP { dist - cp_mean } else { (dist - cp_mean).clamp(-CP_CLIP * unit, CP_CLIP * unit) };
cp_mean += CP_ALPHA * dev;
cp_mad += CP_ALPHA * (dev.abs() - cp_mad);
}
let spread = cfg.cp_threshold * cp_mad;
let thresh = cp_mean
+ if cfg.cp_floor > 0.0 { spread.max(cfg.cp_floor * cp_mean) } else { spread };
if i >= CP_WARMUP {
let spaced = i as isize - last_boundary >= cfg.cp_min_gap as isize;
if cp_armed && dist > thresh && spaced {
boundaries.push(i);
last_boundary = i as isize;
cp_armed = false;
let mut left = [0.0f32; N_CLASSES];
for (l, &y) in left.iter_mut().zip(slow_mix.iter()) {
*l = y * corr_s;
}
regime = Some((left, 0.0, 0, 0));
} else {
if !cp_armed && dist < thresh * 0.5 {
cp_armed = true;
}
if let Some((left, sum, count, under)) = regime.as_mut() {
let back: f32 =
fast_mix.iter().zip(left.iter()).map(|(x, y)| (x * corr_f - y).powi(2)).sum::<f32>().sqrt();
*sum += back;
*count += 1;
*under = if back < CP_RETURN_SHARE * (*sum / *count as f32) { *under + 1 } else { 0 };
if *under >= CP_RETURN_SUSTAIN && spaced {
let at = (i + 1 - CP_RETURN_SUSTAIN as usize)
.max((last_boundary + cfg.cp_min_gap as isize) as usize);
boundaries.push(at);
last_boundary = at as isize;
cp_armed = true;
regime = None;
}
}
}
}
}
if (i + 1) % hop == 0 || i + 1 == n {
frames.push(SpectralFrame {
bands,
entropy: cur_entropy,
period: cur_period,
period_strength: cur_strength,
novelty: cur_novelty,
wide,
});
}
}
crate::trace::counted("the spectral field: period evaluations", period_evals);
SpectralField { len: n, hop, frames, boundaries, needs }
}
pub const BLOB_ENTROPY_PCT: u8 = 85;
pub const BLOB_MIN_LEN: usize = 48;
#[must_use]
pub fn high_entropy_runs(input: &[u8], threshold_pct: u8, min_len: usize) -> Vec<(usize, usize)> {
let n = input.len();
if n < min_len {
return Vec::new();
}
runs_over_spans(input, &long_spans(input, 0, n, min_len), threshold_pct, min_len)
}
#[must_use]
pub fn high_entropy_runs_in_span(
input: &[u8],
a: usize,
b: usize,
threshold_pct: u8,
min_len: usize,
) -> Vec<(usize, usize)> {
if b - a < min_len {
return Vec::new();
}
runs_over_spans(input, &[(a, b)], threshold_pct, min_len)
}
#[must_use]
pub fn high_entropy_runs_within(
input: &[u8],
from: usize,
to: usize,
threshold_pct: u8,
min_len: usize,
) -> Vec<(usize, usize)> {
if to - from < min_len {
return Vec::new();
}
runs_over_spans(input, &long_spans(input, from, to, min_len), threshold_pct, min_len)
}
fn runs_over_spans(
input: &[u8],
spans: &[(usize, usize)],
threshold_pct: u8,
min_len: usize,
) -> Vec<(usize, usize)> {
let mut raw: Vec<(usize, usize)> = Vec::new();
for &(a, b) in spans {
if holds_a_run(input, a, b, threshold_pct, min_len) {
raw.push(whitespace_free_span(input, a, b));
}
}
merge_spans(raw)
}
fn holds_a_run(input: &[u8], from: usize, to: usize, threshold_pct: u8, min_len: usize) -> bool {
let mut qualifies = false;
let mut run_start: Option<usize> = None;
entropy_flags(input, from, to, threshold_pct, |i, hi| match (hi, run_start) {
(true, None) => run_start = Some(i),
(false, Some(rs)) => {
qualifies |= i - rs >= min_len;
run_start = None;
}
_ => {}
});
if let Some(rs) = run_start {
qualifies |= to - rs >= min_len;
}
qualifies
}
fn runs_over_spans_across(
input: &[u8],
spans: &[(usize, usize)],
threshold_pct: u8,
min_len: usize,
least_a_leaf: usize,
) -> Vec<(usize, usize)> {
use flynnel::JobPlan;
use flynnel::sched::par_iter::for_each_chunk_indexed_min_leaf;
let span_bytes: usize = spans.iter().map(|&(a, b)| b - a).sum();
let cores = std::thread::available_parallelism().map_or(1, std::num::NonZero::get);
let leaf_bytes = span_bytes.div_ceil(cores * 4).max(least_a_leaf).max(1);
let mut pieces: Vec<(usize, usize, usize)> = Vec::new();
for (k, &(a, b)) in spans.iter().enumerate() {
let mut p = a;
while p < b {
let q = (p + leaf_bytes).min(b);
pieces.push((k, p, q));
p = q;
}
}
let mut leaves: Vec<(usize, usize)> = Vec::new();
let mut held = 0usize;
let mut first = 0usize;
for (i, &(_, p, q)) in pieces.iter().enumerate() {
held += q - p;
if held >= leaf_bytes {
leaves.push((first, i + 1));
first = i + 1;
held = 0;
}
}
if first < pieces.len() {
leaves.push((first, pieces.len()));
}
if leaves.len() <= 1 {
return runs_over_spans(input, spans, threshold_pct, min_len);
}
let mut found: Vec<Vec<usize>> = vec![Vec::new(); leaves.len()];
let per_leaf_ns = (leaf_bytes as u64 * 4320 / 1000).min(u64::from(u32::MAX)) as u32;
let plan = JobPlan::new(0, leaves.len() as u32)
.with_leaf_shape(flynnel::LeafShape::Streaming)
.with_estimated_per_item_ns(per_leaf_ns);
for_each_chunk_indexed_min_leaf(&plan, &mut found, 1, |start, slots| {
for (i, slot) in slots.iter_mut().enumerate() {
let (lo, hi) = leaves[start + i];
for &(k, p, q) in &pieces[lo..hi] {
let reach = (q + min_len).min(spans[k].1);
if slot.last() != Some(&k) && holds_a_run(input, p, reach, threshold_pct, min_len) {
slot.push(k);
}
}
}
});
let mut qualifies = vec![false; spans.len()];
for k in found.into_iter().flatten() {
qualifies[k] = true;
}
let raw = spans
.iter()
.zip(qualifies)
.filter(|&(_, q)| q)
.map(|(&(a, b), _)| whitespace_free_span(input, a, b))
.collect();
merge_spans(raw)
}
const ENTROPY_FLAGS_MIN_LEAF: usize = 64 * 1024;
const ENTROPY_RUNS_MIN_LEAF: usize = 8 * 1024;
#[must_use]
pub fn high_entropy_runs_parallel(input: &[u8], threshold_pct: u8, min_len: usize) -> Vec<(usize, usize)> {
let n = input.len();
if n < min_len {
return Vec::new();
}
let spanning = crate::trace::phase("the blob table: the long spans");
let spans = long_spans_across(input, min_len, ENTROPY_FLAGS_MIN_LEAF);
let span_bytes: usize = spans.iter().map(|&(a, b)| b - a).sum();
drop(spanning);
crate::trace::counted("the blob table: bytes in the long spans", span_bytes as u64);
let _running = crate::trace::phase("the blob table: the runs over the spans");
runs_over_spans_across(input, &spans, threshold_pct, min_len, ENTROPY_RUNS_MIN_LEAF)
}
#[allow(unsafe_code)]
fn entropy_flags(
input: &[u8],
from: usize,
to: usize,
threshold_pct: u8,
emit: impl FnMut(usize, bool),
) {
#[cfg(target_arch = "x86_64")]
{
match crate::isa::tier() {
crate::isa::Tier::Avx512 => {
return unsafe { entropy_flags_avx512(input, from, to, threshold_pct, emit) };
}
crate::isa::Tier::Avx2 => {
return unsafe { entropy_flags_avx2(input, from, to, threshold_pct, emit) };
}
crate::isa::Tier::Sse2 => {
return unsafe { entropy_flags_sse2(input, from, to, threshold_pct, emit) };
}
crate::isa::Tier::Scalar => {}
}
}
entropy_flags_impl(input, from, to, threshold_pct, emit);
}
#[cfg(target_arch = "x86_64")]
#[allow(unsafe_code)]
#[target_feature(enable = "avx512f", enable = "avx512bw")]
unsafe fn entropy_flags_avx512(
input: &[u8],
from: usize,
to: usize,
threshold_pct: u8,
emit: impl FnMut(usize, bool),
) {
entropy_flags_impl(input, from, to, threshold_pct, emit);
}
#[cfg(target_arch = "x86_64")]
#[allow(unsafe_code)]
#[target_feature(enable = "avx2", enable = "bmi2")]
unsafe fn entropy_flags_avx2(
input: &[u8],
from: usize,
to: usize,
threshold_pct: u8,
emit: impl FnMut(usize, bool),
) {
entropy_flags_impl(input, from, to, threshold_pct, emit);
}
#[cfg(target_arch = "x86_64")]
#[allow(unsafe_code)]
#[target_feature(enable = "sse2")]
unsafe fn entropy_flags_sse2(
input: &[u8],
from: usize,
to: usize,
threshold_pct: u8,
emit: impl FnMut(usize, bool),
) {
entropy_flags_impl(input, from, to, threshold_pct, emit);
}
#[inline(always)]
fn entropy_flags_impl(input: &[u8], from: usize, to: usize, threshold_pct: u8, mut emit: impl FnMut(usize, bool)) {
let w = 64usize;
let mut hist = [0u32; 256];
let mut s = 0i64;
let entropy_norm = (w.min(256) as f32).max(2.0).log2();
let thr = f32::from(threshold_pct) / 100.0;
let full_log2 = (w as f64).log2();
let full_recip = 1.0 / (w as f64 * FIXED_LOG_SCALE);
let full_high = |s: i64| (full_log2 - s as f64 * full_recip) as f32 / entropy_norm >= thr;
let s_high_max: i64 = {
let (mut lo, mut hi) = (-1i64, (w as i64) * 6 * FIXED_LOG_SCALE as i64 + 1);
while hi - lo > 1 {
let mid = lo + (hi - lo) / 2;
if full_high(mid) { lo = mid } else { hi = mid }
}
lo
};
let lut = fixed_log_table();
let dlut = fixed_log_delta_table();
let mut wide_bits = 0u64;
let mut wide_count = 0u32;
let mut wide_until = 0usize;
let mut is_wide = |j: usize| -> bool {
if j < wide_until {
return true;
}
match multibyte_char_at(input, j) {
Some((_, end)) => {
wide_until = end;
true
}
None => false,
}
};
for (j, &b) in input.iter().enumerate().take(from).skip(from.saturating_sub(w)) {
let nb = hist[b as usize];
hist[b as usize] = nb + 1;
s += fixed_term(lut, nb + 1) - fixed_term(lut, nb);
if is_wide(j) {
wide_bits |= 1 << (j % w);
wide_count += 1;
}
}
for i in from..to {
let b = input[i] as usize;
let nb = hist[b];
hist[b] = nb + 1;
let mut delta = dlut[nb as usize];
let slot = 1u64 << (i % w);
if i >= w {
let ob = input[i - w] as usize;
let oc = hist[ob];
hist[ob] = oc - 1;
delta -= dlut[(oc - 1) as usize];
if wide_bits & slot != 0 {
wide_bits &= !slot;
wide_count -= 1;
}
}
s += delta;
if is_wide(i) {
wide_bits |= slot;
wide_count += 1;
}
let high = if wide_count == 0 {
if i + 1 >= w {
s <= s_high_max
} else {
let nwin = f64::from((i + 1) as u32);
(nwin.log2() - s as f64 * (1.0 / (nwin * FIXED_LOG_SCALE))) as f32 / entropy_norm >= thr
}
} else {
let nwin = (i + 1).min(w);
let (log_nwin, recip) = if nwin == w {
(full_log2, full_recip)
} else {
let nw = f64::from(nwin as u32);
(nw.log2(), 1.0 / (nw * FIXED_LOG_SCALE))
};
let share = wide_count as f32 / nwin as f32;
(log_nwin - s as f64 * recip) as f32 / entropy_norm >= thr + (1.0 - thr) * share
};
emit(i, high && !input[i].is_ascii_whitespace());
}
}
fn multibyte_char_at(input: &[u8], j: usize) -> Option<(usize, usize)> {
if input[j] < 0x80 {
return None;
}
let mut start = j;
while (0x80..=0xBF).contains(&input[start]) {
if start == 0 || j - start == 3 {
return None;
}
start -= 1;
}
let len = match input[start] {
0xC2..=0xDF => 2,
0xE0..=0xEF => 3,
0xF0..=0xF4 => 4,
_ => return None,
};
let end = start + len;
(j < end && end <= input.len() && std::str::from_utf8(&input[start..end]).is_ok()).then_some((start, end))
}
fn whitespace_free_span(input: &[u8], rs: usize, re: usize) -> (usize, usize) {
let mut a = rs;
while a > 0 && !input[a - 1].is_ascii_whitespace() {
a -= 1;
}
let mut b = re;
while b < input.len() && !input[b].is_ascii_whitespace() {
b += 1;
}
(a, b)
}
fn long_spans(input: &[u8], from: usize, to: usize, min_len: usize) -> Vec<(usize, usize)> {
let lo = from.saturating_sub(min_len);
let hi = (to + min_len).min(input.len());
crate::byte_simd::nonspace_spans_at_least(&input[lo..hi], min_len)
.into_iter()
.filter_map(|(a, b)| {
let (a, b) = ((lo + a).max(from), (lo + b).min(to));
(a < b).then_some((a, b))
})
.collect()
}
fn long_spans_across(input: &[u8], min_len: usize, least_a_leaf: usize) -> Vec<(usize, usize)> {
use flynnel::JobPlan;
use flynnel::sched::par_iter::for_each_chunk_indexed_min_leaf;
let n = input.len();
let cores = std::thread::available_parallelism().map_or(1, std::num::NonZero::get);
let leaf_bytes = n.div_ceil(cores * 4).max(least_a_leaf).max(1);
let leaves = n.div_ceil(leaf_bytes);
if leaves <= 1 {
return long_spans(input, 0, n, min_len);
}
let mut found: Vec<Vec<(usize, usize)>> = vec![Vec::new(); leaves];
let per_leaf_ns = (leaf_bytes as u64 * 146 / 1000).min(u64::from(u32::MAX)) as u32;
let plan = JobPlan::new(0, leaves as u32)
.with_leaf_shape(flynnel::LeafShape::Streaming)
.with_estimated_per_item_ns(per_leaf_ns);
for_each_chunk_indexed_min_leaf(&plan, &mut found, 1, |start, slots| {
for (i, slot) in slots.iter_mut().enumerate() {
let from = (start + i) * leaf_bytes;
let to = (from + leaf_bytes).min(n);
*slot = long_spans(input, from, to, min_len);
}
});
merge_spans(found.into_iter().flatten().collect())
}
fn merge_spans(raw: Vec<(usize, usize)>) -> Vec<(usize, usize)> {
let mut runs: Vec<(usize, usize)> = Vec::new();
for (a, b) in raw {
if let Some(last) = runs.last_mut()
&& a <= last.1
{
last.1 = last.1.max(b);
} else {
runs.push((a, b));
}
}
runs
}
#[must_use]
pub fn regions(field: &SpectralField) -> Vec<(usize, usize, Texture)> {
if field.len == 0 {
return Vec::new();
}
let mut cuts: Vec<usize> = Vec::with_capacity(field.boundaries.len() + 2);
cuts.push(0);
cuts.extend(field.boundaries.iter().copied());
cuts.push(field.len);
cuts.dedup();
let mut out: Vec<(usize, usize, Texture)> = Vec::new();
for w in cuts.windows(2) {
let (s, e) = (w[0], w[1]);
if e <= s {
continue;
}
let tex = texture_of(&field.signature(s, e));
if let Some(last) = out.last_mut()
&& last.2 == tex
{
last.1 = e;
} else {
out.push((s, e, tex));
}
}
out
}
#[must_use]
pub fn code_regions(input: &[u8]) -> Vec<(usize, usize, CodeTexture)> {
code_regions_with(input, &SpectralConfig::default())
}
#[must_use]
pub fn code_regions_with(input: &[u8], cfg: &SpectralConfig) -> Vec<(usize, usize, CodeTexture)> {
let field = analyze_with(input, cfg);
if field.len == 0 {
return Vec::new();
}
let mut cuts: Vec<usize> = Vec::with_capacity(field.boundaries.len() + 4);
cuts.push(0);
cuts.extend(field.boundaries.iter().copied());
for (s, e) in high_entropy_runs(input, BLOB_ENTROPY_PCT, BLOB_MIN_LEN) {
cuts.push(s);
cuts.push(e);
}
cuts.push(field.len);
cuts.sort_unstable();
cuts.dedup();
let mut out: Vec<(usize, usize, CodeTexture)> = Vec::new();
for w in cuts.windows(2) {
let (s, e) = (w[0], w[1]);
if e <= s {
continue;
}
let tex = code_texture(&analyze(&input[s..e]).signature(0, e - s));
if let Some(last) = out.last_mut()
&& last.2 == tex
{
last.1 = e;
} else {
out.push((s, e, tex));
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_sliding_scanner_agrees_with_the_one_shot_reading() {
let csv: Vec<u8> = (0..300)
.flat_map(|i| format!("{:03},{:03},{:03}\n", i % 1000, (i * 7) % 1000, (i * 13) % 1000).into_bytes())
.collect();
let prose: Vec<u8> = "it was the best of times it was the worst of times it was the age of wisdom "
.bytes().cycle().take(9000).collect();
let mut x = 0x1234_5678u32;
let noise: Vec<u8> = (0..9000)
.map(|_| {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
(x & 0xff) as u8
})
.collect();
for (tag, data) in [("csv", csv), ("prose", prose), ("noise", noise)] {
let (win, lag, hop) = (512usize, 128usize, 64usize);
let mut scan = PeriodScanner::new(&data, win, lag);
let mut end = win;
let mut checked = 0u32;
while end <= data.len() {
let (want, want_st) = dominant_period(&data[end - win..end], lag);
let (got, got_st) = scan.eval(&data, end);
assert_eq!(got, want, "{tag} at {end}: lag disagrees");
assert!(
(got_st - want_st).abs() < 1e-3,
"{tag} at {end}: strength {got_st} vs {want_st}"
);
checked += 1;
end += hop;
}
assert!(checked > 10, "{tag}: the sweep must actually compare windows");
}
}
#[test]
fn the_scanner_reseeds_rather_than_sliding_past_a_whole_window() {
let data: Vec<u8> =
(0..4000).map(|i: usize| b"abcd12"[i % 6]).collect();
let win = 256usize;
let mut scan = PeriodScanner::new(&data, win, 64);
let a = scan.eval(&data, win);
let far = scan.eval(&data, 3000);
let fresh = dominant_period(&data[3000 - win..3000], 64);
assert_eq!(far.0, fresh.0, "a reseeded window reads as a fresh one");
assert_eq!(a.0, 6, "the six-byte cycle is found either way");
}
#[test]
fn csv_rows_have_a_dominant_period() {
let mut input = Vec::new();
for _ in 0..200 {
input.extend_from_slice(b"a,b,c\n");
}
let field = analyze(&input);
assert!(
field.frames.iter().any(|f| f.period == 6 && f.period_strength > 0.3),
"expected a frame with period 6; got periods {:?}",
field.frames.iter().map(|f| f.period).collect::<Vec<_>>()
);
}
#[test]
fn base64_blob_reads_high_entropy() {
let alpha = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let blob: Vec<u8> = (0..800).map(|i| alpha[(i * 7 + i * i * 13) % 64]).collect();
let field = analyze(&blob);
let max_e = field.frames.iter().map(|f| f.entropy).fold(0.0f32, f32::max);
assert!(max_e > 0.7, "base64 blob should read high entropy; max was {max_e}");
}
#[test]
fn prose_and_code_classify_distinctly() {
let prose = b"the quick brown fox jumps over the lazy dog and then the dog sleeps well into the warm afternoon while the fox wanders off across the wide green field again";
let code = b"fn f(x){let y=x+1;return y*2;} fn g(a,b){if(a>b){a-=b;}else{b-=a;} return a;} struct P{x:i32,y:i32}";
let pf = analyze(prose);
let cf = analyze(code);
let pt = texture_of(&pf.signature(0, prose.len()));
let ct = texture_of(&cf.signature(0, code.len()));
assert_eq!(pt, Texture::Prose, "prose misclassified as {}", pt.label());
assert_eq!(ct, Texture::Code, "code misclassified as {}", ct.label());
}
#[test]
fn repeated_template_drops_in_novelty() {
let mut input = Vec::new();
for _ in 0..200 {
input.extend_from_slice(b"INFO: request handled ok\n");
}
let field = analyze(&input);
let n = field.frames.len();
assert!(n >= 4, "need several frames");
let first: f32 = field.frames[..n / 4].iter().map(|f| f.novelty).sum::<f32>()
/ (n / 4).max(1) as f32;
let last: f32 = field.frames[3 * n / 4..].iter().map(|f| f.novelty).sum::<f32>()
/ (n - 3 * n / 4).max(1) as f32;
assert!(last < first, "novelty should drop on repetition: first {first}, last {last}");
}
#[test]
fn regime_switch_records_a_change_point() {
let mut input = Vec::new();
input.extend(std::iter::repeat_n(b'a', 300));
input.extend(std::iter::repeat_n(b'7', 300));
input.extend(std::iter::repeat_n(b'#', 300));
let field = analyze(&input);
assert!(field.boundary_near(300, 24), "no change-point near 300: {:?}", field.boundaries);
assert!(field.boundary_near(600, 24), "no change-point near 600: {:?}", field.boundaries);
}
#[test]
fn a_regime_switch_is_found_past_the_settling_of_the_bias_corrections() {
let mut input = Vec::new();
for b in *b"a7#a7" {
input.extend(std::iter::repeat_n(b, 4000));
}
let field = analyze(&input);
for at in [4000usize, 8000, 12000, 16000] {
assert!(
field.boundary_near(at, 64),
"no change-point near {at}: {:?}",
field.boundaries
);
}
for &b in &field.boundaries {
assert!(
[4000usize, 8000, 12000, 16000].iter().any(|&s| b.abs_diff(s) <= 64),
"a change-point at {b} is inside a regime: {:?}",
field.boundaries
);
}
}
#[test]
fn boundary_lookups_answer_as_a_scan_over_every_change_point_does() {
let boundaries = vec![0usize, 3, 4, 10, 27, 28, 64, 100];
let field = SpectralField {
len: 101,
hop: 1,
frames: Vec::new(),
boundaries: boundaries.clone(),
needs: Needs::all(),
};
for start in 0..=104 {
for end in 0..=104 {
let scan = boundaries.iter().any(|&b| b >= start && b < end);
assert_eq!(field.boundary_in(start, end), scan, "boundary_in({start}, {end})");
}
}
for byte in 0..=104 {
for tol in 0..8 {
let scan = boundaries.iter().any(|&b| b.abs_diff(byte) <= tol);
assert_eq!(field.boundary_near(byte, tol), scan, "boundary_near({byte}, {tol})");
}
}
let none = SpectralField {
len: 0,
hop: 1,
frames: Vec::new(),
boundaries: Vec::new(),
needs: Needs::all(),
};
assert!(!none.boundary_in(0, 10));
assert!(!none.boundary_near(5, 5));
}
#[test]
fn empty_input_is_safe() {
let field = analyze(b"");
assert_eq!(field.len, 0);
assert!(field.frames.is_empty());
assert_eq!(field.signature(0, 0), SpectralFrame::default());
}
fn hi_entropy_blob(n: usize) -> Vec<u8> {
const ALPHA: &[u8; 64] =
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut x = 0x2545_f491_4f6c_dd1du64;
(0..n)
.map(|_| {
x = x.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1_442_695_040_888_963_407);
ALPHA[((x >> 58) % 64) as usize]
})
.collect()
}
#[test]
fn blob_runs_never_hold_whitespace() {
let mut input = Vec::new();
for i in 0..40 {
input.extend_from_slice(format!("line {i} the quick brown fox jumps over\n").as_bytes());
input.extend(hi_entropy_blob(300));
input.push(b'\n');
}
let runs = high_entropy_runs(&input, BLOB_ENTROPY_PCT, BLOB_MIN_LEN);
assert!(!runs.is_empty(), "blobs should be detected at all");
for &(a, b) in &runs {
assert!(
!input[a..b].iter().any(u8::is_ascii_whitespace),
"run [{a}..{b}] holds whitespace: {:?}",
String::from_utf8_lossy(&input[a..b])
);
}
let toks = crate::lexer::lex(&input);
let words = toks
.iter()
.filter(|t| t.kind == crate::token::TokenKind::Word)
.filter(|t| &input[t.span()] == b"quick")
.count();
assert_eq!(words, 40, "every line's prose survives the blob gate");
}
#[test]
fn high_entropy_runs_find_a_base64_blob_not_prose() {
let mut input = Vec::new();
input.extend_from_slice(b"the quick brown fox jumps over the lazy dog near the old bridge today ");
let blob_start = input.len();
input.extend(hi_entropy_blob(300));
let runs = high_entropy_runs(&input, BLOB_ENTROPY_PCT, BLOB_MIN_LEN);
assert!(!runs.is_empty(), "a 300-byte base64 blob should be detected");
let (a, b) = runs[0];
assert!(b - a >= BLOB_MIN_LEN, "blob run too short: {:?}", runs[0]);
assert!(a >= blob_start.saturating_sub(2), "blob should start at/after the prose: {a} vs {blob_start}");
}
fn runs_from_every_flag(input: &[u8], threshold_pct: u8, min_len: usize) -> Vec<(usize, usize)> {
let n = input.len();
if n < min_len {
return Vec::new();
}
let mut raw: Vec<(usize, usize)> = Vec::new();
let mut run_start: Option<usize> = None;
entropy_flags(input, 0, n, threshold_pct, |i, hi| match (hi, run_start) {
(true, None) => run_start = Some(i),
(false, Some(rs)) => {
if i - rs >= min_len {
raw.push(whitespace_free_span(input, rs, i));
}
run_start = None;
}
_ => {}
});
if let Some(rs) = run_start
&& n - rs >= min_len
{
raw.push(whitespace_free_span(input, rs, n));
}
merge_spans(raw)
}
#[test]
fn long_spans_across_leaves_are_the_one_pass_spans() {
let text: &[u8] = include_bytes!("spectral.rs");
let n = text.len();
for min_len in [1usize, 3, 8, BLOB_MIN_LEN, 200] {
let want = long_spans(text, 0, n, min_len);
assert!(!want.is_empty() || min_len == 200, "min_len {min_len}: the file holds spans that long");
for least in [1usize, 5, 64, 1000, 4096, ENTROPY_FLAGS_MIN_LEAF] {
assert_eq!(long_spans_across(text, min_len, least), want, "min_len {min_len}, {least} bytes a leaf");
}
}
}
#[test]
fn runs_over_spans_across_leaves_are_the_whole_span_runs() {
let text: &[u8] = include_bytes!("spectral.rs");
let mut blobbed = Vec::new();
for i in 0..12 {
blobbed.extend_from_slice(b"the quick brown fox jumps over the lazy dog ");
blobbed.extend(hi_entropy_blob(3000 + 700 * i));
blobbed.push(b' ');
}
blobbed.extend(hi_entropy_blob(200_000));
blobbed.push(b'\n');
blobbed.extend_from_slice(text);
for (name, input) in [("this file", text), ("blobs then this file", blobbed.as_slice())] {
for min_len in [16usize, BLOB_MIN_LEN, 200] {
let spans = long_spans(input, 0, input.len(), min_len);
let want = runs_over_spans(input, &spans, BLOB_ENTROPY_PCT, min_len);
assert!(
!want.is_empty() || name == "this file",
"{name}, min_len {min_len}: the blobs are runs to find"
);
for least in [1usize, 7, 100, 4096, ENTROPY_RUNS_MIN_LEAF] {
assert_eq!(
runs_over_spans_across(input, &spans, BLOB_ENTROPY_PCT, min_len, least),
want,
"{name}, min_len {min_len}, {least} bytes a leaf"
);
}
}
}
}
#[test]
fn long_spans_are_the_bytes_whose_span_reaches_min_len() {
let mut input = Vec::new();
for len in [1usize, 7, 47, 48, 49, 60, 100, 47, 48, 3] {
input.extend(std::iter::repeat_n(b'x', len));
input.push(if len % 2 == 0 { b' ' } else { b'\n' });
}
input.extend(std::iter::repeat_n(b'y', 130));
let n = input.len();
let in_long = |p: usize| {
let mut a = p;
while a > 0 && !input[a - 1].is_ascii_whitespace() {
a -= 1;
}
let mut b = p;
while b < n && !input[b].is_ascii_whitespace() {
b += 1;
}
!input[p].is_ascii_whitespace() && b - a >= 48
};
for (from, to) in
[(0, n), (0, 10), (5, 70), (60, 61), (100, 150), (120, n), (n - 20, n), (n - 1, n), (200, 200)]
{
let parts = long_spans(&input, from, to, 48);
let mut covered = vec![false; n];
for &(a, b) in &parts {
assert!(from <= a && a < b && b <= to, "part [{a}, {b}) outside [{from}, {to})");
for c in &mut covered[a..b] {
*c = true;
}
}
for (p, &c) in covered.iter().enumerate().take(to).skip(from) {
assert_eq!(c, in_long(p), "byte {p} of [{from}, {to})");
}
}
}
#[test]
fn runs_over_long_spans_alone_equal_runs_over_every_byte() {
let mut input = Vec::new();
input.extend(hi_entropy_blob(300));
for (i, len) in [40usize, 47, 48, 49, 60, 111, 112, 113, 200, 300].into_iter().enumerate() {
input.extend_from_slice(b" the quick brown fox jumps over the lazy dog ");
input.extend(hi_entropy_blob(len));
if i % 3 == 1 {
input.push(b' ');
input.extend(hi_entropy_blob(len));
}
input.push(if i % 2 == 0 { b'\n' } else { b'\t' });
}
input.extend_from_slice(b"tail text");
input.extend(hi_entropy_blob(300));
for min_len in [16usize, 48, 64, 200] {
assert_eq!(
high_entropy_runs(&input, BLOB_ENTROPY_PCT, min_len),
runs_from_every_flag(&input, BLOB_ENTROPY_PCT, min_len),
"min_len {min_len}"
);
}
let mut unbroken = Vec::new();
while unbroken.len() < 64 * 1024 {
unbroken.extend(hi_entropy_blob(64));
unbroken.extend_from_slice(b"plain.text.between.the.blobs");
}
assert!(
!unbroken.iter().any(u8::is_ascii_whitespace),
"the corpus must hold no whitespace, or it does not read this case"
);
for min_len in [16usize, 48, 200] {
assert_eq!(
high_entropy_runs(&unbroken, BLOB_ENTROPY_PCT, min_len),
runs_from_every_flag(&unbroken, BLOB_ENTROPY_PCT, min_len),
"no whitespace anywhere, min_len {min_len}"
);
}
let mut big = Vec::new();
while big.len() < 4 * ENTROPY_FLAGS_MIN_LEAF {
big.extend_from_slice(&input);
}
assert_eq!(
high_entropy_runs_parallel(&big, BLOB_ENTROPY_PCT, BLOB_MIN_LEN),
runs_from_every_flag(&big, BLOB_ENTROPY_PCT, BLOB_MIN_LEN)
);
}
#[cfg(target_arch = "x86_64")]
#[test]
#[allow(unsafe_code)]
fn entropy_flags_ladder_agrees() {
let mut input = Vec::new();
for i in 0..60 {
input.extend_from_slice(b"the quick brown fox jumps over the lazy dog ");
if i % 5 == 2 {
input.extend(hi_entropy_blob(48 + i));
input.push(b' ');
}
}
type FlagPass = dyn Fn(&[u8], usize, usize, u8, &mut dyn FnMut(usize, bool));
let flags = |f: &FlagPass| {
let mut v = Vec::new();
f(&input, 0, input.len(), BLOB_ENTROPY_PCT, &mut |i, h| v.push((i, h)));
v
};
let scalar = flags(&|inp, a, b, t, e| entropy_flags_impl(inp, a, b, t, e));
assert!(!scalar.is_empty(), "the corpus must produce flags, or this asserts nothing");
assert!(scalar.iter().any(|&(_, h)| h), "some byte must flag high, or this is vacuous");
let mut ran = vec!["scalar"];
if std::is_x86_feature_detected!("sse2") {
let got = flags(&|inp, a, b, t, e| unsafe { entropy_flags_sse2(inp, a, b, t, e) });
assert_eq!(got, scalar, "sse2 rung disagrees with the scalar body");
ran.push("sse2");
}
if std::is_x86_feature_detected!("avx2") && std::is_x86_feature_detected!("bmi2") {
let got = flags(&|inp, a, b, t, e| unsafe { entropy_flags_avx2(inp, a, b, t, e) });
assert_eq!(got, scalar, "avx2 rung disagrees with the scalar body");
ran.push("avx2");
}
if std::is_x86_feature_detected!("avx512f") && std::is_x86_feature_detected!("avx512bw") {
let got = flags(&|inp, a, b, t, e| unsafe { entropy_flags_avx512(inp, a, b, t, e) });
assert_eq!(got, scalar, "avx512 rung disagrees with the scalar body");
ran.push("avx512");
}
eprintln!("entropy_flags ladder rungs exercised on this host: {}", ran.join(", "));
}
fn entropy_flags_by_expression(input: &[u8], threshold_pct: u8, wide: bool) -> Vec<(usize, bool)> {
let w = 64usize;
let mut hist = [0u32; 256];
let mut s = 0i64;
let entropy_norm = (w.min(256) as f32).max(2.0).log2();
let thr = f32::from(threshold_pct) / 100.0;
let lut = fixed_log_table();
let inside = bytes_inside_characters(input);
let mut out = Vec::with_capacity(input.len());
for i in 0..input.len() {
let b = input[i] as usize;
let nb = hist[b];
hist[b] = nb + 1;
s += fixed_term(lut, nb + 1) - fixed_term(lut, nb);
if i >= w {
let ob = input[i - w] as usize;
let oc = hist[ob];
hist[ob] = oc - 1;
s -= fixed_term(lut, oc) - fixed_term(lut, oc - 1);
}
let nwin = (i + 1).min(w);
let share = if wide { inside[i + 1 - nwin..=i].iter().filter(|&&x| x).count() as f32 / nwin as f32 } else { 0.0 };
let nw = f64::from(nwin as u32);
let h = (nw.log2() - s as f64 * (1.0 / (nw * FIXED_LOG_SCALE))) as f32 / entropy_norm;
let gate = if share == 0.0 { thr } else { thr + (1.0 - thr) * share };
out.push((i, h >= gate && !input[i].is_ascii_whitespace()));
}
out
}
fn bytes_inside_characters(input: &[u8]) -> Vec<bool> {
let mut out = vec![false; input.len()];
let mut at = 0;
for chunk in input.utf8_chunks() {
for (o, ch) in chunk.valid().char_indices() {
if ch.len_utf8() > 1 {
out[at + o..at + o + ch.len_utf8()].fill(true);
}
}
at += chunk.valid().len() + chunk.invalid().len();
}
out
}
fn multibyte_text() -> Vec<u8> {
let mut v = Vec::new();
v.extend_from_slice(CHINESE.as_bytes());
v.extend_from_slice(b" \xE4\xB8 \xC0\x80 \xED\xA0\x80 \x80\xBF ");
v.extend_from_slice(BULGARIAN.as_bytes());
v.extend_from_slice(" \u{1F600}\u{1F680} ".as_bytes());
v.extend_from_slice(JAPANESE.as_bytes());
v
}
const BULGARIAN: &str = "Правителството обяви нови мерки за подкрепа на малкия бизнес и домакинствата, като министърът на финансите увери, че средствата ще бъдат изплатени до края на месеца. Опозицията поиска повече подробности за разходите.";
const GREEK: &str = "Η κυβέρνηση ανακοίνωσε νέα μέτρα στήριξης για τις μικρές επιχειρήσεις και τα νοικοκυριά, ενώ ο υπουργός Οικονομικών διαβεβαίωσε ότι τα χρήματα θα καταβληθούν μέχρι το τέλος του μήνα. Η αντιπολίτευση ζήτησε περισσότερες λεπτομέρειες.";
const CHINESE: &str = "政府宣布了支持小企业和家庭的新措施,财政部长表示,相关资金将在本月底之前发放到位。反对派要求公布更多关于支出的细节,并呼吁议会尽快展开辩论。专家认为,这些措施能够缓解物价上涨带来的压力,但长期效果仍有待观察。";
const JAPANESE: &str = "政府は中小企業と家庭を支援する新たな対策を発表し、財務大臣は資金が今月末までに支給されると述べた。野党は支出についてさらに詳しい説明を求め、議会での早期の審議を呼びかけた。";
#[test]
fn the_full_window_boundary_flags_exactly_as_the_expression_does() {
let mut input = Vec::new();
for i in 0..80 {
input.extend_from_slice(b"the quick brown fox jumps over the lazy dog ");
match i % 4 {
0 => input.extend(hi_entropy_blob(40 + i)),
1 => input.extend(std::iter::repeat_n(b'x', i)),
2 => input.extend((0..=255u8).skip(i % 7)),
_ => input.extend(hi_entropy_blob(16).iter().chain(b"aaaa").copied()),
}
input.push(b' ');
if i % 9 == 4 {
input.extend(multibyte_text());
input.push(b' ');
}
}
for pct in [50u8, 70, BLOB_ENTROPY_PCT, 95, 100] {
let want = entropy_flags_by_expression(&input, pct, true);
let mut got = Vec::new();
entropy_flags(&input, 0, input.len(), pct, |i, h| got.push((i, h)));
assert_eq!(got, want, "threshold {pct}: the pass disagrees with the expression");
let highs = want.iter().filter(|(_, h)| *h).count();
assert!(pct == 100 || (highs > 0 && highs < want.len()), "threshold {pct} flags {highs} of {}, so the comparison asserts little", want.len());
}
}
#[test]
fn entropy_flags_from_mid_stream_read_what_the_whole_stream_reads() {
let mut input = Vec::new();
for i in 0..40 {
input.extend_from_slice(b"the quick brown fox jumps over the lazy dog ");
if i % 7 == 3 {
input.extend(hi_entropy_blob(60 + i));
input.push(b' ');
}
if i % 11 == 5 {
input.extend(multibyte_text());
input.push(b' ');
}
}
let n = input.len();
let mut whole = vec![false; n];
entropy_flags(&input, 0, n, BLOB_ENTROPY_PCT, |i, h| whole[i] = h);
let first_wide = input.iter().position(|&b| b >= 0xE0).expect("the text holds three-byte characters");
for from in [1, 17, 63, 64, 65, 200, 777, n / 2, n - 70, n - 1, first_wide + 1, first_wide + 2, first_wide + 64, first_wide + 66] {
let mut part = vec![false; n];
entropy_flags(&input, from, n, BLOB_ENTROPY_PCT, |i, h| part[i] = h);
assert_eq!(&part[from..], &whole[from..], "flags from {from}");
}
let mut big = Vec::new();
while big.len() < 4 * ENTROPY_FLAGS_MIN_LEAF {
big.extend_from_slice(&input);
}
assert_eq!(
high_entropy_runs_parallel(&big, BLOB_ENTROPY_PCT, BLOB_MIN_LEN),
high_entropy_runs(&big, BLOB_ENTROPY_PCT, BLOB_MIN_LEN),
);
}
#[test]
fn well_formed_multibyte_text_is_never_a_blob_run() {
let zh = "份。\n可以通过 community@hasura.io 联系项目团队来报告辱骂、骚扰或其他不可接受的行为。\n所有投诉都将得到审查和调查".as_bytes();
let blind = entropy_flags_by_expression(zh, BLOB_ENTROPY_PCT, false);
let longest = blind.split(|&(_, h)| !h).map(<[(usize, bool)]>::len).max().unwrap_or(0);
assert!(longest >= BLOB_MIN_LEN, "a run of {longest} bytes clears the bare threshold, so this asserts nothing");
assert_eq!(high_entropy_runs(zh, BLOB_ENTROPY_PCT, BLOB_MIN_LEN), Vec::new(), "the paragraph is text");
assert!(crate::lexer::lex(zh).iter().all(|t| t.kind != crate::token::TokenKind::Other), "no opaque token");
eprintln!("longest run the bare threshold flags in the paragraph: {longest} bytes");
let packed = random_bytes(300);
assert_eq!(high_entropy_runs(&packed, BLOB_ENTROPY_PCT, BLOB_MIN_LEN), vec![(0, packed.len())]);
let b64 = hi_entropy_blob(300);
assert_eq!(high_entropy_runs(&b64, BLOB_ENTROPY_PCT, BLOB_MIN_LEN), vec![(0, b64.len())]);
}
fn random_bytes(n: usize) -> Vec<u8> {
let mut x = 0x9e37_79b9_7f4a_7c15u64;
let mut v = Vec::with_capacity(n);
while v.len() < n {
x = x.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1_442_695_040_888_963_407);
let b = (x >> 56) as u8;
if !b.is_ascii_whitespace() {
v.push(b);
}
}
v
}
#[test]
fn prose_in_cyrillic_greek_and_cjk_reads_as_prose() {
for (name, text) in [("bulgarian", BULGARIAN), ("greek", GREEK), ("chinese", CHINESE), ("japanese", JAPANESE)] {
let sig = analyze(text.as_bytes()).signature(0, text.len());
assert_eq!(texture_of(&sig), Texture::Prose, "{name}: {sig:?}");
assert_eq!(code_texture(&sig), CodeTexture::Prose, "{name}: {sig:?}");
}
let zh = analyze(CHINESE.as_bytes()).signature(0, CHINESE.len());
assert!(zh.entropy > 0.78, "entropy {} is under the gate, so this asserts nothing", zh.entropy);
let bin = random_bytes(400);
let sig = analyze(&bin).signature(0, bin.len());
assert_eq!(texture_of(&sig), Texture::Data);
assert_eq!(code_texture(&sig), CodeTexture::Blob);
}
#[test]
fn a_mark_of_several_bytes_weighs_like_its_ascii_counterpart() {
let typographic = "\u{201C}We\u{2019}ll ship it,\u{201D} she said \u{2014} and they did, though the report\u{2019}s authors \u{201C}doubted\u{201D} it would last the winter.";
let ascii = "\"We'll ship it,\" she said - and they did, though the report's authors \"doubted\" it would last the winter.";
let punct = |s: &str| {
let m = analyze(s.as_bytes()).signature(0, s.len()).texture_mix();
m[3] / m.iter().sum::<f32>()
};
let tripled = "\"\"\"We'''ll ship it,\"\"\" she said --- and they did, though the report'''s authors \"\"\"doubted\"\"\" it would last the winter.";
let (t, a, x) = (punct(typographic), punct(ascii), punct(tripled));
eprintln!("punctuation share: typographic {t}, ascii {a}, tripled {x}");
assert!((t - a).abs() * 3.0 < (x - a).abs(), "punctuation share {t} against {a}, where a unit a byte reads {x}");
}
#[test]
fn the_utf8_reader_releases_each_character_whole_and_broken_bytes_as_class_4() {
let mut r = Utf8Classes::default();
let mut got = Vec::new();
for &b in b"a\xC3\xA9\xE2\x82\xAC\xF0\x9F\x98\x80\xFF\xE4\xB8x" {
got.extend(r.push(b).into_iter().flatten());
}
let rel = |class, count, age| Release { class, count, age };
assert_eq!(
got,
[
rel(1, 1, 0),
rel(1, 2, 0),
rel(3, 3, 0),
rel(3, 4, 0),
rel(4, 1, 0),
rel(4, 2, 1),
rel(1, 1, 0),
],
"a, e-acute, euro sign, emoji, 0xFF, a character cut short by x"
);
r.push(0xE4);
assert_eq!(r.finish(), Some(rel(4, 1, 0)), "a character the input ends inside");
assert_eq!(char_class('\u{3002}'), 3, "an ideographic full stop is punctuation");
assert_eq!(char_class('\u{00A0}'), 2, "a no-break space is a space");
assert_eq!(char_class('\u{0301}'), 1, "a combining accent belongs to its letter");
}
#[test]
fn a_short_span_of_code_mid_sentence_gets_an_entry_and_an_exit() {
let text = "The nightly report lists every host that answered within the window and the latency it measured for each one. For every reply it runs a[i]=(b[j]*c[k]+d[i-1])>>2;if(a[i]>m){m=a[i];k=i;} on the payload, and it sends the totals back to the collector before the next window opens, so the morning shift starts with fresh numbers.";
let entry = text.find("a[i]=").expect("the span");
let exit = text.find(" on the payload").expect("the span's end");
let cuts = analyze(text.as_bytes()).boundaries;
assert!(cuts.iter().any(|&c| (entry..entry + 16).contains(&c)), "an entry near {entry}: {cuts:?}");
assert!(cuts.iter().any(|&c| (exit..exit + 12).contains(&c)), "an exit near {exit}: {cuts:?}");
}
#[test]
fn a_plain_sentence_holds_no_change_point() {
for text in [
"The nightly report lists every host that answered within the window and the latency it measured for each one.",
"The walk reports what it found rather than what it was asked for, and a filter that silently drops a file reads the same as a directory that never held one.",
] {
assert_eq!(analyze(text.as_bytes()).boundaries, Vec::<usize>::new(), "{text}");
}
}
#[test]
fn regions_surface_distinct_textures() {
let mut doc = Vec::new();
for _ in 0..3 {
doc.extend_from_slice(b"the quick brown fox jumps over the lazy dog near the old stone bridge today ");
}
let mut x = 0x9e37_79b9_7f4a_7c15u64;
for _ in 0..300 {
x = x.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1_442_695_040_888_963_407);
doc.push(0x80 | ((x >> 56) as u8 & 0x7f)); }
let field = analyze(&doc);
let regs = regions(&field);
let textures: std::collections::HashSet<Texture> = regs.iter().map(|r| r.2).collect();
assert!(regs.len() >= 2, "expected multiple regions, got {regs:?}");
assert!(textures.contains(&Texture::Data), "the blob region should read Data: {regs:?}");
}
}