java-topology/defects/obs-studio/unit/ObsStudioTest.java

96 lines
3.6 KiB
Java
Raw 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.

/**
* CWE-407 simulation: OBS Studio push_audio_tree da_find O(N²) per audio frame.
*
* Simulates the defect in libobs/obs-audio.c where push_audio_tree()
* and push_audio_tree2() use da_find(audio->render_order, &source, 0)
* — a linear scan — to check if a source is already in the render order.
* Called once per source during audio tree traversal, this is O(N²) per
* audio frame (~47 Hz). With complex scenes (many sources), this causes
* measurable audio processing overhead.
*
* Fix: use a HashSet alongside the render_order list for O(1) membership.
*/
import java.util.*;
public class ObsStudioTest {
/* --- Defective path: linear scan per source (da_find) --- */
static int buildRenderOrderDefective(Object[] sources) {
int ops = 0;
List<Object> renderOrder = new ArrayList<>();
for (Object source : sources) {
// da_find: linear scan of render_order
boolean found = false;
for (int i = 0; i < renderOrder.size(); i++) {
ops++;
if (renderOrder.get(i) == source) {
found = true;
break;
}
}
if (!found) {
renderOrder.add(source);
}
}
return ops;
}
/* --- Fixed path: hash set for O(1) membership --- */
static int buildRenderOrderFixed(Object[] sources) {
int ops = 0;
List<Object> renderOrder = new ArrayList<>();
Set<Object> seen = new HashSet<>();
for (Object source : sources) {
ops++; // HashSet.contains is O(1)
if (seen.add(source)) {
renderOrder.add(source);
}
}
return ops;
}
public static void main(String[] args) {
int[] sizes = {100, 500, 1000};
boolean allPass = true;
System.out.println("=== OBS Studio push_audio_tree CWE-407 Test ===");
System.out.printf("%-8s %-12s %-12s %-8s %-6s%n",
"N", "Defective", "Fixed", "Ratio", "Pass");
for (int N : sizes) {
// Simulate N unique sources being added to render order
Object[] sources = new Object[N];
for (int i = 0; i < N; i++) sources[i] = new Object();
int opsDefective = buildRenderOrderDefective(sources);
int opsFixed = buildRenderOrderFixed(sources);
double ratio = (double) opsDefective / opsFixed;
boolean pass = ratio > 2.0;
if (!pass) allPass = false;
System.out.printf("%-8d %-12d %-12d %-8.1fx %-6s%n",
N, opsDefective, opsFixed, ratio, pass ? "PASS" : "FAIL");
}
// Simulate repeated frames (the defect runs every audio frame)
System.out.println("\n--- Per-second overhead at 47 frames/sec ---");
int N = 200; // realistic complex scene
Object[] sources = new Object[N];
for (int i = 0; i < N; i++) sources[i] = new Object();
int perFrameDefective = buildRenderOrderDefective(sources);
int perFrameFixed = buildRenderOrderFixed(sources);
System.out.printf("N=%d: defective=%d ops/frame × 47 = %d ops/sec%n",
N, perFrameDefective, perFrameDefective * 47);
System.out.printf("N=%d: fixed=%d ops/frame × 47 = %d ops/sec%n",
N, perFrameFixed, perFrameFixed * 47);
System.out.println();
if (allPass) {
System.out.println("ALL PASS — defective path is quadratic, fixed path is linear");
} else {
System.out.println("FAIL — ratio not demonstrated");
System.exit(1);
}
}
}