validate_wires/
validate_wires.rs1use brdb::{Brdb, Brz, IntoReader, WireChunkSoA};
6use std::collections::HashMap;
7use std::path::PathBuf;
8
9fn main() -> Result<(), Box<dyn std::error::Error>> {
10 let path = PathBuf::from(std::env::args().nth(1).expect("usage: validate_wires <brz|brdb>"));
11 if path.extension().is_some_and(|e| e == "brdb") {
12 run(Brdb::open(&path)?.into_reader())
13 } else {
14 run(Brz::open(&path)?.into_reader())
15 }
16}
17
18fn run<T: brdb::BrFsReader>(
19 db: brdb::BrReader<T>,
20) -> Result<(), Box<dyn std::error::Error>> {
21 let data = db.global_data()?;
22
23 let mut grid_ids = vec![1usize];
25 for index in db.entity_chunk_index()? {
26 for e in db.entity_chunk(index)? {
27 if e.is_brick_grid() || e.is_microchip_grid() {
28 if let Some(id) = e.id {
29 grid_ids.push(id);
30 }
31 }
32 }
33 }
34
35 let mut brick_counts: HashMap<(usize, String), usize> = HashMap::new();
38 let mut brick_components: HashMap<(usize, String), HashMap<u32, Vec<u16>>> = HashMap::new();
39 let mut brick_types: HashMap<(usize, String), Vec<u32>> = HashMap::new();
40 for &gid in &grid_ids {
41 let chunks = match db.brick_chunk_index(gid) {
42 Ok(c) => c,
43 Err(_) => continue,
44 };
45 for chunk in &chunks {
46 let key = (gid, format!("{:?}", chunk.index));
47 let soa = db.brick_chunk_soa(gid, chunk.index)?;
48 brick_counts.insert(key.clone(), soa.brick_type_indices.len());
49 brick_types.insert(key.clone(), soa.brick_type_indices.clone());
50 let mut per_brick: HashMap<u32, Vec<u16>> = HashMap::new();
51 if chunk.num_components > 0 {
52 let (csoa, _components) = db.component_chunk_soa(gid, chunk.index)?;
53 let type_indices: Vec<u16> = csoa
54 .component_type_counters
55 .iter()
56 .flat_map(|v| {
57 let index = v.type_index as u16;
58 (0..v.num_instances).map(move |_| index)
59 })
60 .collect();
61 for (i, bi) in csoa.component_brick_indices.iter().enumerate() {
62 let brick_index = *bi;
63 per_brick.entry(brick_index).or_default().push(type_indices[i]);
64 }
65 }
66 brick_components.insert(key, per_brick);
67 }
68 }
69
70 let cname = |t: u16| -> String {
71 data.component_type_names
72 .get_index(t as usize)
73 .map(|s| s.to_string())
74 .unwrap_or_else(|| format!("<type {t}>"))
75 };
76 let pname = |p: u16| -> String {
77 data.component_wire_port_names
78 .get_index(p as usize)
79 .map(|s| s.to_string())
80 .unwrap_or_else(|| format!("<port {p}>"))
81 };
82
83 let mut total = 0u64;
85 let mut bad = 0u64;
86 for &gid in &grid_ids {
87 let chunks = match db.brick_chunk_index(gid) {
88 Ok(c) => c,
89 Err(_) => continue,
90 };
91 for chunk in &chunks {
92 if chunk.num_wires == 0 {
93 continue;
94 }
95 let key = (gid, format!("{:?}", chunk.index));
96 let soa = db.wire_chunk_soa(gid, chunk.index)?.to_value();
97 let soa: WireChunkSoA = (&soa).try_into()?;
98
99 let mut check = |ctx: &str,
100 ggid: usize,
101 ckey: &str,
102 brick_index: u32,
103 ct: u16,
104 port: u16| {
105 total += 1;
106 let k = (ggid, ckey.to_string());
107 let n = brick_counts.get(&k).copied().unwrap_or(0);
108 if brick_index as usize >= n {
109 bad += 1;
110 println!(
111 "BAD {ctx}: grid {ggid} chunk {ckey} brick {brick_index} OUT OF RANGE (chunk has {n} bricks); wanted {} {}",
112 cname(ct),
113 pname(port)
114 );
115 return;
116 }
117 let comps = brick_components
118 .get(&k)
119 .and_then(|m| m.get(&brick_index))
120 .cloned()
121 .unwrap_or_default();
122 if !comps.contains(&ct) {
123 bad += 1;
124 println!(
125 "BAD {ctx}: grid {ggid} chunk {ckey} brick {brick_index} has components [{}] but wire wants {} {}",
126 comps.iter().map(|c| cname(*c)).collect::<Vec<_>>().join(", "),
127 cname(ct),
128 pname(port)
129 );
130 }
131 };
132
133 for p in &soa.local_wire_sources {
134 check("local-src", gid, &key.1, p.brick_index_in_chunk, p.component_type_index, p.port_index);
135 }
136 for p in &soa.local_wire_targets {
137 check("local-tgt", gid, &key.1, p.brick_index_in_chunk, p.component_type_index, p.port_index);
138 }
139 for p in &soa.remote_wire_sources {
140 let ckey = format!("{:?}", p.chunk_index);
141 check(
142 "remote-src",
143 p.grid_persistent_index as usize,
144 &ckey,
145 p.brick_index_in_chunk,
146 p.component_type_index,
147 p.port_index,
148 );
149 }
150 for p in &soa.remote_wire_targets {
153 check("remote-tgt", gid, &key.1, p.brick_index_in_chunk, p.component_type_index, p.port_index);
154 }
155 }
156 }
157
158 println!("validated {total} wire endpoints, {bad} bad");
159
160 let args: Vec<String> = std::env::args().collect();
162 if args.len() >= 5 {
163 let gid: usize = args[2].parse()?;
164 let lo: u32 = args[3].parse()?;
165 let hi: u32 = args[4].parse()?;
166 for ((g, ckey), types) in &brick_types {
167 if *g != gid {
168 continue;
169 }
170 for i in lo..=hi.min(types.len().saturating_sub(1) as u32) {
171 let comps = brick_components
172 .get(&(gid, ckey.clone()))
173 .and_then(|m| m.get(&i))
174 .cloned()
175 .unwrap_or_default();
176 let asset = data
177 .basic_brick_asset_names
178 .get_index(types[i as usize] as usize)
179 .map(|s| s.to_string())
180 .unwrap_or_else(|| format!("<pb {}>", types[i as usize]));
181 println!(
182 "grid {gid} chunk {ckey} brick {i}: {asset} [{}]",
183 comps.iter().map(|c| cname(*c)).collect::<Vec<_>>().join(", ")
184 );
185 }
186 }
187 }
188 Ok(())
189}