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 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 paths = new ArrayList<>(); private final Map 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); } } }