java-topology/defects/asterisk/unit/CdrVarMergeAlgorithm.java

180 lines
6.1 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* CWE-407 unit test: asterisk-0003
*
* Models cdr_object_create_public_records() variable merge.
* SLOW: nested list traversal with strcasecmp — O(B × V)
* FAST: hash set membership check — O(B + V)
*/
public class CdrVarMergeAlgorithm {
static long slowOps = 0;
static long fastOps = 0;
/** Simulated variable: name -> value pair */
static class Var {
String name;
String value;
Var(String name, String value) { this.name = name; this.value = value; }
}
/**
* SLOW path: nested linear scan to deduplicate variables.
* Models the defective AST_LIST_TRAVERSE inside AST_LIST_TRAVERSE.
*
* @param partyAVars already in varshead
* @param partyBVars party_b variables to merge in
* @return merged list
*/
static List<Var> mergeVarsSlow(List<Var> partyAVars, List<Var> partyBVars) {
List<Var> varshead = new ArrayList<>(partyAVars);
for (Var bVar : partyBVars) { // outer: B iterations
boolean found = false;
for (Var existing : varshead) { // inner: V iterations — O(B×V)
slowOps++;
if (bVar.name.equalsIgnoreCase(existing.name)) {
found = true;
break;
}
}
if (!found) {
varshead.add(new Var(bVar.name, bVar.value));
}
}
return varshead;
}
/**
* FAST path: hash set membership check — O(B + V).
* Fix: build a HashMap of existing names first, then check in O(1).
*
* @param partyAVars already in varshead
* @param partyBVars party_b variables to merge in
* @return merged list
*/
static List<Var> mergeVarsFast(List<Var> partyAVars, List<Var> partyBVars) {
List<Var> varshead = new ArrayList<>(partyAVars);
HashMap<String, Boolean> existingNames = new HashMap<>();
for (Var v : partyAVars) { // O(V) build
fastOps++;
existingNames.put(v.name.toLowerCase(), Boolean.TRUE);
}
for (Var bVar : partyBVars) { // outer: B iterations
fastOps++; // O(1) lookup
if (!existingNames.containsKey(bVar.name.toLowerCase())) {
varshead.add(new Var(bVar.name, bVar.value));
existingNames.put(bVar.name.toLowerCase(), Boolean.TRUE);
}
}
return varshead;
}
static boolean runTest(int numVarsA, int numVarsB, int overlap) {
// Build party_a vars: var_a_0 ... var_a_(numVarsA-1)
List<Var> partyA = new ArrayList<>();
for (int i = 0; i < numVarsA; i++) {
partyA.add(new Var("var_a_" + i, "val_a_" + i));
}
// Build party_b vars: first 'overlap' vars share names with party_a
// rest are unique to party_b
List<Var> partyB = new ArrayList<>();
for (int i = 0; i < overlap; i++) {
partyB.add(new Var("var_a_" + i, "val_b_" + i)); // duplicate
}
for (int i = 0; i < numVarsB - overlap; i++) {
partyB.add(new Var("var_b_" + i, "val_b_" + i)); // unique
}
long slowBefore = slowOps;
long fastBefore = fastOps;
List<Var> slowResult = mergeVarsSlow(partyA, partyB);
List<Var> fastResult = mergeVarsFast(partyA, partyB);
long slowCount = slowOps - slowBefore;
long fastCount = fastOps - fastBefore;
// Both should produce same merged count: numVarsA + (numVarsB - overlap) unique vars
int expectedSize = numVarsA + (numVarsB - overlap);
if (slowResult.size() != expectedSize) {
System.out.println("FAIL: slow result size " + slowResult.size() + " expected " + expectedSize);
return false;
}
if (fastResult.size() != expectedSize) {
System.out.println("FAIL: fast result size " + fastResult.size() + " expected " + expectedSize);
return false;
}
// Slow ops should be at least numVarsB (inner loop on each), approx numVarsB * numVarsA
// Fast ops should be at most numVarsA + numVarsB
double ratio = (double) slowCount / (double) fastCount;
System.out.printf(" N=%d B=%d overlap=%d | slowOps=%d fastOps=%d ratio=%.1fx%n",
numVarsA, numVarsB, overlap, slowCount, fastCount, ratio);
if (ratio < 5.0) {
System.out.printf("FAIL: ratio %.1fx < 5x threshold%n", ratio);
return false;
}
return true;
}
public static void main(String[] args) {
int pass = 0;
int fail = 0;
System.out.println("=== asterisk-0003: CDR Variable Merge O(B×V) ===");
System.out.println();
// Test cases: (numVarsA, numVarsB, overlap)
int[][] tests = {
{20, 20, 5},
{50, 50, 10},
{100, 100, 20},
{200, 200, 50},
{500, 500, 100},
};
for (int[] t : tests) {
slowOps = 0;
fastOps = 0;
boolean ok = runTest(t[0], t[1], t[2]);
if (ok) { pass++; } else { fail++; }
}
System.out.println();
// Verify quadratic growth in slow path
System.out.println("Quadratic growth verification (slow path):");
for (int n : new int[]{10, 50, 100, 200}) {
slowOps = 0;
fastOps = 0;
mergeVarsSlow(buildVarList("a", n), buildVarList("b", n));
System.out.printf(" N=%d slow_ops=%d (expected ~%d quadratic)%n",
n, slowOps, n * n);
}
System.out.println();
System.out.printf("%d/%d PASS%n", pass, pass + fail);
if (fail > 0) {
System.exit(1);
}
}
static List<Var> buildVarList(String prefix, int n) {
List<Var> list = new ArrayList<>();
for (int i = 0; i < n; i++) {
list.add(new Var(prefix + "_var_" + i, "val_" + i));
}
return list;
}
}