import java.util.*; /** * CWE-407 simulation: FFmpeg libavfilter/formats.c MERGE_FORMATS nested loop * * libavfilter/formats.c MERGE_FORMATS macro intersects two format lists using * a nested loop: for i in a.formats: for j in b.formats: if a[i]==b[j] ... * Complexity: O(A * B) where A = |a->nb_formats|, B = |b->nb_formats|. * * This is called during avfilter_graph_config() format negotiation for every * link in the filter graph. AVPixelFormat has ~380 entries (AV_PIX_FMT_NB). * A filter that accepts all formats triggers worst-case A=B=380: * 380 * 380 = 144,400 comparisons per link. * A graph with L links pays L * 144,400 comparisons just for format merging. * * Fix: sort a copy of b, then use binary search for O(log B) membership test. * Overall: O((A+B) log B) — at A=B=380 that is ~3,420 comparisons, 42x fewer. * * This test simulates both approaches and verifies correctness + speedup. */ public class FFmpegTest { static int nestedLoopOps; static int bsearchOps; /** * Simulate the original MERGE_FORMATS nested-loop intersection. * Returns the intersection set. */ static int[] mergeFormatsNested(int[] a, int[] b) { nestedLoopOps = 0; List result = new ArrayList<>(); for (int i = 0; i < a.length; i++) { for (int j = 0; j < b.length; j++) { nestedLoopOps++; if (a[i] == b[j]) { result.add(a[i]); break; } } } return result.stream().mapToInt(Integer::intValue).toArray(); } /** * Simulate the patched MERGE_FORMATS: sort b, then bsearch for each a[i]. * Returns the intersection set. */ static int[] mergeFormatsBsearch(int[] a, int[] b) { bsearchOps = 0; // Sort b (O(B log B)) int[] sortedB = b.clone(); Arrays.sort(sortedB); // counts as B log B but we only count comparisons // For each a[i], bsearch sortedB List result = new ArrayList<>(); for (int i = 0; i < a.length; i++) { bsearchOps++; // one binary search per a[i], modeled as log(B) ops // (Java's Arrays.binarySearch is O(log B); we count it as 1 unit // representing log(B) comparisons for clarity) int idx = Arrays.binarySearch(sortedB, a[i]); if (idx >= 0) { result.add(a[i]); } } return result.stream().mapToInt(Integer::intValue).toArray(); } /** * Build a synthetic pixel-format list of size n drawn from [0, maxFmt). * Represents a filter that supports n distinct pixel formats. */ static int[] buildFormatList(int n, int maxFmt, long seed) { Random rng = new Random(seed); Set seen = new LinkedHashSet<>(); while (seen.size() < n) { seen.add(rng.nextInt(maxFmt)); } return seen.stream().mapToInt(Integer::intValue).toArray(); } /** Check that two sorted int arrays are equal */ static boolean equal(int[] a, int[] b) { int[] sa = a.clone(); Arrays.sort(sa); int[] sb = b.clone(); Arrays.sort(sb); return Arrays.equals(sa, sb); } public static void main(String[] args) { final int AV_PIX_FMT_NB = 380; // approximate AV_PIX_FMT_NB System.out.println("CWE-407 simulation: FFmpeg MERGE_FORMATS nested loop"); System.out.println("====================================================="); System.out.printf("%-8s %-8s %-14s %-14s %-10s%n", "A", "B", "Nested ops", "Bsearch ops", "Ratio"); boolean allPassed = true; int[][] sizes = {{50, 50}, {100, 100}, {200, 200}, {380, 380}}; for (int[] sz : sizes) { int A = sz[0], B = sz[1]; int[] fmtA = buildFormatList(A, AV_PIX_FMT_NB, 42L); int[] fmtB = buildFormatList(B, AV_PIX_FMT_NB, 99L); int[] resNested = mergeFormatsNested(fmtA, fmtB); int nested = nestedLoopOps; int[] resBsearch = mergeFormatsBsearch(fmtA, fmtB); // bsearch units represent log(B) comparisons each; scale for display int bsearchScaled = (int)(bsearchOps * Math.ceil(Math.log(B) / Math.log(2))); if (!equal(resNested, resBsearch)) { System.out.printf("FAIL: intersection results differ at A=%d B=%d%n", A, B); allPassed = false; continue; } double ratio = (double) nested / Math.max(bsearchScaled, 1); System.out.printf("%-8d %-8d %-14d %-14d %-10.1f%n", A, B, nested, bsearchScaled, ratio); if (nested <= bsearchScaled && A >= 50) { System.out.printf( "FAIL: expected nested > bsearch at A=%d B=%d%n", A, B); allPassed = false; } } // Worst-case check: at A=B=380, nested should be >= 10x bsearch ops int[] fmtA = buildFormatList(AV_PIX_FMT_NB, AV_PIX_FMT_NB, 1L); int[] fmtB = buildFormatList(AV_PIX_FMT_NB, AV_PIX_FMT_NB, 2L); mergeFormatsNested(fmtA, fmtB); int wNested = nestedLoopOps; mergeFormatsBsearch(fmtA, fmtB); int wBsearch = (int)(bsearchOps * Math.ceil(Math.log(AV_PIX_FMT_NB) / Math.log(2))); double wRatio = (double) wNested / Math.max(wBsearch, 1); System.out.printf("%nWorst-case A=B=%d: nested=%d bsearch~=%d ratio=%.1fx%n", AV_PIX_FMT_NB, wNested, wBsearch, wRatio); if (wRatio < 5.0) { System.out.printf("FAIL: ratio %.1f too low (expected >= 5x)%n", wRatio); allPassed = false; } System.out.println(); if (allPassed) { System.out.println("PASS: bsearch approach uses fewer operations at all sizes."); System.out.println("PASS: both approaches produce identical intersection results."); } else { System.out.println("FAIL"); System.exit(1); } } }