java-topology/defects/actix/unit/ActixUpdateUniqueAlgorithm.java

172 lines
6 KiB
Java
Raw Permalink 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.*;
/**
* actix-0001: CWE-407 actix-web introspection update_unique O(R×G) Vec linear dedup.
*
* Simulates the defective update_unique() pattern from actix-web's
* introspection.rs:984-989:
*
* fn update_unique<T>(existing: &mut Vec<T>, new_items: &[T]) {
* for item in new_items {
* if !existing.contains(item) { // O(E) linear scan
* existing.push(item.clone());
* }
* }
* }
*
* Called once per route registration for guards, patterns, and methods vecs.
* With R routes sharing a scope and G unique guards accumulating, total work
* is O(R × G).
*
* Fix: pre-build a HashSet for O(1) membership, reducing to O(R + G).
*/
public class ActixUpdateUniqueAlgorithm {
static long slowOps = 0;
static long fastOps = 0;
// --- SLOW: Vec.contains() linear scan (actix-web defective pattern) ---
static void updateUniqueSlow(List<String> existing, List<String> newItems) {
for (String item : newItems) {
slowOps++;
boolean found = false;
for (String e : existing) { // O(E) inner scan
slowOps++;
if (e.equals(item)) {
found = true;
break;
}
}
if (!found) {
existing.add(item);
}
}
}
// --- FAST: HashSet membership check O(1) per item ---
static void updateUniqueFast(List<String> existing, Set<String> existingSet, List<String> newItems) {
for (String item : newItems) {
fastOps++;
if (!existingSet.contains(item)) {
existing.add(item);
existingSet.add(item);
}
}
}
// Simulate R route registrations, each contributing G guard names to a shared path's accumulator.
// In actix-web: register_pattern_detail() calls update_unique() for guards on each route.
static List<String> simulateSlow(int R, int G) {
List<String> accumulated = new ArrayList<>();
for (int r = 0; r < R; r++) {
List<String> routeGuards = new ArrayList<>();
for (int g = 0; g < G; g++) {
// Routes share some guards + add one unique guard each
routeGuards.add("guard_" + (g % (G / 2 + 1)));
}
routeGuards.add("route_guard_" + r);
updateUniqueSlow(accumulated, routeGuards);
}
return accumulated;
}
static List<String> simulateFast(int R, int G) {
List<String> accumulated = new ArrayList<>();
Set<String> accumulatedSet = new HashSet<>();
for (int r = 0; r < R; r++) {
List<String> routeGuards = new ArrayList<>();
for (int g = 0; g < G; g++) {
routeGuards.add("guard_" + (g % (G / 2 + 1)));
}
routeGuards.add("route_guard_" + r);
updateUniqueFast(accumulated, accumulatedSet, routeGuards);
}
return accumulated;
}
public static void main(String[] args) {
System.out.println("actix-0001: update_unique O(R×G) Vec linear dedup vs HashSet");
System.out.println("=============================================================");
int[] sizes = {50, 100, 200, 500};
int passes = 0;
int failures = 0;
for (int N : sizes) {
slowOps = 0;
fastOps = 0;
int R = N; // routes sharing a scope
int G = 20; // guards per route registration
List<String> slowResult = simulateSlow(R, G);
List<String> fastResult = simulateFast(R, G);
// Sort both for comparison
List<String> sortedSlow = new ArrayList<>(slowResult);
List<String> sortedFast = new ArrayList<>(fastResult);
Collections.sort(sortedSlow);
Collections.sort(sortedFast);
boolean match = sortedSlow.equals(sortedFast);
double ratio = slowOps > 0 ? (double) slowOps / Math.max(fastOps, 1) : 0;
String status = (match && ratio >= 5.0) ? "PASS" : "FAIL";
System.out.printf(" N=%3d routes, G=%2d guards/reg: slow=%7d ops fast=%6d ops ratio=%.1fx result_match=%s [%s]%n",
R, G, slowOps, fastOps, ratio, match, status);
if (match && ratio >= 5.0) {
passes++;
} else {
failures++;
if (!match) {
System.out.println(" ERROR: slow and fast results differ!");
System.out.println(" slow size=" + sortedSlow.size() + " fast size=" + sortedFast.size());
}
if (ratio < 5.0) {
System.out.printf(" ERROR: ratio %.1fx below required 5x minimum%n", ratio);
}
}
}
System.out.println();
// Larger test: G=50 guards per route (high guard density scenario)
System.out.println("High-density guard test (R=500, G=50 guards/reg):");
{
int R = 500;
int G = 50;
slowOps = 0;
fastOps = 0;
List<String> slowResult = simulateSlow(R, G);
List<String> fastResult = simulateFast(R, G);
List<String> sortedSlow = new ArrayList<>(slowResult);
List<String> sortedFast = new ArrayList<>(fastResult);
Collections.sort(sortedSlow);
Collections.sort(sortedFast);
boolean match = sortedSlow.equals(sortedFast);
double ratio = (double) slowOps / Math.max(fastOps, 1);
String status = (match && ratio >= 5.0) ? "PASS" : "FAIL";
System.out.printf(" R=%d routes, G=%d: slow=%d ops fast=%d ops ratio=%.1fx match=%s [%s]%n",
R, G, slowOps, fastOps, ratio, match, status);
if (match && ratio >= 5.0) passes++;
else failures++;
}
System.out.println();
System.out.printf("%d/%d PASS%n", passes, passes + failures);
if (failures > 0) {
System.exit(1);
}
}
}