| 170 | // ── Validation ──────────────────────────────────────────────────────────── |
| 171 | |
| 172 | fn validate_and_build( |
| 173 | fd: std::fs::File, |
| 174 | base: *const u8, |
| 175 | file_size: usize, |
| 176 | path: &Path, |
| 177 | policy: VectorSegmentDropPolicy, |
| 178 | budget_guard: Option<BudgetGuard>, |
| 179 | ) -> std::io::Result<Self> { |
| 180 | // Validate magic + format version. |
| 181 | let header = unsafe { std::slice::from_raw_parts(base, HEADER_SIZE) }; |
| 182 | if &header[0..4] != MAGIC.as_slice() { |
| 183 | return Err(std::io::Error::new( |
| 184 | std::io::ErrorKind::InvalidData, |
| 185 | "invalid NDVS magic bytes", |
| 186 | )); |
| 187 | } |
| 188 | let fv = u16::from_le_bytes([header[4], header[5]]); |
| 189 | if fv != FORMAT_VERSION { |
| 190 | return Err(std::io::Error::new( |
| 191 | std::io::ErrorKind::InvalidData, |
| 192 | format!("unsupported segment format version {fv}; expected {FORMAT_VERSION}"), |
| 193 | )); |
| 194 | } |
| 195 | |
| 196 | let dim = u32::from_le_bytes([header[8], header[9], header[10], header[11]]) as usize; |
| 197 | let count = u64::from_le_bytes([ |
| 198 | header[12], header[13], header[14], header[15], header[16], header[17], header[18], |
| 199 | header[19], |
| 200 | ]) as usize; |
| 201 | let compression_byte = header[21]; |
| 202 | |
| 203 | // Codec dispatch — exhaustive match; non-None variants will be |
| 204 | // obvious when compression is wired in the future. |
| 205 | let codec = VectorSegmentCodec::from_u8(compression_byte)?; |
| 206 | match codec { |
| 207 | VectorSegmentCodec::None => { /* raw packed f32 — proceed */ } |
| 208 | } |
| 209 | |
| 210 | if dim == 0 && count > 0 { |
| 211 | return Err(std::io::Error::new( |
| 212 | std::io::ErrorKind::InvalidData, |
| 213 | "segment has dim=0 with nonzero count", |
| 214 | )); |
| 215 | } |
| 216 | |
| 217 | // Validate total file size with overflow-safe arithmetic. |
| 218 | let vec_bytes = dim |
| 219 | .checked_mul(count) |
| 220 | .and_then(|n| n.checked_mul(4)) |
| 221 | .ok_or_else(|| { |
| 222 | std::io::Error::new( |
| 223 | std::io::ErrorKind::InvalidData, |
| 224 | format!("segment header overflow: dim={dim}, count={count}"), |
| 225 | ) |
| 226 | })?; |
| 227 | let sid_bytes = count.checked_mul(8).ok_or_else(|| { |
| 228 | std::io::Error::new( |
| 229 | std::io::ErrorKind::InvalidData, |