package unit; import java.util.ArrayDeque; import java.util.Deque; /** * Allegro5SamplePoolTest — CWE-407 allegro5-0001 * * Models al_play_sample() free-slot acquisition: * slow() = O(n) linear scan of auto_samples pool (current defect) * fast() = O(1) amortized free-slot stack pop (patch) * * Assert: slowOps > fastOps * Nx at N=256 reserved samples. */ public class Allegro5SamplePoolTest { static class SampleSlot { boolean playing; boolean locked; int id; SampleSlot() { playing = false; locked = false; id = 0; } } static long slowOps; static long fastOps; /** * slow: O(n) linear scan — models al_play_sample defect. * Returns slot index found, or -1 if no free slot. */ static int playSampleSlow(SampleSlot[] pool) { for (int i = 0; i < pool.length; i++) { slowOps++; if (!pool[i].playing && !pool[i].locked) { pool[i].playing = true; return i; } } return -1; } /** * Simulate sample finishing: mark slot as not playing (called on timeout/stop). * In the defect version, slot is simply cleared — no tracking. */ static void stopSampleSlow(SampleSlot[] pool, int idx) { pool[idx].playing = false; } /** * fast: O(1) amortized free-slot stack — models patched al_play_sample. * Returns slot index found, or -1 if no free slot. */ static int playSampleFast(SampleSlot[] pool, Deque freeStack) { while (!freeStack.isEmpty()) { fastOps++; int idx = freeStack.pop(); if (!pool[idx].playing && !pool[idx].locked) { pool[idx].playing = true; return idx; } // Slot was reused — discard stale entry, keep scanning stack } return -1; } static void stopSampleFast(SampleSlot[] pool, Deque freeStack, int idx) { pool[idx].playing = false; freeStack.push(idx); // return to free stack } public static void main(String[] args) { final int N = 256; // al_reserve_samples count final int NX = 5; // minimum required speedup factor final int PLAYS = 2000; // play calls to measure // Build pool — all slots initially free SampleSlot[] pool = new SampleSlot[N]; for (int i = 0; i < N; i++) pool[i] = new SampleSlot(); // Build free stack — all slots free at start Deque freeStack = new ArrayDeque<>(); for (int i = N - 1; i >= 0; i--) freeStack.push(i); slowOps = 0; fastOps = 0; // Simulate: play PLAYS samples, immediately stopping them. // Worst case for linear scan: all slots occupied except the last one. // Set all slots except the last as playing. for (int i = 0; i < N - 1; i++) pool[i].playing = true; // Reset free stack to only have the last slot freeStack.clear(); freeStack.push(N - 1); for (int p = 0; p < PLAYS; p++) { // slow: always scans N-1 busy slots before finding free one // (reset pool each call to keep it worst-case) for (int i = 0; i < N - 1; i++) pool[i].playing = true; pool[N - 1].playing = false; int idxSlow = playSampleSlow(pool); if (idxSlow >= 0) stopSampleSlow(pool, idxSlow); } long totalSlowOps = slowOps; // Reset for fast for (SampleSlot s : pool) { s.playing = false; s.locked = false; } freeStack.clear(); for (int i = N - 1; i >= 0; i--) freeStack.push(i); // Mark all but last as playing; fast stack has only last index for (int i = 0; i < N - 1; i++) pool[i].playing = true; freeStack.clear(); freeStack.push(N - 1); for (int p = 0; p < PLAYS; p++) { for (int i = 0; i < N - 1; i++) pool[i].playing = true; pool[N - 1].playing = false; freeStack.push(N - 1); int idxFast = playSampleFast(pool, freeStack); if (idxFast >= 0) stopSampleFast(pool, freeStack, idxFast); } long totalFastOps = fastOps; // Correctness: both should find a free slot for (SampleSlot s : pool) { s.playing = false; s.locked = false; } pool[0].playing = true; // make index 0 busy // slow should find index 1 (scan 0=busy, 1=free) SampleSlot[] poolB = new SampleSlot[N]; for (int i = 0; i < N; i++) poolB[i] = new SampleSlot(); poolB[0].playing = true; int slowIdx = playSampleSlow(poolB); for (SampleSlot s : pool) { s.playing = false; } pool[0].playing = true; Deque fsB = new ArrayDeque<>(); fsB.push(1); // free stack knows slot 1 is free int fastIdx = playSampleFast(pool, fsB); boolean correctnessOk = (slowIdx == 1 && fastIdx == 1); boolean speedupOk = totalSlowOps > totalFastOps * NX; System.out.printf("N=%d pool slots, PLAYS=%d%n", N, PLAYS); System.out.printf("slow (linear) ops: %d%n", totalSlowOps); System.out.printf("fast (stack) ops: %d%n", totalFastOps); System.out.printf("speedup ratio: %.1fx (required >%dx)%n", (double) totalSlowOps / totalFastOps, NX); System.out.printf("correctness: slow found slot %d, fast found slot %d%n", slowIdx, fastIdx); int passed = 0, total = 2; if (correctnessOk) { System.out.println("1/2 PASS correctness: both found slot 1"); passed++; } else { System.out.printf("1/2 FAIL correctness: slow=%d fast=%d%n", slowIdx, fastIdx); } if (speedupOk) { System.out.printf("2/2 PASS speedup: %d > %d * %d%n", totalSlowOps, totalFastOps, NX); passed++; } else { System.out.printf("2/2 FAIL speedup: %d not > %d * %d%n", totalSlowOps, totalFastOps, NX); } System.out.printf("%d/%d PASS%n", passed, total); if (passed < total) System.exit(1); } }