100 lines
3.1 KiB
Java
100 lines
3.1 KiB
Java
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<String> slowDedup(String[] headers) {
|
|
List<String> 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<String> fastDedup(String[] headers) {
|
|
List<String> deduped = new ArrayList<>();
|
|
Set<String> 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<String> slowResult = slowDedup(headers);
|
|
List<String> 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);
|
|
}
|
|
}
|
|
}
|