package unit; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Gstreamer0003TracerDeduplicateTest — CWE-407 gstreamer-0003 * * Models gst_tracing_get_active_tracers() in gst/gsttracerutils.c: * slow() = O(H × T²) via list-indexOf dedup (current defect) * fast() = O(H × T) via HashSet dedup (patch) * * Parameters: H hook types, T tracers registered per hook (with overlap). * Assert: slowOps > fastOps * Nx at H=54 hooks, T=5 tracers each covering 10 hooks. */ public class Gstreamer0003TracerDeduplicateTest { static long slowOps; static long fastOps; /** * Simulates the tracer seen-list: a List (tracer ids). * g_list_index equivalent = list.indexOf() = O(n). */ static int listIndexOf(List list, int val) { for (int i = 0; i < list.size(); i++) { slowOps++; if (list.get(i) == val) return i; } return -1; } /** * slow: O(H × T²) — models original gst_tracing_get_active_tracers. * For each hook bucket, for each tracer-hook entry, calls listIndexOf. */ static List getActiveTracersSlow(int[][] hookBuckets) { List tracers = new ArrayList<>(); for (int[] bucket : hookBuckets) { for (int tracerId : bucket) { if (listIndexOf(tracers, tracerId) < 0) { tracers.add(tracerId); } } } return tracers; } /** * fast: O(H × T) — models HashSet fix. * Uses O(1) set membership test. */ static List getActiveTracersFast(int[][] hookBuckets) { List tracers = new ArrayList<>(); Set seen = new HashSet<>(); for (int[] bucket : hookBuckets) { for (int tracerId : bucket) { fastOps++; if (!seen.contains(tracerId)) { seen.add(tracerId); tracers.add(tracerId); } } } return tracers; } public static void main(String[] args) { final int NX = 5; // H=54 hook types; T=5 tracers, each registered under ~10 of 54 hooks final int H = 54; final int T = 5; // distinct tracers final int HOOKS_PER_TRACER = 10; // Build hookBuckets: H buckets, each containing tracers registered for that hook int[][] hookBuckets = new int[H][]; int[] hookCounts = new int[H]; // Assign each tracer to HOOKS_PER_TRACER hooks int[][] tracerHooks = new int[T][]; for (int t = 0; t < T; t++) { tracerHooks[t] = new int[HOOKS_PER_TRACER]; for (int h = 0; h < HOOKS_PER_TRACER; h++) { tracerHooks[t][h] = (t * HOOKS_PER_TRACER + h) % H; } } // Count tracer registrations per hook int[] buckSize = new int[H]; for (int t = 0; t < T; t++) { for (int h : tracerHooks[t]) buckSize[h]++; } hookBuckets = new int[H][]; for (int h = 0; h < H; h++) hookBuckets[h] = new int[buckSize[h]]; int[] buckIdx = new int[H]; for (int t = 0; t < T; t++) { for (int h : tracerHooks[t]) { hookBuckets[h][buckIdx[h]++] = t; } } // Warm up slowOps = 0; fastOps = 0; getActiveTracersSlow(hookBuckets); getActiveTracersFast(hookBuckets); // Measure slowOps = 0; fastOps = 0; final int CALLS = 1000; List slowResult = null, fastResult = null; for (int c = 0; c < CALLS; c++) { slowResult = getActiveTracersSlow(hookBuckets); fastResult = getActiveTracersFast(hookBuckets); } // Correctness: both should find T unique tracers boolean correctnessOk = (slowResult != null && fastResult != null && slowResult.size() == T && fastResult.size() == T); boolean speedupOk = slowOps > fastOps * NX; System.out.printf("H=%d hooks, T=%d tracers, %d hooks/tracer, CALLS=%d%n", H, T, HOOKS_PER_TRACER, CALLS); System.out.printf("slow (list-indexOf) ops: %d%n", slowOps); System.out.printf("fast (HashSet) ops: %d%n", fastOps); System.out.printf("speedup ratio: %.1fx (required >%dx)%n", (double) slowOps / fastOps, NX); System.out.printf("tracers found: slow=%d fast=%d (expected %d)%n", slowResult == null ? -1 : slowResult.size(), fastResult == null ? -1 : fastResult.size(), T); int passed = 0, total = 2; if (correctnessOk) { System.out.printf("1/2 PASS correctness: both found %d tracers%n", T); passed++; } else { System.out.printf("1/2 FAIL correctness: slow=%d fast=%d expected=%d%n", slowResult == null ? -1 : slowResult.size(), fastResult == null ? -1 : fastResult.size(), T); } if (speedupOk) { System.out.printf("2/2 PASS speedup: %d > %d * %d%n", slowOps, fastOps, NX); passed++; } else { System.out.printf("2/2 FAIL speedup: %d not > %d * %d%n", slowOps, fastOps, NX); } System.out.printf("%d/%d PASS%n", passed, total); if (passed < total) System.exit(1); } }