openssl-0004 + uwsgi-0001: 2 new defects (500x/249x); all 10 missing whitepaper entries restored; count 590→592
This commit is contained in:
parent
2e4f7807d5
commit
34e8d9212f
18 changed files with 1366 additions and 4 deletions
151
defects/uwsgi/unit/UwsgiHeaderDedupAlgorithm.java
Normal file
151
defects/uwsgi/unit/UwsgiHeaderDedupAlgorithm.java
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
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");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue