darktable-0001: map_locations image diff g_list_find O(N*M) MEDIUM 375x darktable-0002: tags _tag_add_tags_to_list g_list_find O(T*L) MEDIUM 375x darktable-0003: map view clustering g_list_find(sel_imgs) O(I²×S) HIGH 160x scribus-0001: getSortedStyleList retList.contains O(N²) MEDIUM 167x (×4 copies) scribus-0002: getUsedPatterns results.contains O(I×R) MEDIUM 98x scribus-0003: Selection::addItems m_SelList.contains O(N×M) MEDIUM 2100x
195 lines
7.3 KiB
Java
195 lines
7.3 KiB
Java
import java.util.*;
|
||
|
||
/**
|
||
* CWE-407 simulation tests for Scribus defects.
|
||
*
|
||
* scribus-0001: getSortedStyleList retList.contains O(N²) dedup
|
||
* scribus-0002: getUsedPatterns results.contains O(I×R) pattern accumulation
|
||
* scribus-0003: Selection::addItems m_SelList.contains O(N×M) selection dedup
|
||
*/
|
||
public class ScribusTest {
|
||
|
||
// ---------------------------------------------------------------
|
||
// scribus-0001: getSortedStyleList — QList<int>::contains inside loop
|
||
// Simulates walking parent chains and deduplicating into retList
|
||
// ---------------------------------------------------------------
|
||
|
||
/** Defective: List.contains O(N) inside O(N) loop */
|
||
static int sortedStyleListDefective(int styleCount, int[] parentIdx) {
|
||
int ops = 0;
|
||
List<Integer> retList = new ArrayList<>();
|
||
for (int i = 0; i < styleCount; i++) {
|
||
// Check if already in retList
|
||
for (int x : retList) { ops++; if (x == i) break; }
|
||
if (!retList.contains(i)) {
|
||
// Walk parent chain
|
||
List<Integer> chain = new ArrayList<>();
|
||
chain.add(i);
|
||
int p = parentIdx[i];
|
||
while (p >= 0) {
|
||
boolean found = false;
|
||
for (int x : chain) { ops++; if (x == p) { found = true; break; } }
|
||
if (!found) chain.add(0, p);
|
||
p = parentIdx[p];
|
||
}
|
||
// Merge chain into retList with contains check
|
||
for (int idx : chain) {
|
||
boolean found = false;
|
||
for (int x : retList) { ops++; if (x == idx) { found = true; break; } }
|
||
if (!found) retList.add(idx);
|
||
}
|
||
}
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
/** Fixed: QSet<int> companion */
|
||
static int sortedStyleListFixed(int styleCount, int[] parentIdx) {
|
||
int ops = 0;
|
||
List<Integer> retList = new ArrayList<>();
|
||
Set<Integer> retSet = new HashSet<>();
|
||
for (int i = 0; i < styleCount; i++) {
|
||
ops++;
|
||
if (!retSet.contains(i)) {
|
||
List<Integer> chain = new ArrayList<>();
|
||
Set<Integer> chainSet = new HashSet<>();
|
||
chain.add(i); chainSet.add(i); ops++;
|
||
int p = parentIdx[i];
|
||
while (p >= 0) {
|
||
ops++;
|
||
if (!chainSet.contains(p)) { chain.add(0, p); chainSet.add(p); }
|
||
p = parentIdx[p];
|
||
}
|
||
for (int idx : chain) {
|
||
ops++;
|
||
if (!retSet.contains(idx)) { retList.add(idx); retSet.add(idx); }
|
||
}
|
||
}
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
static void testSortedStyleList() {
|
||
int N = 500;
|
||
int[] parentIdx = new int[N];
|
||
// Linear parent chain: 0←1←2←...←N-1 (worst case)
|
||
parentIdx[0] = -1;
|
||
for (int i = 1; i < N; i++) parentIdx[i] = i - 1;
|
||
|
||
int defectOps = sortedStyleListDefective(N, parentIdx);
|
||
int fixedOps = sortedStyleListFixed(N, parentIdx);
|
||
double ratio = (double) defectOps / fixedOps;
|
||
|
||
System.out.printf("scribus-0001 sorted style list: defect=%d fixed=%d ratio=%.1fx%n",
|
||
defectOps, fixedOps, ratio);
|
||
assert ratio > 10 : "Expected >10x ratio, got " + ratio;
|
||
System.out.println(" PASS");
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// scribus-0002: getUsedPatterns — QStringList::contains inside item loop
|
||
// ---------------------------------------------------------------
|
||
|
||
/** Defective: linear scan for each pattern check */
|
||
static int getUsedPatternsDefective(String[][] itemPatterns) {
|
||
int ops = 0;
|
||
List<String> results = new ArrayList<>();
|
||
for (String[] pats : itemPatterns) {
|
||
for (String pat : pats) {
|
||
if (pat == null || pat.isEmpty()) continue;
|
||
boolean found = false;
|
||
for (String r : results) { ops++; if (r.equals(pat)) { found = true; break; } }
|
||
if (!found) results.add(pat);
|
||
}
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
/** Fixed: HashSet companion */
|
||
static int getUsedPatternsFixed(String[][] itemPatterns) {
|
||
int ops = 0;
|
||
Set<String> resultSet = new HashSet<>();
|
||
for (String[] pats : itemPatterns) {
|
||
for (String pat : pats) {
|
||
if (pat == null || pat.isEmpty()) continue;
|
||
ops++;
|
||
if (!resultSet.contains(pat)) resultSet.add(pat);
|
||
}
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
static void testGetUsedPatterns() {
|
||
int items = 2000;
|
||
int patsPerItem = 3; // fill, stroke, mask
|
||
int uniquePatterns = 200;
|
||
Random rng = new Random(42);
|
||
String[][] itemPatterns = new String[items][patsPerItem];
|
||
for (int i = 0; i < items; i++)
|
||
for (int j = 0; j < patsPerItem; j++)
|
||
itemPatterns[i][j] = "pattern_" + rng.nextInt(uniquePatterns);
|
||
|
||
int defectOps = getUsedPatternsDefective(itemPatterns);
|
||
int fixedOps = getUsedPatternsFixed(itemPatterns);
|
||
double ratio = (double) defectOps / fixedOps;
|
||
|
||
System.out.printf("scribus-0002 used patterns: defect=%d fixed=%d ratio=%.1fx%n",
|
||
defectOps, fixedOps, ratio);
|
||
assert ratio > 10 : "Expected >10x ratio, got " + ratio;
|
||
System.out.println(" PASS");
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// scribus-0003: Selection::addItems — m_SelList.contains inside loop
|
||
// ---------------------------------------------------------------
|
||
|
||
/** Defective: QList::contains O(M) for each of N items */
|
||
static int selectionAddItemsDefective(int existing, int toAdd) {
|
||
int ops = 0;
|
||
List<Integer> selList = new ArrayList<>();
|
||
for (int i = 0; i < existing; i++) selList.add(i);
|
||
|
||
for (int i = existing; i < existing + toAdd; i++) {
|
||
// linear scan
|
||
for (int s : selList) { ops++; if (s == i) break; }
|
||
selList.add(i);
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
/** Fixed: QSet companion for O(1) check */
|
||
static int selectionAddItemsFixed(int existing, int toAdd) {
|
||
int ops = 0;
|
||
Set<Integer> selSet = new HashSet<>();
|
||
for (int i = 0; i < existing; i++) { selSet.add(i); ops++; }
|
||
|
||
for (int i = existing; i < existing + toAdd; i++) {
|
||
ops++;
|
||
if (!selSet.contains(i)) selSet.add(i);
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
static void testSelectionAddItems() {
|
||
int existing = 2000;
|
||
int toAdd = 3000;
|
||
|
||
int defectOps = selectionAddItemsDefective(existing, toAdd);
|
||
int fixedOps = selectionAddItemsFixed(existing, toAdd);
|
||
double ratio = (double) defectOps / fixedOps;
|
||
|
||
System.out.printf("scribus-0003 selection addItems: defect=%d fixed=%d ratio=%.1fx%n",
|
||
defectOps, fixedOps, ratio);
|
||
assert ratio > 10 : "Expected >10x ratio, got " + ratio;
|
||
System.out.println(" PASS");
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
|
||
public static void main(String[] args) {
|
||
testSortedStyleList();
|
||
testGetUsedPatterns();
|
||
testSelectionAddItems();
|
||
System.out.println("\nAll 3 Scribus CWE-407 tests PASSED.");
|
||
}
|
||
}
|