import java.util.*; /** * Unit test for OpenSCAD MOAD-0001: export_amf.cc add_vertex() O(V²) vertex dedup. * * Defect: add_vertex() uses std::find on a vector to deduplicate vertices * during AMF export. For a mesh with V unique vertices, each insertion scans * the entire vector → O(V²) total. * * Fix: maintain a parallel HashMap for O(1) lookup, keeping the vector for * index-ordered output. * * CWE-407: Inefficient Algorithmic Complexity */ public class OpenScadAmfVertexDedupTest { // --- Defective: linear scan on list for every vertex --- static int addVertexDefective(List vertices, String v) { int idx = vertices.indexOf(v); if (idx == -1) { vertices.add(v); return vertices.size() - 1; } return idx; } // --- Fixed: HashMap for O(1) lookup --- static int addVertexFixed(List vertices, Map index, String v) { Integer idx = index.get(v); if (idx == null) { int newIdx = vertices.size(); vertices.add(v); index.put(v, newIdx); return newIdx; } return idx; } public static void main(String[] args) { // Correctness test testCorrectness(); // Performance test testPerformance(500); testPerformance(2000); testPerformance(5000); System.out.println("ALL TESTS PASSED"); } static void testCorrectness() { List vertsDef = new ArrayList<>(); List vertsFix = new ArrayList<>(); Map index = new HashMap<>(); // Simulate vertices with some duplicates String[] inputs = { "1.0,2.0,3.0", "4.0,5.0,6.0", "1.0,2.0,3.0", "7.0,8.0,9.0", "4.0,5.0,6.0", "10.0,11.0,12.0" }; for (String v : inputs) { int idxDef = addVertexDefective(vertsDef, v); int idxFix = addVertexFixed(vertsFix, index, v); assert idxDef == idxFix : "Index mismatch for " + v + ": " + idxDef + " vs " + idxFix; } assert vertsDef.size() == vertsFix.size() : "Size mismatch"; assert vertsDef.size() == 4 : "Expected 4 unique vertices, got " + vertsDef.size(); for (int i = 0; i < vertsDef.size(); i++) { assert vertsDef.get(i).equals(vertsFix.get(i)) : "Content mismatch at " + i; } System.out.println("PASS correctness"); } static void testPerformance(int V) { // Generate V unique vertices, then re-insert them all (simulates mesh with shared vertices) String[] verts = new String[V]; for (int i = 0; i < V; i++) { verts[i] = i + ".0," + (i * 2) + ".0," + (i * 3) + ".0"; } // Defective path: insert all unique, then look up all again List vertsDef = new ArrayList<>(); long t0 = System.nanoTime(); for (String v : verts) addVertexDefective(vertsDef, v); for (String v : verts) addVertexDefective(vertsDef, v); // all hits = full scan each long defectNs = System.nanoTime() - t0; // Fixed path List vertsFix = new ArrayList<>(); Map index = new HashMap<>(); long t1 = System.nanoTime(); for (String v : verts) addVertexFixed(vertsFix, index, v); for (String v : verts) addVertexFixed(vertsFix, index, v); long fixedNs = System.nanoTime() - t1; double ratio = (double) defectNs / fixedNs; System.out.printf("PASS V=%d defect=%dms fixed=%dms ratio=%.1fx%n", V, defectNs / 1_000_000, fixedNs / 1_000_000, ratio); assert ratio > 2.0 : "Expected ratio > 2x at V=" + V + " but got " + ratio; } }