package unit; import java.util.*; /** * CWE-407 unit test: emacs-0002 * Models bytecomp--code-strings member-based deduplication in Emacs byte-compiler. * * SLOW path: List + .contains() — O(F²) over all lambdas in a file * FAST path: HashMap — O(F) total * * The pattern in bytecomp.el: * (let* ((code (cadr compiled)) * (prev (member code bytecomp--code-strings))) ; ← O(L), L grows * (if prev (car prev) * (push code bytecomp--code-strings) ; list grows * code)) * * For F lambdas per file, work is: 0 + 1 + 2 + … + (F-1) = O(F²/2). */ public class BytecompCodeStringsAlgorithm { // ---- SLOW path --------------------------------------------------------- static long slowOps; /** * Simulate compiling F lambdas with list-based dedup. * Each code string is unique (worst case for list growth). */ static void slowCompileFile(int F) { slowOps = 0; List codeStrings = new ArrayList<>(); for (int i = 0; i < F; i++) { String code = "bytecode-" + i; // all unique — worst case // (member code bytecomp--code-strings) boolean found = false; for (String existing : codeStrings) { slowOps++; if (existing.equals(code)) { found = true; break; } } if (!found) { codeStrings.add(code); // (push code bytecomp--code-strings) } } } // ---- FAST path --------------------------------------------------------- static long fastOps; /** * Same simulation using HashMap for O(1) dedup. */ static void fastCompileFile(int F) { fastOps = 0; Map codeStringsHt = new HashMap<>(); for (int i = 0; i < F; i++) { String code = "bytecode-" + i; fastOps++; // one hash lookup codeStringsHt.putIfAbsent(code, code); } } // ---- main -------------------------------------------------------------- public static void main(String[] args) { System.out.println("=== emacs-0002: BytecompCodeStringsAlgorithm (member dedup) ==="); int[] sizes = {50, 100, 200, 500, 1000}; System.out.printf("%-12s %-15s %-15s %-10s%n", "F (fns)", "slow_ops", "fast_ops", "ratio"); System.out.println("-".repeat(55)); for (int F : sizes) { slowCompileFile(F); long s = slowOps; fastCompileFile(F); long f = fastOps; double ratio = f == 0 ? 1.0 : (double) s / f; System.out.printf("%-12d %-15d %-15d %-10.1f%n", F, s, f, ratio); } // Correctness: both paths should produce same deduplicated set { int F = 100; // collect slow results List slowResult = new ArrayList<>(); long dummy = 0; List codeStrings = new ArrayList<>(); for (int i = 0; i < F; i++) { String code = "bytecode-" + (i % 60); // introduce duplicates boolean found = false; for (String e : codeStrings) { dummy++; if (e.equals(code)) { found=true; break; } } if (!found) codeStrings.add(code); } slowResult.addAll(codeStrings); Map fastResult = new LinkedHashMap<>(); for (int i = 0; i < F; i++) { String code = "bytecode-" + (i % 60); fastResult.putIfAbsent(code, code); } Set s = new HashSet<>(slowResult); Set f2 = new HashSet<>(fastResult.keySet()); if (!s.equals(f2)) { System.err.println("FAIL: dedup sets differ"); System.exit(1); } System.out.println("\nCORRECTNESS: PASS"); } // Ratio assertion at F=500 { slowCompileFile(500); long s = slowOps; fastCompileFile(500); long f = fastOps; double r = (double) s / f; System.out.printf("Ratio at F=500: %.1fx%n", r); if (r < 100.0) { System.err.println("FAIL: expected ratio >= 100x at F=500"); System.exit(1); } System.out.println("RATIO ASSERTION: PASS"); } } }