172 lines
6 KiB
Java
172 lines
6 KiB
Java
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);
|
||
}
|
||
}
|
||
}
|