use crate::source::{EntropySource, Platform, Requirement, SourceCategory, SourceInfo};
#[cfg(target_os = "macos")]
use crate::sources::helpers::{extract_timing_entropy, mach_time};
static NL_INFERENCE_TIMING_INFO: SourceInfo = SourceInfo {
name: "nl_inference_timing",
description: "NaturalLanguage ANE inference timing via system-wide NLP cache state",
physics: "Times NLLanguageRecognizer.processString() calls that route through the \
Apple Neural Engine. The NL framework maintains a system-wide inference \
cache shared across all running processes. Timing varies by ANE queue \
depth (from other apps), cache hit/miss (system-wide cache occupancy), \
ANE thermal state, and memory pressure. Measured: CV=686%, H\u{2248}7.65 bits/byte, \
LSB=0.502 — approaching theoretical maximum, combining ANE scheduling \
nondeterminism with system-wide NLP activity.",
category: SourceCategory::GPU,
platform: Platform::MacOS,
requirements: &[Requirement::Metal], entropy_rate_estimate: 2.0,
composite: false,
is_fast: false,
};
pub struct NLInferenceTimingSource;
#[cfg(target_os = "macos")]
mod objc_nl {
use std::ffi::{CStr, c_void};
pub type Id = *mut c_void;
pub type Sel = *mut c_void;
pub type Class = *mut c_void;
pub type MsgSendFn = unsafe extern "C" fn(Id, Sel) -> Id;
pub type MsgSendFn1 = unsafe extern "C" fn(Id, Sel, Id) -> Id;
pub type MsgSendStr = unsafe extern "C" fn(Id, Sel, *const i8) -> Id;
#[link(name = "objc", kind = "dylib")]
#[allow(clashing_extern_declarations)]
unsafe extern "C" {
pub fn objc_getClass(name: *const i8) -> Class;
pub fn sel_registerName(name: *const i8) -> Sel;
pub fn objc_msgSend();
}
#[link(name = "NaturalLanguage", kind = "framework")]
unsafe extern "C" {}
#[link(name = "Foundation", kind = "framework")]
unsafe extern "C" {}
#[inline(always)]
pub fn msg_send() -> MsgSendFn {
unsafe { core::mem::transmute(objc_msgSend as *const ()) }
}
#[inline(always)]
pub fn msg_send1() -> MsgSendFn1 {
unsafe { core::mem::transmute(objc_msgSend as *const ()) }
}
#[inline(always)]
pub fn msg_send_str() -> MsgSendStr {
unsafe { core::mem::transmute(objc_msgSend as *const ()) }
}
pub unsafe fn ns_string(s: &CStr) -> Id {
let class = unsafe { objc_getClass(c"NSString".as_ptr()) };
let sel = unsafe { sel_registerName(c"stringWithUTF8String:".as_ptr()) };
unsafe { msg_send_str()(class, sel, s.as_ptr()) }
}
}
#[cfg(target_os = "macos")]
static CORPUS: &[&str] = &[
"The quick brown fox jumps over the lazy dog\0",
"Quantum entanglement defies local hidden variables\0",
"El gato duerme sobre la alfombra roja\0",
"Die Quantenverschraenkung widerspricht dem lokalen Realismus\0",
"photosynthesis chlorophyll absorption spectrum wavelength\0",
"random noise entropy measurement hardware oscillator\0",
"cryptographic hash function pseudorandom deterministic\0",
"serendipitous juxtaposition kaleidoscopic iridescent\0",
];
#[cfg(target_os = "macos")]
mod imp {
use std::ffi::CStr;
use super::objc_nl::*;
use super::*;
impl EntropySource for NLInferenceTimingSource {
fn info(&self) -> &SourceInfo {
&NL_INFERENCE_TIMING_INFO
}
fn is_available(&self) -> bool {
let class = unsafe { objc_getClass(c"NLLanguageRecognizer".as_ptr()) };
!class.is_null()
}
fn collect(&self, n_samples: usize) -> Vec<u8> {
let raw_count = n_samples + 64;
let mut timings = Vec::with_capacity(raw_count);
let send = msg_send();
let send1 = msg_send1();
let alloc_sel = unsafe { sel_registerName(c"alloc".as_ptr()) };
let init_sel = unsafe { sel_registerName(c"init".as_ptr()) };
let process_sel = unsafe { sel_registerName(c"processString:".as_ptr()) };
let dominant_sel = unsafe { sel_registerName(c"dominantLanguage".as_ptr()) };
let reset_sel = unsafe { sel_registerName(c"reset".as_ptr()) };
let class = unsafe { objc_getClass(c"NLLanguageRecognizer".as_ptr()) };
if class.is_null() {
return Vec::new();
}
let alloc = unsafe { send(class, alloc_sel) };
if alloc.is_null() {
return Vec::new();
}
let rec = unsafe { send(alloc, init_sel) };
if rec.is_null() {
return Vec::new();
}
for i in 0..2_usize {
let corpus_entry = CORPUS[i % CORPUS.len()];
let ns_str = unsafe {
ns_string(CStr::from_bytes_with_nul_unchecked(corpus_entry.as_bytes()))
};
if ns_str.is_null() {
continue;
}
unsafe {
send1(rec, process_sel, ns_str);
send(rec, dominant_sel);
send(rec, reset_sel);
};
}
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(3);
for i in 0..raw_count {
if i % 64 == 0 && std::time::Instant::now() >= deadline {
break;
}
let corpus_entry = CORPUS[i % CORPUS.len()];
let ns_str = unsafe {
ns_string(CStr::from_bytes_with_nul_unchecked(corpus_entry.as_bytes()))
};
if ns_str.is_null() {
continue;
}
let t0 = mach_time();
unsafe {
send1(rec, process_sel, ns_str);
let lang = send(rec, dominant_sel);
send(rec, reset_sel);
let _ = lang;
};
let elapsed = mach_time().wrapping_sub(t0);
if elapsed < 12_000_000 {
timings.push(elapsed);
}
}
let release_sel = unsafe { sel_registerName(c"release".as_ptr()) };
unsafe { send(rec, release_sel) };
extract_timing_entropy(&timings, n_samples)
}
}
}
#[cfg(not(target_os = "macos"))]
impl EntropySource for NLInferenceTimingSource {
fn info(&self) -> &SourceInfo {
&NL_INFERENCE_TIMING_INFO
}
fn is_available(&self) -> bool {
false
}
fn collect(&self, _n_samples: usize) -> Vec<u8> {
Vec::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn info() {
let src = NLInferenceTimingSource;
assert_eq!(src.info().name, "nl_inference_timing");
assert!(matches!(src.info().category, SourceCategory::GPU));
assert_eq!(src.info().platform, Platform::MacOS);
assert!(!src.info().composite);
}
#[test]
#[cfg(target_os = "macos")]
fn is_available_on_macos() {
assert!(NLInferenceTimingSource.is_available());
}
#[test]
#[ignore] fn collects_bytes_with_variation() {
let src = NLInferenceTimingSource;
if !src.is_available() {
return;
}
let data = src.collect(32);
assert!(!data.is_empty());
let unique: std::collections::HashSet<u8> = data.iter().copied().collect();
assert!(
unique.len() > 4,
"expected high variation from NL inference timing"
);
}
}