java-topology/defects/prusaslicer-0002/test/PrusaSlicerSupportIslandWorklistTest.java
russell@unturf.com d15b9b06d3 wekan-0001/wekan-0002, kodi CLEAN: 5-MOAD scan across PrusaSlicer/Kodi/Wekan
wekan-0001: boards.js setNewLabelOrder indexOf in sort comparator O(L^2 logL) MEDIUM 808x
wekan-0002: cards.js moveToBoard filter+includes O(M*A) MEDIUM 58x
kodi: CLEAN for CWE-407 (proper maps/sets/contains throughout)
prusaslicer-0002: fix test N parameter for reasonable runtime
cleanup: remove .class artifacts from prusaslicer tests
2026-03-31 08:05:29 -04:00

85 lines
3.1 KiB
Java

import java.util.*;
/**
* Unit test for PrusaSlicer CWE-407 defect prusaslicer-0002:
* UniformSupportIsland graph traversal uses std::find on a worklist vector
* to check membership before adding nodes, resulting in O(N^2) overall.
* Fix: maintain a parallel unordered_set for O(1) membership checks.
*
* Defect location: src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp
* Pattern: std::find(process.begin(), process.end(), item_index) in while-true graph loop
*/
public class PrusaSlicerSupportIslandWorklistTest {
// --- DEFECTIVE: O(N^2) worklist with linear membership check ---
static int processWorklistDefective(int N) {
List<Integer> process = new ArrayList<>();
for (int i = 0; i < N; i++) process.add(i);
int ops = 0;
// Simulate the graph traversal checking membership for each neighbor
for (int current = 0; current < N; current++) {
// Each node has ~3 neighbors to check
for (int d = 0; d < 3; d++) {
int neighbor = (current * 3 + d) % N;
if (process.contains(neighbor)) { // O(N) linear scan
ops++;
}
}
}
return ops;
}
// --- FIXED: O(N) worklist with hash set membership ---
static int processWorklistFixed(int N) {
List<Integer> process = new ArrayList<>();
Set<Integer> processSet = new HashSet<>();
for (int i = 0; i < N; i++) {
process.add(i);
processSet.add(i);
}
int ops = 0;
for (int current = 0; current < N; current++) {
for (int d = 0; d < 3; d++) {
int neighbor = (current * 3 + d) % N;
if (processSet.contains(neighbor)) { // O(1) hash lookup
ops++;
}
}
}
return ops;
}
public static void main(String[] args) {
// Correctness
int rDef = processWorklistDefective(100);
int rFix = processWorklistFixed(100);
assert rDef == rFix : "Operation count must match: " + rDef + " vs " + rFix;
System.out.println("PASS correctness: " + rFix + " operations");
int N = 20_000;
// Warmup
for (int i = 0; i < 3; i++) {
processWorklistDefective(N);
processWorklistFixed(N);
}
long t0 = System.nanoTime();
int rDefLarge = processWorklistDefective(N);
long t1 = System.nanoTime();
int rFixLarge = processWorklistFixed(N);
long t2 = System.nanoTime();
double defMs = (t1 - t0) / 1e6;
double fixMs = (t2 - t1) / 1e6;
double ratio = defMs / fixMs;
assert rDefLarge == rFixLarge : "Large results must match";
System.out.printf("PASS defective: %.1f ms, fixed: %.1f ms, ratio: %.1fx%n", defMs, fixMs, ratio);
assert ratio > 2.0 : "Fixed should be at least 2x faster, got " + ratio + "x";
System.out.println("PASS performance: ratio " + String.format("%.1f", ratio) + "x");
System.out.println("ALL TESTS PASSED");
}
}