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
}
}
#[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,
}
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;
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
}
#[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)
}
}
#[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 f.entropy > 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 f.entropy > 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 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_WARMUP: usize = 32;
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 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];
let c = classify(b);
if needs.bands {
for (v, &dk) in bands.iter_mut().zip(decay.iter()) {
*v *= dk;
}
for (d, &od) in oma.iter().enumerate() {
bands[d * N_CLASSES + c] += od;
}
}
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 dev = (dist - cp_mean).abs();
cp_mean += CP_ALPHA * (dist - cp_mean);
cp_mad += CP_ALPHA * (dev - 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 {
if cp_armed && dist > thresh && i as isize - last_boundary >= cfg.cp_min_gap as isize
{
boundaries.push(i);
last_boundary = i as isize;
cp_armed = false;
} else if !cp_armed && dist < thresh * 0.5 {
cp_armed = true;
}
}
}
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,
});
}
}
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();
for &b in &input[from.saturating_sub(w)..from] {
let nb = hist[b as usize];
hist[b as usize] = nb + 1;
s += fixed_term(lut, nb + 1) - fixed_term(lut, nb);
}
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];
if i >= w {
let ob = input[i - w] as usize;
let oc = hist[ob];
hist[ob] = oc - 1;
delta -= dlut[(oc - 1) as usize];
}
s += delta;
let high = 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
};
emit(i, high && !input[i].is_ascii_whitespace());
}
}
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)> {
let field = analyze(input);
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 fall 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) -> 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 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 = f64::from((i + 1).min(w) as u32);
let h = (nwin.log2() - s as f64 * (1.0 / (nwin * FIXED_LOG_SCALE))) as f32 / entropy_norm;
out.push((i, h >= thr && !input[i].is_ascii_whitespace()));
}
out
}
#[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' ');
}
for pct in [50u8, 70, BLOB_ENTROPY_PCT, 95, 100] {
let want = entropy_flags_by_expression(&input, pct);
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' ');
}
}
let n = input.len();
let mut whole = vec![false; n];
entropy_flags(&input, 0, n, BLOB_ENTROPY_PCT, |i, h| whole[i] = h);
for from in [1, 17, 63, 64, 65, 200, 777, n / 2, n - 70, n - 1] {
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 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:?}");
}
}