java-topology/defects/vlc/unit/Vlc0002SubtitleDeduplicateTest.java
russell@unturf.com ba818693db nmap-0002 + haproxy-0004 + nginx-0004 + weechat-0003 + zeek-0002 + curl-0004: 6 new CWE-407 defects in network tools; count 693→699
nmap-0002:     nmap.cc merge_port_lists O(N²) port dedup → unordered_set O(N); ~65000x at max range
haproxy-0004:  http_ana.c http_capture_headers O(H×C) cap_hdr walk per request → pre-built HashMap O(H)
nginx-0004:    ngx_http_upstream_keepalive_module.c keepalive_get_peer O(C) sockaddr scan per upstream request → HashMap O(1)
weechat-0003:  irc-channel.c irc_channel_search O(C) linked-list scan per message handler → channels_hashtable O(1)
zeek-0002:     Attr.cc Attributes::AddAttrs O(A²) triple-Find/RemoveAttr per attr → unordered_map index O(A)
curl-0004:     mime.c search_header O(P×H) 3x per part per mime_add_headers → pre-indexed header name set O(P)
2026-03-29 22:22:11 -04:00

177 lines
6.1 KiB
Java

package unit;
import java.util.ArrayList;
import java.util.List;
/**
* Vlc0002SubtitleDeduplicateTest — CWE-407 vlc-0002
*
* Models subtitles_Detect() .sub/.idx dedup in src/input/subtitles.c:
* slow() = O(N²) nested scan of all slaves for each .sub entry (defect)
* fast() = O(N) pre-filter .idx list, O(N) outer scan (patch)
*
* Assert: slowOps > fastOps * Nx at N=100 subtitle files.
*/
public class Vlc0002SubtitleDeduplicateTest {
static long slowOps;
static long fastOps;
/**
* Represents a subtitle slave entry (simplified).
* uri format: "movie_name.ext"
*/
static class Slave {
String uri;
boolean rejected;
Slave(String uri) { this.uri = uri; this.rejected = false; }
}
/** Returns the extension (lowercase) of a filename, or "" if none. */
static String getExt(String uri) {
int dot = uri.lastIndexOf('.');
return (dot >= 0) ? uri.substring(dot + 1).toLowerCase() : "";
}
/** Returns the base name (before last dot). */
static String getBase(String uri) {
int dot = uri.lastIndexOf('.');
return (dot >= 0) ? uri.substring(0, dot) : uri;
}
/**
* slow: O(N²) — models original subtitles.c nested loop.
* For each .sub entry (outer), scan all slaves (inner) for matching .idx.
*/
static void deduplicateSlow(Slave[] slaves) {
int n = slaves.length;
for (int i = 0; i < n; i++) {
if (slaves[i] == null || slaves[i].rejected) continue;
String ext = getExt(slaves[i].uri);
if (!ext.equals("sub")) continue;
String subBase = getBase(slaves[i].uri);
// Inner O(N) scan — the defect
for (int j = 0; j < n; j++) {
slowOps++;
if (slaves[j] == null || slaves[j].rejected) continue;
if (!getBase(slaves[j].uri).equals(subBase)) continue;
if (getExt(slaves[j].uri).equals("idx")) {
slaves[i].rejected = true;
break;
}
}
}
}
/**
* fast: O(N) — models patch: pre-collect .idx bases, then scan .sub.
*/
static void deduplicateFast(Slave[] slaves) {
int n = slaves.length;
// Pre-collect .idx base names in O(N)
List<String> idxBases = new ArrayList<>();
for (int j = 0; j < n; j++) {
fastOps++;
if (slaves[j] == null || slaves[j].rejected) continue;
if (getExt(slaves[j].uri).equals("idx")) {
idxBases.add(getBase(slaves[j].uri));
}
}
// Scan .sub entries, check against pre-collected .idx bases in O(N_idx)
for (int i = 0; i < n; i++) {
if (slaves[i] == null || slaves[i].rejected) continue;
String ext = getExt(slaves[i].uri);
if (!ext.equals("sub")) continue;
String subBase = getBase(slaves[i].uri);
for (String idxBase : idxBases) {
fastOps++;
if (idxBase.equals(subBase)) {
slaves[i].rejected = true;
break;
}
}
}
}
/** Build a test set: N subtitle files, half .sub, half .idx, all paired. */
static Slave[] buildSlaves(int n) {
Slave[] slaves = new Slave[n];
for (int i = 0; i < n / 2; i++) {
slaves[i * 2] = new Slave("movie_" + i + ".sub");
slaves[i * 2 + 1] = new Slave("movie_" + i + ".idx");
}
return slaves;
}
static Slave[] cloneSlaves(Slave[] src) {
Slave[] dst = new Slave[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = (src[i] != null) ? new Slave(src[i].uri) : null;
}
return dst;
}
public static void main(String[] args) {
final int NX = 5;
final int N = 100; // 50 .sub + 50 .idx pairs
Slave[] template = buildSlaves(N);
// Warm up
slowOps = 0; fastOps = 0;
deduplicateSlow(cloneSlaves(template));
deduplicateFast(cloneSlaves(template));
// Measure
slowOps = 0; fastOps = 0;
final int RUNS = 100;
Slave[] slowFinal = null, fastFinal = null;
for (int r = 0; r < RUNS; r++) {
Slave[] s = cloneSlaves(template);
deduplicateSlow(s);
if (r == RUNS - 1) slowFinal = s;
Slave[] f = cloneSlaves(template);
deduplicateFast(f);
if (r == RUNS - 1) fastFinal = f;
}
// Count rejected in final run
int slowRejected = 0, fastRejected = 0;
for (Slave s : slowFinal) { if (s != null && s.rejected) slowRejected++; }
for (Slave s : fastFinal) { if (s != null && s.rejected) fastRejected++; }
boolean correctnessOk = (slowRejected == fastRejected && slowRejected == N / 2);
boolean speedupOk = slowOps > fastOps * NX;
System.out.printf("N=%d slaves (%d .sub + %d .idx pairs), RUNS=%d%n",
N, N/2, N/2, RUNS);
System.out.printf("slow (nested scan) ops: %d%n", slowOps);
System.out.printf("fast (pre-filter) ops: %d%n", fastOps);
System.out.printf("speedup ratio: %.1fx (required >%dx)%n",
(double) slowOps / fastOps, NX);
System.out.printf("rejected: slow=%d fast=%d (expected %d)%n",
slowRejected, fastRejected, N / 2);
int passed = 0, total = 2;
if (correctnessOk) {
System.out.printf("1/2 PASS correctness: both rejected %d .sub entries%n", N/2);
passed++;
} else {
System.out.printf("1/2 FAIL correctness: slow=%d fast=%d expected=%d%n",
slowRejected, fastRejected, N/2);
}
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);
}
}