package unit; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * nginx-0002: ngx_http_upstream_hide_headers_hash dedup O(H²) vs O(H). * * slow: for each of H headers in user config, walk the already-accumulated * list with strcasecmp to detect duplicates — mirrors: * for (i=0; i < conf->hide_headers->nelts; i++) { * hk = hide_headers.elts; * for (j=0; j < hide_headers.nelts; j++) { * if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) goto exist; * } * } * * fast: accumulate into HashSet (case-insensitive by lowercasing), * O(H) total — mirrors using ngx_hash for O(1) per check. * * Assert: slowOps > fastOps * (H/2) for H=100 headers. */ public class NginxHideHeadersDedupAlgorithmTest { static long slowOps; static long fastOps; // ---- slow: O(H²) dedup (defect) ----------------------------------------- static List slowDedup(String[] headers) { List deduped = new ArrayList<>(); for (String h : headers) { boolean found = false; for (String existing : deduped) { // O(D) inner scan slowOps++; if (existing.equalsIgnoreCase(h)) { found = true; break; } } if (!found) { deduped.add(h); } } return deduped; } // ---- fast: O(H) dedup via HashSet (fix) --------------------------------- static List fastDedup(String[] headers) { List deduped = new ArrayList<>(); Set seen = new HashSet<>(); for (String h : headers) { fastOps++; // O(1) set lookup String lower = h.toLowerCase(); if (seen.add(lower)) { deduped.add(h); } } return deduped; } // ---- benchmark driver --------------------------------------------------- public static void main(String[] args) { final int H = 100; // hide_headers entries // All unique, lowercase (no duplicates = maximum inner work) String[] headers = new String[H]; for (int i = 0; i < H; i++) { headers[i] = "X-Hide-Header-" + i; } slowOps = 0; fastOps = 0; List slowResult = slowDedup(headers); List fastResult = fastDedup(headers); if (slowResult.size() != fastResult.size()) { System.err.printf("FAIL: slow=%d entries fast=%d entries (mismatch)%n", slowResult.size(), fastResult.size()); System.exit(1); } long ratio = slowOps / Math.max(fastOps, 1); boolean pass = slowOps > fastOps * (H / 2 - 2); System.out.printf("nginx-0002 slow=%d fast=%d ratio=%dx %s%n", slowOps, fastOps, ratio, pass ? "PASS" : "FAIL"); if (!pass) { System.err.printf("FAIL: expected slowOps(%d) > fastOps(%d) * %d%n", slowOps, fastOps, H / 2 - 2); System.exit(1); } } }