578/240 — rustc-0004/vertx-0001/asterisk-0003/bitcoin-0001/actix-web-0003/hazelcast-0001+0002/rabbitmq-0005/nginx-0003/haproxy-0003/envoy-0003/istio-0003

This commit is contained in:
russell@unturf.com 2026-03-27 21:25:37 -04:00
parent dde5ec97fb
commit e4ee168b1e
50 changed files with 4775 additions and 5 deletions

View file

@ -0,0 +1,111 @@
package unit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* CWE-407 unit test vertx-0001
*
* HAManager.nodeLeft() scans clusterMap (C entries) and calls nodes.contains() on a
* List<String> for each entry O(C × N) = O(N²) in cluster size.
*
* Fix: convert nodes List to HashSet before the loop O(N) total.
*/
public class HAManagerNodeLeftAlgorithm {
// Simulated clusterMap: nodeId -> haInfo (one entry per cluster node)
static Map<String, String> buildClusterMap(int clusterSize) {
Map<String, String> map = new HashMap<>();
for (int i = 0; i < clusterSize; i++) {
map.put("node-" + i, "{\"group\":\"default\",\"id\":\"node-" + i + "\"}");
}
return map;
}
// Simulated getNodes() returns a List (as per ClusteredNode SPI contract)
static List<String> buildNodesList(int clusterSize) {
List<String> list = new ArrayList<>();
for (int i = 0; i < clusterSize; i++) {
list.add("node-" + i);
}
return list;
}
// SLOW: original pattern List.contains inside loop
static long slowNodeLeft(Map<String, String> clusterMap, List<String> nodes, String leftNodeID) {
long slowOps = 0;
for (Map.Entry<String, String> entry : clusterMap.entrySet()) {
if (!leftNodeID.equals(entry.getKey())) {
// O(N) scan per iteration the defect
for (String n : nodes) {
slowOps++;
if (n.equals(entry.getKey())) {
break;
}
}
}
}
return slowOps;
}
// FAST: fixed pattern HashSet.contains inside loop
static long fastNodeLeft(Map<String, String> clusterMap, List<String> nodes, String leftNodeID) {
Set<String> nodesSet = new HashSet<>(nodes); // O(N) once
long fastOps = 0;
for (Map.Entry<String, String> entry : clusterMap.entrySet()) {
if (!leftNodeID.equals(entry.getKey())) {
// O(1) lookup
fastOps++;
nodesSet.contains(entry.getKey());
}
}
return fastOps;
}
public static void main(String[] args) {
int[] sizes = {100, 250, 500};
int pass = 0;
int fail = 0;
double minRatio = Double.MAX_VALUE;
for (int N : sizes) {
Map<String, String> clusterMap = buildClusterMap(N);
List<String> nodes = buildNodesList(N);
String leftNodeID = "node-" + (N - 1);
long slowOps = slowNodeLeft(clusterMap, nodes, leftNodeID);
long fastOps = fastNodeLeft(clusterMap, nodes, leftNodeID);
// Expected: slow triangular number sum of scan depths N*(N-1)/2
// fast = O(N-1) operations (one per remaining entry, O(1) each)
long expectedSlow = (long) N * (N - 1) / 2; // approximate
long expectedFast = N - 1;
double ratio = (double) slowOps / fastOps;
// Verify ordering
boolean ok = slowOps > fastOps && ratio >= 5.0;
if (ok) {
pass++;
} else {
fail++;
System.out.printf("FAIL N=%d: slowOps=%d fastOps=%d ratio=%.1f%n",
N, slowOps, fastOps, ratio);
}
System.out.printf("N=%d: slowOps=%d fastOps=%d ratio=%.1fx [%s]%n",
N, slowOps, fastOps, ratio, ok ? "PASS" : "FAIL");
minRatio = Math.min(minRatio, ratio);
}
System.out.printf("%d/%d PASS (min ratio=%.1fx)%n", pass, pass + fail, minRatio);
if (fail > 0) {
System.exit(1);
}
}
}