use std::collections::BTreeMap;
use std::fmt;
use dsfb_gpu_debug_core::event::TraceEvent;
use dsfb_gpu_debug_core::hash::sha256;
#[derive(Clone, PartialEq, Debug, Default)]
pub struct ResidualProjectionFixture {
pub fixture_sha256_hex: String,
pub metadata: BTreeMap<String, String>,
pub declared_num_windows: u32,
pub declared_num_signals: u32,
pub declared_healthy_window_end: u32,
pub rows: Vec<Vec<Option<f64>>>,
}
#[derive(Clone, Eq, PartialEq, Debug, Default)]
pub struct IngestReport {
pub fixture_sha256_hex: String,
pub fixture_byte_size: u64,
pub declared_num_windows: u32,
pub declared_num_signals: u32,
pub observed_num_windows: u32,
pub observed_num_signals: u32,
pub nan_cell_count: u32,
pub finite_cell_count: u32,
pub emitted_event_count: u32,
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum IngestError {
EmptyFile,
NotUtf8 {
byte_offset: usize,
},
MissingHeader {
key: &'static str,
},
MalformedNumericHeader {
key: String,
value: String,
},
RowColumnCountMismatch {
line: usize,
expected: usize,
found: usize,
},
BadCell {
line: usize,
column: usize,
token: String,
},
NoDataRows,
Sha256Mismatch {
expected: String,
actual: String,
},
Sha256NotLowerHex64 {
provided: String,
},
}
impl fmt::Display for IngestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EmptyFile => write!(f, "fixture file is empty"),
Self::NotUtf8 { byte_offset } => {
write!(
f,
"fixture file is not UTF-8 (invalid at byte {byte_offset})"
)
}
Self::MissingHeader { key } => {
write!(f, "fixture missing required `# {key}=...` header")
}
Self::MalformedNumericHeader { key, value } => write!(
f,
"fixture header `{key}` is not a non-negative integer: {value:?}"
),
Self::RowColumnCountMismatch {
line,
expected,
found,
} => write!(
f,
"fixture data row on line {line} has {found} columns; \
declared num_signals = {expected}"
),
Self::BadCell {
line,
column,
token,
} => write!(
f,
"fixture cell on line {line} column {column} is neither a \
finite float nor `nan`: {token:?}"
),
Self::NoDataRows => write!(
f,
"fixture file contains comment headers but no TAB-delimited \
data rows"
),
Self::Sha256Mismatch { expected, actual } => write!(
f,
"fixture SHA-256 mismatch (expected {expected}, computed {actual})"
),
Self::Sha256NotLowerHex64 { provided } => write!(
f,
"expected fixture SHA-256 must be 64 lowercase hex characters; \
got {provided:?}"
),
}
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct LoweringConfig {
pub value_to_microsecond_scale: u32,
pub latency_clamp_us: u32,
pub window_size_ns: u64,
}
impl Default for LoweringConfig {
fn default() -> Self {
Self {
value_to_microsecond_scale: 1000,
latency_clamp_us: 32_767_000,
window_size_ns: 1_000_000_000,
}
}
}
#[must_use]
pub fn sha256_to_hex_lower(bytes: &[u8; 32]) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut out = String::with_capacity(64);
for b in bytes {
out.push(HEX[(b >> 4) as usize] as char);
out.push(HEX[(b & 0x0f) as usize] as char);
}
out
}
pub fn verify_fixture_sha256(bytes: &[u8], expected_hex: &str) -> Result<String, IngestError> {
if expected_hex.len() != 64 || !expected_hex.bytes().all(|c| c.is_ascii_hexdigit()) {
return Err(IngestError::Sha256NotLowerHex64 {
provided: expected_hex.to_string(),
});
}
if !expected_hex.bytes().all(|c| !c.is_ascii_uppercase()) {
return Err(IngestError::Sha256NotLowerHex64 {
provided: expected_hex.to_string(),
});
}
let actual = sha256_to_hex_lower(&sha256(bytes));
if actual != expected_hex {
return Err(IngestError::Sha256Mismatch {
expected: expected_hex.to_string(),
actual,
});
}
Ok(actual)
}
pub fn load_residual_projection_tsv(
bytes: &[u8],
expected_sha256_hex: &str,
) -> Result<ResidualProjectionFixture, IngestError> {
if bytes.is_empty() {
return Err(IngestError::EmptyFile);
}
let actual_hex = verify_fixture_sha256(bytes, expected_sha256_hex)?;
let text = match std::str::from_utf8(bytes) {
Ok(s) => s,
Err(e) => {
return Err(IngestError::NotUtf8 {
byte_offset: e.valid_up_to(),
});
}
};
let mut metadata: BTreeMap<String, String> = BTreeMap::new();
let mut rows: Vec<Vec<Option<f64>>> = Vec::new();
let mut expected_columns: Option<usize> = None;
for (idx, raw_line) in text.lines().enumerate() {
let line_no = idx + 1;
let line = raw_line.trim_end_matches('\r');
if line.is_empty() {
continue;
}
if let Some(rest) = line.strip_prefix('#') {
let trimmed = rest.trim_start();
if let Some((k, v)) = trimmed.split_once('=') {
metadata.insert(k.trim().to_string(), v.trim().to_string());
} else if trimmed.is_empty() {
} else if trimmed.starts_with("residual-projection") {
metadata.insert("format".to_string(), trimmed.to_string());
}
continue;
}
let cols: Vec<&str> = line.split('\t').collect();
let cell_count = cols.len();
if let Some(prev) = expected_columns {
if cell_count != prev {
return Err(IngestError::RowColumnCountMismatch {
line: line_no,
expected: prev,
found: cell_count,
});
}
} else {
expected_columns = Some(cell_count);
}
let mut row: Vec<Option<f64>> = Vec::with_capacity(cell_count);
for (col_idx, tok) in cols.iter().enumerate() {
let token = tok.trim();
if token.eq_ignore_ascii_case("nan") {
row.push(None);
} else {
match token.parse::<f64>() {
Ok(v) if v.is_finite() => row.push(Some(v)),
_ => {
return Err(IngestError::BadCell {
line: line_no,
column: col_idx + 1,
token: token.to_string(),
});
}
}
}
}
rows.push(row);
}
if rows.is_empty() {
return Err(IngestError::NoDataRows);
}
let declared_num_windows = require_u32_header(&metadata, "num_windows")?;
let declared_num_signals = require_u32_header(&metadata, "num_signals")?;
let declared_healthy_window_end = require_u32_header(&metadata, "healthy_window_end")?;
let observed_cols = expected_columns.unwrap_or(0);
if observed_cols != declared_num_signals as usize {
return Err(IngestError::RowColumnCountMismatch {
line: 0,
expected: declared_num_signals as usize,
found: observed_cols,
});
}
Ok(ResidualProjectionFixture {
fixture_sha256_hex: actual_hex,
metadata,
declared_num_windows,
declared_num_signals,
declared_healthy_window_end,
rows,
})
}
fn require_u32_header(
meta: &BTreeMap<String, String>,
key: &'static str,
) -> Result<u32, IngestError> {
let v = meta
.get(key)
.ok_or(IngestError::MissingHeader { key })?
.trim();
v.parse::<u32>()
.map_err(|_| IngestError::MalformedNumericHeader {
key: key.to_string(),
value: v.to_string(),
})
}
#[must_use]
#[allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "Cell-value lowering is deterministic-by-construction and recorded \
in audit_report.html section 2; precision / sign / truncation \
losses are intentional under the documented rule."
)]
pub fn lower_to_trace_events(
fixture: &ResidualProjectionFixture,
config: &LoweringConfig,
) -> Vec<TraceEvent> {
let mut events: Vec<TraceEvent> = Vec::new();
let scale = config.value_to_microsecond_scale as u64;
let clamp = config.latency_clamp_us;
let win_ns = config.window_size_ns;
for (w_idx, row) in fixture.rows.iter().enumerate() {
let ts_ns = (w_idx as u64).saturating_mul(win_ns);
for (s_idx, cell) in row.iter().enumerate() {
let Some(v) = cell else {
continue;
};
let latency_us = if !v.is_finite() || *v <= 0.0 {
0_u32
} else {
let scaled = *v * scale as f64;
if scaled >= clamp as f64 {
clamp
} else {
scaled as u32
}
};
let span_id = (w_idx as u64).saturating_mul(65_536) + s_idx as u64;
events.push(TraceEvent::new(
ts_ns,
s_idx as u32, 0, span_id,
0, latency_us,
200, 0, 0, 0, ));
}
}
events
}
#[must_use]
pub fn build_ingest_report(
fixture: &ResidualProjectionFixture,
events: &[TraceEvent],
fixture_byte_size: u64,
) -> IngestReport {
let observed_windows = fixture.rows.len() as u32;
let observed_signals = fixture.declared_num_signals;
let total_cells = fixture.rows.iter().map(Vec::len).sum::<usize>() as u32;
let nan_cell_count = fixture
.rows
.iter()
.flat_map(|r| r.iter())
.filter(|c| c.is_none())
.count() as u32;
IngestReport {
fixture_sha256_hex: fixture.fixture_sha256_hex.clone(),
fixture_byte_size,
declared_num_windows: fixture.declared_num_windows,
declared_num_signals: fixture.declared_num_signals,
observed_num_windows: observed_windows,
observed_num_signals: observed_signals,
nan_cell_count,
finite_cell_count: total_cells.saturating_sub(nan_cell_count),
emitted_event_count: events.len() as u32,
}
}
#[cfg(test)]
mod tests {
use super::*;
const PIN_AIOPS: &str = "be17110ebe6647d00fad79dc1ca69b1b01b22788773202bad6e3322e97b0602e";
fn aiops_path() -> std::path::PathBuf {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR");
std::path::PathBuf::from(manifest_dir)
.parent()
.and_then(std::path::Path::parent)
.expect("workspace root")
.join("data/fixtures/aiops_challenge.tsv")
}
#[test]
fn sha256_to_hex_lower_roundtrip() {
let bytes = sha256(b"hello world");
let hex = sha256_to_hex_lower(&bytes);
assert_eq!(hex.len(), 64);
assert!(hex
.chars()
.all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()));
}
#[test]
fn verify_fixture_sha256_admits_correct_pin() {
let bytes = std::fs::read(aiops_path()).expect("read aiops fixture");
let actual = verify_fixture_sha256(&bytes, PIN_AIOPS).expect("admit");
assert_eq!(actual, PIN_AIOPS);
}
#[test]
fn verify_fixture_sha256_rejects_uppercase_hex() {
let bytes = std::fs::read(aiops_path()).expect("read aiops fixture");
let upper: String = PIN_AIOPS.chars().map(|c| c.to_ascii_uppercase()).collect();
match verify_fixture_sha256(&bytes, &upper) {
Err(IngestError::Sha256NotLowerHex64 { .. }) => {}
other => panic!("expected Sha256NotLowerHex64, got {other:?}"),
}
}
#[test]
fn verify_fixture_sha256_rejects_corrupted_bytes() {
let mut bytes = std::fs::read(aiops_path()).expect("read aiops fixture");
if !bytes.is_empty() {
let mid = bytes.len() / 2;
bytes[mid] ^= 0x01;
}
match verify_fixture_sha256(&bytes, PIN_AIOPS) {
Err(IngestError::Sha256Mismatch { .. }) => {}
other => panic!("expected Sha256Mismatch, got {other:?}"),
}
}
#[test]
fn load_aiops_admits_canonical_shape() {
let bytes = std::fs::read(aiops_path()).expect("read aiops fixture");
let fixture = load_residual_projection_tsv(&bytes, PIN_AIOPS).expect("load");
assert_eq!(fixture.declared_num_signals, 4);
assert_eq!(fixture.declared_num_windows, 32);
for row in &fixture.rows {
assert_eq!(row.len(), 4);
assert!(row.iter().all(Option::is_some));
}
assert!(fixture.metadata.contains_key("license"));
}
#[test]
fn lower_to_trace_events_canonical_ordering() {
let bytes = std::fs::read(aiops_path()).expect("read aiops fixture");
let fixture = load_residual_projection_tsv(&bytes, PIN_AIOPS).expect("load");
let cfg = LoweringConfig::default();
let events = lower_to_trace_events(&fixture, &cfg);
assert_eq!(
events.len(),
fixture
.rows
.iter()
.map(|r| r.iter().filter(|c| c.is_some()).count())
.sum::<usize>()
);
assert_eq!(events[0].ts_ns, 0);
assert_eq!(events[0].entity_id, 0);
let signals = fixture.declared_num_signals as usize;
for i in 1..signals.min(events.len()) {
assert!(events[i].entity_id >= events[i - 1].entity_id);
}
}
#[test]
fn lowering_is_deterministic_across_two_runs() {
let bytes = std::fs::read(aiops_path()).expect("read aiops fixture");
let a = load_residual_projection_tsv(&bytes, PIN_AIOPS).expect("load");
let b = load_residual_projection_tsv(&bytes, PIN_AIOPS).expect("load");
let cfg = LoweringConfig::default();
let ea = lower_to_trace_events(&a, &cfg);
let eb = lower_to_trace_events(&b, &cfg);
assert_eq!(ea, eb);
}
#[test]
fn empty_file_rejected() {
match load_residual_projection_tsv(b"", "0".repeat(64).as_str()) {
Err(IngestError::EmptyFile) => {}
other => panic!("expected EmptyFile, got {other:?}"),
}
}
}