java-topology/defects/uwsgi/unit/UwsgiHeaderDedupAlgorithm.java

151 lines
4.8 KiB
Java

package unit;
import java.util.HashMap;
/**
* uwsgi-0001: HTTP header deduplication O(H^2) vs O(H)
*
* Models the uWSGI pattern: for each incoming header, scan the
* previously-seen-header linked list to find duplicates (O(H) per header,
* O(H^2) total). Fix: use a HashMap for O(1) lookup.
*
* Worst-case input: all-unique header names. For each header H_i, the slow
* path must walk the entire list built so far (i-1 entries) and find nothing.
* Total comparisons = 0+1+2+...+(H-1) = H*(H-1)/2 = O(H^2).
*/
public class UwsgiHeaderDedupAlgorithm {
// ---- SLOW: linked-list scan (mirrors uwsgi_string_list_has_item) --------
static class ListEntry {
String key;
String value;
ListEntry next;
ListEntry(String k, String v) { key = k; value = v; }
}
static long slowOps;
static ListEntry listFind(ListEntry head, String key) {
ListEntry cur = head;
while (cur != null) {
slowOps++;
if (cur.key.equalsIgnoreCase(key)) return cur;
cur = cur.next;
}
return null;
}
/** Parse H unique header names using linked-list dedup. */
static void slowDedup(String[] headers) {
slowOps = 0;
ListEntry head = null, tail = null;
for (String h : headers) {
ListEntry found = listFind(head, h);
if (found != null) {
found.value = found.value + ", " + h;
} else {
ListEntry e = new ListEntry(h, h);
if (head == null) { head = tail = e; }
else { tail.next = e; tail = e; }
}
}
}
// ---- FAST: HashMap lookup O(1) per header --------------------------------
static long fastOps;
static void fastDedup(String[] headers) {
fastOps = 0;
HashMap<String, String> map = new HashMap<>();
for (String h : headers) {
fastOps++; // one O(1) map operation
String lk = h.toLowerCase();
if (map.containsKey(lk)) {
map.put(lk, map.get(lk) + ", " + h);
} else {
map.put(lk, h);
}
}
}
// ---- Test ----------------------------------------------------------------
static boolean pass = true;
static void test(String name, int N, int minRatio) {
// Worst case: all-unique header names — maximises the list-scan cost
// because each new header must scan all prior entries and finds nothing
String[] headers = new String[N];
for (int i = 0; i < N; i++) {
headers[i] = "X-Header-" + i;
}
slowDedup(headers);
fastDedup(headers);
long sOps = slowOps;
long fOps = fastOps;
double ratio = (fOps > 0) ? (double) sOps / fOps : sOps;
boolean ok = ratio >= minRatio;
if (!ok) pass = false;
System.out.printf("%-40s N=%4d slow=%7d fast=%4d ratio=%6.1fx %s%n",
name, N, sOps, fOps, ratio, ok ? "PASS" : "FAIL");
}
static void testCorrectness() {
// Verify both algorithms produce same number of unique keys
String[] input = {"Cookie", "Cookie", "Accept", "X-Foo", "Cookie", "Accept"};
// Slow
slowOps = 0;
ListEntry head = null, tail = null;
for (String h : input) {
ListEntry found = listFind(head, h);
if (found != null) { found.value += ",v"; }
else {
ListEntry e = new ListEntry(h, h);
if (head == null) { head = tail = e; }
else { tail.next = e; tail = e; }
}
}
int slowUnique = 0;
for (ListEntry c = head; c != null; c = c.next) slowUnique++;
// Fast
fastOps = 0;
HashMap<String, String> map = new HashMap<>();
for (String h : input) {
fastOps++;
String lk = h.toLowerCase();
if (map.containsKey(lk)) map.put(lk, map.get(lk) + ",v");
else map.put(lk, h);
}
int fastUnique = map.size();
boolean ok = slowUnique == fastUnique && slowUnique == 3;
if (!ok) pass = false;
System.out.printf("%-40s correctness: slow=%d fast=%d unique %s%n",
"correctness-check", slowUnique, fastUnique, ok ? "PASS" : "FAIL");
}
public static void main(String[] args) {
System.out.println("uwsgi-0001: HTTP header dedup O(H^2) -> O(H)");
System.out.println("=".repeat(72));
testCorrectness();
test("all-unique/N=50", 50, 5);
test("all-unique/N=100", 100, 20);
test("all-unique/N=200", 200, 50);
test("all-unique/N=500", 500, 100);
System.out.println("=".repeat(72));
if (!pass) {
System.out.println("RESULT: FAIL");
System.exit(1);
}
System.out.println("RESULT: PASS");
}
}