use std::sync::OnceLock;
static LOG2_TABLE: OnceLock<[f64; 256]> = OnceLock::new();
#[inline]
pub(crate) fn get_log2_table() -> &'static [f64; 256] {
LOG2_TABLE.get_or_init(|| {
let mut table = [0.0f64; 256];
for i in 1..256 {
let val = i as f64;
table[i] = val * val.log2();
}
table
})
}
#[cfg(target_arch = "x86_64")]
pub fn shannon_entropy_simd(data: &[u8]) -> f64 {
if data.is_empty() {
return 0.0;
}
unsafe {
if is_x86_feature_detected!("avx512f")
&& is_x86_feature_detected!("avx512bw")
&& is_x86_feature_detected!("avx512dq")
{
return crate::entropy_avx512::calculate_shannon_entropy(data);
}
if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
return crate::entropy_fast_x86::shannon_entropy_avx2(data);
}
if is_x86_feature_detected!("sse2") {
return crate::entropy_fast_x86::shannon_entropy_sse2(data);
}
}
shannon_entropy_scalar(data)
}
#[cfg(target_arch = "aarch64")]
pub fn shannon_entropy_simd(data: &[u8]) -> f64 {
crate::entropy_fast_neon::shannon_entropy_neon(data)
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
pub fn shannon_entropy_simd(data: &[u8]) -> f64 {
shannon_entropy_scalar(data)
}
#[inline]
pub(crate) fn histogram_8way(data: &[u8]) -> ([u32; 256], usize) {
let mut c0 = [0u32; 256];
let mut c1 = [0u32; 256];
let mut c2 = [0u32; 256];
let mut c3 = [0u32; 256];
let mut c4 = [0u32; 256];
let mut c5 = [0u32; 256];
let mut c6 = [0u32; 256];
let mut c7 = [0u32; 256];
let mut active_len = data.len();
let mut chunks = data.chunks_exact(8);
for chunk in &mut chunks {
if chunk[0] == 0
&& chunk[1] == 0
&& chunk[2] == 0
&& chunk[3] == 0
&& chunk[4] == 0
&& chunk[5] == 0
&& chunk[6] == 0
&& chunk[7] == 0
{
active_len -= 8;
continue;
}
c0[chunk[0] as usize] += 1;
c1[chunk[1] as usize] += 1;
c2[chunk[2] as usize] += 1;
c3[chunk[3] as usize] += 1;
c4[chunk[4] as usize] += 1;
c5[chunk[5] as usize] += 1;
c6[chunk[6] as usize] += 1;
c7[chunk[7] as usize] += 1;
}
for &byte in chunks.remainder() {
if byte == 0 {
active_len -= 1;
} else {
c0[byte as usize] += 1;
}
}
let mut counts = [0u32; 256];
for j in 0..256 {
counts[j] = c0[j] + c1[j] + c2[j] + c3[j] + c4[j] + c5[j] + c6[j] + c7[j];
}
(counts, active_len)
}
#[inline]
pub fn shannon_entropy_scalar(data: &[u8]) -> f64 {
if data.is_empty() {
return 0.0;
}
let (counts, active_len) = histogram_8way(data);
if active_len == 0 {
return 0.0;
}
if active_len <= 255 {
let table = get_log2_table();
let mut sum = 0.0;
for &count in &counts {
if count > 0 {
sum += table[count as usize];
}
}
return (active_len as f64).log2() - sum / (active_len as f64);
}
let len_f = active_len as f64;
let mut entropy = 0.0;
for &count in &counts {
if count > 0 {
let p = count as f64 / len_f;
entropy -= p * p.log2();
}
}
entropy
}
pub fn has_high_entropy_fast(data: &[u8], threshold: f64) -> bool {
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("sse2") {
unsafe {
return crate::entropy_fast_x86::has_high_entropy_fast_x86(data, threshold);
}
}
}
#[cfg(target_arch = "aarch64")]
{
unsafe {
return crate::entropy_fast_neon::has_high_entropy_fast_neon(data, threshold);
}
}
has_high_entropy_fast_scalar(data, threshold)
}
#[inline]
pub(crate) fn distinct_byte_count(data: &[u8]) -> u32 {
let mut seen = [0u64; 4];
for &b in data {
seen[(b >> 6) as usize] |= 1u64 << (b & 63);
}
seen[0].count_ones() + seen[1].count_ones() + seen[2].count_ones() + seen[3].count_ones()
}
#[inline]
fn has_high_entropy_fast_scalar(data: &[u8], threshold: f64) -> bool {
if data.is_empty() {
return shannon_entropy_scalar(data) >= threshold;
}
let unique = distinct_byte_count(data);
if (unique as f64).log2() < threshold {
return false;
}
shannon_entropy_simd(data) >= threshold
}