89 lines
3.1 KiB
Java
89 lines
3.1 KiB
Java
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<String> unityFiles) {
|
|
long ops = 0;
|
|
List<String> 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<String> unityFiles) {
|
|
long ops = 0;
|
|
Set<String> sourcePathsSet = new HashSet<>();
|
|
List<String> 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<String> 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<String> input = Arrays.asList("a.cpp", "b.cpp", "a.cpp", "c.cpp");
|
|
List<String> slowOut = new ArrayList<>();
|
|
for (String s : input) {
|
|
if (!slowOut.contains(s)) slowOut.add(s);
|
|
}
|
|
Set<String> 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);
|
|
}
|
|
}
|