libtorrent-0001: file_storage::get_or_add_path std::find on m_paths vector O(F*P) MEDIUM 250x qBittorrent: CLEAN (QSet/QHash throughout for membership tests)
114 lines
3.9 KiB
Java
114 lines
3.9 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* CWE-407 simulation: libtorrent file_storage::get_or_add_path
|
|
*
|
|
* Reproduces the O(F*P) linear-scan deduplication defect in
|
|
* file_storage::get_or_add_path where std::find scans m_paths
|
|
* vector for every file added to a torrent.
|
|
*
|
|
* libtorrent-0001: get_or_add_path uses std::find on vector O(F*P)
|
|
* Fix: unordered_map for O(1) lookup → O(F) total
|
|
*/
|
|
public class LibtorrentTest {
|
|
|
|
// === DEFECTIVE: linear scan dedup (std::find on vector) ===
|
|
static class FileStorageDefective {
|
|
private final List<String> paths = new ArrayList<>();
|
|
|
|
/** Simulates get_or_add_path with linear scan */
|
|
int getOrAddPath(String path) {
|
|
// std::find(m_paths.rbegin(), m_paths.rend(), path)
|
|
for (int i = paths.size() - 1; i >= 0; i--) {
|
|
if (paths.get(i).equals(path)) {
|
|
return i;
|
|
}
|
|
}
|
|
int ret = paths.size();
|
|
paths.add(path);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
// === FIXED: hash map dedup ===
|
|
static class FileStorageFixed {
|
|
private final List<String> paths = new ArrayList<>();
|
|
private final Map<String, Integer> pathIndex = new HashMap<>();
|
|
|
|
int getOrAddPath(String path) {
|
|
Integer idx = pathIndex.get(path);
|
|
if (idx != null) return idx;
|
|
int ret = paths.size();
|
|
paths.add(path);
|
|
pathIndex.put(path, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Simulate adding F files across P unique directory paths.
|
|
* Each file's directory is looked up via get_or_add_path.
|
|
*/
|
|
static long benchmarkDefective(int numFiles, int numPaths) {
|
|
FileStorageDefective fs = new FileStorageDefective();
|
|
String[] dirs = new String[numPaths];
|
|
for (int i = 0; i < numPaths; i++) {
|
|
dirs[i] = "dir" + i + "/subdir" + (i % 10);
|
|
}
|
|
long ops = 0;
|
|
for (int f = 0; f < numFiles; f++) {
|
|
String dir = dirs[f % numPaths];
|
|
// linear scan: worst case scans all P paths
|
|
for (int i = fs.paths.size() - 1; i >= 0; i--) {
|
|
ops++;
|
|
if (fs.paths.get(i).equals(dir)) break;
|
|
}
|
|
fs.getOrAddPath(dir);
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
static long benchmarkFixed(int numFiles, int numPaths) {
|
|
FileStorageFixed fs = new FileStorageFixed();
|
|
String[] dirs = new String[numPaths];
|
|
for (int i = 0; i < numPaths; i++) {
|
|
dirs[i] = "dir" + i + "/subdir" + (i % 10);
|
|
}
|
|
long ops = 0;
|
|
for (int f = 0; f < numFiles; f++) {
|
|
String dir = dirs[f % numPaths];
|
|
ops++; // O(1) hash lookup
|
|
fs.getOrAddPath(dir);
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== LibtorrentTest: CWE-407 file_storage::get_or_add_path ===\n");
|
|
|
|
// libtorrent-0001: get_or_add_path linear dedup
|
|
int numFiles = 50000;
|
|
int numPaths = 500;
|
|
long defectOps = benchmarkDefective(numFiles, numPaths);
|
|
long fixedOps = benchmarkFixed(numFiles, numPaths);
|
|
double ratio = (double) defectOps / fixedOps;
|
|
|
|
System.out.printf("libtorrent-0001: get_or_add_path linear path dedup%n");
|
|
System.out.printf(" F=%d files, P=%d unique paths%n", numFiles, numPaths);
|
|
System.out.printf(" defect ops: %,d%n", defectOps);
|
|
System.out.printf(" fixed ops: %,d%n", fixedOps);
|
|
System.out.printf(" ratio: %.1fx%n", ratio);
|
|
|
|
boolean pass = ratio > 10.0;
|
|
System.out.printf(" result: %s%n%n", pass ? "PASS" : "FAIL");
|
|
|
|
// Summary
|
|
System.out.println("=== SUMMARY ===");
|
|
System.out.printf("libtorrent-0001 get_or_add_path O(F*P) -> O(F): %s (%.1fx)%n",
|
|
pass ? "PASS" : "FAIL", ratio);
|
|
|
|
if (!pass) {
|
|
System.exit(1);
|
|
}
|
|
}
|
|
}
|