package unit; import java.util.*; /** * CWE-407 unit test: cmake-0004 * AddSource — O(n²) std::find_if per call in Unity build loop vs O(n) set guard. * * Simulates cmTarget::AddSource called in loop over unity_files: * slow: scan Sources.Entries vector (find_if) for each add → O(n²) * fast: use HashSet shadow, skip scan if already present → O(n) */ public class AddSourceAlgorithm { // Slow: O(n^2) — find_if scan per AddSource call (mirrors TargetPropertyEntryFinder) static long slowAddSources(List unityFiles) { long ops = 0; List sourcesEntries = new ArrayList<>(); for (String src : unityFiles) { boolean found = false; for (String existing : sourcesEntries) { // O(n) scan ops++; if (existing.equals(src)) { found = true; break; } } if (!found) { sourcesEntries.add(src); } } return ops; } // Fast: HashSet shadow for O(1) check, O(n) total static long fastAddSources(List unityFiles) { long ops = 0; Set sourcePathsSet = new HashSet<>(); List sourcesEntries = new ArrayList<>(); for (String src : unityFiles) { ops++; if (sourcePathsSet.add(src)) { sourcesEntries.add(src); } } return ops; } public static void main(String[] args) { int[] sizes = {200, 500, 1000, 2000}; int passed = 0, total = 0; for (int S : sizes) { // S unity source files, 20% duplicates (re-added after PCH generation) List unityFiles = new ArrayList<>(S + S / 5); for (int i = 0; i < S; i++) { unityFiles.add("/src/unity_" + i + ".cpp"); } for (int i = 0; i < S / 5; i++) { unityFiles.add("/src/unity_" + i + ".cpp"); // duplicate } long slowOps = slowAddSources(unityFiles); long fastOps = fastAddSources(unityFiles); double ratio = (double) slowOps / fastOps; total++; System.out.printf("S=%4d slow=%8d fast=%6d ratio=%.1fx%n", S, slowOps, fastOps, ratio); // At S=2000: slow is O(S^2/2) ~ 2M, fast is O(S) ~ 2000 → >100x assert ratio > 20.0 : "Expected ratio > 20 at S=" + S + ", got " + ratio; passed++; } // Correctness: same unique list preserved List input = Arrays.asList("a.cpp", "b.cpp", "a.cpp", "c.cpp"); List slowOut = new ArrayList<>(); for (String s : input) { if (!slowOut.contains(s)) slowOut.add(s); } Set fastOut = new LinkedHashSet<>(); fastOut.addAll(input); assert new ArrayList<>(fastOut).equals(slowOut) : "Correctness: " + fastOut + " vs " + slowOut; passed++; total++; System.out.printf("%d/%d PASS%n", passed, total); } }