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)
177 lines
6.1 KiB
Java
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);
|
|
}
|
|
}
|