package unit; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * CWE-407 unit test: gstreamer-0005 — WebRTC seen_transceivers O(T²) → O(T) * * Models _create_offer_task() in * subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c * * The function tracks which transceivers have already been assigned to * an SDP m-line via a GList *seen_transceivers. Multiple loops iterate * over all T transceivers and call g_list_find(seen_transceivers, trans) * (O(T) per call). The full function is O(T^2). * * Fix: replace GList with GHashTable (g_direct_hash / g_direct_equal) * for O(1) contains/add operations, reducing the full function to O(T). * * Parameters: T = number of transceivers (audio+video streams). * For a 25-participant WebRTC conference: T = 50 (25a + 25v). * Expected ratio: >20x op-count reduction at T=100. */ public class Gstreamer0005WebrtcSeenTransceiversTest { static long slowOps = 0; static long fastOps = 0; /** * Slow: GList-style seen-set — O(T) membership check. * Simulates g_list_find(seen_transceivers, trans). */ static boolean listContains(List seen, int trans) { for (int s : seen) { slowOps++; if (s == trans) return true; } return false; } /** * Slow simulation of _create_offer_task seen_transceivers loops. * * Three passes over T transceivers each calling g_list_find: * Pass 1: renegotiation loop (inner j-loop over T, find on grown list) * Pass 2: gather existing mids loop (T iterations, find check) * Pass 3: add extra streams loop (T iterations, find check) * * Total: O(T) inserts + O(T^2) finds = O(T^2) */ static void createOfferSlow(int T) { List seen = new ArrayList<>(); // Pass 1: renegotiation — inner loop T finds on partially-filled list for (int i = 0; i < T; i++) { for (int j = 0; j < T; j++) { if (listContains(seen, j)) continue; // only add first match per outer i seen.add(j); break; } } seen.clear(); // Pass 2: gather mids — T iterations, each O(T) find for (int i = 0; i < T; i++) { if (listContains(seen, i)) continue; } // Pass 3: add extra streams — T iterations, each O(T) find for (int i = 0; i < T; i++) { if (!listContains(seen, i)) { seen.add(i); } } } /** * Fast simulation using HashSet — O(1) contains/add. * Simulates g_hash_table_contains + g_hash_table_add. */ static void createOfferFast(int T) { Set seen = new HashSet<>(); // Pass 1: renegotiation for (int i = 0; i < T; i++) { for (int j = 0; j < T; j++) { fastOps++; if (seen.contains(j)) continue; seen.add(j); break; } } seen.clear(); // Pass 2: gather mids for (int i = 0; i < T; i++) { fastOps++; if (seen.contains(i)) continue; } // Pass 3: add extra streams for (int i = 0; i < T; i++) { fastOps++; if (!seen.contains(i)) { seen.add(i); } } } public static void main(String[] args) { final int T = 100; // 50 audio + 50 video transceivers // Warm up createOfferSlow(10); createOfferFast(10); slowOps = 0; fastOps = 0; // Measure createOfferSlow(T); long measuredSlowOps = slowOps; createOfferFast(T); long measuredFastOps = fastOps; double ratio = (double) measuredSlowOps / measuredFastOps; System.out.printf("gstreamer-0005 WebRTC seen_transceivers O(T^2) vs O(T)%n"); System.out.printf(" T=%d transceivers%n", T); System.out.printf(" slow ops (GList find): %d%n", measuredSlowOps); System.out.printf(" fast ops (GHashTable): %d%n", measuredFastOps); System.out.printf(" ratio: %.1fx%n", ratio); assert ratio > 10.0 : "Expected >10x ratio at T=" + T + ", got " + ratio; System.out.println("PASS"); } }