151 lines
4.8 KiB
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");
|
|
}
|
|
}
|