diff --git a/src/manifold/mod.rs b/src/manifold/mod.rs index 6e5c6fa4..2e4c6c8e 100644 --- a/src/manifold/mod.rs +++ b/src/manifold/mod.rs @@ -41,15 +41,18 @@ impl Manifold { if !pos.len().is_multiple_of(3) { return Err("pos must be a multiple of 3".into()); } if !idx.len().is_multiple_of(3) { return Err("idx must be a multiple of 3".into()); } if !val.is_empty() && val.len() != idx.len() { return Err("val must hold one value per corner".into()); } + let nv = pos.len() / 3; + if idx.iter().any(|&i| i >= nv) { return Err("idx must refer to vertices in pos".into()); } // dedup vertices - let mut hash = HashMap::with_capacity(pos.len() / 3); - let mut weld = Vec::with_capacity(pos.len() / 3); - let mut rmap = vec![0; pos.len()]; + let mut hash = HashMap::with_capacity(nv); + let mut weld = Vec::with_capacity(nv); + let mut rmap = vec![0; nv]; let mut hs = Vec::with_capacity(idx.len()); for (i, p) in pos.chunks(3).enumerate() { let v = Vec3::new(p[0] as Real, p[1] as Real, p[2] as Real); + if !v.is_finite() { return Err("pos must not contain NaN or infinite coordinates".into()); } let k = (v.x.to_bits(), v.y.to_bits(), v.z.to_bits()); if let Some(&w) = hash.get(&k) { rmap[i] = w; } else { let n = weld.len(); diff --git a/src/tests.rs b/src/tests.rs index 7ec812a1..31cee227 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -763,3 +763,41 @@ mod test_values { } } + +#[cfg(test)] +mod test_input_validation { + use crate::prelude::Manifold; + + // A unit tetrahedron: valid input, so each test changes one thing. + const POS: [f64; 12] = [0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 1.]; + const IDX: [usize; 12] = [0, 2, 1, 0, 1, 3, 0, 3, 2, 1, 2, 3]; + + #[test] + fn test_valid_tet_is_accepted() { + assert!(Manifold::new(&POS, &IDX).is_ok()); + } + + #[test] + fn test_index_past_end_of_pos_is_an_error() { + let mut idx = IDX; + idx[0] = 99; + assert!(Manifold::new(&POS, &idx).is_err()); + } + + #[test] + fn test_index_past_last_vertex_is_an_error() { + // 4 vertices, but `pos.len()` is 12: index 5 used to be read as vertex 0. + let mut idx = IDX; + idx[0] = 5; + assert!(Manifold::new(&POS, &idx).is_err()); + } + + #[test] + fn test_non_finite_coordinates_are_errors() { + for bad in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + let mut pos = POS; + pos[3] = bad; + assert!(Manifold::new(&pos, &IDX).is_err(), "{bad} was accepted"); + } + } +}