re: coverage is 96.4% (6069/6294) -- and the 225 misses get a gate breakdown

undecoded.rs supplies the denominator the coverage numbers never had; the disc
holds 6294 XBG7 resources, 6069 decode, 225 are searched and missed, 0 lack a
descriptor. gate_histogram.rs attributes each miss to the furthest gate its best
candidate reached: 120 connectivity, 74 grouped-pool (different path), 15
degenerate/extent, 9 winding, 7 buffer-not-covered. Recorded as a work-list, not
a verdict -- a wrong candidate can pass more gates than the true block.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 04:22:43 +00:00
parent db8104ab99
commit c9c98cf942
4 changed files with 209 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
//! Which gate stops the resources that never decode?
//! Usage: gate_histogram <resource3d_dir> [max_resources]
use sylpheed_formats::mesh::{debug_best_rejection, xbg7_resource_names, Xbg7Model};
use std::collections::{BTreeMap, HashSet};
fn main() {
let dir = std::env::args().nth(1).expect("resource3d dir");
let cap: usize = std::env::args().nth(2).and_then(|s| s.parse().ok()).unwrap_or(usize::MAX);
let mut files: Vec<_> = std::fs::read_dir(&dir)
.unwrap()
.flatten()
.map(|e| e.path())
.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("xpr"))
.collect();
files.sort();
let mut hist: BTreeMap<usize, (usize, String)> = BTreeMap::new();
let mut done = 0usize;
for f in &files {
if done >= cap {
break;
}
let Ok(bytes) = std::fs::read(f) else { continue };
let names = xbg7_resource_names(&bytes);
if names.is_empty() {
continue;
}
let got: HashSet<String> = Xbg7Model::anchor_models_cancellable(&bytes, 0.0, &|| false)
.into_iter()
.map(|m| m.name)
.collect();
for n in names.iter().filter(|n| !got.contains(*n)) {
if done >= cap {
break;
}
done += 1;
if let Some((rank, why)) = debug_best_rejection(&bytes, n) {
let e = hist.entry(rank).or_insert((0, String::new()));
e.0 += 1;
if e.1.is_empty() {
e.1 = format!("{n}: {why}");
}
}
}
}
println!("furthest gate reached, over {done} resources that never decode:");
let label = |r: usize| match r {
0 => "no gate reached",
1 => "index out of range",
2 => "buffer not covered by indices",
3 => "degenerate / implausible positions",
4 => "connectivity (mean edge / diagonal)",
5 => "winding consistency",
9 => "grouped pool (different path)",
_ => "?",
};
for (r, (n, ex)) in &hist {
println!(" {:<38} {n:>5} e.g. {ex}", label(*r));
}
}

View File

@@ -0,0 +1,56 @@
//! Which XBG7 resources never decode, and how big is that population?
//!
//! Coverage has been reported as "resources decoded" without a denominator. This
//! prints both, per container and in total, and names the misses so the gate
//! attribution (`why_rejected`) has a work list.
use sylpheed_formats::mesh::{debug_resource_params, xbg7_resource_names, Xbg7Model};
use std::collections::HashSet;
fn main() {
let dir = std::env::args().nth(1).expect("resource3d dir");
let show = std::env::args().nth(2).is_some();
let mut files: Vec<_> = std::fs::read_dir(&dir)
.unwrap()
.flatten()
.map(|e| e.path())
.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("xpr"))
.collect();
files.sort();
let (mut total, mut decoded, mut no_decl) = (0usize, 0usize, 0usize);
let mut misses: Vec<String> = Vec::new();
for f in &files {
let Ok(bytes) = std::fs::read(f) else { continue };
let names = xbg7_resource_names(&bytes);
if names.is_empty() {
continue;
}
let got: HashSet<String> = Xbg7Model::anchor_models_cancellable(&bytes, 0.0, &|| false)
.into_iter()
.map(|m| m.name)
.collect();
for n in &names {
total += 1;
if got.contains(n) {
decoded += 1;
} else if debug_resource_params(&bytes, n).is_none() {
// No vertex declaration / no index markers: the anchor scan
// never even considers these, so they are a different question
// from "searched and not found".
no_decl += 1;
} else {
misses.push(format!("{}|{n}", f.file_name().unwrap().to_string_lossy()));
}
}
}
println!(
"XBG7 resources: {total} total, {decoded} decoded ({:.1}%), {no_decl} without a usable descriptor, {} searched-and-missed",
100.0 * decoded as f64 / total as f64,
misses.len()
);
if show {
for m in &misses {
println!("{m}");
}
}
}