game engines/web frameworks: 27 CWE-407 defects + 3 CLEAN; 194 sites, 78 ecosystems

This commit is contained in:
russell@unturf.com 2026-03-27 14:14:49 -04:00
parent 547a9f5738
commit 4d3fcc8e73
76 changed files with 6216 additions and 17 deletions

View file

@ -0,0 +1,170 @@
package unit;
import java.util.*;
/**
* OGRETest — Java analogs of three OGRE CWE-407 defects.
*
* Defect 1 (ogre-0001): Node::~Node — std::find on vector of queued-update nodes.
* Slow: ArrayList.remove(Object) — O(N) scan per node destroyed.
* Fast: LinkedHashSet.remove(Object) — O(1) per node destroyed.
*
* Defect 2 (ogre-0002): ResourceGroupManager::_notifyAllResourcesRemoved — std::find
* in nested loop erasing resources from a list.
* Slow: ArrayList.remove(Object) inside an erase loop — O(N²).
* Fast: Build HashSet of to-remove pointers, single-pass removeIf — O(N).
*
* Defect 3 (ogre-0003): RibbonTrail::clearChain — std::find on parallel index vector
* to reverse-map chain-index → node.
* Slow: ArrayList.indexOf(chainIndex) — O(N).
* Fast: HashMap<Integer,Object> reverse map — O(1).
*/
public class OGRETest {
static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) {
// warmup
slow.run();
fast.run();
long t0 = System.nanoTime();
slow.run();
long sMs = (System.nanoTime() - t0) / 1_000_000;
long t1 = System.nanoTime();
fast.run();
long fMs = (System.nanoTime() - t1) / 1_000_000;
double ratio = fOps > 0 ? (double) sOps / fOps : 0;
System.out.printf(" %-56s slow:%5dms (%,d ops) fast:%5dms (%,d ops) speedup:%.0fx%n",
label, sMs, sOps, fMs, fOps, ratio);
}
// -----------------------------------------------------------------------
// ogre-0001: Node queued-update removal during bulk destruction
// -----------------------------------------------------------------------
static void benchNodeQueuedUpdates(int N) {
// SLOW: vector — O(N) scan per node destruction
Runnable slow = () -> {
List<Object> queue = new ArrayList<>(N);
Object[] nodes = new Object[N];
for (int i = 0; i < N; i++) {
nodes[i] = new Object();
queue.add(nodes[i]);
}
// simulate N node destructions: each does std::find + erase
for (int i = 0; i < N; i++) {
queue.remove(nodes[i]); // O(N) scan
}
};
// FAST: unordered_set — O(1) erase per node destruction
Runnable fast = () -> {
Set<Object> queue = new LinkedHashSet<>(N * 2);
Object[] nodes = new Object[N];
for (int i = 0; i < N; i++) {
nodes[i] = new Object();
queue.add(nodes[i]);
}
for (int i = 0; i < N; i++) {
queue.remove(nodes[i]); // O(1) hash remove
}
};
long sOps = (long) N * N / 2; // average scan length N/2 × N destructions
long fOps = N;
bench(String.format("ogre-0001 Node::~Node queued-update scan N=%d", N),
slow, fast, sOps, fOps);
}
// -----------------------------------------------------------------------
// ogre-0002: ResourceGroupManager notify-removed — nested find+erase
// -----------------------------------------------------------------------
static void benchResourceGroupRemove(int R) {
// SLOW: std::find on list for each item in arDel — O(R²)
Runnable slow = () -> {
List<Object> resourceList = new ArrayList<>(R);
for (int i = 0; i < R; i++) resourceList.add(new Object());
List<Object> arDel = new ArrayList<>(resourceList); // remove all
for (Object item : arDel) {
resourceList.remove(item); // O(R) scan each time
}
};
// FAST: build HashSet, single-pass removeIf — O(R)
Runnable fast = () -> {
List<Object> resourceList = new ArrayList<>(R);
for (int i = 0; i < R; i++) resourceList.add(new Object());
List<Object> arDel = new ArrayList<>(resourceList);
Set<Object> toRemove = new HashSet<>(arDel);
resourceList.removeIf(toRemove::contains); // O(R) single pass
};
long sOps = (long) R * R;
long fOps = R;
bench(String.format("ogre-0002 ResourceGroupManager::_notifyRemoved R=%d", R),
slow, fast, sOps, fOps);
}
// -----------------------------------------------------------------------
// ogre-0003: RibbonTrail::clearChain — parallel-vector reverse lookup
// -----------------------------------------------------------------------
static void benchRibbonTrailClearChain(int K, int clears) {
Object[] nodes = new Object[K];
for (int i = 0; i < K; i++) nodes[i] = new Object();
// SLOW: parallel arrays, indexOf for reverse lookup
Runnable slow = () -> {
List<Object> nodeList = new ArrayList<>(Arrays.asList(nodes));
List<Integer> chainSegments = new ArrayList<>();
for (int i = 0; i < K; i++) chainSegments.add(i);
long dummy = 0;
for (int c = 0; c < clears; c++) {
int chainIndex = c % K;
// std::find on mNodeToChainSegment — O(K)
int pos = chainSegments.indexOf(chainIndex);
if (pos >= 0) dummy += pos;
}
if (dummy < 0) System.out.println("never");
};
// FAST: HashMap for reverse lookup — O(1) per clear
Runnable fast = () -> {
Map<Integer, Object> chainToNode = new HashMap<>(K * 2);
for (int i = 0; i < K; i++) chainToNode.put(i, nodes[i]);
long dummy = 0;
for (int c = 0; c < clears; c++) {
int chainIndex = c % K;
// HashMap.get — O(1)
Object n = chainToNode.get(chainIndex);
if (n != null) dummy++;
}
if (dummy < 0) System.out.println("never");
};
long sOps = (long) clears * K;
long fOps = clears;
bench(String.format("ogre-0003 RibbonTrail::clearChain K=%d clears=%d", K, clears),
slow, fast, sOps, fOps);
}
public static void main(String[] args) {
System.out.println("OGRE CWE-407 defect benchmarks");
System.out.println("=".repeat(100));
System.out.println("\n[ogre-0001] Node::~Node — msQueuedUpdates linear scan");
benchNodeQueuedUpdates(500);
benchNodeQueuedUpdates(2_000);
benchNodeQueuedUpdates(10_000);
System.out.println("\n[ogre-0002] ResourceGroupManager::_notifyAllResourcesRemoved");
benchResourceGroupRemove(500);
benchResourceGroupRemove(2_000);
benchResourceGroupRemove(10_000);
System.out.println("\n[ogre-0003] RibbonTrail::clearChain — parallel-vector reverse lookup");
benchRibbonTrailClearChain(50, 100_000);
benchRibbonTrailClearChain(200, 100_000);
benchRibbonTrailClearChain(1_000, 100_000);
}
}