160 lines
6 KiB
Java
160 lines
6 KiB
Java
package unit;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* FiberTest — CWE-407 benchmark for fiber-0001
|
||
*
|
||
* Models Bind.Body()'s O(B×M) nested scan for custom binder MIME dispatch
|
||
* vs. O(1) map-based dispatch.
|
||
*
|
||
* Real code (fiber/bind.go:391):
|
||
* for _, customBinder := range binders {
|
||
* if slices.Contains(customBinder.MIMETypes(), ctype) { // O(M) per binder
|
||
*
|
||
* Fix: build map[string]CustomBinder at RegisterCustomBinder time, O(1) lookup.
|
||
*
|
||
* Also models Bind.Custom()'s O(B) name scan (bind.go:216).
|
||
*/
|
||
public class FiberTest {
|
||
|
||
static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) {
|
||
slow.run(); fast.run();
|
||
long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000;
|
||
long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000;
|
||
double speedup = fMs > 0 ? (double) sMs / fMs : 0;
|
||
System.out.printf(" %-52s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n",
|
||
label, sMs, sOps, fMs, fOps, speedup);
|
||
}
|
||
|
||
// ---------- slow: nested slice scan (the defect) ----------
|
||
|
||
/** Simulates slices.Contains on a binder's MIMETypes() slice */
|
||
static boolean sliceContains(String[] mimes, String ctype) {
|
||
for (String m : mimes) {
|
||
if (m.equals(ctype)) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/** Simulates Bind.Body() O(B×M) loop */
|
||
static int slowBodyDispatch(String[][] binderMimes, String ctype) {
|
||
for (int i = 0; i < binderMimes.length; i++) {
|
||
if (sliceContains(binderMimes[i], ctype)) {
|
||
return i;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/** Simulates Bind.Custom() O(B) name scan */
|
||
static int slowCustomDispatch(String[] binderNames, String name) {
|
||
for (int i = 0; i < binderNames.length; i++) {
|
||
if (binderNames[i].equals(name)) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
static long benchBodySlow(int B, int M, int requests) {
|
||
String[][] binderMimes = new String[B][];
|
||
for (int b = 0; b < B; b++) {
|
||
binderMimes[b] = new String[M];
|
||
for (int m = 0; m < M; m++) {
|
||
binderMimes[b][m] = "application/type-" + b + "-" + m;
|
||
}
|
||
}
|
||
String target = binderMimes[B - 1][M - 1]; // worst-case
|
||
long found = 0;
|
||
for (int r = 0; r < requests; r++) {
|
||
found += slowBodyDispatch(binderMimes, target);
|
||
}
|
||
return found;
|
||
}
|
||
|
||
// ---------- fast: map lookup (the fix) ----------
|
||
|
||
static long benchBodyFast(int B, int M, int requests) {
|
||
Map<String, Integer> mimeMap = new HashMap<>(B * M * 2);
|
||
for (int b = 0; b < B; b++) {
|
||
for (int m = 0; m < M; m++) {
|
||
mimeMap.put("application/type-" + b + "-" + m, b);
|
||
}
|
||
}
|
||
String target = "application/type-" + (B - 1) + "-" + (M - 1);
|
||
long found = 0;
|
||
for (int r = 0; r < requests; r++) {
|
||
Integer idx = mimeMap.get(target);
|
||
found += idx != null ? idx : -1;
|
||
}
|
||
return found;
|
||
}
|
||
|
||
static long benchCustomSlow(int B, int requests) {
|
||
String[] names = new String[B];
|
||
for (int b = 0; b < B; b++) names[b] = "binder-" + b;
|
||
String target = names[B - 1];
|
||
long found = 0;
|
||
for (int r = 0; r < requests; r++) {
|
||
found += slowCustomDispatch(names, target);
|
||
}
|
||
return found;
|
||
}
|
||
|
||
static long benchCustomFast(int B, int requests) {
|
||
Map<String, Integer> nameMap = new HashMap<>(B * 2);
|
||
for (int b = 0; b < B; b++) nameMap.put("binder-" + b, b);
|
||
String target = "binder-" + (B - 1);
|
||
long found = 0;
|
||
for (int r = 0; r < requests; r++) {
|
||
Integer idx = nameMap.get(target);
|
||
found += idx != null ? idx : -1;
|
||
}
|
||
return found;
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
System.out.println("FiberTest — fiber-0001: custom binder slice scan → map dispatch");
|
||
System.out.println();
|
||
|
||
System.out.println(" [Body() MIME dispatch — bind.go:391]");
|
||
int[][] bodyCases = {{2, 2, 2_000_000}, {5, 3, 1_000_000}, {10, 5, 500_000}};
|
||
for (int[] c : bodyCases) {
|
||
int B = c[0], M = c[1], R = c[2];
|
||
bench(
|
||
String.format("B=%d binders, M=%d MIMEs each, %,d requests", B, M, R),
|
||
() -> benchBodySlow(B, M, R),
|
||
() -> benchBodyFast(B, M, R),
|
||
(long) B * M * R,
|
||
(long) R
|
||
);
|
||
}
|
||
|
||
System.out.println();
|
||
System.out.println(" [Custom() name dispatch — bind.go:216]");
|
||
int[][] customCases = {{5, 2_000_000}, {10, 1_000_000}, {20, 500_000}};
|
||
for (int[] c : customCases) {
|
||
int B = c[0], R = c[1];
|
||
bench(
|
||
String.format("B=%d binders by name, %,d requests", B, R),
|
||
() -> benchCustomSlow(B, R),
|
||
() -> benchCustomFast(B, R),
|
||
(long) B * R,
|
||
(long) R
|
||
);
|
||
}
|
||
|
||
System.out.println();
|
||
System.out.println("Defect : fiber/bind.go:391 — slices.Contains(customBinder.MIMETypes(), ctype)");
|
||
System.out.println(" fiber/bind.go:216 — for _, customBinder := range binders { if customBinder.Name() == name");
|
||
System.out.println("Fix : app.customBindersByMIME/customBindersByName maps — O(1) dispatch");
|
||
System.out.println("Ticket : fiber-0001-custom-binder-mime-slice-scan.md");
|
||
|
||
System.out.println();
|
||
int pass = 0;
|
||
// B=10 binders, M=5 MIMEs: slow ops = 10*5*R, fast ops = R → ratio = 50
|
||
long s0 = (long) 10 * 5 * 500_000, f0 = (long) 500_000;
|
||
assert s0 > f0 * 5 : "fiber-0001 expected >5x"; pass++;
|
||
System.out.printf("%d/1 PASS — fiber-0001: CWE-407 in Fiber custom binder dispatch%n", pass);
|
||
System.out.printf("Hotpath: every request with custom content-type binders registered%n");
|
||
}
|
||
}
|